2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. 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 <foder/FileLoader.h>
19 #include <luci/Importer.h>
20 #include <luci/CircleOptimizer.h>
21 #include <luci/Service/Validate.h>
22 #include <luci/CircleExporter.h>
23 #include <luci/CircleFileExpContract.h>
24 #include <luci/UserSettings.h>
26 #include <oops/InternalExn.h>
27 #include <arser/arser.h>
28 #include <vconone/vconone.h>
35 using OptionHook = std::function<int(const char **)>;
37 using Algorithms = luci::CircleOptimizer::Options::Algorithm;
38 using AlgorithmParameters = luci::CircleOptimizer::Options::AlgorithmParameters;
40 void print_exclusive_options(void)
42 std::cout << "Use only one of the 3 options below." << std::endl;
43 std::cout << " --quantize_dequantize_weights" << std::endl;
44 std::cout << " --quantize_with_minmax" << std::endl;
45 std::cout << " --requantize" << std::endl;
46 std::cout << " --force_quantparam" << std::endl;
49 void print_version(void)
51 std::cout << "circle-quantizer version " << vconone::get_string() << std::endl;
52 std::cout << vconone::get_copyright() << std::endl;
55 int entry(int argc, char **argv)
57 // Simple argument parser (based on map)
58 std::map<std::string, OptionHook> argparse;
59 luci::CircleOptimizer optimizer;
61 auto options = optimizer.options();
62 auto settings = luci::UserSettings::settings();
64 const std::string qdqw = "--quantize_dequantize_weights";
65 const std::string qwmm = "--quantize_with_minmax";
66 const std::string rq = "--requantize";
67 const std::string fq = "--force_quantparam";
69 const std::string gpd = "--generate_profile_data";
71 arser::Arser arser("circle-quantizer provides circle model quantization");
73 arser.add_argument("--version")
77 .help("Show version information and exit")
78 .exit_with(print_version);
80 arser.add_argument("-V", "--verbose")
84 .help("output additional information to stdout or stderr");
86 arser.add_argument(qdqw)
88 .type(arser::DataType::STR_VEC)
90 .help("Quantize-dequantize weight values required action before quantization. "
91 "Three arguments required: input_model_dtype(float32) "
92 "output_model_dtype(uint8) granularity(layer, channel)");
94 arser.add_argument(qwmm)
96 .type(arser::DataType::STR_VEC)
98 .help("Quantize with min/max values. "
99 "Three arguments required: input_model_dtype(float32) "
100 "output_model_dtype(uint8) granularity(layer, channel)");
102 arser.add_argument(rq)
104 .type(arser::DataType::STR_VEC)
106 .help("Requantize a quantized model. "
107 "Two arguments required: input_model_dtype(int8) "
108 "output_model_dtype(uint8)");
110 arser.add_argument(fq)
112 .type(arser::DataType::STR_VEC)
115 .help("Write quantization parameters to the specified tensor. "
116 "Three arguments required: tensor_name(string), "
117 "scale(float) zero_point(int)");
119 arser.add_argument("--input_type")
121 .type(arser::DataType::STR)
123 .help("Input type of quantized model (uint8 or int16)");
125 arser.add_argument("--output_type")
127 .type(arser::DataType::STR)
129 .help("Output type of quantized model (uint8 or int16)");
131 arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
132 arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
134 arser.add_argument(gpd).nargs(0).required(false).default_value(false).help(
135 "This will turn on profiling data generation.");
139 arser.parse(argc, argv);
141 catch (const std::runtime_error &err)
143 std::cerr << err.what() << std::endl;
149 // only one of qdqw, qwmm, rq, fq option can be used
150 int32_t opt_used = arser[qdqw] ? 1 : 0;
151 opt_used += arser[qwmm] ? 1 : 0;
152 opt_used += arser[rq] ? 1 : 0;
153 opt_used += arser[fq] ? 1 : 0;
156 print_exclusive_options();
161 if (arser.get<bool>("--verbose"))
163 // The third parameter of setenv means REPLACE.
164 // If REPLACE is zero, it does not overwrite an existing value.
165 setenv("LUCI_LOG", "100", 0);
170 auto values = arser.get<std::vector<std::string>>(qdqw);
171 if (values.size() != 3)
176 options->enable(Algorithms::QuantizeDequantizeWeights);
178 options->param(AlgorithmParameters::Quantize_input_model_dtype, values.at(0));
179 options->param(AlgorithmParameters::Quantize_output_model_dtype, values.at(1));
180 options->param(AlgorithmParameters::Quantize_granularity, values.at(2));
185 auto values = arser.get<std::vector<std::string>>(qwmm);
186 if (values.size() != 3)
191 options->enable(Algorithms::QuantizeWithMinMax);
193 options->param(AlgorithmParameters::Quantize_input_model_dtype, values.at(0));
194 options->param(AlgorithmParameters::Quantize_output_model_dtype, values.at(1));
195 options->param(AlgorithmParameters::Quantize_granularity, values.at(2));
197 if (arser["--input_type"])
198 options->param(AlgorithmParameters::Quantize_input_type,
199 arser.get<std::string>("--input_type"));
201 if (arser["--output_type"])
202 options->param(AlgorithmParameters::Quantize_output_type,
203 arser.get<std::string>("--output_type"));
208 auto values = arser.get<std::vector<std::string>>(rq);
209 if (values.size() != 2)
214 options->enable(Algorithms::Requantize);
216 options->param(AlgorithmParameters::Quantize_input_model_dtype, values.at(0));
217 options->param(AlgorithmParameters::Quantize_output_model_dtype, values.at(1));
222 auto values = arser.get<std::vector<std::vector<std::string>>>(fq);
224 std::vector<std::string> tensors;
225 std::vector<std::string> scales;
226 std::vector<std::string> zero_points;
228 for (auto const value : values)
230 if (value.size() != 3)
236 tensors.push_back(value[0]);
237 scales.push_back(value[1]);
238 zero_points.push_back(value[2]);
241 options->enable(Algorithms::ForceQuantParam);
243 options->params(AlgorithmParameters::Quantize_tensor_names, tensors);
244 options->params(AlgorithmParameters::Quantize_scales, scales);
245 options->params(AlgorithmParameters::Quantize_zero_points, zero_points);
248 std::string input_path = arser.get<std::string>("input");
249 std::string output_path = arser.get<std::string>("output");
252 settings->set(luci::UserSettings::Key::ProfilingDataGen, true);
254 // Load model from the file
255 foder::FileLoader file_loader{input_path};
256 std::vector<char> model_data = file_loader.load();
258 // Verify flatbuffers
259 flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
260 if (!circle::VerifyModelBuffer(verifier))
262 std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
266 const circle::Model *circle_model = circle::GetModel(model_data.data());
267 if (circle_model == nullptr)
269 std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
273 // Import from input Circle file
274 luci::Importer importer;
275 auto module = importer.importModule(circle_model);
277 for (size_t idx = 0; idx < module->size(); ++idx)
279 auto graph = module->graph(idx);
281 // quantize the graph
282 optimizer.quantize(graph);
284 if (!luci::validate(graph))
286 std::cerr << "ERROR: Quantized graph is invalid" << std::endl;
291 // Export to output Circle file
292 luci::CircleExporter exporter;
294 luci::CircleFileExpContract contract(module.get(), output_path);
296 if (!exporter.invoke(&contract))
298 std::cerr << "ERROR: Failed to export '" << output_path << "'" << std::endl;