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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  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. }
  93. int QuantNet::get_conv_names()
  94. {
  95. for (size_t i=0; i<layers.size(); i++)
  96. {
  97. ncnn::Layer* layer = layers[i];
  98. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  99. {
  100. std::string name = layer->name;
  101. conv_names.push_back(name);
  102. }
  103. }
  104. return 0;
  105. }
  106. int QuantNet::get_conv_bottom_blob_names()
  107. {
  108. // find conv bottom name or index
  109. for (size_t i=0; i<layers.size(); i++)
  110. {
  111. ncnn::Layer* layer = layers[i];
  112. if (layer->type == "Convolution" || layer->type == "ConvolutionDepthWise" || layer->type == "InnerProduct")
  113. {
  114. std::string name = layer->name;
  115. std::string bottom_blob_name = blobs[layer->bottoms[0]].name;
  116. conv_bottom_blob_names[name] = bottom_blob_name;
  117. }
  118. }
  119. return 0;
  120. }
  121. int QuantNet::get_conv_weight_blob_scales()
  122. {
  123. for (size_t i=0; i<layers.size(); i++)
  124. {
  125. ncnn::Layer* layer = layers[i];
  126. if (layer->type == "Convolution")
  127. {
  128. std::string name = layer->name;
  129. const int weight_data_size_output = ((ncnn::Convolution*)layer)->weight_data_size / ((ncnn::Convolution*)layer)->num_output;
  130. std::vector<float> scales;
  131. // int8 winograd F43 needs weight data to use 6bit quantization
  132. bool quant_6bit = false;
  133. int kernel_w = ((ncnn::Convolution*)layer)->kernel_w;
  134. int kernel_h = ((ncnn::Convolution*)layer)->kernel_h;
  135. int dilation_w = ((ncnn::Convolution*)layer)->dilation_w;
  136. int dilation_h = ((ncnn::Convolution*)layer)->dilation_h;
  137. int stride_w = ((ncnn::Convolution*)layer)->stride_w;
  138. int stride_h = ((ncnn::Convolution*)layer)->stride_h;
  139. if (kernel_w == 3 && kernel_h == 3 && dilation_w == 1 && dilation_h == 1 && stride_w == 1 && stride_h == 1)
  140. quant_6bit = true;
  141. for (int n=0; n<((ncnn::Convolution*)layer)->num_output; n++)
  142. {
  143. const ncnn::Mat weight_data_n = ((ncnn::Convolution*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  144. const float *data_n = weight_data_n;
  145. float max_value = std::numeric_limits<float>::min();
  146. for (int i = 0; i < weight_data_size_output; i++)
  147. max_value = std::max(max_value, std::fabs(data_n[i]));
  148. if (quant_6bit)
  149. scales.push_back(31 / max_value);
  150. else
  151. scales.push_back(127 / max_value);
  152. }
  153. weight_scales[name] = scales;
  154. }
  155. if (layer->type == "ConvolutionDepthWise")
  156. {
  157. std::string name = layer->name;
  158. const int weight_data_size_output = ((ncnn::ConvolutionDepthWise*)layer)->weight_data_size / ((ncnn::ConvolutionDepthWise*)layer)->group;
  159. std::vector<float> scales;
  160. for (int n=0; n<((ncnn::ConvolutionDepthWise*)layer)->group; n++)
  161. {
  162. const ncnn::Mat weight_data_n = ((ncnn::ConvolutionDepthWise*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  163. const float *data_n = weight_data_n;
  164. float max_value = std::numeric_limits<float>::min();
  165. for (int i = 0; i < weight_data_size_output; i++)
  166. max_value = std::max(max_value, std::fabs(data_n[i]));
  167. scales.push_back(127 / max_value);
  168. }
  169. weight_scales[name] = scales;
  170. }
  171. if (layer->type == "InnerProduct")
  172. {
  173. std::string name = layer->name;
  174. const int weight_data_size_output = ((ncnn::InnerProduct*)layer)->weight_data_size / ((ncnn::InnerProduct*)layer)->num_output;
  175. std::vector<float> scales;
  176. for (int n=0; n<((ncnn::InnerProduct*)layer)->num_output; n++)
  177. {
  178. const ncnn::Mat weight_data_n = ((ncnn::InnerProduct*)layer)->weight_data.range(weight_data_size_output * n, weight_data_size_output);
  179. const float *data_n = weight_data_n;
  180. float max_value = std::numeric_limits<float>::min();
  181. for (int i = 0; i < weight_data_size_output; i++)
  182. max_value = std::max(max_value, std::fabs(data_n[i]));
  183. scales.push_back(127 / max_value);
  184. }
  185. weight_scales[name] = scales;
  186. }
  187. }
  188. return 0;
  189. }
  190. class QuantizeData
  191. {
  192. public:
  193. QuantizeData(std::string layer_name, int num);
  194. int initial_blob_max(ncnn::Mat data);
  195. int initial_histogram_interval();
  196. int initial_histogram_value();
  197. int normalize_histogram();
  198. int update_histogram(ncnn::Mat data);
  199. float compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b);
  200. int threshold_distribution(const std::vector<float> &distribution, const int target_bin=128);
  201. float get_data_blob_scale();
  202. public:
  203. std::string name;
  204. float max_value;
  205. int num_bins;
  206. float histogram_interval;
  207. std::vector<float> histogram;
  208. float threshold;
  209. int threshold_bin;
  210. float scale;
  211. };
  212. QuantizeData::QuantizeData(std::string layer_name, int num)
  213. {
  214. name = layer_name;
  215. max_value = 0.0;
  216. num_bins = num;
  217. histogram_interval = 0.0;
  218. histogram.resize(num_bins);
  219. initial_histogram_value();
  220. }
  221. int QuantizeData::initial_blob_max(ncnn::Mat data)
  222. {
  223. int channel_num = data.c;
  224. int size = data.w * data.h;
  225. for (int q=0; q<channel_num; q++)
  226. {
  227. const float *data_n = data.channel(q);
  228. for(int i=0; i<size; i++)
  229. {
  230. max_value = std::max(max_value, std::fabs(data_n[i]));
  231. }
  232. }
  233. return 0;
  234. }
  235. int QuantizeData::initial_histogram_interval()
  236. {
  237. histogram_interval = max_value / num_bins;
  238. return 0;
  239. }
  240. int QuantizeData::initial_histogram_value()
  241. {
  242. for (size_t i=0; i<histogram.size(); i++)
  243. {
  244. histogram[i] = 0.00001;
  245. }
  246. return 0;
  247. }
  248. int QuantizeData::normalize_histogram()
  249. {
  250. const int length = histogram.size();
  251. float sum = 0;
  252. for (int i=0; i<length; i++)
  253. sum += histogram[i];
  254. for (int i=0; i<length; i++)
  255. histogram[i] /= sum;
  256. return 0;
  257. }
  258. int QuantizeData::update_histogram(ncnn::Mat data)
  259. {
  260. int channel_num = data.c;
  261. int size = data.w * data.h;
  262. for (int q=0; q<channel_num; q++)
  263. {
  264. const float *data_n = data.channel(q);
  265. for(int i=0; i<size; i++)
  266. {
  267. if (data_n[i] == 0)
  268. continue;
  269. int index = std::min(static_cast<int>(std::abs(data_n[i]) / histogram_interval), 2047);
  270. histogram[index]++;
  271. }
  272. }
  273. return 0;
  274. }
  275. float QuantizeData::compute_kl_divergence(const std::vector<float> &dist_a, const std::vector<float> &dist_b)
  276. {
  277. const int length = dist_a.size();
  278. assert(dist_b.size() == length);
  279. float result = 0;
  280. for (int i=0; i<length; i++)
  281. {
  282. if (dist_a[i] != 0)
  283. {
  284. if (dist_b[i] == 0)
  285. {
  286. result += 1;
  287. }
  288. else
  289. {
  290. result += dist_a[i] * log(dist_a[i] / dist_b[i]);
  291. }
  292. }
  293. }
  294. return result;
  295. }
  296. int QuantizeData::threshold_distribution(const std::vector<float> &distribution, const int target_bin)
  297. {
  298. int target_threshold = target_bin;
  299. float min_kl_divergence = 1000;
  300. const int length = distribution.size();
  301. std::vector<float> quantize_distribution(target_bin);
  302. float threshold_sum = 0;
  303. for (int threshold=target_bin; threshold<length; threshold++)
  304. {
  305. threshold_sum += distribution[threshold];
  306. }
  307. for (int threshold=target_bin; threshold<length; threshold++)
  308. {
  309. std::vector<float> t_distribution(distribution.begin(), distribution.begin()+threshold);
  310. t_distribution[threshold-1] += threshold_sum;
  311. threshold_sum -= distribution[threshold];
  312. // get P
  313. fill(quantize_distribution.begin(), quantize_distribution.end(), 0);
  314. const float num_per_bin = static_cast<float>(threshold) / target_bin;
  315. for (int i=0; i<target_bin; i++)
  316. {
  317. const float start = i * num_per_bin;
  318. const float end = start + num_per_bin;
  319. const int left_upper = ceil(start);
  320. if (left_upper > start)
  321. {
  322. const float left_scale = left_upper - start;
  323. quantize_distribution[i] += left_scale * distribution[left_upper - 1];
  324. }
  325. const int right_lower = floor(end);
  326. if (right_lower < end)
  327. {
  328. const float right_scale = end - right_lower;
  329. quantize_distribution[i] += right_scale * distribution[right_lower];
  330. }
  331. for (int j=left_upper; j<right_lower; j++)
  332. {
  333. quantize_distribution[i] += distribution[j];
  334. }
  335. }
  336. // get Q
  337. std::vector<float> expand_distribution(threshold, 0);
  338. for (int i=0; i<target_bin; i++)
  339. {
  340. const float start = i * num_per_bin;
  341. const float end = start + num_per_bin;
  342. float count = 0;
  343. const int left_upper = ceil(start);
  344. float left_scale = 0;
  345. if (left_upper > start)
  346. {
  347. left_scale = left_upper - start;
  348. if (distribution[left_upper - 1] != 0)
  349. {
  350. count += left_scale;
  351. }
  352. }
  353. const int right_lower = floor(end);
  354. float right_scale = 0;
  355. if (right_lower < end)
  356. {
  357. right_scale = end - right_lower;
  358. if (distribution[right_lower] != 0)
  359. {
  360. count += right_scale;
  361. }
  362. }
  363. for (int j=left_upper; j<right_lower; j++)
  364. {
  365. if (distribution[j] != 0)
  366. {
  367. count++;
  368. }
  369. }
  370. const float expand_value = quantize_distribution[i] / count;
  371. if (left_upper > start)
  372. {
  373. if (distribution[left_upper - 1] != 0)
  374. {
  375. expand_distribution[left_upper - 1] += expand_value * left_scale;
  376. }
  377. }
  378. if (right_lower < end)
  379. {
  380. if (distribution[right_lower] != 0)
  381. {
  382. expand_distribution[right_lower] += expand_value * right_scale;
  383. }
  384. }
  385. for (int j=left_upper; j<right_lower; j++)
  386. {
  387. if (distribution[j] != 0)
  388. {
  389. expand_distribution[j] += expand_value;
  390. }
  391. }
  392. }
  393. // kl
  394. float kl_divergence = compute_kl_divergence(t_distribution, expand_distribution);
  395. // the best num of bin
  396. if (kl_divergence < min_kl_divergence)
  397. {
  398. min_kl_divergence = kl_divergence;
  399. target_threshold = threshold;
  400. }
  401. }
  402. return target_threshold;
  403. }
  404. float QuantizeData::get_data_blob_scale()
  405. {
  406. normalize_histogram();
  407. threshold_bin = threshold_distribution(histogram);
  408. threshold = (threshold_bin + 0.5) * histogram_interval;
  409. scale = 127 / threshold;
  410. return scale;
  411. }
  412. struct PreParam
  413. {
  414. float mean[3];
  415. float norm[3];
  416. int weith;
  417. int height;
  418. bool swapRB;
  419. };
  420. 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)
  421. {
  422. int size = filenames.size();
  423. QuantNet net;
  424. net.opt = g_default_option;
  425. net.load_param(param_path);
  426. net.load_model(bin_path);
  427. float mean_vals[3], norm_vals[3];
  428. int weith = per_param.weith;
  429. int height = per_param.height;
  430. bool swapRB = per_param.swapRB;
  431. mean_vals[0] = per_param.mean[0];
  432. mean_vals[1] = per_param.mean[1];
  433. mean_vals[2] = per_param.mean[2];
  434. norm_vals[0] = per_param.norm[0];
  435. norm_vals[1] = per_param.norm[1];
  436. norm_vals[2] = per_param.norm[2];
  437. g_blob_pool_allocator.clear();
  438. g_workspace_pool_allocator.clear();
  439. net.get_input_names();
  440. net.get_conv_names();
  441. net.get_conv_bottom_blob_names();
  442. net.get_conv_weight_blob_scales();
  443. FILE *fp=fopen(table_path, "w");
  444. // save quantization scale of weight
  445. printf("====> Quantize the parameters.\n");
  446. for (size_t i=0; i<net.conv_names.size(); i++)
  447. {
  448. std::string layer_name = net.conv_names[i];
  449. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  450. std::vector<float> weight_scale_n = net.weight_scales[layer_name];
  451. fprintf(fp, "%s_param_0 ", layer_name.c_str());
  452. for (size_t j=0; j<weight_scale_n.size(); j++)
  453. fprintf(fp, "%f ", weight_scale_n[j]);
  454. fprintf(fp, "\n");
  455. }
  456. // initial quantization data
  457. std::vector<QuantizeData> quantize_datas;
  458. for (size_t i=0; i<net.conv_names.size(); i++)
  459. {
  460. std::string layer_name = net.conv_names[i];
  461. QuantizeData quantize_data(layer_name, 2048);
  462. quantize_datas.push_back(quantize_data);
  463. }
  464. // step 1 count the max value
  465. printf("====> Quantize the activation.\n");
  466. printf(" ====> step 1 : find the max value.\n");
  467. for (size_t i=0; i<filenames.size(); i++)
  468. {
  469. std::string img_name = filenames[i];
  470. if ((i+1)%100 == 0)
  471. fprintf(stderr, " %d/%d\n", (int)(i+1), (int)size);
  472. #if OpenCV_VERSION_MAJOR > 2
  473. cv::Mat bgr = cv::imread(img_name, cv::IMREAD_COLOR);
  474. #else
  475. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  476. #endif
  477. if (bgr.empty())
  478. {
  479. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  480. return -1;
  481. }
  482. 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);
  483. in.substract_mean_normalize(mean_vals, norm_vals);
  484. ncnn::Extractor ex = net.create_extractor();
  485. ex.input(net.input_names[0].c_str(), in);
  486. for (size_t i=0; i<net.conv_names.size(); i++)
  487. {
  488. std::string layer_name = net.conv_names[i];
  489. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  490. ncnn::Mat out;
  491. ex.extract(blob_name.c_str(), out);
  492. for (size_t j=0; j<quantize_datas.size(); j++)
  493. {
  494. if (quantize_datas[j].name == layer_name)
  495. {
  496. quantize_datas[j].initial_blob_max(out);
  497. break;
  498. }
  499. }
  500. }
  501. }
  502. // step 2 histogram_interval
  503. printf(" ====> step 2 : generatue the histogram_interval.\n");
  504. for (size_t i=0; i<net.conv_names.size(); i++)
  505. {
  506. std::string layer_name = net.conv_names[i];
  507. for (size_t j=0; j<quantize_datas.size(); j++)
  508. {
  509. if (quantize_datas[j].name == layer_name)
  510. {
  511. quantize_datas[j].initial_histogram_interval();
  512. fprintf(stderr, "%-20s : max = %-15f interval = %-10f\n", quantize_datas[j].name.c_str(), quantize_datas[j].max_value, quantize_datas[j].histogram_interval);
  513. break;
  514. }
  515. }
  516. }
  517. // step 3 histogram
  518. printf(" ====> step 3 : generatue the histogram.\n");
  519. for (size_t i=0; i<filenames.size(); i++)
  520. {
  521. std::string img_name = filenames[i];
  522. if ((i+1)%100 == 0)
  523. fprintf(stderr, " %d/%d\n", (int)(i+1), (int)size);
  524. #if OpenCV_VERSION_MAJOR > 2
  525. cv::Mat bgr = cv::imread(img_name, cv::IMREAD_COLOR);
  526. #else
  527. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  528. #endif
  529. if (bgr.empty())
  530. {
  531. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  532. return -1;
  533. }
  534. 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);
  535. in.substract_mean_normalize(mean_vals, norm_vals);
  536. ncnn::Extractor ex = net.create_extractor();
  537. ex.input(net.input_names[0].c_str(), in);
  538. for (size_t i=0; i<net.conv_names.size(); i++)
  539. {
  540. std::string layer_name = net.conv_names[i];
  541. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  542. ncnn::Mat out;
  543. ex.extract(blob_name.c_str(), out);
  544. for (size_t j=0; j<quantize_datas.size(); j++)
  545. {
  546. if (quantize_datas[j].name == layer_name)
  547. {
  548. quantize_datas[j].update_histogram(out);
  549. break;
  550. }
  551. }
  552. }
  553. }
  554. // step4 kld
  555. printf(" ====> step 4 : using kld to find the best threshold value.\n");
  556. for (size_t i=0; i<net.conv_names.size(); i++)
  557. {
  558. std::string layer_name = net.conv_names[i];
  559. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  560. fprintf(stderr, "%-20s ", layer_name.c_str());
  561. for (size_t j=0; j<quantize_datas.size(); j++)
  562. {
  563. if (quantize_datas[j].name == layer_name)
  564. {
  565. quantize_datas[j].get_data_blob_scale();
  566. fprintf(stderr, "bin : %-8d threshold : %-15f interval : %-10f scale : %-10f\n", \
  567. quantize_datas[j].threshold_bin, \
  568. quantize_datas[j].threshold, \
  569. quantize_datas[j].histogram_interval, \
  570. quantize_datas[j].scale);
  571. fprintf(fp, "%s %f\n", layer_name.c_str(), quantize_datas[j].scale);
  572. break;
  573. }
  574. }
  575. }
  576. fclose(fp);
  577. printf("====> Save the calibration table done.\n");
  578. return 0;
  579. }
  580. // usage
  581. void showUsage()
  582. {
  583. std::cout << "usage: ncnn2table [-h] [-p] [-b] [-o] [-m] [-n] [-s] [-t]" << std::endl;
  584. std::cout << " -h, --help show this help message and exit" << std::endl;
  585. std::cout << " -p, --param path to ncnn.param file" << std::endl;
  586. std::cout << " -b, --bin path to ncnn.bin file" << std::endl;
  587. std::cout << " -i, --images path to calibration images" << std::endl;
  588. std::cout << " -o, --output path to output calibration tbale file" << std::endl;
  589. std::cout << " -m, --mean value of mean" << std::endl;
  590. std::cout << " -n, --norm value of normalize(scale value,defualt is 1)" << std::endl;
  591. std::cout << " -s, --size the size of input image(using the resize the original image,default is w=224,h=224)" << std::endl;
  592. std::cout << " -c --swapRB flag which indicates that swap first and last channels in 3-channel image is necessary" << std::endl;
  593. std::cout << " -t, --thread number of threads(defalut is 1)" << std::endl;
  594. 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;
  595. }
  596. // string.split('x')
  597. std::vector<std::string> split(const std::string &str,const std::string &pattern)
  598. {
  599. //const char* convert to char*
  600. char * strc = new char[strlen(str.c_str())+1];
  601. strcpy(strc, str.c_str());
  602. std::vector<std::string> resultVec;
  603. char* tmpStr = strtok(strc, pattern.c_str());
  604. while (tmpStr != NULL)
  605. {
  606. resultVec.push_back(std::string(tmpStr));
  607. tmpStr = strtok(NULL, pattern.c_str());
  608. }
  609. delete[] strc;
  610. return resultVec;
  611. }
  612. int main(int argc, char** argv)
  613. {
  614. std::cout << "--- ncnn post training quantization tool --- " << __TIME__ << " " << __DATE__ << std::endl;
  615. char* imagepath = NULL;
  616. char* parampath = NULL;
  617. char* binpath = NULL;
  618. char* tablepath = NULL;
  619. int num_threads = 1;
  620. struct PreParam pre_param = {
  621. .mean = {104.f, 117.f, 103.f},
  622. .norm = {1.f, 1.f, 1.f},
  623. .weith = 224,
  624. .height =224,
  625. .swapRB = false
  626. };
  627. int c;
  628. while (1)
  629. {
  630. int option_index = 0;
  631. static struct option long_options[] =
  632. {
  633. {"param", required_argument, 0, 'p' },
  634. {"bin", required_argument, 0, 'b' },
  635. {"images", required_argument, 0, 'i' },
  636. {"output", required_argument, 0, 'o' },
  637. {"mean", required_argument, 0, 'm' },
  638. {"norm", required_argument, 0, 'n' },
  639. {"size", required_argument, 0, 's' },
  640. {"swapRB", no_argument, 0, 'c' },
  641. {"thread", required_argument, 0, 't' },
  642. {"help", no_argument, 0, 'h' },
  643. {0, 0, 0, 0 }
  644. };
  645. c = getopt_long(argc, argv, "p:b:i:o:m:n:s:ct:h", long_options, &option_index);
  646. if (c == -1)
  647. break;
  648. switch (c)
  649. {
  650. case 'p':
  651. printf("param = '%s'\n", optarg);
  652. parampath = optarg;
  653. break;
  654. case 'b':
  655. printf("bin = '%s'\n", optarg);
  656. binpath = optarg;
  657. break;
  658. case 'i':
  659. printf("images = '%s'\n", optarg);
  660. imagepath = optarg;
  661. break;
  662. case 'o':
  663. printf("output = '%s'\n", optarg);
  664. tablepath = optarg;
  665. break;
  666. case 'm':
  667. {
  668. printf("mean = '%s'\n", optarg);
  669. std::string temp(optarg);
  670. std::vector<std::string> array = split(temp, ",");
  671. pre_param.mean[0] = atof(array[0].c_str());
  672. pre_param.mean[1] = atof(array[1].c_str());
  673. pre_param.mean[2] = atof(array[2].c_str());
  674. }
  675. break;
  676. case 'n':
  677. {
  678. printf("norm = '%s'\n", optarg);
  679. std::string temp(optarg);
  680. std::vector<std::string> array = split(temp, ",");
  681. pre_param.norm[0] = atof(array[0].c_str());
  682. pre_param.norm[1] = atof(array[1].c_str());
  683. pre_param.norm[2] = atof(array[2].c_str());
  684. }
  685. break;
  686. case 's':
  687. {
  688. printf("size = '%s'\n", optarg);
  689. std::string temp(optarg);
  690. std::vector<std::string> array = split(temp, ",");
  691. pre_param.weith = atoi(array[0].c_str());
  692. pre_param.height = atoi(array[1].c_str());
  693. }
  694. break;
  695. case 'c':
  696. {
  697. printf("swapRB = '%s'\n", "true");
  698. pre_param.swapRB = true;
  699. }
  700. break;
  701. case 't':
  702. printf("thread = '%s'\n", optarg);
  703. num_threads = atoi(optarg);
  704. break;
  705. case 'h':
  706. case '?':
  707. showUsage();
  708. return 0;
  709. default:
  710. showUsage();
  711. }
  712. }
  713. // check the input param
  714. if (imagepath == NULL || parampath == NULL || binpath == NULL || tablepath == NULL)
  715. {
  716. fprintf(stderr, "someone path maybe empty,please check it and try again.\n");
  717. return 0;
  718. }
  719. g_blob_pool_allocator.set_size_compare_ratio(0.0f);
  720. g_workspace_pool_allocator.set_size_compare_ratio(0.5f);
  721. // default option
  722. g_default_option.lightmode = true;
  723. g_default_option.num_threads = num_threads;
  724. g_default_option.blob_allocator = &g_blob_pool_allocator;
  725. g_default_option.workspace_allocator = &g_workspace_pool_allocator;
  726. g_default_option.use_winograd_convolution = true;
  727. g_default_option.use_sgemm_convolution = true;
  728. g_default_option.use_int8_inference = true;
  729. g_default_option.use_fp16_packed = true;
  730. g_default_option.use_fp16_storage = true;
  731. g_default_option.use_fp16_arithmetic = true;
  732. g_default_option.use_int8_storage = true;
  733. g_default_option.use_int8_arithmetic = true;
  734. ncnn::set_cpu_powersave(2);
  735. ncnn::set_omp_dynamic(0);
  736. ncnn::set_omp_num_threads(num_threads);
  737. std::vector<std::string> filenames;
  738. // parse the image file.
  739. parse_images_dir(imagepath, filenames);
  740. // get the calibration table file, and save it.
  741. int ret = post_training_quantize(filenames, parampath, binpath, tablepath, pre_param);
  742. if (!ret)
  743. fprintf(stderr, "\nNCNN Int8 Calibration table create success, best wish for your INT8 inference has a low accuracy loss...\\(^▽^)/...233...\n");
  744. return 0;
  745. }