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

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