当前位置:   article > 正文

DNN模型压缩:量化实验之ncnn_ncnn 量化

ncnn 量化

ncnn

ncnn 是一个为手机端极致优化的高性能神经网络前向计算框架。ncnn 从设计之初深刻考虑手机端的部署和使用。无第三方依赖,跨平台,手机端 cpu 的速度快于目前所有已知的开源框架。基于 ncnn,开发者能够将深度学习算法轻松移植到手机端高效执行,开发出人工智能 APP,将 AI 带到你的指尖。ncnn 目前已在腾讯多款应用中使用,如 QQ,Qzone,微信,天天P图等。

1. 安装

  • 参考:安装使用

  • 下载ncnn
    git clone https://github.com/Tencent/ncnn

  • 进入ncnn根目录cd<ncnn-root-dir>

  • 注:在CMakeLists.txt中取消注释add_subdirectory(examples),以便编译examples中的cpp文件。

  • 执行以下命令,编译ncnn:

$ mkdir -p build
$ cd build
$ cmake ..
$ make -j4
$ make install
  • 1
  • 2
  • 3
  • 4
  • 5

这样就得到了build/examples文件下的多个模型的可执行文件。

2. 使用

Squeezenet进行分类测试

  • 移动ncnn所需的param和bin文件到build/examples/
$ cp examples/squeezenet_v1.1.param build/examples/
$ cp examples/squeezenet_v1.1.bin build/examples/
  • 1
  • 2
  • 分类预测
$ cd build/examples/
$ ./squeezenet dog.jpg 
  • 1
  • 2
  • 预测结果
258 = 0.191417
257 = 0.109412
151 = 0.060365
  • 1
  • 2
  • 3

3. ncnn量化:8位整型的实现

量化cifar_small模型

cifar_small模型是DarkNet框架的小型网络,它包含7层卷积。其中,将其模型文件cifar_small.cfg改写成caffe框架的配置文件请参考这里

  • 准备caffe网络和模型
train.prototxt
deploy.prototxt
snapshot_10000.caffemodel
  • 1
  • 2
  • 3
  • 执行量化脚本
python caffe-int8-convert-tool-dev.py --proto=cifar_small-master/cifar_small_deploy.prototxt --model=cifar_small-master/cifar_small_iter_10000.caffemodel --mean 125.3 123.0 113.9 --norm=1 --images=cifar_test_100/ --output=cifar_small.table --group=1  --gpu=0 
  • 1
  • 得到量化结果:
cifar_test_100//128_dog.png forward time : 0.001 s
loop stage 2 : 54
add cost 0.003 s
normalize_distribution 168664 2048
caffe-int8-convert-tool-dev.py:193: RuntimeWarning: divide by zero encountered in true_divide
  return np.sum(dist_a[nonzero_inds] * np.log(dist_a[nonzero_inds] / dist_b[nonzero_inds]))
conv1                group : 0     bin : 2034     threshold : 140.169904 interval : 0.068896   scale : 0.906043  
normalize_distribution 450560 2048
conv2                group : 0     bin : 1189     threshold : 3.555792   interval : 0.002989   scale : 35.716380 
normalize_distribution 225280 2048
conv3                group : 0     bin : 1545     threshold : 2.780417   interval : 0.001799   scale : 45.676601 
normalize_distribution 225280 2048
conv4                group : 0     bin : 1569     threshold : 1.943042   interval : 0.001238   scale : 65.361413 
normalize_distribution 112640 2048
conv5                group : 0     bin : 1588     threshold : 2.604649   interval : 0.001640   scale : 48.758978 
normalize_distribution 450560 2048
conv6                group : 0     bin : 1287     threshold : 1.208493   interval : 0.000939   scale : 105.089525
normalize_distribution 225280 2048
conv7                group : 0     bin : 1175     threshold : 5.926937   interval : 0.005042   scale : 21.427592 

Caffe Int8 Calibration table create success, it's cost 0:00:41.287920, best wish for your INT8 inference has a low accuracy loss...\(^^)/...2333...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

生成 cifarsmall.table文件

  • 统计量化结果

参考squeezent.cpp并利用darknet的validate_classifier_single函数进行批量测试:

// Tencent is pleased to support the open source community by making ncnn available.                                                                                                                    
  2 //
  3 // Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
  4 //
  5 // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  6 // in compliance with the License. You may obtain a copy of the License at
  7 //
  8 // https://opensource.org/licenses/BSD-3-Clause
  9 //
 10 // Unless required by applicable law or agreed to in writing, software distributed
 11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
 13 // specific language governing permissions and limitations under the License.
 14 
 15 #include <stdio.h>
 16 #include <algorithm>
 17 #include <vector>
 18 #include <opencv2/core/core.hpp>
 19 #include <opencv2/highgui/highgui.hpp>
 20 
 21 #include "darknet.h"
 22 #include "net.h"
 23 
 24 static int detect_cifarsmall(const cv::Mat& bgr, std::vector<float>& cls_scores, char *param, char *bin)
 25 {
 26     ncnn::Net cifarsmall;
 27     // cifarsmall.load_param("cifar_small.param");
 28     // cifarsmall.load_model("cifar_small.bin");
 29     cifarsmall.load_param(param);
 30     cifarsmall.load_model(bin);
 31     
 32     ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 28, 28);
 33     
 34     const float mean_vals[3] = {104.f, 117.f, 123.f};
 35     in.substract_mean_normalize(mean_vals, 0);
 36     
 37     ncnn::Extractor ex = cifarsmall.create_extractor();
 38     
 39     ex.input("data", in);
 40     
 41     ncnn::Mat out;
 42     ex.extract("prob", out);
 43     
 44     cls_scores.resize(out.w);
 45     for (int j=0; j<out.w; j++)
 46     {   
 47         cls_scores[j] = out[j];
 48     }
 49     
 50     return 0;
 51 }
 52 
 53 static int print_topk(const std::vector<float>& cls_scores, int topk)
 54 {
 55     // partial sort topk with index
 56     int size = cls_scores.size();
 57     std::vector< std::pair<float, int> > vec;
 58     vec.resize(size);
 59     for (int i=0; i<size; i++)
 60     {
 61         vec[i] = std::make_pair(cls_scores[i], i);
 62     }
 63 
 64     std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(),
 65                       std::greater< std::pair<float, int> >());
 66 
 67     // print topk and score
 68     for (int i=0; i<topk; i++)
 69     {
 70         float score = vec[i].first;
 71         int index = vec[i].second;                                                                                                                                                                      
 72         fprintf(stderr, "%d = %f\n", index, score);
 73     }
 74 
 75     return 0;
 76 }
 77 
 78 void validate_classifier_single(char *datacfg, char *param, char *bin, char *labelfile, char *validfile)
 79 {
 80     int i, j;
 81     srand(time(0));
 82 
 83     // 其实不需要,因为下面会用到options。
 84     list *options = read_data_cfg(datacfg);
 85 
 86     char *label_list = option_find_str(options, "labels", labelfile);
 87     char *valid_list = option_find_str(options, "valid", validfile);
 88     // char *label_list = option_find_str(options, "labels", "data/labels.list");
 89     // char *valid_list = option_find_str(options, "valid", "data/train.list");
 90     int classes = option_find_int(options, "classes", 2);
 91     int topk = option_find_int(options, "top", 1);
 92 
 93     char **labels = get_labels(label_list);
 94     list *plist = get_paths(valid_list);
 95 
 96     char **paths = (char **)list_to_array(plist);
 97     int m = plist->size;
 98     free_list(plist);
 99 
100     float avg_acc = 0;
101     float avg_topk = 0;
102     int *indexes = (int*)calloc(topk, sizeof(int));
103     // 统计每类的分类正确率
104     float *mycls = (float *)calloc(classes, sizeof(float));
105     // 初始化二维数组,统计包含1000个样本的测试集的准确率
106     int cls[2][1000];
107     for (i = 0; i < 1000; i++) {
108         cls[0][i] = 0;
109         cls[1][i] = 0;
110     }
111     for(i = 0; i < m; ++i){
112         int class1 = -1;
113         char *path = paths[i];
114         for(j = 0; j < classes; ++j){
115             if(strstr(path, labels[j])){
116                 class1 = j;
117                 break;
118             }
119         }
120 
121         // ncnn的预测
122         const char* imagepath = paths[i];
123 
124         cv::Mat m = cv::imread(imagepath, CV_LOAD_IMAGE_COLOR);
125         if (m.empty())
126         {
127             fprintf(stderr, "cv::imread %s failed\n", imagepath);
128             continue;
129         }
130         std::vector<float> cls_scores;
131         detect_cifarsmall(m, cls_scores, param, bin);
132 
133         print_topk(cls_scores, 3);
134                                                                                                                                                                                                         
135         for(j=0; j < classes; ++j){
136           mycls[j] = cls_scores[j];
137         }
138         top_k(mycls, classes, topk, indexes);
139         // 统计每一类分类正确的次数
140         if(indexes[0] == class1) {
141             avg_acc += 1;
142             cls[1][class1]++;
143             cls[0][class1]++;
144         }else{
145             cls[0][class1]++;
146         }
147 
148         for(j = 0; j < topk; ++j){
149             if(indexes[j] == class1) avg_topk += 1;
150         }
151
152         printf("%s, %d, %f, %f, \n", paths[i], class1, mycls[0], mycls[1]);
153         printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1));
154     }
155     for (i = 0; i < classes; i++) {
156         printf("[%d] %d %d %f \n", i, cls[0][i]);
157     }
158 }
159 
160 int main(int argc, char **argv)
161 {
162   if (argc != 6)
163   {
164     fprintf(
165         stderr,
166         "Usage: %s [datacfg] [ncnn.param] [ncnn.bin] [label.list] [train.list]\n",
167         argv[0]);
168     return -1;
169   }
170 
171 
172   validate_classifier_single(argv[1], argv[2], argv[3], argv[4], argv[5]);
173 
174   return 0;
175 }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

修改example/CMakeLists.txt如下,假设你的tools文件夹已经生成了darknet相关文件,添加头文件darknet.h、libdarknet.so以及cifarsmall 链接库。

  1                                                                                                                                                                                                         
  2 find_package(OpenCV QUIET COMPONENTS core highgui imgproc imgcodecs)
  3 if(NOT OpenCV_FOUND)
  4     find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc)
  5 endif()
  6 
  7 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
  8 include_directories(${CMAKE_CURRENT_BINARY_DIR}/../src)
  9 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../tools/darknet/darknet2ncnn/darknet/include)
 10 
 11 add_executable(squeezenet squeezenet.cpp)
 12 target_link_libraries(squeezenet ncnn ${OpenCV_LIBS})
 13 
 14 add_executable(fasterrcnn fasterrcnn.cpp)
 15 target_link_libraries(fasterrcnn ncnn ${OpenCV_LIBS})
 16 
 17 add_executable(rfcn rfcn.cpp)
 18 target_link_libraries(rfcn ncnn ${OpenCV_LIBS})
 19 
 20 add_executable(yolov2 yolov2.cpp)
 21 target_link_libraries(yolov2 ncnn ${OpenCV_LIBS})
 22 
 23 add_executable(yolov3 yolov3.cpp)
 24 target_link_libraries(yolov3 ncnn ${OpenCV_LIBS})
 25 
 26 add_executable(mobilenetv2ssdlite mobilenetv2ssdlite.cpp)
 27 target_link_libraries(mobilenetv2ssdlite ncnn ${OpenCV_LIBS})
 28 
 29 add_executable(mobilenetssd mobilenetssd.cpp)
 30 target_link_libraries(mobilenetssd ncnn ${OpenCV_LIBS})
 31 
 32 add_executable(squeezenetssd squeezenetssd.cpp)
 33 target_link_libraries(squeezenetssd ncnn ${OpenCV_LIBS})
 34 
 35 add_executable(shufflenetv2 shufflenetv2.cpp)
 36 target_link_libraries(shufflenetv2 ncnn ${OpenCV_LIBS})
 37 
 38 link_libraries("/home/huangqp/Coding/deep-learning/ncnn/ncnn/tools/darknet/darknet2ncnn/darknet/libdarknet.so")
 39 add_executable(cifarsmall cifarsmall.cpp)
 40 target_link_libraries(cifarsmall ncnn ${OpenCV_LIBS} libdarknet.so)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

进入ncnn/build文件下执行以下命令

$ make clean
$ cmake..
$ make -j4
  • 1
  • 2
  • 3
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/703212
推荐阅读
相关标签
  

闽ICP备14008679号