2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "luci/Pass/RequantizePass.h"
18 #include "QuantizationUtils.h"
20 #include <luci/IR/CircleNodes.h>
21 #include <luci/IR/CircleNodeVisitor.h>
24 #include <oops/UserExn.h>
35 // Requantize Non-const node from int8 to uint8
36 // Original values: -128 ~ 127
37 // After requantization: 0 ~ 255
38 void requant_nonconst_int8_to_uint8(CircleNode *circle_node)
40 assert(circle_node->dtype() == loco::DataType::S8);
42 auto quantparam = circle_node->quantparam();
43 assert(quantparam != nullptr);
44 for (size_t i = 0; i < quantparam->zerop.size(); ++i)
46 quantparam->zerop[i] += 128;
48 circle_node->dtype(loco::DataType::U8);
51 // Requantize CircleConst from symmetric int8 to asymmetric uint8
52 // Original values: -127 ~ 127
53 // After requantization: 1 ~ 255 (zp <- zp + 128)
54 void requant_const_int8_to_uint8(CircleConst *node)
56 assert(node->dtype() == loco::DataType::S8);
58 uint32_t size = node->size<loco::DataType::S8>();
59 std::vector<int32_t> requantized_values(size);
60 for (uint32_t i = 0; i < size; ++i)
62 int32_t data = node->at<loco::DataType::S8>(i);
63 requantized_values[i] = data + 128;
66 node->dtype(loco::DataType::U8); // change the type of tensor
67 node->size<loco::DataType::U8>(size);
68 for (uint32_t i = 0; i < size; ++i)
70 assert(1 <= requantized_values[i] && requantized_values[i] <= 255);
71 node->at<loco::DataType::U8>(i) = requantized_values[i];
74 auto quantparam = node->quantparam();
75 assert(quantparam != nullptr);
76 for (size_t i = 0; i < quantparam->zerop.size(); ++i)
78 quantparam->zerop[i] += 128;
82 #define RETURN_UNLESS(cond) \
87 * @brief Requantize int8 quantized tensors to uint8 tensors
89 struct RequantizeS8ToU8 final : public luci::CircleNodeMutableVisitor<void>
91 // Requantize non-const tensors
92 void visit(luci::CircleNode *node)
95 INFO(l) << "RequantizeS8ToU8 visit non-const node: " << node->name() << std::endl;
97 // Ignore non-quantized tensors
98 RETURN_UNLESS(node->quantparam() != nullptr);
100 // Check dtype is int8
101 RETURN_UNLESS(node->dtype() == loco::DataType::S8);
103 requant_nonconst_int8_to_uint8(node);
106 // Requantize const tensors
107 void visit(luci::CircleConst *node)
110 INFO(l) << "RequantizeS8ToU8 visit const node: " << node->name() << std::endl;
112 // Ignore non-quantized tensors
113 RETURN_UNLESS(node->quantparam() != nullptr);
115 // Check dtype is int8
116 RETURN_UNLESS(node->dtype() == loco::DataType::S8);
118 requant_const_int8_to_uint8(node);
126 bool RequantizePass::run(loco::Graph *g)
129 INFO(l) << "RequantizePass Start" << std::endl;
132 // Output: uint8 model
133 if (_input_dtype == loco::DataType::S8 and _output_dtype == loco::DataType::U8)
135 for (auto node : loco::active_nodes(loco::output_nodes(g)))
138 auto circle_node = loco::must_cast<luci::CircleNode *>(node);
139 circle_node->accept(&rq);
144 // Ignore other cases
148 // Update output dtype
149 auto graph_outputs = g->outputs();
150 for (auto node : loco::output_nodes(g))
152 auto circle_node = loco::must_cast<luci::CircleOutput *>(node);
153 auto from_node = loco::must_cast<luci::CircleNode *>(circle_node->from());
154 if (from_node->dtype() == _output_dtype)
156 circle_node->dtype(_output_dtype);
157 auto graph_output = graph_outputs->at(circle_node->index());
158 graph_output->dtype(_output_dtype);
162 INFO(l) << "RequantizePass End" << std::endl;
163 return false; // one time run