You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ncnn2table.cpp 30 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. // BUG1989 is pleased to support the open source community by supporting ncnn available.
  2. //
  3. // author:BUG1989 (https://github.com/BUG1989/) Long-term support.
  4. // author:JansonZhu (https://github.com/JansonZhu) Implemented the function of entropy calibration.
  5. //
  6. // Copyright (C) 2019 BUG1989. All rights reserved.
  7. //
  8. // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  9. // in compliance with the License. You may obtain a copy of the License at
  10. //
  11. // https://opensource.org/licenses/BSD-3-Clause
  12. //
  13. // Unless required by applicable law or agreed to in writing, software distributed
  14. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  15. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  16. // specific language governing permissions and limitations under the License.
  17. #ifdef _MSC_VER
  18. #define _CRT_SECURE_NO_DEPRECATE
  19. #endif
  20. #include <cstdio>
  21. #include <cstring>
  22. #include <vector>
  23. #include <iostream>
  24. #include <cstdlib>
  25. #include <algorithm>
  26. #include <map>
  27. #include <opencv2/opencv.hpp>
  28. // ncnn public header
  29. #include "net.h"
  30. #include "cpu.h"
  31. #include "benchmark.h"
  32. // ncnn private header
  33. #include "layer/convolution.h"
  34. #include "layer/convolutiondepthwise.h"
  35. #include "layer/innerproduct.h"
  36. static ncnn::Option g_default_option;
  37. static ncnn::UnlockedPoolAllocator g_blob_pool_allocator;
  38. static ncnn::PoolAllocator g_workspace_pool_allocator;
  39. // Get the file names from direct path
  40. int parse_images_dir(const std::string& base_path, std::vector<std::string>& file_path)
  41. {
  42. file_path.clear();
  43. const cv::String base_path_str(base_path);
  44. std::vector<cv::String> image_list;
  45. cv::glob(base_path_str, image_list, true);
  46. for (auto& image_path : image_list)
  47. {
  48. file_path.emplace_back(image_path);
  49. }
  50. return 0;
  51. }
  52. class QuantNet : public ncnn::Net
  53. {
  54. public:
  55. int get_conv_names();
  56. int get_conv_bottom_blob_names();
  57. int get_conv_weight_blob_scales();
  58. int get_input_names();
  59. public:
  60. std::vector<std::string> conv_names;
  61. std::map<std::string, std::string> conv_bottom_blob_names;
  62. std::map<std::string, std::vector<float> > weight_scales;
  63. std::vector<std::string> input_names;
  64. };
  65. int QuantNet::get_input_names()
  66. {
  67. for (auto layer : layers)
  68. {
  69. if (layer->type == "Input")
  70. {
  71. for (int blob_index : layer->tops)
  72. {
  73. std::string name = blobs[blob_index].name;
  74. input_names.push_back(name);
  75. }
  76. }
  77. }
  78. return 0;
  79. }
  80. int QuantNet::get_conv_names()
  81. {
  82. for (size_t i = 0; i < layers.size(); i++)
  83. {
  84. const auto layer = layers[i];
  85. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  86. {
  87. std::string name = layer->name;
  88. conv_names.push_back(name);
  89. }
  90. }
  91. return 0;
  92. }
  93. int QuantNet::get_conv_bottom_blob_names()
  94. {
  95. // find conv bottom name or index
  96. for (auto layer : layers)
  97. {
  98. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  99. {
  100. auto name = layer->name;
  101. const auto bottom_blob_name = blobs[layer->bottoms[0]].name;
  102. conv_bottom_blob_names[name] = bottom_blob_name;
  103. }
  104. }
  105. return 0;
  106. }
  107. int QuantNet::get_conv_weight_blob_scales()
  108. {
  109. for (auto layer : layers)
  110. {
  111. if (layer->type == "Convolution")
  112. {
  113. std::string name = layer->name;
  114. const int weight_data_size_output = static_cast<ncnn::Convolution*>(layer)->weight_data_size / static_cast<ncnn::Convolution*>(layer)->num_output;
  115. std::vector<float> scales;
  116. // int8 winograd F43 needs weight data to use 6bit quantization
  117. bool quant_6bit = false;
  118. int kernel_w = static_cast<ncnn::Convolution*>(layer)->kernel_w;
  119. int kernel_h = static_cast<ncnn::Convolution*>(layer)->kernel_h;
  120. int dilation_w = static_cast<ncnn::Convolution*>(layer)->dilation_w;
  121. int dilation_h = static_cast<ncnn::Convolution*>(layer)->dilation_h;
  122. int stride_w = static_cast<ncnn::Convolution*>(layer)->stride_w;
  123. int stride_h = static_cast<ncnn::Convolution*>(layer)->stride_h;
  124. if (kernel_w == 3 && kernel_h == 3 && dilation_w == 1 && dilation_h == 1 && stride_w == 1 && stride_h == 1)
  125. quant_6bit = true;
  126. for (int n = 0; n < static_cast<ncnn::Convolution*>(layer)->num_output; n++)
  127. {
  128. const ncnn::Mat weight_data_n = static_cast<ncnn::Convolution*>(layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  129. const float *data_n = weight_data_n;
  130. float max_value = std::numeric_limits<float>::min();
  131. for (int k = 0; k < weight_data_size_output; k++)
  132. {
  133. max_value = std::max(max_value, std::fabs(data_n[k]));
  134. }
  135. if (quant_6bit)
  136. {
  137. scales.push_back(31 / max_value);
  138. }
  139. else
  140. {
  141. scales.push_back(127 / max_value);
  142. }
  143. }
  144. weight_scales[name] = scales;
  145. }
  146. if (layer->type == "ConvolutionDepthWise")
  147. {
  148. std::string name = layer->name;
  149. const int weight_data_size_output = static_cast<ncnn::ConvolutionDepthWise*>(layer)->weight_data_size / static_cast<ncnn::ConvolutionDepthWise*>(layer)->group;
  150. std::vector<float> scales;
  151. for (int n = 0; n < static_cast<ncnn::ConvolutionDepthWise*>(layer)->group; n++)
  152. {
  153. const ncnn::Mat weight_data_n = static_cast<ncnn::ConvolutionDepthWise*>(layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  154. const float *data_n = weight_data_n;
  155. float max_value = std::numeric_limits<float>::min();
  156. for (int k = 0; k < weight_data_size_output; k++)
  157. {
  158. max_value = std::max(max_value, std::fabs(data_n[k]));
  159. }
  160. scales.push_back(127 / max_value);
  161. }
  162. weight_scales[name] = scales;
  163. }
  164. if (layer->type == "InnerProduct")
  165. {
  166. std::string name = layer->name;
  167. const int weight_data_size_output = static_cast<ncnn::InnerProduct*>(layer)->weight_data_size / static_cast<ncnn::InnerProduct*>(layer)->num_output;
  168. std::vector<float> scales;
  169. for (int n = 0; n < static_cast<ncnn::InnerProduct*>(layer)->num_output; n++)
  170. {
  171. const ncnn::Mat weight_data_n = static_cast<ncnn::InnerProduct*>(layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  172. const float *data_n = weight_data_n;
  173. float max_value = std::numeric_limits<float>::min();
  174. for (int k = 0; k < weight_data_size_output; k++)
  175. max_value = std::max(max_value, std::fabs(data_n[k]));
  176. scales.push_back(127 / max_value);
  177. }
  178. weight_scales[name] = scales;
  179. }
  180. }
  181. return 0;
  182. }
  183. class QuantizeData
  184. {
  185. public:
  186. QuantizeData(const std::string& layer_name, const int& num);
  187. int initial_blob_max(ncnn::Mat data);
  188. int initial_histogram_interval();
  189. int initial_histogram_value();
  190. int normalize_histogram();
  191. int update_histogram(ncnn::Mat data);
  192. float compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b) const;
  193. int threshold_distribution(const std::vector<float> &distribution, const int target_bin = 128) const;
  194. float get_data_blob_scale();
  195. public:
  196. std::string name;
  197. float max_value;
  198. int num_bins;
  199. float histogram_interval;
  200. std::vector<float> histogram;
  201. float threshold;
  202. int threshold_bin;
  203. float scale;
  204. };
  205. QuantizeData::QuantizeData(const std::string& layer_name, const int& num)
  206. {
  207. name = layer_name;
  208. max_value = 0.f;
  209. num_bins = num;
  210. histogram_interval = 0.f;
  211. histogram.resize(num_bins);
  212. initial_histogram_value();
  213. threshold = 0.f;
  214. threshold_bin = 0;
  215. scale = 1.0f;
  216. }
  217. int QuantizeData::initial_blob_max(ncnn::Mat data)
  218. {
  219. const int channel_num = data.c;
  220. const int size = data.w * data.h;
  221. for (int q = 0; q < channel_num; q++)
  222. {
  223. const float *data_n = data.channel(q);
  224. for (int i = 0; i < size; i++)
  225. {
  226. max_value = std::max(max_value, std::fabs(data_n[i]));
  227. }
  228. }
  229. return 0;
  230. }
  231. int QuantizeData::initial_histogram_interval()
  232. {
  233. histogram_interval = max_value / static_cast<float>(num_bins);
  234. return 0;
  235. }
  236. int QuantizeData::initial_histogram_value()
  237. {
  238. for (float& i : histogram)
  239. {
  240. i = 0.00001f;
  241. }
  242. return 0;
  243. }
  244. int QuantizeData::normalize_histogram()
  245. {
  246. const auto length = histogram.size();
  247. float sum = 0;
  248. for (size_t i = 0; i < length; i++)
  249. sum += histogram[i];
  250. for (size_t i = 0; i < length; i++)
  251. histogram[i] /= sum;
  252. return 0;
  253. }
  254. int QuantizeData::update_histogram(ncnn::Mat data)
  255. {
  256. const int channel_num = data.c;
  257. const int size = data.w * data.h;
  258. for (int q = 0; q < channel_num; q++)
  259. {
  260. const float *data_n = data.channel(q);
  261. for (int i = 0; i < size; i++)
  262. {
  263. if (data_n[i] == 0)
  264. continue;
  265. const int index = std::min(static_cast<int>(std::abs(data_n[i]) / histogram_interval), 2047);
  266. histogram[index]++;
  267. }
  268. }
  269. return 0;
  270. }
  271. float QuantizeData::compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b) const
  272. {
  273. const auto length = dist_a.size();
  274. assert(dist_b.size() == length);
  275. float result = 0;
  276. for (size_t i = 0; i < length; i++)
  277. {
  278. if (dist_a[i] != 0)
  279. {
  280. if (dist_b[i] == 0)
  281. {
  282. result += 1;
  283. }
  284. else
  285. {
  286. result += dist_a[i] * log(dist_a[i] / dist_b[i]);
  287. }
  288. }
  289. }
  290. return result;
  291. }
  292. int QuantizeData::threshold_distribution(const std::vector<float> &distribution, const int target_bin) const
  293. {
  294. int target_threshold = target_bin;
  295. float min_kl_divergence = 1000;
  296. const int length = static_cast<int>(distribution.size());
  297. std::vector<float> quantize_distribution(target_bin);
  298. float threshold_sum = 0;
  299. for (int threshold = target_bin; threshold < length; threshold++)
  300. {
  301. threshold_sum += distribution[threshold];
  302. }
  303. for (int threshold = target_bin; threshold < length; threshold++)
  304. {
  305. std::vector<float> t_distribution(distribution.begin(), distribution.begin() + threshold);
  306. t_distribution[threshold - 1] += threshold_sum;
  307. threshold_sum -= distribution[threshold];
  308. // get P
  309. fill(quantize_distribution.begin(), quantize_distribution.end(), 0.0f);
  310. const auto num_per_bin = static_cast<float>(threshold) / static_cast<float>(target_bin);
  311. for (int i = 0; i < target_bin; i++)
  312. {
  313. const auto start = static_cast<float>(i) * num_per_bin;
  314. const auto end = start + num_per_bin;
  315. const auto left_upper = static_cast<int>(ceil(start));
  316. if (static_cast<float>(left_upper) > start)
  317. {
  318. const auto left_scale = static_cast<float>(left_upper) - start;
  319. quantize_distribution[i] += left_scale * distribution[left_upper - 1];
  320. }
  321. const auto right_lower = static_cast<int>(floor(end));
  322. if (static_cast<float>(right_lower) < end)
  323. {
  324. const auto right_scale = end - static_cast<float>(right_lower);
  325. quantize_distribution[i] += right_scale * distribution[right_lower];
  326. }
  327. for (int j = left_upper; j < right_lower; j++)
  328. {
  329. quantize_distribution[i] += distribution[j];
  330. }
  331. }
  332. // get Q
  333. std::vector<float> expand_distribution(threshold, 0);
  334. for (int i = 0; i < target_bin; i++)
  335. {
  336. const auto start = static_cast<float>(i) * num_per_bin;
  337. const auto end = start + num_per_bin;
  338. float count = 0;
  339. const int left_upper = static_cast<int>(ceil(start));
  340. float left_scale = 0;
  341. if (static_cast<float>(left_upper) > start)
  342. {
  343. left_scale = static_cast<float>(left_upper) - start;
  344. if (distribution[left_upper - 1] != 0)
  345. {
  346. count += left_scale;
  347. }
  348. }
  349. const int right_lower = static_cast<int>(floor(end));
  350. float right_scale = 0;
  351. if (static_cast<float>(right_lower) < end)
  352. {
  353. right_scale = end - static_cast<float>(right_lower);
  354. if (distribution[right_lower] != 0)
  355. {
  356. count += right_scale;
  357. }
  358. }
  359. for (int j = left_upper; j < right_lower; j++)
  360. {
  361. if (distribution[j] != 0)
  362. {
  363. count++;
  364. }
  365. }
  366. const auto expand_value = quantize_distribution[i] / count;
  367. if (static_cast<float>(left_upper) > start)
  368. {
  369. if (distribution[left_upper - 1] != 0)
  370. {
  371. expand_distribution[left_upper - 1] += expand_value * left_scale;
  372. }
  373. }
  374. if (static_cast<float>(right_lower) < end)
  375. {
  376. if (distribution[right_lower] != 0)
  377. {
  378. expand_distribution[right_lower] += expand_value * right_scale;
  379. }
  380. }
  381. for (int j = left_upper; j < right_lower; j++)
  382. {
  383. if (distribution[j] != 0)
  384. {
  385. expand_distribution[j] += expand_value;
  386. }
  387. }
  388. }
  389. // kl
  390. const float kl_divergence = compute_kl_divergence(t_distribution, expand_distribution);
  391. // the best num of bin
  392. if (kl_divergence < min_kl_divergence)
  393. {
  394. min_kl_divergence = kl_divergence;
  395. target_threshold = threshold;
  396. }
  397. }
  398. return target_threshold;
  399. }
  400. float QuantizeData::get_data_blob_scale()
  401. {
  402. normalize_histogram();
  403. threshold_bin = threshold_distribution(histogram);
  404. threshold = (static_cast<float>(threshold_bin) + 0.5f) * histogram_interval;
  405. scale = 127 / threshold;
  406. return scale;
  407. }
  408. struct PreParam
  409. {
  410. float mean[3];
  411. float norm[3];
  412. int width;
  413. int height;
  414. bool swapRB;
  415. };
  416. static int post_training_quantize(const std::vector<std::string>& image_list, const std::string& param_path, const std::string& bin_path, const std::string& table_path, struct PreParam& per_param)
  417. {
  418. auto size = image_list.size();
  419. QuantNet net;
  420. net.opt = g_default_option;
  421. net.load_param(param_path.c_str());
  422. net.load_model(bin_path.c_str());
  423. float mean_vals[3];
  424. float norm_vals[3];
  425. int width = per_param.width;
  426. int height = per_param.height;
  427. bool swapRB = per_param.swapRB;
  428. mean_vals[0] = per_param.mean[0];
  429. mean_vals[1] = per_param.mean[1];
  430. mean_vals[2] = per_param.mean[2];
  431. norm_vals[0] = per_param.norm[0];
  432. norm_vals[1] = per_param.norm[1];
  433. norm_vals[2] = per_param.norm[2];
  434. g_blob_pool_allocator.clear();
  435. g_workspace_pool_allocator.clear();
  436. net.get_input_names();
  437. net.get_conv_names();
  438. net.get_conv_bottom_blob_names();
  439. net.get_conv_weight_blob_scales();
  440. if (net.input_names.empty())
  441. {
  442. fprintf(stderr, "not found [Input] Layer, Check your ncnn.param \n");
  443. return -1;
  444. }
  445. FILE *fp = fopen(table_path.c_str(), "w");
  446. // save quantization scale of weight
  447. printf("====> Quantize the parameters.\n");
  448. for (size_t i = 0; i < net.conv_names.size(); i++)
  449. {
  450. std::string layer_name = net.conv_names[i];
  451. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  452. std::vector<float> weight_scale_n = net.weight_scales[layer_name];
  453. fprintf(fp, "%s_param_0 ", layer_name.c_str());
  454. for (float j : weight_scale_n)
  455. {
  456. fprintf(fp, "%f ", j);
  457. }
  458. fprintf(fp, "\n");
  459. }
  460. // initial quantization data
  461. std::vector<QuantizeData> quantize_datas;
  462. for (size_t i = 0; i < net.conv_names.size(); i++)
  463. {
  464. std::string layer_name = net.conv_names[i];
  465. QuantizeData quantize_data(layer_name, 2048);
  466. quantize_datas.push_back(quantize_data);
  467. }
  468. // step 1 count the max value
  469. printf("====> Quantize the activation.\n");
  470. printf(" ====> step 1 : find the max value.\n");
  471. for (size_t i = 0; i < image_list.size(); i++)
  472. {
  473. std::string img_name = image_list[i];
  474. if ((i + 1) % 100 == 0)
  475. {
  476. fprintf(stderr, " %d/%d\n", static_cast<int>(i + 1), static_cast<int>(size));
  477. }
  478. #if OpenCV_VERSION_MAJOR > 2
  479. cv::Mat bgr = cv::imread(img_name, cv::IMREAD_COLOR);
  480. #else
  481. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  482. #endif
  483. if (bgr.empty())
  484. {
  485. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  486. return -1;
  487. }
  488. ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, swapRB ? ncnn::Mat::PIXEL_BGR2RGB : ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, width, height);
  489. in.substract_mean_normalize(mean_vals, norm_vals);
  490. ncnn::Extractor ex = net.create_extractor();
  491. ex.input(net.input_names[0].c_str(), in);
  492. for (size_t j = 0; j < net.conv_names.size(); j++)
  493. {
  494. std::string layer_name = net.conv_names[j];
  495. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  496. ncnn::Mat out;
  497. ex.extract(blob_name.c_str(), out);
  498. for (auto& quantize_data : quantize_datas)
  499. {
  500. if (quantize_data.name == layer_name)
  501. {
  502. quantize_data.initial_blob_max(out);
  503. break;
  504. }
  505. }
  506. }
  507. }
  508. // step 2 histogram_interval
  509. printf(" ====> step 2 : generate the histogram_interval.\n");
  510. for (size_t i = 0; i < net.conv_names.size(); i++)
  511. {
  512. std::string layer_name = net.conv_names[i];
  513. for (auto& quantize_data : quantize_datas)
  514. {
  515. if (quantize_data.name == layer_name)
  516. {
  517. quantize_data.initial_histogram_interval();
  518. fprintf(stderr, "%-20s : max = %-15f interval = %-10f\n", quantize_data.name.c_str(), quantize_data.max_value, quantize_data.histogram_interval);
  519. break;
  520. }
  521. }
  522. }
  523. // step 3 histogram
  524. printf(" ====> step 3 : generate the histogram.\n");
  525. for (size_t i = 0; i < image_list.size(); i++)
  526. {
  527. std::string img_name = image_list[i];
  528. if ((i + 1) % 100 == 0)
  529. fprintf(stderr, " %d/%d\n", (int)(i + 1), (int)size);
  530. #if OpenCV_VERSION_MAJOR > 2
  531. cv::Mat bgr = cv::imread(img_name, cv::IMREAD_COLOR);
  532. #else
  533. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  534. #endif
  535. if (bgr.empty())
  536. {
  537. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  538. return -1;
  539. }
  540. ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, swapRB ? ncnn::Mat::PIXEL_BGR2RGB : ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, width, height);
  541. in.substract_mean_normalize(mean_vals, norm_vals);
  542. ncnn::Extractor ex = net.create_extractor();
  543. ex.input(net.input_names[0].c_str(), in);
  544. for (size_t k = 0; k < net.conv_names.size(); k++)
  545. {
  546. std::string layer_name = net.conv_names[k];
  547. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  548. ncnn::Mat out;
  549. ex.extract(blob_name.c_str(), out);
  550. for (auto& quantize_data : quantize_datas)
  551. {
  552. if (quantize_data.name == layer_name)
  553. {
  554. quantize_data.update_histogram(out);
  555. break;
  556. }
  557. }
  558. }
  559. }
  560. // step4 kld
  561. printf(" ====> step 4 : using kld to find the best threshold value.\n");
  562. for (size_t i = 0; i < net.conv_names.size(); i++)
  563. {
  564. std::string layer_name = net.conv_names[i];
  565. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  566. fprintf(stderr, "%-20s ", layer_name.c_str());
  567. for (auto& quantize_data : quantize_datas)
  568. {
  569. if (quantize_data.name == layer_name)
  570. {
  571. quantize_data.get_data_blob_scale();
  572. fprintf(stderr, "bin : %-8d threshold : %-15f interval : %-10f scale : %-10f\n",
  573. quantize_data.threshold_bin,
  574. quantize_data.threshold,
  575. quantize_data.histogram_interval,
  576. quantize_data.scale);
  577. fprintf(fp, "%s %f\n", layer_name.c_str(), quantize_data.scale);
  578. break;
  579. }
  580. }
  581. }
  582. fclose(fp);
  583. printf("====> Save the calibration table done.\n");
  584. return 0;
  585. }
  586. // usage
  587. void showUsage()
  588. {
  589. std::cout << "example: ./ncnn2table --param=squeezenet-fp32.param --bin=squeezenet-fp32.bin --images=images/ --output=squeezenet.table --mean=104,117,123 --norm=1,1,1 --size=227,227 --swapRB --thread=2" << std::endl;
  590. }
  591. int main(int argc, char** argv)
  592. {
  593. std::cout << "--- ncnn post training quantization tool --- " << __TIME__ << " " << __DATE__ << std::endl;
  594. const cv::CommandLineParser parser(argc, argv,
  595. {
  596. "{help h usage ? | | print this message }"
  597. "{param p | | path to ncnn.param file }"
  598. "{bin b | | path to ncnn.bin file }"
  599. "{images i | | path to calibration images }"
  600. "{output o | | path to output calibration table file }"
  601. "{mean m | | value of mean }"
  602. "{norm n | | value of normalize(scale value,default is 1 }"
  603. "{size s | | the size of input image(using the resize the original image,default is w=224,h=224) }"
  604. "{swapRB c | | flag which indicates that swap first and last channels in 3-channel image is necessary }"
  605. "{thread t | 4 | count of processing threads }"
  606. });
  607. if (parser.has("help"))
  608. {
  609. parser.printMessage();
  610. showUsage();
  611. return 0;
  612. }
  613. if (!parser.has("param") || !parser.has("bin") || !parser.has("images") || !parser.has("output") || !parser.has("mean") || !parser.has("norm"))
  614. {
  615. std::cout << "Inputs is does not include all needed param, pleas check..." << std::endl;
  616. parser.printMessage();
  617. showUsage();
  618. return 0;
  619. }
  620. const std::string image_folder_path = parser.get<cv::String>("images");
  621. const std::string ncnn_param_file_path = parser.get<cv::String>("param");
  622. const std::string ncnn_bin_file_path = parser.get<cv::String>("bin");
  623. const std::string saved_table_file_path = parser.get<cv::String>("output");
  624. // check the input param
  625. if (image_folder_path.empty() || ncnn_param_file_path.empty() || ncnn_bin_file_path.empty() || saved_table_file_path.empty())
  626. {
  627. fprintf(stderr, "One or more path may be empty, please check and try again.\n");
  628. return 0;
  629. }
  630. const auto num_threads = parser.get<int>("thread");
  631. struct PreParam pre_param {
  632. {104.f, 117.f, 103.f},
  633. { 1.f, 1.f, 1.f },
  634. 224,
  635. 224,
  636. false
  637. };
  638. const auto find_all_value_in_string = [](const std::string& values_string, std::vector<float>& value)
  639. {
  640. std::vector<int> masks_pos;
  641. for (size_t i = 0; i < values_string.size(); i++)
  642. {
  643. if (',' == values_string[i])
  644. {
  645. masks_pos.push_back(static_cast<int>(i));
  646. }
  647. }
  648. // check
  649. if (masks_pos.empty())
  650. {
  651. fprintf(stderr, "ERROR: Cannot find any ',' in string, please check.\n");
  652. return -1;
  653. }
  654. if (2 != masks_pos.size())
  655. {
  656. fprintf(stderr, "ERROR: Char ',' in fist of string, please check.\n");
  657. return -1;
  658. }
  659. if (masks_pos.front() == 0)
  660. {
  661. fprintf(stderr, "ERROR: Char ',' in fist of string, please check.\n");
  662. return -1;
  663. }
  664. if (masks_pos.back() == 0)
  665. {
  666. fprintf(stderr, "ERROR: Char ',' in last of string, please check.\n");
  667. return -1;
  668. }
  669. for (size_t i = 0; i < masks_pos.size(); i++)
  670. {
  671. if (i > 0)
  672. {
  673. if (!(masks_pos[i] - masks_pos[i - 1] > 1))
  674. {
  675. fprintf(stderr, "ERROR: Neighbouring char ',' was found.\n");
  676. return -1;
  677. }
  678. }
  679. }
  680. const cv::String ch0_val_str = values_string.substr(0, masks_pos[0]);
  681. const cv::String ch1_val_str = values_string.substr(masks_pos[0] + 1, masks_pos[1] - masks_pos[0] - 1);
  682. const cv::String ch2_val_str = values_string.substr(masks_pos[1] + 1, values_string.size() - masks_pos[1] - 1);
  683. value.emplace_back(static_cast<float>(std::atof(std::string(ch0_val_str).c_str())));
  684. value.emplace_back(static_cast<float>(std::atof(std::string(ch1_val_str).c_str())));
  685. value.emplace_back(static_cast<float>(std::atof(std::string(ch2_val_str).c_str())));
  686. return 0;
  687. };
  688. if (parser.has("mean"))
  689. {
  690. const std::string mean_str = parser.get<std::string>("mean");
  691. std::vector<float> mean_values;
  692. const auto ret = find_all_value_in_string(mean_str, mean_values);
  693. if (0 != ret && 3 != mean_values.size())
  694. {
  695. fprintf(stderr, "ERROR: Searching mean value from --mean was failed.\n");
  696. return -1;
  697. }
  698. pre_param.mean[0] = mean_values[0];
  699. pre_param.mean[1] = mean_values[1];
  700. pre_param.mean[2] = mean_values[2];
  701. }
  702. if (parser.has("norm"))
  703. {
  704. const std::string norm_str = parser.get<std::string>("norm");
  705. std::vector<float> norm_values;
  706. const auto ret = find_all_value_in_string(norm_str, norm_values);
  707. if (0 != ret && 3 != norm_values.size())
  708. {
  709. fprintf(stderr, "ERROR: Searching mean value from --mean was failed, please check --mean param.\n");
  710. return -1;
  711. }
  712. pre_param.norm[0] = norm_values[0];
  713. pre_param.norm[1] = norm_values[1];
  714. pre_param.norm[2] = norm_values[2];
  715. }
  716. if (parser.has("size"))
  717. {
  718. cv::String size_str = parser.get<std::string>("size");
  719. auto sep_pos = size_str.find_first_of(',');
  720. if (cv::String::npos != sep_pos && sep_pos < size_str.size())
  721. {
  722. cv::String width_value_str;
  723. cv::String height_value_str;
  724. width_value_str = size_str.substr(0, sep_pos);
  725. height_value_str = size_str.substr(sep_pos + 1, size_str.size() - sep_pos - 1);
  726. pre_param.width = static_cast<int>(std::atoi(std::string(width_value_str).c_str()));
  727. pre_param.height = static_cast<int>(std::atoi(std::string(height_value_str).c_str()));
  728. }
  729. else
  730. {
  731. fprintf(stderr, "ERROR: Searching size value from --size was failed, please check --size param.\n");
  732. return -1;
  733. }
  734. }
  735. if (parser.has("swapRB"))
  736. {
  737. pre_param.swapRB = true;
  738. }
  739. g_blob_pool_allocator.set_size_compare_ratio(0.0f);
  740. g_workspace_pool_allocator.set_size_compare_ratio(0.5f);
  741. // default option
  742. g_default_option.lightmode = true;
  743. g_default_option.num_threads = num_threads;
  744. g_default_option.blob_allocator = &g_blob_pool_allocator;
  745. g_default_option.workspace_allocator = &g_workspace_pool_allocator;
  746. g_default_option.use_winograd_convolution = true;
  747. g_default_option.use_sgemm_convolution = true;
  748. g_default_option.use_int8_inference = true;
  749. g_default_option.use_fp16_packed = true;
  750. g_default_option.use_fp16_storage = true;
  751. g_default_option.use_fp16_arithmetic = true;
  752. g_default_option.use_int8_storage = true;
  753. g_default_option.use_int8_arithmetic = true;
  754. ncnn::set_cpu_powersave(2);
  755. ncnn::set_omp_dynamic(0);
  756. ncnn::set_omp_num_threads(num_threads);
  757. std::vector<std::string> image_file_path_list;
  758. // parse the image file.
  759. parse_images_dir(image_folder_path, image_file_path_list);
  760. // get the calibration table file, and save it.
  761. const auto ret = post_training_quantize(image_file_path_list, ncnn_param_file_path, ncnn_bin_file_path, saved_table_file_path, pre_param);
  762. if (!ret)
  763. {
  764. fprintf(stderr, "\nNCNN Int8 Calibration table create success, best wish for your INT8 inference has a low accuracy loss...\\(^0^)/...233...\n");
  765. }
  766. return 0;
  767. }