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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  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. bool swapRB;
  401. };
  402. 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)
  403. {
  404. int size = filenames.size();
  405. QuantNet net;
  406. net.opt = g_default_option;
  407. net.load_param(param_path);
  408. net.load_model(bin_path);
  409. float mean_vals[3], norm_vals[3];
  410. int weith = per_param.weith;
  411. int height = per_param.height;
  412. bool swapRB = per_param.swapRB;
  413. mean_vals[0] = per_param.mean[0];
  414. mean_vals[1] = per_param.mean[1];
  415. mean_vals[2] = per_param.mean[2];
  416. norm_vals[0] = per_param.norm[0];
  417. norm_vals[1] = per_param.norm[1];
  418. norm_vals[2] = per_param.norm[2];
  419. g_blob_pool_allocator.clear();
  420. g_workspace_pool_allocator.clear();
  421. net.get_conv_names();
  422. net.get_conv_bottom_blob_names();
  423. net.get_conv_weight_blob_scales();
  424. FILE *fp=fopen(table_path, "w");
  425. // save quantization scale of weight
  426. printf("====> Quantize the parameters.\n");
  427. for (size_t i=0; i<net.conv_names.size(); i++)
  428. {
  429. std::string layer_name = net.conv_names[i];
  430. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  431. std::vector<float> weight_scale_n = net.weight_scales[layer_name];
  432. fprintf(fp, "%s_param_0 ", layer_name.c_str());
  433. for (size_t j=0; j<weight_scale_n.size(); j++)
  434. fprintf(fp, "%f ", weight_scale_n[j]);
  435. fprintf(fp, "\n");
  436. }
  437. // initial quantization data
  438. std::vector<QuantizeData> quantize_datas;
  439. for (size_t i=0; i<net.conv_names.size(); i++)
  440. {
  441. std::string layer_name = net.conv_names[i];
  442. QuantizeData quantize_data(layer_name, 2048);
  443. quantize_datas.push_back(quantize_data);
  444. }
  445. // step 1 count the max value
  446. printf("====> Quantize the activation.\n");
  447. printf(" ====> step 1 : find the max value.\n");
  448. for (size_t i=0; i<filenames.size(); i++)
  449. {
  450. std::string img_name = filenames[i];
  451. if ((i+1)%100 == 0)
  452. fprintf(stderr, " %d/%d\n", (int)(i+1), (int)size);
  453. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  454. if (bgr.empty())
  455. {
  456. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  457. return -1;
  458. }
  459. 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);
  460. in.substract_mean_normalize(mean_vals, norm_vals);
  461. ncnn::Extractor ex = net.create_extractor();
  462. ex.input("data", in);
  463. for (size_t i=0; i<net.conv_names.size(); i++)
  464. {
  465. std::string layer_name = net.conv_names[i];
  466. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  467. ncnn::Mat out;
  468. ex.extract(blob_name.c_str(), out);
  469. for (size_t j=0; j<quantize_datas.size(); j++)
  470. {
  471. if (quantize_datas[j].name == layer_name)
  472. {
  473. quantize_datas[j].initial_blob_max(out);
  474. break;
  475. }
  476. }
  477. }
  478. }
  479. // step 2 histogram_interval
  480. printf(" ====> step 2 : generatue the histogram_interval.\n");
  481. for (size_t i=0; i<net.conv_names.size(); i++)
  482. {
  483. std::string layer_name = net.conv_names[i];
  484. for (size_t j=0; j<quantize_datas.size(); j++)
  485. {
  486. if (quantize_datas[j].name == layer_name)
  487. {
  488. quantize_datas[j].initial_histogram_interval();
  489. fprintf(stderr, "%-20s : max = %-15f interval = %-10f\n", quantize_datas[j].name.c_str(), quantize_datas[j].max_value, quantize_datas[j].histogram_interval);
  490. break;
  491. }
  492. }
  493. }
  494. // step 3 histogram
  495. printf(" ====> step 3 : generatue the histogram.\n");
  496. for (size_t i=0; i<filenames.size(); i++)
  497. {
  498. std::string img_name = filenames[i];
  499. if ((i+1)%100 == 0)
  500. fprintf(stderr, " %d/%d\n", (int)(i+1), (int)size);
  501. cv::Mat bgr = cv::imread(img_name, CV_LOAD_IMAGE_COLOR);
  502. if (bgr.empty())
  503. {
  504. fprintf(stderr, "cv::imread %s failed\n", img_name.c_str());
  505. return -1;
  506. }
  507. 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);
  508. in.substract_mean_normalize(mean_vals, norm_vals);
  509. ncnn::Extractor ex = net.create_extractor();
  510. ex.input("data", in);
  511. for (size_t i=0; i<net.conv_names.size(); i++)
  512. {
  513. std::string layer_name = net.conv_names[i];
  514. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  515. ncnn::Mat out;
  516. ex.extract(blob_name.c_str(), out);
  517. for (size_t j=0; j<quantize_datas.size(); j++)
  518. {
  519. if (quantize_datas[j].name == layer_name)
  520. {
  521. quantize_datas[j].update_histogram(out);
  522. break;
  523. }
  524. }
  525. }
  526. }
  527. // step4 kld
  528. printf(" ====> step 4 : using kld to find the best threshold value.\n");
  529. for (size_t i=0; i<net.conv_names.size(); i++)
  530. {
  531. std::string layer_name = net.conv_names[i];
  532. std::string blob_name = net.conv_bottom_blob_names[layer_name];
  533. fprintf(stderr, "%-20s ", layer_name.c_str());
  534. for (size_t j=0; j<quantize_datas.size(); j++)
  535. {
  536. if (quantize_datas[j].name == layer_name)
  537. {
  538. quantize_datas[j].get_data_blob_scale();
  539. fprintf(stderr, "bin : %-8d threshold : %-15f interval : %-10f scale : %-10f\n", \
  540. quantize_datas[j].threshold_bin, \
  541. quantize_datas[j].threshold, \
  542. quantize_datas[j].histogram_interval, \
  543. quantize_datas[j].scale);
  544. fprintf(fp, "%s %f\n", layer_name.c_str(), quantize_datas[j].scale);
  545. break;
  546. }
  547. }
  548. }
  549. fclose(fp);
  550. printf("====> Save the calibration table done.\n");
  551. return 0;
  552. }
  553. // usage
  554. void showUsage()
  555. {
  556. std::cout << "usage: ncnn2table [-h] [-p] [-b] [-o] [-m] [-n] [-s] [-t]" << std::endl;
  557. std::cout << " -h, --help show this help message and exit" << std::endl;
  558. std::cout << " -p, --param path to ncnn.param file" << std::endl;
  559. std::cout << " -b, --bin path to ncnn.bin file" << std::endl;
  560. std::cout << " -i, --images path to calibration images" << std::endl;
  561. std::cout << " -o, --output path to output calibration tbale file" << std::endl;
  562. std::cout << " -m, --mean value of mean" << std::endl;
  563. std::cout << " -n, --norm value of normalize(scale value,defualt is 1)" << std::endl;
  564. std::cout << " -s, --size the size of input image(using the resize the original image,default is w=224,h=224)" << std::endl;
  565. std::cout << " -c --swapRB flag which indicates that swap first and last channels in 3-channel image is necessary" << std::endl;
  566. std::cout << " -t, --thread number of threads(defalut is 1)" << std::endl;
  567. 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;
  568. }
  569. // string.split('x')
  570. std::vector<std::string> split(const std::string &str,const std::string &pattern)
  571. {
  572. //const char* convert to char*
  573. char * strc = new char[strlen(str.c_str())+1];
  574. strcpy(strc, str.c_str());
  575. std::vector<std::string> resultVec;
  576. char* tmpStr = strtok(strc, pattern.c_str());
  577. while (tmpStr != NULL)
  578. {
  579. resultVec.push_back(std::string(tmpStr));
  580. tmpStr = strtok(NULL, pattern.c_str());
  581. }
  582. delete[] strc;
  583. return resultVec;
  584. }
  585. int main(int argc, char** argv)
  586. {
  587. std::cout << "--- ncnn post training quantization tool --- " << __TIME__ << " " << __DATE__ << std::endl;
  588. char* imagepath = NULL;
  589. char* parampath = NULL;
  590. char* binpath = NULL;
  591. char* tablepath = NULL;
  592. int num_threads = 1;
  593. struct PreParam pre_param = {
  594. .mean = {104.f, 117.f, 103.f},
  595. .norm = {1.f, 1.f, 1.f},
  596. .weith = 224,
  597. .height =224,
  598. .swapRB = false
  599. };
  600. int c;
  601. while (1)
  602. {
  603. int option_index = 0;
  604. static struct option long_options[] =
  605. {
  606. {"param", required_argument, 0, 'p' },
  607. {"bin", required_argument, 0, 'b' },
  608. {"images", required_argument, 0, 'i' },
  609. {"output", required_argument, 0, 'o' },
  610. {"mean", required_argument, 0, 'm' },
  611. {"norm", required_argument, 0, 'n' },
  612. {"size", required_argument, 0, 's' },
  613. {"swapRB", no_argument, 0, 'c' },
  614. {"thread", required_argument, 0, 't' },
  615. {"help", no_argument, 0, 'h' },
  616. {0, 0, 0, 0 }
  617. };
  618. c = getopt_long(argc, argv, "p:b:i:o:m:n:s:ct:h", long_options, &option_index);
  619. if (c == -1)
  620. break;
  621. switch (c)
  622. {
  623. case 'p':
  624. printf("param = '%s'\n", optarg);
  625. parampath = optarg;
  626. break;
  627. case 'b':
  628. printf("bin = '%s'\n", optarg);
  629. binpath = optarg;
  630. break;
  631. case 'i':
  632. printf("images = '%s'\n", optarg);
  633. imagepath = optarg;
  634. break;
  635. case 'o':
  636. printf("output = '%s'\n", optarg);
  637. tablepath = optarg;
  638. break;
  639. case 'm':
  640. {
  641. printf("mean = '%s'\n", optarg);
  642. std::string temp(optarg);
  643. std::vector<std::string> array = split(temp, ",");
  644. pre_param.mean[0] = atof(array[0].c_str());
  645. pre_param.mean[1] = atof(array[1].c_str());
  646. pre_param.mean[2] = atof(array[2].c_str());
  647. }
  648. break;
  649. case 'n':
  650. {
  651. printf("norm = '%s'\n", optarg);
  652. std::string temp(optarg);
  653. std::vector<std::string> array = split(temp, ",");
  654. pre_param.norm[0] = atof(array[0].c_str());
  655. pre_param.norm[1] = atof(array[1].c_str());
  656. pre_param.norm[2] = atof(array[2].c_str());
  657. }
  658. break;
  659. case 's':
  660. {
  661. printf("size = '%s'\n", optarg);
  662. std::string temp(optarg);
  663. std::vector<std::string> array = split(temp, ",");
  664. pre_param.weith = atoi(array[0].c_str());
  665. pre_param.height = atoi(array[1].c_str());
  666. }
  667. break;
  668. case 'c':
  669. {
  670. printf("swapRB = '%s'\n", "true");
  671. pre_param.swapRB = true;
  672. }
  673. break;
  674. case 't':
  675. printf("thread = '%s'\n", optarg);
  676. num_threads = atoi(optarg);
  677. break;
  678. case 'h':
  679. case '?':
  680. showUsage();
  681. return 0;
  682. default:
  683. showUsage();
  684. }
  685. }
  686. // check the input param
  687. if (imagepath == NULL || parampath == NULL || binpath == NULL || tablepath == NULL)
  688. {
  689. fprintf(stderr, "someone path maybe empty,please check it and try again.\n");
  690. return 0;
  691. }
  692. g_blob_pool_allocator.set_size_compare_ratio(0.0f);
  693. g_workspace_pool_allocator.set_size_compare_ratio(0.5f);
  694. // default option
  695. g_default_option.lightmode = true;
  696. g_default_option.num_threads = num_threads;
  697. g_default_option.blob_allocator = &g_blob_pool_allocator;
  698. g_default_option.workspace_allocator = &g_workspace_pool_allocator;
  699. g_default_option.use_winograd_convolution = true;
  700. g_default_option.use_sgemm_convolution = true;
  701. g_default_option.use_int8_inference = true;
  702. g_default_option.use_fp16_packed = true;
  703. g_default_option.use_fp16_storage = true;
  704. g_default_option.use_fp16_arithmetic = true;
  705. g_default_option.use_int8_storage = true;
  706. g_default_option.use_int8_arithmetic = true;
  707. ncnn::set_cpu_powersave(2);
  708. ncnn::set_omp_dynamic(0);
  709. ncnn::set_omp_num_threads(num_threads);
  710. std::vector<std::string> filenames;
  711. // parse the image file.
  712. parse_images_dir(imagepath, filenames);
  713. // get the calibration table file, and save it.
  714. int ret = post_training_quantize(filenames, parampath, binpath, tablepath, pre_param);
  715. if (!ret)
  716. fprintf(stderr, "\nNCNN Int8 Calibration table create success, best wish for your INT8 inference has a low accuracy loss...\\(^▽^)/...233...\n");
  717. return 0;
  718. }