Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / samples / calibration_tool / main.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 /**
6  * @brief The entry point for Inference Engine validation application
7  * @file validation_app/main.cpp
8  */
9 #include <gflags/gflags.h>
10 #include <algorithm>
11 #include <functional>
12 #include <iostream>
13 #include <map>
14 #include <fstream>
15 #include <random>
16 #include <string>
17 #include <tuple>
18 #include <vector>
19 #include <limits>
20 #include <iomanip>
21 #include <memory>
22
23 #include <ext_list.hpp>
24
25 #include <samples/common.hpp>
26 #include <samples/slog.hpp>
27
28 #include "user_exception.hpp"
29 #include "calibrator_processors.h"
30 #include "SSDObjectDetectionProcessor.hpp"
31 #include "YOLOObjectDetectionProcessor.hpp"
32 #include "ie_icnn_network_stats.hpp"
33 #include "details/caseless.hpp"
34
35 using namespace std;
36 using namespace InferenceEngine;
37 using namespace InferenceEngine::details;
38
39 using InferenceEngine::details::InferenceEngineException;
40
41 /// @brief Message for help argument
42 static const char help_message[] = "Print a help message";
43 /// @brief Message for images argument
44 static const char image_message[] = "Required. Path to a directory with validation images. For Classification models, the directory must contain"
45                                     " folders named as labels with images inside or a .txt file with"
46                                     " a list of images. For Object Detection models, the dataset must be in"
47                                     " VOC format.";
48 /// @brief Message for plugin_path argument
49 static const char plugin_path_message[] = "Path to a plugin folder";
50 /// @brief message for model argument
51 static const char model_message[] = "Required. Path to an .xml file with a trained model, including model name and "
52                                     "extension.";
53 /// @brief Message for plugin argument
54 static const char plugin_message[] = "Plugin name. For example, CPU. If this parameter is passed, "
55                                      "the sample looks for a specified plugin only.";
56 /// @brief Message for assigning cnn calculation to device
57 static const char target_device_message[] = "Target device to infer on: CPU (default), GPU, FPGA, HDDL or MYRIAD."
58                                             " The application looks for a suitable plugin for the specified device.";
59 /// @brief Message for label argument
60 static const char label_message[] = "Path to a file with labels for a model";
61 /// @brief M`essage for batch argumenttype
62 static const char batch_message[] = "Batch size value. If not specified, the batch size value is taken from IR";
63 /// @brief Message for dump argument
64 static const char dump_message[] = "Dump file names and inference results to a .csv file";
65 /// @brief Message for network type
66 static const char type_message[] = "Type of an inferred network (\"C\" by default)";
67 /// @brief Message for pp-type
68 static const char preprocessing_type[] = "Preprocessing type. Options: \"None\", \"Resize\", \"ResizeCrop\"";
69 /// @brief Message for pp-crop-size
70 static const char preprocessing_size[] = "Preprocessing size (used with ppType=\"ResizeCrop\")";
71 static const char preprocessing_width[] = "Preprocessing width (overrides -ppSize, used with ppType=\"ResizeCrop\")";
72 static const char preprocessing_height[] = "Preprocessing height (overrides -ppSize, used with ppType=\"ResizeCrop\")";
73
74 static const char obj_detection_annotations_message[] = "Required for Object Detection models. Path to a directory"
75                                                         " containing an .xml file with annotations for images.";
76
77 static const char obj_detection_classes_message[] = "Required for Object Detection models. Path to a file with"
78                                                     " a list of classes";
79
80 static const char obj_detection_subdir_message[] = "Directory between the path to images (specified with -i) and image name (specified in the"
81                                                    " .xml file). For VOC2007 dataset, use JPEGImages.";
82
83 static const char obj_detection_kind_message[] = "Type of an Object Detection model. Options: SSD";
84
85 /// @brief Message for GPU custom kernels desc
86 static const char custom_cldnn_message[] = "Required for GPU custom kernels. "
87                                            "Absolute path to an .xml file with the kernel descriptions.";
88
89 /// @brief Message for user library argument
90 static const char custom_cpu_library_message[] = "Required for CPU custom layers. "
91                                                  "Absolute path to a shared library with the kernel implementations.";
92 /// @brief Message for labels file
93 static const char labels_file_message[] = "Labels file path. The labels file contains names of the dataset classes";
94
95 static const char zero_background_message[] = "\"Zero is a background\" flag. Some networks are trained with a modified"
96                                               " dataset where the class IDs "
97                                               " are enumerated from 1, but 0 is an undefined \"background\" class"
98                                               " (which is never detected)";
99
100 static const char stream_output_message[] = "Flag for printing progress as a plain text. When used, interactive progress"
101                                             " bar is replaced with multiline output";
102
103 static const char convert_fc_message[] = "Convert FullyConnected layers to Int8 or not (false by default)";
104
105
106 /// @brief Network type options and their descriptions
107 static const char* types_descriptions[][2] = {
108     { "C", "calibrate Classification network and write the calibrated network to IR" },
109 //    { "SS", "semantic segmentation" },    // Not supported yet
110     { "OD", "calibrate Object Detection network and write the calibrated network to IR" },
111     { "RawC", "collect only statistics for Classification network and write statistics to IR. With this option, a model is not calibrated. For calibration "
112               "and statisctics collection, use \"-t C\" instead." },
113     { "RawOD", "collect only statistics for Object Detection network and write statistics to IR. With this option, a model is not calibrated. For calibration "
114                "and statisctics collection, use \"-t OD\" instead" },
115     { nullptr, nullptr }
116 };
117
118 static const char accuracy_threshold_message[] = "Threshold for a maximum accuracy drop of quantized model."
119                                                  " Must be an integer number (percents)"
120                                                  " without a percent sign. Default value is 1, which stands for accepted"
121                                                  " accuracy drop in 1%";
122 static const char number_of_pictures_message[] = "Number of pictures from the whole validation set to"
123                                                  "create the calibration dataset. Default value is 0, which stands for"
124                                                  "the whole provided dataset";
125 static const char output_model_name[] = "Output name for calibrated model. Default is <original_model_name>_i8.xml|bin";
126
127 /// @brief Define flag for showing help message <br>
128 DEFINE_bool(h, false, help_message);
129 /// @brief Define parameter for a path to images <br>
130 /// It is a required parameter
131 DEFINE_string(i, "", image_message);
132 /// @brief Define parameter for a path to model file <br>
133 /// It is a required parameter
134 DEFINE_string(m, "", model_message);
135 /// @brief Define parameter for a plugin name <br>
136 /// It is a required parameter
137 DEFINE_string(p, "", plugin_message);
138 /// @brief Define parameter for a path to a file with labels <br>
139 /// Default is empty
140 DEFINE_string(OCl, "", label_message);
141 /// @brief Define parameter for a path to plugins <br>
142 /// Default is ./lib
143 DEFINE_string(pp, "", plugin_path_message);
144 /// @brief Define paraneter for a target device to infer on <br>
145 DEFINE_string(d, "CPU", target_device_message);
146 /// @brief Define parameter for batch size <br>
147 /// Default is 0 (which means that batch size is not specified)
148 DEFINE_int32(b, 0, batch_message);
149 /// @brief Define flag to dump results to a file <br>
150 DEFINE_bool(dump, false, dump_message);
151 /// @brief Define parameter for a network type
152 DEFINE_string(t, "C", type_message);
153
154 /// @brief Define parameter for preprocessing type
155 DEFINE_string(ppType, "", preprocessing_type);
156
157 /// @brief Define parameter for preprocessing size
158 DEFINE_int32(ppSize, 0, preprocessing_size);
159 DEFINE_int32(ppWidth, 0, preprocessing_width);
160 DEFINE_int32(ppHeight, 0, preprocessing_height);
161
162 DEFINE_bool(Czb, false, zero_background_message);
163
164 DEFINE_string(ODa, "", obj_detection_annotations_message);
165
166 DEFINE_string(ODc, "", obj_detection_classes_message);
167
168 DEFINE_string(ODsubdir, "", obj_detection_subdir_message);
169
170 /// @brief Define parameter for a type of Object Detection network
171 DEFINE_string(ODkind, "SSD", obj_detection_kind_message);
172
173 /// @brief Define parameter for GPU kernels path <br>
174 /// Default is ./lib
175 DEFINE_string(c, "", custom_cldnn_message);
176
177 /// @brief Define parameter for a path to CPU library with user layers <br>
178 /// It is an optional parameter
179 DEFINE_string(l, "", custom_cpu_library_message);
180
181 /// @brief Define parameter for accuracy drop threshold
182 DEFINE_double(threshold, 1.0f, accuracy_threshold_message);
183
184 /// @brief Define path to output calibrated model
185 DEFINE_bool(stream_output, false, stream_output_message);
186
187 DEFINE_int32(subset, 0, number_of_pictures_message);
188
189 DEFINE_string(output, "", output_model_name);
190
191 DEFINE_string(lbl, "", labels_file_message);
192
193 DEFINE_bool(convert_fc, false, convert_fc_message);
194
195 /**
196  * @brief This function shows a help message
197  */
198 static void showUsage() {
199     std::cout << std::endl;
200     std::cout << "Usage: calibration_tool [OPTION]" << std::endl << std::endl;
201     std::cout << "Available options:" << std::endl;
202     std::cout << std::endl;
203     std::cout << "    -h                        " << help_message << std::endl;
204     std::cout << "    -t <type>                 " << type_message << std::endl;
205     for (int i = 0; types_descriptions[i][0] != nullptr; i++) {
206         std::cout << "      -t \"" << types_descriptions[i][0] << "\" to " << types_descriptions[i][1] << std::endl;
207     }
208     std::cout << "    -i <path>                 " << image_message << std::endl;
209     std::cout << "    -m <path>                 " << model_message << std::endl;
210     std::cout << "    -lbl <path>               " << labels_file_message << std::endl;
211     std::cout << "    -l <absolute_path>        " << custom_cpu_library_message << std::endl;
212     std::cout << "    -c <absolute_path>        " << custom_cldnn_message << std::endl;
213     std::cout << "    -d <device>               " << target_device_message << std::endl;
214     std::cout << "    -b N                      " << batch_message << std::endl;
215     std::cout << "    -ppType <type>            " << preprocessing_type << std::endl;
216     std::cout << "    -ppSize N                 " << preprocessing_size << std::endl;
217     std::cout << "    -ppWidth W                " << preprocessing_width << std::endl;
218     std::cout << "    -ppHeight H               " << preprocessing_height << std::endl;
219     std::cout << "    --dump                    " << dump_message << std::endl;
220     std::cout << "    -subset                  " << number_of_pictures_message << std::endl;
221     std::cout << "    -output <output_IR>      " << output_model_name << std::endl;
222     std::cout << "    -threshold               " << accuracy_threshold_message << std::endl;
223
224     std::cout << std::endl;
225     std::cout << "    Classification-specific options:" << std::endl;
226     std::cout << "      -Czb true               " << zero_background_message << std::endl;
227
228     std::cout << std::endl;
229     std::cout << "    Object detection-specific options:" << std::endl;
230     std::cout << "      -ODkind <kind>          " << obj_detection_kind_message << std::endl;
231     std::cout << "      -ODa <path>             " << obj_detection_annotations_message << std::endl;
232     std::cout << "      -ODc <file>             " << obj_detection_classes_message << std::endl;
233     std::cout << "      -ODsubdir <name>        " << obj_detection_subdir_message << std::endl << std::endl;
234
235     std::cout << std::endl;
236     std::cout << "    -stream_output                   " << stream_output_message << std::endl;
237 }
238
239 enum NetworkType {
240     Undefined = -1,
241     Classification,
242     ObjDetection,
243     RawC,
244     RawOD
245 };
246
247 std::string strtolower(const std::string& s) {
248     std::string res = s;
249     std::transform(res.begin(), res.end(), res.begin(), ::tolower);
250     return res;
251 }
252
253 void SaveCalibratedIR(const std::string &originalName,
254                       const std::string &outModelName,
255                       const std::map<std::string, bool>& layersToInt8,
256                       const InferenceEngine::NetworkStatsMap& statMap,
257                       bool convertFullyConnected) {
258     slog::info << "Layers profile for Int8 quantization\n";
259     CNNNetReader networkReader;
260     networkReader.ReadNetwork(originalName);
261     if (!networkReader.isParseSuccess())THROW_IE_EXCEPTION << "cannot load a failed Model";
262
263     /** Extract model name and load weights **/
264     std::string binFileName = fileNameNoExt(originalName)+ ".bin";
265     networkReader.ReadWeights(binFileName.c_str());
266
267     auto network = networkReader.getNetwork();
268     for (auto &&layer : network) {
269         if (CaselessEq<std::string>()(layer->type, "convolution")) {
270             auto it = layersToInt8.find(layer->name);
271             if (it != layersToInt8.end() && it->second == false) {
272                 layer->params["quantization_level"] = "FP32";
273                 std::cout << layer->name << ": " << "FP32" << std::endl;
274             } else {
275                 layer->params["quantization_level"] = "I8";
276                 std::cout << layer->name << ": " << "I8" << std::endl;
277             }
278         } else if (CaselessEq<std::string>()(layer->type, "fullyconnected")) {
279             if (!convertFullyConnected) {
280                 layer->params["quantization_level"] = "FP32";
281                 std::cout << layer->name << ": " << "FP32" << std::endl;
282             } else {
283                 layer->params["quantization_level"] = "I8";
284                 std::cout << layer->name << ": " << "I8" << std::endl;
285             }
286         }
287     }
288
289
290     ICNNNetworkStats* pstats = nullptr;
291     StatusCode s = ((ICNNNetwork&)networkReader.getNetwork()).getStats(&pstats, nullptr);
292     if (s == StatusCode::OK && pstats) {
293         pstats->setNodesStats(statMap);
294     }
295
296     slog::info << "Write calibrated network to " << outModelName << ".(xml|bin) IR file\n";
297     networkReader.getNetwork().serialize(outModelName + ".xml", outModelName + ".bin");
298 }
299
300 /**
301  * @brief The main function of inference engine sample application
302  * @param argc - The number of arguments
303  * @param argv - Arguments
304  * @return 0 if all good
305  */
306 int main(int argc, char *argv[]) {
307     try {
308         slog::info << "InferenceEngine: " << GetInferenceEngineVersion() << slog::endl;
309
310         // ---------------------------Parsing and validating input arguments--------------------------------------
311         slog::info << "Parsing input parameters" << slog::endl;
312
313         bool noOptions = argc == 1;
314
315         gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);
316         if (FLAGS_h || noOptions) {
317             showUsage();
318             return 1;
319         }
320
321         UserExceptions ee;
322
323         NetworkType netType = Undefined;
324         // Checking the network type
325         if (std::string(FLAGS_t) == "C") {
326             netType = Classification;
327         } else if (std::string(FLAGS_t) == "OD") {
328             netType = ObjDetection;
329         } else if (std::string(FLAGS_t) == "RawC") {
330             netType = RawC;
331         } else if (std::string(FLAGS_t) == "RawOD") {
332             netType = RawOD;
333         } else {
334             ee << UserException(5, "Unknown network type specified (invalid -t option)");
335         }
336
337         // Checking required options
338         if (FLAGS_m.empty()) ee << UserException(3, "Model file is not specified (missing -m option)");
339         if (FLAGS_i.empty()) ee << UserException(4, "Images list is not specified (missing -i option)");
340         if (FLAGS_d.empty()) ee << UserException(5, "Target device is not specified (missing -d option)");
341         if (FLAGS_b < 0) ee << UserException(6, "Batch must be positive (invalid -b option value)");
342
343         if (netType == ObjDetection) {
344             // Checking required OD-specific options
345             if (FLAGS_ODa.empty()) ee << UserException(11, "Annotations folder is not specified for object detection (missing -a option)");
346             if (FLAGS_ODc.empty()) ee << UserException(12, "Classes file is not specified (missing -c option)");
347         }
348
349         if (!ee.empty()) throw ee;
350         // -----------------------------------------------------------------------------------------------------
351
352         // ---------------------Loading plugin for Inference Engine------------------------------------------------
353         slog::info << "Loading plugin" << slog::endl;
354         /** Loading the library with extensions if provided**/
355         InferencePlugin plugin = PluginDispatcher({ FLAGS_pp }).getPluginByDevice(FLAGS_d);
356
357         /** Loading default extensions **/
358         if (FLAGS_d.find("CPU") != std::string::npos) {
359             /**
360              * cpu_extensions library is compiled from "extension" folder containing
361              * custom CPU plugin layer implementations. These layers are not supported
362              * by CPU, but they can be useful for inferring custom topologies.
363             **/
364             plugin.AddExtension(std::make_shared<Extensions::Cpu::CpuExtensions>());
365         }
366
367         if (!FLAGS_l.empty()) {
368             // CPU extensions are loaded as a shared library and passed as a pointer to base extension
369             IExtensionPtr extension_ptr = make_so_pointer<IExtension>(FLAGS_l);
370             plugin.AddExtension(extension_ptr);
371             slog::info << "CPU Extension loaded: " << FLAGS_l << slog::endl;
372         }
373         if (!FLAGS_c.empty()) {
374             // GPU extensions are loaded from an .xml description and OpenCL kernel files
375             plugin.SetConfig({{PluginConfigParams::KEY_CONFIG_FILE, FLAGS_c}});
376             slog::info << "GPU Extension loaded: " << FLAGS_c << slog::endl;
377         }
378
379         printPluginVersion(plugin, std::cout);
380
381         CsvDumper dumper(FLAGS_dump);
382
383         std::shared_ptr<Processor> processor;
384
385         PreprocessingOptions preprocessingOptions;
386         if (strtolower(FLAGS_ppType.c_str()) == "none") {
387             preprocessingOptions = PreprocessingOptions(false, ResizeCropPolicy::DoNothing);
388         } else if (strtolower(FLAGS_ppType) == "resizecrop") {
389             size_t ppWidth = FLAGS_ppSize;
390             size_t ppHeight = FLAGS_ppSize;
391
392             if (FLAGS_ppWidth > 0) ppWidth = FLAGS_ppSize;
393             if (FLAGS_ppHeight > 0) ppHeight = FLAGS_ppSize;
394
395             if (FLAGS_ppSize > 0 || (FLAGS_ppWidth > 0 && FLAGS_ppHeight > 0)) {
396                 preprocessingOptions = PreprocessingOptions(false, ResizeCropPolicy::ResizeThenCrop, ppWidth, ppHeight);
397             } else {
398                 THROW_USER_EXCEPTION(2) << "Size must be specified for preprocessing type " << FLAGS_ppType;
399             }
400         } else if (strtolower(FLAGS_ppType) == "resize" || FLAGS_ppType.empty()) {
401             preprocessingOptions = PreprocessingOptions(false, ResizeCropPolicy::Resize);
402         } else {
403             THROW_USER_EXCEPTION(2) << "Unknown preprocessing type: " << FLAGS_ppType;
404         }
405
406         if (netType == Classification || netType == RawC) {
407             processor = std::shared_ptr<Processor>(
408                 new ClassificationCalibrator(FLAGS_subset, FLAGS_m, FLAGS_d, FLAGS_i, FLAGS_b,
409                                                 plugin, dumper, FLAGS_lbl, preprocessingOptions, FLAGS_Czb));
410         } else if (netType == ObjDetection || netType == RawOD) {
411             if (FLAGS_ODkind == "SSD") {
412                 processor = std::shared_ptr<Processor>(
413                     new SSDObjectDetectionCalibrator(FLAGS_subset, FLAGS_m, FLAGS_d, FLAGS_i, FLAGS_ODsubdir, FLAGS_b,
414                                                         0.5, plugin, dumper, FLAGS_ODa, FLAGS_ODc));
415 /*            } else if (FLAGS_ODkind == "YOLO") {
416                 processor = std::shared_ptr<Processor>(
417                         new YOLOObjectDetectionProcessor(FLAGS_m, FLAGS_d, FLAGS_i, FLAGS_ODsubdir, FLAGS_b,
418                                                          0.5, plugin, dumper, FLAGS_ODa, FLAGS_ODc));
419 */
420             }
421         } else {
422             THROW_USER_EXCEPTION(2) <<  "Unknown network type specified" << FLAGS_ppType;
423         }
424         if (!processor.get()) {
425             THROW_USER_EXCEPTION(2) <<  "Processor pointer is invalid" << FLAGS_ppType;
426         }
427
428         Int8Calibrator* calibrator = dynamic_cast<Int8Calibrator*>(processor.get());
429
430         if (netType != RawC && netType != RawOD) {
431             slog::info << "Collecting accuracy metric in FP32 mode to get a baseline, collecting activation statistics" << slog::endl;
432         } else {
433             slog::info << "Collecting activation statistics" << slog::endl;
434         }
435         calibrator->collectFP32Statistic();
436         shared_ptr<Processor::InferenceMetrics> pIMFP32 = processor->Process(FLAGS_stream_output);
437         const CalibrationMetrics* mFP32 = dynamic_cast<const CalibrationMetrics*>(pIMFP32.get());
438         std:: cout << "  FP32 Accuracy: " << OUTPUT_FLOATING(100.0 * mFP32->AccuracyResult) << "% " << std::endl;
439
440         InferenceEngine::NetworkStatsMap statMap;
441         std::map<std::string, bool> layersToInt8;
442         bool bAccuracy = false;
443
444         if (netType != RawC && netType != RawOD) {
445             slog::info << "Verification of network accuracy if all possible layers converted to INT8" << slog::endl;
446             float bestThreshold = 100.f;
447             float maximalAccuracy = 0.f;
448             for (float threshold = 100.0f; threshold > 95.0f; threshold -= 0.5) {
449                 std::cout << "Validate int8 accuracy, threshold for activation statistics = " << threshold << std::endl;
450                 InferenceEngine::NetworkStatsMap tmpStatMap = calibrator->getStatistic(threshold);
451                 calibrator->validateInt8Config(tmpStatMap, {}, FLAGS_convert_fc);
452                 shared_ptr<Processor::InferenceMetrics> pIM_I8 = processor->Process(FLAGS_stream_output);
453                 const CalibrationMetrics *mI8 = dynamic_cast<const CalibrationMetrics *>(pIM_I8.get());
454                 if (maximalAccuracy < mI8->AccuracyResult) {
455                     maximalAccuracy = mI8->AccuracyResult;
456                     bestThreshold = threshold;
457                 }
458                 std::cout << "   Accuracy is " << OUTPUT_FLOATING(100.0 * mI8->AccuracyResult) << "%" << std::endl;
459             }
460
461             statMap = calibrator->getStatistic(bestThreshold);
462
463             if ((mFP32->AccuracyResult - maximalAccuracy) > (FLAGS_threshold / 100)) {
464                 slog::info << "Accuracy of all layers conversion does not correspond to the required threshold\n";
465                 cout << "FP32 Accuracy: " << OUTPUT_FLOATING(100.0 * mFP32->AccuracyResult) << "% vs " <<
466                     "all Int8 layers Accuracy: " << OUTPUT_FLOATING(100.0 * maximalAccuracy) << "%, " <<
467                     "threshold for activation statistics: " << bestThreshold << "%" << std::endl;
468                 slog::info << "Collecting intermediate per-layer accuracy drop" << slog::endl;
469                 // getting statistic on accuracy drop by layers
470                 calibrator->collectByLayerStatistic(statMap);
471                 processor->Process(FLAGS_stream_output);
472                 // starting to reduce number of layers being converted to Int8
473                 std::map<std::string, float>  layersAccuracyDrop = calibrator->layersAccuracyDrop();
474
475                 std::map<float, std::string> orderedLayersAccuracyDrop;
476                 for (auto d : layersAccuracyDrop) {
477                     orderedLayersAccuracyDrop[d.second] = d.first;
478                     layersToInt8[d.first] = true;
479                 }
480                 std::map<float, std::string>::const_reverse_iterator it = orderedLayersAccuracyDrop.crbegin();
481
482                 shared_ptr<Processor::InferenceMetrics> pIM_I8;
483                 const CalibrationMetrics *mI8;
484                 while (it != orderedLayersAccuracyDrop.crend() && bAccuracy == false) {
485                     slog::info << "Returning of '" << it->second << "' to FP32 precision, start validation\n";
486                     layersToInt8[it->second] = false;
487                     calibrator->validateInt8Config(statMap, layersToInt8, FLAGS_convert_fc);
488                     pIM_I8 = processor->Process(FLAGS_stream_output);
489                     mI8 = dynamic_cast<const CalibrationMetrics *>(pIM_I8.get());
490                     maximalAccuracy = mI8->AccuracyResult;
491                     if ((mFP32->AccuracyResult - maximalAccuracy) > (FLAGS_threshold / 100)) {
492                         cout << "FP32 Accuracy: " << OUTPUT_FLOATING(100.0 * mFP32->AccuracyResult) << "% vs " <<
493                             "current Int8 configuration Accuracy: " << OUTPUT_FLOATING(100.0 * maximalAccuracy) << "%" << std::endl;
494                     } else {
495                         bAccuracy = true;
496                     }
497                     it++;
498                 }
499             } else {
500                 bAccuracy = true;
501             }
502
503             if (bAccuracy) {
504                 slog::info << "Achieved required accuracy drop satisfying threshold\n";
505                 cout << "FP32 accuracy: " << OUTPUT_FLOATING(100.0 * mFP32->AccuracyResult) << "% vs " <<
506                     "current Int8 configuration accuracy: " << OUTPUT_FLOATING(100.0 * maximalAccuracy) << "% " <<
507                     "with threshold for activation statistic: " << bestThreshold << "%" << std::endl;
508                 std::string outModelName = FLAGS_output.empty() ? fileNameNoExt(FLAGS_m) + "_i8" : fileNameNoExt(FLAGS_output);
509                 SaveCalibratedIR(FLAGS_m, outModelName, layersToInt8, statMap, FLAGS_convert_fc);
510             } else {
511                 slog::info << "Required threshold of accuracy drop cannot be achieved with any int8 quantization\n";
512             }
513         } else {
514             std::cout << "Collected activation statistics, writing maximum values to IR" << std::endl;
515             statMap = calibrator->getStatistic(100.0f);
516             std::string outModelName = FLAGS_output.empty() ? fileNameNoExt(FLAGS_m) + "_i8" : fileNameNoExt(FLAGS_output);
517             SaveCalibratedIR(FLAGS_m, outModelName, layersToInt8, statMap, FLAGS_convert_fc);
518         }
519
520         if (dumper.dumpEnabled()) {
521             slog::info << "Dump file generated: " << dumper.getFilename() << slog::endl;
522         }
523     } catch (const InferenceEngineException& ex) {
524         slog::err << "Inference problem: \n" << ex.what() << slog::endl;
525         return 1;
526     } catch (const UserException& ex) {
527         slog::err << "Input problem: \n" << ex.what() << slog::endl;
528         showUsage();
529         return ex.exitCode();
530     } catch (const UserExceptions& ex) {
531         if (ex.list().size() == 1) {
532             slog::err << "Input problem: " << ex.what() << slog::endl;
533             showUsage();
534             return ex.list().begin()->exitCode();
535         } else {
536             slog::err << "Input problems: \n" << ex.what() << slog::endl;
537             showUsage();
538             return ex.list().begin()->exitCode();
539         }
540     }
541     return 0;
542 }