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 29 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  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. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <getopt.h>
  20. #include <string.h>
  21. #include <vector>
  22. #include <iostream>
  23. #include <fstream>
  24. #include <dirent.h>
  25. #include <stdlib.h>
  26. #include <algorithm>
  27. #include <map>
  28. #include <opencv2/core/core.hpp>
  29. #include <opencv2/highgui/highgui.hpp>
  30. // ncnn public header
  31. #include "platform.h"
  32. #include "net.h"
  33. #include "cpu.h"
  34. #include "benchmark.h"
  35. // ncnn private header
  36. #include "layer/convolution.h"
  37. #include "layer/convolutiondepthwise.h"
  38. #include "layer/innerproduct.h"
  39. static ncnn::Option g_default_option;
  40. static ncnn::UnlockedPoolAllocator g_blob_pool_allocator;
  41. static ncnn::PoolAllocator g_workspace_pool_allocator;
  42. // Get the filenames from direct path
  43. int parse_images_dir(const char *base_path, std::vector<std::string>& file_path)
  44. {
  45. DIR *dir;
  46. struct dirent *ptr;
  47. if ((dir=opendir(base_path)) == NULL)
  48. {
  49. perror("Open dir error...");
  50. exit(1);
  51. }
  52. while ((ptr=readdir(dir)) != NULL)
  53. {
  54. if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0) ///current dir OR parrent dir
  55. {
  56. continue;
  57. }
  58. std::string path = base_path;
  59. file_path.push_back(path + ptr->d_name);
  60. }
  61. closedir(dir);
  62. return 0;
  63. }
  64. class QuantNet : public ncnn::Net
  65. {
  66. public:
  67. int get_conv_names();
  68. int get_conv_bottom_blob_names();
  69. int get_conv_weight_blob_scales();
  70. int get_input_names();
  71. public:
  72. std::vector<std::string> conv_names;
  73. std::map<std::string,std::string> conv_bottom_blob_names;
  74. std::map<std::string,std::vector<float> > weight_scales;
  75. std::vector<std::string> input_names;
  76. };
  77. int QuantNet::get_input_names()
  78. {
  79. for (size_t i=0; i<layers.size(); i++)
  80. {
  81. ncnn::Layer* layer = layers[i];
  82. if (layer->type == "Input")
  83. {
  84. for (size_t j=0; j<layer->tops.size(); j++)
  85. {
  86. int blob_index = layer->tops[j];
  87. std::string name = blobs[blob_index].name.c_str();
  88. input_names.push_back(name);
  89. }
  90. }
  91. }
  92. return 0;
  93. }
  94. int QuantNet::get_conv_names()
  95. {
  96. for (size_t i=0; i<layers.size(); i++)
  97. {
  98. ncnn::Layer* layer = layers[i];
  99. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  100. {
  101. std::string name = layer->name;
  102. conv_names.push_back(name);
  103. }
  104. }
  105. return 0;
  106. }
  107. int QuantNet::get_conv_bottom_blob_names()
  108. {
  109. // find conv bottom name or index
  110. for (size_t i=0; i<layers.size(); i++)
  111. {
  112. ncnn::Layer* layer = layers[i];
  113. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  114. {
  115. std::string name = layer->name;
  116. std::string bottom_blob_name = blobs[layer->bottoms[0]].name;
  117. conv_bottom_blob_names[name] = bottom_blob_name;
  118. }
  119. }
  120. return 0;
  121. }
  122. int QuantNet::get_conv_weight_blob_scales()
  123. {
  124. for (size_t i=0; i<layers.size(); i++)
  125. {
  126. ncnn::Layer* layer = layers[i];
  127. if (layer->type == "Convolution")
  128. {
  129. std::string name = layer->name;
  130. const int weight_data_size_output = ((ncnn::Convolution*)layer)->weight_data_size / ((ncnn::Convolution*)layer)->num_output;
  131. std::vector<float> scales;
  132. // int8 winograd F43 needs weight data to use 6bit quantization
  133. bool quant_6bit = false;
  134. int kernel_w = ((ncnn::Convolution*)layer)->kernel_w;
  135. int kernel_h = ((ncnn::Convolution*)layer)->kernel_h;
  136. int dilation_w = ((ncnn::Convolution*)layer)->dilation_w;
  137. int dilation_h = ((ncnn::Convolution*)layer)->dilation_h;
  138. int stride_w = ((ncnn::Convolution*)layer)->stride_w;
  139. int stride_h = ((ncnn::Convolution*)layer)->stride_h;
  140. if (kernel_w == 3 && kernel_h == 3 && dilation_w == 1 && dilation_h == 1 && stride_w == 1 && stride_h == 1)
  141. quant_6bit = true;
  142. for (int n=0; n<((ncnn::Convolution*)layer)->num_output; n++)
  143. {
  144. const ncnn::Mat weight_data_n = ((ncnn::Convolution*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  145. const float *data_n = weight_data_n;
  146. float max_value = std::numeric_limits<float>::min();
  147. for (int i = 0; i < weight_data_size_output; i++)
  148. max_value = std::max(max_value, std::fabs(data_n[i]));
  149. if (quant_6bit)
  150. scales.push_back(31 / max_value);
  151. else
  152. scales.push_back(127 / max_value);
  153. }
  154. weight_scales[name] = scales;
  155. }
  156. if (layer->type == "ConvolutionDepthWise")
  157. {
  158. std::string name = layer->name;
  159. const int weight_data_size_output = ((ncnn::ConvolutionDepthWise*)layer)->weight_data_size / ((ncnn::ConvolutionDepthWise*)layer)->group;
  160. std::vector<float> scales;
  161. for (int n=0; n<((ncnn::ConvolutionDepthWise*)layer)->group; n++)
  162. {
  163. const ncnn::Mat weight_data_n = ((ncnn::ConvolutionDepthWise*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  164. const float *data_n = weight_data_n;
  165. float max_value = std::numeric_limits<float>::min();
  166. for (int i = 0; i < weight_data_size_output; i++)
  167. max_value = std::max(max_value, std::fabs(data_n[i]));
  168. scales.push_back(127 / max_value);
  169. }
  170. weight_scales[name] = scales;
  171. }
  172. if (layer->type == "InnerProduct")
  173. {
  174. std::string name = layer->name;
  175. const int weight_data_size_output = ((ncnn::InnerProduct*)layer)->weight_data_size / ((ncnn::InnerProduct*)layer)->num_output;
  176. std::vector<float> scales;
  177. for (int n=0; n<((ncnn::InnerProduct*)layer)->num_output; n++)
  178. {
  179. const ncnn::Mat weight_data_n = ((ncnn::InnerProduct*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  180. const float *data_n = weight_data_n;
  181. float max_value = std::numeric_limits<float>::min();
  182. for (int i = 0; i < weight_data_size_output; i++)
  183. max_value = std::max(max_value, std::fabs(data_n[i]));
  184. scales.push_back(127 / max_value);
  185. }
  186. weight_scales[name] = scales;
  187. }
  188. }
  189. return 0;
  190. }
  191. class QuantizeData
  192. {
  193. public:
  194. QuantizeData(std::string layer_name, int num);
  195. int initial_blob_max(ncnn::Mat data);
  196. int initial_histogram_interval();
  197. int initial_histogram_value();
  198. int normalize_histogram();
  199. int update_histogram(ncnn::Mat data);
  200. float compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b);
  201. int threshold_distribution(const std::vector<float> &distribution, const int target_bin=128);
  202. float get_data_blob_scale();
  203. public:
  204. std::string name;
  205. float max_value;
  206. int num_bins;
  207. float histogram_interval;
  208. std::vector<float> histogram;
  209. float threshold;
  210. int threshold_bin;
  211. float scale;
  212. };
  213. QuantizeData::QuantizeData(std::string layer_name, int num)
  214. {
  215. name = layer_name;
  216. max_value = 0.0;
  217. num_bins = num;
  218. histogram_interval = 0.0;
  219. histogram.resize(num_bins);
  220. initial_histogram_value();
  221. }
  222. int QuantizeData::initial_blob_max(ncnn::Mat data)
  223. {
  224. int channel_num = data.c;
  225. int size = data.w * data.h;
  226. for (int q=0; q<channel_num; q++)
  227. {
  228. const float *data_n = data.channel(q);
  229. for(int i=0; i<size; i++)
  230. {
  231. max_value = std::max(max_value, std::fabs(data_n[i]));
  232. }
  233. }
  234. return 0;
  235. }
  236. int QuantizeData::initial_histogram_interval()
  237. {
  238. histogram_interval = max_value / num_bins;
  239. return 0;
  240. }
  241. int QuantizeData::initial_histogram_value()
  242. {
  243. for (size_t i=0; i<histogram.size(); i++)
  244. {
  245. histogram[i] = 0.00001;
  246. }
  247. return 0;
  248. }
  249. int QuantizeData::normalize_histogram()
  250. {
  251. const int length = histogram.size();
  252. float sum = 0;
  253. for (int i=0; i<length; i++)
  254. sum += histogram[i];
  255. for (int i=0; i<length; i++)
  256. histogram[i] /= sum;
  257. return 0;
  258. }
  259. int QuantizeData::update_histogram(ncnn::Mat data)
  260. {
  261. int channel_num = data.c;
  262. int size = data.w * data.h;
  263. for (int q=0; q<channel_num; q++)
  264. {
  265. const float *data_n = data.channel(q);
  266. for(int i=0; i<size; i++)
  267. {
  268. if (data_n[i] == 0)
  269. continue;
  270. int index = std::min(static_cast<int>(std::abs(data_n[i]) / histogram_interval), 2047);
  271. histogram[index]++;
  272. }
  273. }
  274. return 0;
  275. }
  276. float QuantizeData::compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b)
  277. {
  278. const int length = dist_a.size();
  279. assert(dist_b.size() == length);
  280. float result = 0;
  281. for (int i=0; i<length; i++)
  282. {
  283. if (dist_a[i] != 0)
  284. {
  285. if (dist_b[i] == 0)
  286. {
  287. result += 1;
  288. }
  289. else
  290. {
  291. result += dist_a[i] * log(dist_a[i] / dist_b[i]);
  292. }
  293. }
  294. }
  295. return result;
  296. }
  297. int QuantizeData::threshold_distribution(const std::vector<float> &distribution, const int target_bin)
  298. {
  299. int target_threshold = target_bin;
  300. float min_kl_divergence = 1000;
  301. const int length = distribution.size();
  302. std::vector<float> quantize_distribution(target_bin);
  303. float threshold_sum = 0;
  304. for (int threshold=target_bin; threshold<length; threshold++)
  305. {
  306. threshold_sum += distribution[threshold];
  307. }
  308. for (int threshold=target_bin; threshold<length; threshold++)
  309. {
  310. std::vector<float> t_distribution(distribution.begin(), distribution.begin()+threshold);
  311. t_distribution[threshold-1] += threshold_sum;
  312. threshold_sum -= distribution[threshold];
  313. // get P
  314. fill(quantize_distribution.begin(), quantize_distribution.end(), 0);
  315. const float num_per_bin = static_cast<float>(threshold) / target_bin;
  316. for (int i=0; i<target_bin; i++)
  317. {
  318. const float start = i * num_per_bin;
  319. const float end = start + num_per_bin;
  320. const int left_upper = ceil(start);
  321. if (left_upper > start)
  322. {
  323. const float left_scale = left_upper - start;
  324. quantize_distribution[i] += left_scale * distribution[left_upper - 1];
  325. }
  326. const int right_lower = floor(end);
  327. if (right_lower < end)
  328. {
  329. const float right_scale = end - right_lower;
  330. quantize_distribution[i] += right_scale * distribution[right_lower];
  331. }
  332. for (int j=left_upper; j<right_lower; j++)
  333. {
  334. quantize_distribution[i] += distribution[j];
  335. }
  336. }
  337. // get Q
  338. std::vector<float> expand_distribution(threshold, 0);
  339. for (int i=0; i<target_bin; i++)
  340. {
  341. const float start = i * num_per_bin;
  342. const float end = start + num_per_bin;
  343. float count = 0;
  344. const int left_upper = ceil(start);
  345. float left_scale = 0;
  346. if (left_upper > start)
  347. {
  348. left_scale = left_upper - start;
  349. if (distribution[left_upper - 1] != 0)
  350. {
  351. count += left_scale;
  352. }
  353. }
  354. const int right_lower = floor(end);
  355. float right_scale = 0;
  356. if (right_lower < end)
  357. {
  358. right_scale = end - right_lower;
  359. if (distribution[right_lower] != 0)
  360. {
  361. count += right_scale;
  362. }
  363. }
  364. for (int j=left_upper; j<right_lower; j++)
  365. {
  366. if (distribution[j] != 0)
  367. {
  368. count++;
  369. }
  370. }
  371. const float expand_value = quantize_distribution[i] / count;
  372. if (left_upper > start)
  373. {
  374. if (distribution[left_upper - 1] != 0)
  375. {
  376. expand_distribution[left_upper - 1] += expand_value * left_scale;
  377. }
  378. }
  379. if (right_lower < end)
  380. {
  381. if (distribution[right_lower] != 0)
  382. {
  383. expand_distribution[right_lower] += expand_value * right_scale;
  384. }
  385. }
  386. for (int j=left_upper; j<right_lower; j++)
  387. {
  388. if (distribution[j] != 0)
  389. {
  390. expand_distribution[j] += expand_value;
  391. }
  392. }
  393. }
  394. // kl
  395. float kl_divergence = compute_kl_divergence(t_distribution, expand_distribution);
  396. // the best num of bin
  397. if (kl_divergence < min_kl_divergence)
  398. {
  399. min_kl_divergence = kl_divergence;
  400. target_threshold = threshold;
  401. }
  402. }
  403. return target_threshold;
  404. }
  405. float QuantizeData::get_data_blob_scale()
  406. {
  407. normalize_histogram();
  408. threshold_bin = threshold_distribution(histogram);
  409. threshold = (threshold_bin + 0.5) * histogram_interval;
  410. scale = 127 / threshold;
  411. return scale;
  412. }
  413. struct PreParam
  414. {
  415. float mean[3];
  416. float norm[3];
  417. int weith;
  418. int height;
  419. bool swapRB;
  420. };
  421. static int post_training_quantize(const std::vector<std::string> filenames, const char* param_path, const char* bin_path, const char* table_path, struct PreParam per_param)
  422. {
  423. int size = filenames.size();
  424. QuantNet net;
  425. net.opt = g_default_option;
  426. net.load_param(param_path);
  427. net.load_model(bin_path);
  428. float mean_vals[3], norm_vals[3];
  429. int weith = per_param.weith;
  430. int height = per_param.height;
  431. bool swapRB = per_param.swapRB;
  432. mean_vals[0] = per_param.mean[0];
  433. mean_vals[1] = per_param.mean[1];
  434. mean_vals[2] = per_param.mean[2];
  435. norm_vals[0] = per_param.norm[0];
  436. norm_vals[1] = per_param.norm[1];
  437. norm_vals[2] = per_param.norm[2];
  438. g_blob_pool_allocator.clear();
  439. g_workspace_pool_allocator.clear();
  440. net.get_input_names();
  441. net.get_conv_names();
  442. net.get_conv_bottom_blob_names();
  443. net.get_conv_weight_blob_scales();
  444. if (net.input_names.size() <= 0)
  445. {
  446. fprintf(stderr, "not found [Input] Layer, Check your ncnn.param \n");
  447. return -1;
  448. }
  449. FILE *fp=fopen(table_path, "w");
  450. // save quantization scale of weight
  451. printf("====> Quantize the parameters.\n");
  452. for (size_t i=0; i<net.conv_names.size(); i++)
  453. {
  454. std::string layer_name = net.conv_names[i];
  455. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  456. std::vector<float> weight_scale_n = net.weight_scales[layer_name];
  457. fprintf(fp, "%s_param_0 ", layer_name.c_str());
  458. for (size_t j=0; j<weight_scale_n.size(); j++)
  459. fprintf(fp, "%f ", weight_scale_n[j]);
  460. fprintf(fp, "\n");
  461. }
  462. // initial quantization data
  463. std::vector<QuantizeData> quantize_datas;
  464. for (size_t i=0; i<net.conv_names.size(); i++)
  465. {
  466. std::string layer_name = net.conv_names[i];
  467. QuantizeData quantize_data(layer_name, 2048);
  468. quantize_datas.push_back(quantize_data);
  469. }
  470. // step 1 count the max value
  471. printf("====> Quantize the activation.\n");
  472. printf(" ====> step 1 : find the max value.\n");
  473. for (size_t i=0; i<filenames.size(); i++)
  474. {
  475. std::string img_name = filenames[i];
  476. if ((i+1)%100 == 0)
  477. fprintf(stderr, " %d/%d\n", (int)(i+1), (int)size);
  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, weith, 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 i=0; i<net.conv_names.size(); i++)
  493. {
  494. std::string layer_name = net.conv_names[i];
  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 (size_t j=0; j<quantize_datas.size(); j++)
  499. {
  500. if (quantize_datas[j].name == layer_name)
  501. {
  502. quantize_datas[j].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 (size_t j=0; j<quantize_datas.size(); j++)
  514. {
  515. if (quantize_datas[j].name == layer_name)
  516. {
  517. quantize_datas[j].initial_histogram_interval();
  518. fprintf(stderr, "%-20s : max = %-15f interval = %-10f\n", quantize_datas[j].name.c_str(), quantize_datas[j].max_value, quantize_datas[j].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<filenames.size(); i++)
  526. {
  527. std::string img_name = filenames[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, weith, 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 i=0; i<net.conv_names.size(); i++)
  545. {
  546. std::string layer_name = net.conv_names[i];
  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 (size_t j=0; j<quantize_datas.size(); j++)
  551. {
  552. if (quantize_datas[j].name == layer_name)
  553. {
  554. quantize_datas[j].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 (size_t j=0; j<quantize_datas.size(); j++)
  568. {
  569. if (quantize_datas[j].name == layer_name)
  570. {
  571. quantize_datas[j].get_data_blob_scale();
  572. fprintf(stderr, "bin : %-8d threshold : %-15f interval : %-10f scale : %-10f\n", \
  573. quantize_datas[j].threshold_bin, \
  574. quantize_datas[j].threshold, \
  575. quantize_datas[j].histogram_interval, \
  576. quantize_datas[j].scale);
  577. fprintf(fp, "%s %f\n", layer_name.c_str(), quantize_datas[j].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 << "usage: ncnn2table [-h] [-p] [-b] [-o] [-m] [-n] [-s] [-t]" << std::endl;
  590. std::cout << " -h, --help show this help message and exit" << std::endl;
  591. std::cout << " -p, --param path to ncnn.param file" << std::endl;
  592. std::cout << " -b, --bin path to ncnn.bin file" << std::endl;
  593. std::cout << " -i, --images path to calibration images" << std::endl;
  594. std::cout << " -o, --output path to output calibration tbale file" << std::endl;
  595. std::cout << " -m, --mean value of mean" << std::endl;
  596. std::cout << " -n, --norm value of normalize(scale value,defualt is 1)" << std::endl;
  597. std::cout << " -s, --size the size of input image(using the resize the original image,default is w=224,h=224)" << std::endl;
  598. std::cout << " -c --swapRB flag which indicates that swap first and last channels in 3-channel image is necessary" << std::endl;
  599. std::cout << " -t, --thread number of threads(defalut is 1)" << std::endl;
  600. 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;
  601. }
  602. // string.split('x')
  603. std::vector<std::string> split(const std::string &str,const std::string &pattern)
  604. {
  605. //const char* convert to char*
  606. char * strc = new char[strlen(str.c_str())+1];
  607. strcpy(strc, str.c_str());
  608. std::vector<std::string> resultVec;
  609. char* tmpStr = strtok(strc, pattern.c_str());
  610. while (tmpStr != NULL)
  611. {
  612. resultVec.push_back(std::string(tmpStr));
  613. tmpStr = strtok(NULL, pattern.c_str());
  614. }
  615. delete[] strc;
  616. return resultVec;
  617. }
  618. int main(int argc, char** argv)
  619. {
  620. std::cout << "--- ncnn post training quantization tool --- " << __TIME__ << " " << __DATE__ << std::endl;
  621. char* imagepath = NULL;
  622. char* parampath = NULL;
  623. char* binpath = NULL;
  624. char* tablepath = NULL;
  625. int num_threads = 1;
  626. struct PreParam pre_param = {
  627. .mean = {104.f, 117.f, 103.f},
  628. .norm = {1.f, 1.f, 1.f},
  629. .weith = 224,
  630. .height =224,
  631. .swapRB = false
  632. };
  633. int c;
  634. while (1)
  635. {
  636. int option_index = 0;
  637. static struct option long_options[] =
  638. {
  639. {"param", required_argument, 0, 'p' },
  640. {"bin", required_argument, 0, 'b' },
  641. {"images", required_argument, 0, 'i' },
  642. {"output", required_argument, 0, 'o' },
  643. {"mean", required_argument, 0, 'm' },
  644. {"norm", required_argument, 0, 'n' },
  645. {"size", required_argument, 0, 's' },
  646. {"swapRB", no_argument, 0, 'c' },
  647. {"thread", required_argument, 0, 't' },
  648. {"help", no_argument, 0, 'h' },
  649. {0, 0, 0, 0 }
  650. };
  651. c = getopt_long(argc, argv, "p:b:i:o:m:n:s:ct:h", long_options, &option_index);
  652. if (c == -1)
  653. break;
  654. switch (c)
  655. {
  656. case 'p':
  657. printf("param = '%s'\n", optarg);
  658. parampath = optarg;
  659. break;
  660. case 'b':
  661. printf("bin = '%s'\n", optarg);
  662. binpath = optarg;
  663. break;
  664. case 'i':
  665. printf("images = '%s'\n", optarg);
  666. imagepath = optarg;
  667. break;
  668. case 'o':
  669. printf("output = '%s'\n", optarg);
  670. tablepath = optarg;
  671. break;
  672. case 'm':
  673. {
  674. printf("mean = '%s'\n", optarg);
  675. std::string temp(optarg);
  676. std::vector<std::string> array = split(temp, ",");
  677. pre_param.mean[0] = atof(array[0].c_str());
  678. pre_param.mean[1] = atof(array[1].c_str());
  679. pre_param.mean[2] = atof(array[2].c_str());
  680. }
  681. break;
  682. case 'n':
  683. {
  684. printf("norm = '%s'\n", optarg);
  685. std::string temp(optarg);
  686. std::vector<std::string> array = split(temp, ",");
  687. pre_param.norm[0] = atof(array[0].c_str());
  688. pre_param.norm[1] = atof(array[1].c_str());
  689. pre_param.norm[2] = atof(array[2].c_str());
  690. }
  691. break;
  692. case 's':
  693. {
  694. printf("size = '%s'\n", optarg);
  695. std::string temp(optarg);
  696. std::vector<std::string> array = split(temp, ",");
  697. pre_param.weith = atoi(array[0].c_str());
  698. pre_param.height = atoi(array[1].c_str());
  699. }
  700. break;
  701. case 'c':
  702. {
  703. printf("swapRB = '%s'\n", "true");
  704. pre_param.swapRB = true;
  705. }
  706. break;
  707. case 't':
  708. printf("thread = '%s'\n", optarg);
  709. num_threads = atoi(optarg);
  710. break;
  711. case 'h':
  712. case '?':
  713. showUsage();
  714. return 0;
  715. default:
  716. showUsage();
  717. }
  718. }
  719. // check the input param
  720. if (imagepath == NULL || parampath == NULL || binpath == NULL || tablepath == NULL)
  721. {
  722. fprintf(stderr, "someone path maybe empty,please check it and try again.\n");
  723. return 0;
  724. }
  725. g_blob_pool_allocator.set_size_compare_ratio(0.0f);
  726. g_workspace_pool_allocator.set_size_compare_ratio(0.5f);
  727. // default option
  728. g_default_option.lightmode = true;
  729. g_default_option.num_threads = num_threads;
  730. g_default_option.blob_allocator = &g_blob_pool_allocator;
  731. g_default_option.workspace_allocator = &g_workspace_pool_allocator;
  732. g_default_option.use_winograd_convolution = true;
  733. g_default_option.use_sgemm_convolution = true;
  734. g_default_option.use_int8_inference = true;
  735. g_default_option.use_fp16_packed = true;
  736. g_default_option.use_fp16_storage = true;
  737. g_default_option.use_fp16_arithmetic = true;
  738. g_default_option.use_int8_storage = true;
  739. g_default_option.use_int8_arithmetic = true;
  740. ncnn::set_cpu_powersave(2);
  741. ncnn::set_omp_dynamic(0);
  742. ncnn::set_omp_num_threads(num_threads);
  743. std::vector<std::string> filenames;
  744. // parse the image file.
  745. parse_images_dir(imagepath, filenames);
  746. // get the calibration table file, and save it.
  747. int ret = post_training_quantize(filenames, parampath, binpath, tablepath, pre_param);
  748. if (!ret)
  749. fprintf(stderr, "\nNCNN Int8 Calibration table create success, best wish for your INT8 inference has a low accuracy loss...\\(^▽^)/...233...\n");
  750. return 0;
  751. }