2 ''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # '''
3 ''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
4 ''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
5 ''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
8 # Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
10 # Licensed under the Apache License, Version 2.0 (the "License");
11 # you may not use this file except in compliance with the License.
12 # You may obtain a copy of the License at
14 # http://www.apache.org/licenses/LICENSE-2.0
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS,
18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 # See the License for the specific language governing permissions and
20 # limitations under the License.
28 import utils as _utils
30 # TODO Find better way to suppress trackback on error
31 sys.tracebacklimit = 0
35 parser = argparse.ArgumentParser(
36 description='command line tool to quantize circle model')
38 _utils._add_default_arg(parser)
40 # input and output path.
42 '-i', '--input_path', type=str, help='full filepath of the input file')
48 'full filepath of the input data file. if not specified, run with random input data.'
52 '--input_data_format',
55 'file format of input data. h5/hdf5 (default), list/filelist (a text file where a file path of input data is written in each line), or dir/directory (a directory where input data are saved)'
58 '-o', '--output_path', type=str, help='full filepath of the output file')
60 # argument for profiling
63 '--generate_profile_data',
65 help='generate profiling data')
67 ## arguments for quantization
68 quantization_group = parser.add_argument_group('arguments for quantization')
70 quantization_group.add_argument(
73 help='input data type (supported: float32, default=float32)')
74 quantization_group.add_argument(
77 help='output quantized data type (supported: uint8, int16, default=uint8)')
78 quantization_group.add_argument(
81 help='quantize granularity (supported: layer, channel, default=layer)')
82 quantization_group.add_argument(
83 '--min_percentile', type=str, help='minimum percentile (0.0~100.0, default=1.0)')
84 quantization_group.add_argument(
85 '--max_percentile', type=str, help='maximum percentile (0.0~100.0, default=99.0)')
86 quantization_group.add_argument(
89 help='record mode (supported: percentile/moving_average, default=percentile)')
91 # arguments for force_quantparam
95 help='write quantparam to the specified tensor')
97 '--tensor_name', type=str, action='append', help='tensor name (string)')
98 parser.add_argument('--scale', type=float, action='append', help='scale (float)')
100 '--zero_point', type=int, action='append', help='zero point (int)')
105 def _set_default_values(args):
106 if not _utils._is_valid_attr(args, 'input_dtype'):
107 setattr(args, 'input_dtype', 'float32')
108 if not _utils._is_valid_attr(args, 'quantized_dtype'):
109 setattr(args, 'quantized_dtype', 'uint8')
110 if not _utils._is_valid_attr(args, 'granularity'):
111 setattr(args, 'granularity', 'layer')
112 if not _utils._is_valid_attr(args, 'mode'):
113 setattr(args, 'mode', 'percentile')
114 if not _utils._is_valid_attr(args, 'min_percentile'):
115 setattr(args, 'min_percentile', '1.0')
116 if not _utils._is_valid_attr(args, 'max_percentile'):
117 setattr(args, 'max_percentile', '99.0')
120 def _verify_arg(parser, args):
121 """verify given arguments"""
122 # check if required arguments is given
124 if not _utils._is_valid_attr(args, 'input_path'):
125 missing.append('-i/--input_path')
126 if not _utils._is_valid_attr(args, 'output_path'):
127 missing.append('-o/--output_path')
128 if _utils._is_valid_attr(args, 'force_quantparam'):
129 if not _utils._is_valid_attr(args, 'tensor_name'):
130 missing.append('--tensor_name')
131 if not _utils._is_valid_attr(args, 'scale'):
132 missing.append('--scale')
133 if not _utils._is_valid_attr(args, 'zero_point'):
134 missing.append('--zero_point')
136 parser.error('the following arguments are required: ' + ' '.join(missing))
137 if _utils._is_valid_attr(args, 'force_quantparam'):
138 tensors = getattr(args, 'tensor_name')
139 scales = getattr(args, 'scale')
140 zerops = getattr(args, 'zero_point')
141 if len(tensors) != len(scales) or len(tensors) != len(zerops):
143 'The same number of tensor_name, scale, and zero_point should be given.')
146 def _parse_arg(parser):
147 args = parser.parse_args()
150 _utils._print_version_and_exit(__file__)
156 if _utils._is_valid_attr(args, 'force_quantparam'):
157 # write quantization parameters
161 # get file path to log
162 dir_path = os.path.dirname(os.path.realpath(__file__))
163 logfile_path = os.path.realpath(args.output_path) + '.log'
165 with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir:
167 circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer')
168 record_minmax_path = os.path.join(dir_path, 'record-minmax')
170 ## make a command to quantize and dequantize the weights of the model
171 circle_quantizer_cmd = [circle_quantizer_path]
173 if _utils._is_valid_attr(args, 'verbose'):
174 circle_quantizer_cmd.append('--verbose')
175 # quantize_dequantize_weights
176 circle_quantizer_cmd.append('--quantize_dequantize_weights')
177 if _utils._is_valid_attr(args, 'input_dtype'):
178 circle_quantizer_cmd.append(getattr(args, 'input_dtype'))
179 if _utils._is_valid_attr(args, 'quantized_dtype'):
180 circle_quantizer_cmd.append(getattr(args, 'quantized_dtype'))
181 if _utils._is_valid_attr(args, 'granularity'):
182 circle_quantizer_cmd.append(getattr(args, 'granularity'))
183 # input and output path
184 if _utils._is_valid_attr(args, 'input_path'):
185 circle_quantizer_cmd.append(getattr(args, 'input_path'))
186 tmp_output_path_1 = os.path.join(
188 os.path.splitext(os.path.basename(args.input_path))[0]) + '1.circle'
189 circle_quantizer_cmd.append(tmp_output_path_1)
191 if _utils._is_valid_attr(args, 'generate_profile_data'):
192 circle_quantizer_cmd.append('--generate_profile_data')
194 f.write((' '.join(circle_quantizer_cmd) + '\n').encode())
196 # run circle-quantizer
197 _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f)
199 ## make a command to record min-max value of each tensor while running the representative dataset
200 circle_record_minmax_cmd = [record_minmax_path]
202 if _utils._is_valid_attr(args, 'verbose'):
203 circle_record_minmax_cmd.append('--verbose')
204 # input and output path
205 circle_record_minmax_cmd.append('--input_model')
206 circle_record_minmax_cmd.append(tmp_output_path_1)
207 tmp_output_path_2 = os.path.join(
209 os.path.splitext(os.path.basename(args.input_path))[0]) + '2.circle'
210 circle_record_minmax_cmd.append('--output_model')
211 circle_record_minmax_cmd.append(tmp_output_path_2)
213 if _utils._is_valid_attr(args, 'input_data'):
214 circle_record_minmax_cmd.append('--input_data')
215 circle_record_minmax_cmd.append(getattr(args, 'input_data'))
216 if _utils._is_valid_attr(args, 'input_data_format'):
217 circle_record_minmax_cmd.append('--input_data_format')
218 circle_record_minmax_cmd.append(getattr(args, 'input_data_format'))
219 # min and max percentile
220 if _utils._is_valid_attr(args, 'min_percentile'):
221 circle_record_minmax_cmd.append('--min_percentile')
222 circle_record_minmax_cmd.append(getattr(args, 'min_percentile'))
223 if _utils._is_valid_attr(args, 'max_percentile'):
224 circle_record_minmax_cmd.append('--max_percentile')
225 circle_record_minmax_cmd.append(getattr(args, 'max_percentile'))
227 if _utils._is_valid_attr(args, 'mode'):
228 circle_record_minmax_cmd.append('--mode')
229 circle_record_minmax_cmd.append(getattr(args, 'mode'))
231 if _utils._is_valid_attr(args, 'generate_profile_data'):
232 circle_record_minmax_cmd.append('--generate_profile_data')
234 f.write((' '.join(circle_record_minmax_cmd) + '\n').encode())
237 _utils._run(circle_record_minmax_cmd, err_prefix="record_minmax", logfile=f)
239 ## make a second command to quantize the model using the embedded information
240 circle_quantizer_cmd = [circle_quantizer_path]
242 if _utils._is_valid_attr(args, 'verbose'):
243 circle_quantizer_cmd.append('--verbose')
244 # quantize_dequantize_weights
245 circle_quantizer_cmd.append('--quantize_with_minmax')
246 if _utils._is_valid_attr(args, 'input_dtype'):
247 circle_quantizer_cmd.append(getattr(args, 'input_dtype'))
248 if _utils._is_valid_attr(args, 'quantized_dtype'):
249 circle_quantizer_cmd.append(getattr(args, 'quantized_dtype'))
250 if _utils._is_valid_attr(args, 'granularity'):
251 circle_quantizer_cmd.append(getattr(args, 'granularity'))
252 # input and output path
253 circle_quantizer_cmd.append(tmp_output_path_2)
254 if _utils._is_valid_attr(args, 'output_path'):
255 circle_quantizer_cmd.append(getattr(args, 'output_path'))
257 if _utils._is_valid_attr(args, 'generate_profile_data'):
258 circle_quantizer_cmd.append('--generate_profile_data')
260 f.write((' '.join(circle_quantizer_cmd) + '\n').encode())
262 # run circle-quantizer
263 _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f)
266 def _write_qparam(args):
267 # get file path to log
268 dir_path = os.path.dirname(os.path.realpath(__file__))
269 logfile_path = os.path.realpath(args.output_path) + '.log'
271 with open(logfile_path, 'wb') as f:
273 circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer')
275 # make a command to write qparams to the tensors
276 circle_quantizer_cmd = [circle_quantizer_path]
278 if _utils._is_valid_attr(args, 'verbose'):
279 circle_quantizer_cmd.append('--verbose')
280 if _utils._is_valid_attr(args, 'tensor_name'):
281 tensor_name = getattr(args, 'tensor_name')
282 if _utils._is_valid_attr(args, 'scale'):
283 scale = getattr(args, 'scale')
284 if _utils._is_valid_attr(args, 'zero_point'):
285 zero_point = getattr(args, 'zero_point')
286 for (t, s, zp) in zip(tensor_name, scale, zero_point):
287 circle_quantizer_cmd.append('--force_quantparam')
288 circle_quantizer_cmd.append(t)
289 circle_quantizer_cmd.append(str(s))
290 circle_quantizer_cmd.append(str(zp))
291 # input and output path
292 if _utils._is_valid_attr(args, 'input_path'):
293 circle_quantizer_cmd.append(getattr(args, 'input_path'))
294 if _utils._is_valid_attr(args, 'output_path'):
295 circle_quantizer_cmd.append(getattr(args, 'output_path'))
297 f.write((' '.join(circle_quantizer_cmd) + '\n').encode())
299 # run circle-quantizer
300 _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f)
305 parser = _get_parser()
306 args = _parse_arg(parser)
308 # parse configuration file
309 _utils._parse_cfg(args, 'one-quantize')
312 _set_default_values(args)
315 _verify_arg(parser, args)
321 if __name__ == '__main__':
322 _utils._safemain(main, __file__)