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

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