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

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