Ubuntu系統NCNN框架入門指南
深度學習交流QQ群:811460433
前言
兩天前騰訊釋出NCNN深度學習框架後,發現可能有些同學對如何使用這些框架並不是十分的瞭解,一方面這是一個新的框架,另一方面Tencent出的文件對一些細節沒有敘述,可能大牛們覺得很容易的步驟,我們往往會卡在那裡,所以寫下這篇部落格來幫助一些對NCNN框架不是很熟悉的人來快速入門。
NCNN原始碼的地址為https://github.com/Tencent/ncnn
在Ubuntu上安裝NCNN
1. 下載編譯原始碼
ruyiwei@ruyiwei:~/code$ git clone https://github.com/Tencent/ncnn
下載完成後,需要對原始碼進行編譯
cd ncnn
mkdir build && cd build
cmake ..
make -j
make install
執行完畢後我們可以看到
Install the project...
-- Install configuration: "release"
-- Installing: /home/ruyiwei/code/ncnn/build/install/lib/libncnn.a
-- Installing: /home/ruyiwei/code/ncnn/build/install/include/blob.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include /cpu.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include/layer.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include/mat.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include/net.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include/opencv.h
-- Installing: /home/ruyiwei/code/ncnn/build/install/include /platform.h
我們進入 ncnn/build/tools 目錄下,如下所示,我們可以看到已經生成了 caffe2ncnn 可ncnn2mem這兩個可執行檔案,這兩個可執行檔案的作用是將caffe模型生成ncnn 模型,並且對模型進行加密,具體的使用方法我門在下一節講述。
[email protected]:~/code/ncnn/build/tools$ ll
total 3024
drwxrwxr-x 3 ruyiwei ruyiwei 4096 7月 27 15:36 ./
drwxrwxr-x 6 ruyiwei ruyiwei 4096 7月 27 15:36 ../
-rwxrwxr-x 1 ruyiwei ruyiwei 833720 7月 27 15:36 caffe2ncnn*
-rw-rw-r-- 1 ruyiwei ruyiwei 1102486 7月 27 15:36 caffe.pb.cc
-rw-rw-r-- 1 ruyiwei ruyiwei 894690 7月 27 15:36 caffe.pb.h
drwxrwxr-x 4 ruyiwei ruyiwei 4096 7月 27 15:36 CMakeFiles/
-rw-rw-r-- 1 ruyiwei ruyiwei 1018 7月 27 15:36 cmake_install.cmake
-rw-rw-r-- 1 ruyiwei ruyiwei 9353 7月 27 15:36 Makefile
-rwxrwxr-x 1 ruyiwei ruyiwei 228032 7月 27 15:36 ncnn2mem*
2. 將caffe下Alexnet網路模型轉換為NCNN模型
我們在測試的過程中需要caffemodel以及deploy.prototxt,所以我們再將caffe模型轉換為NCNN模型的時候,同樣也需要caffemodel以及deploy.prototxt這兩個檔案,為了方便,我們使用AlexNet為例講解。
由於NCNN提供的轉換工具只支援轉換新版的caffe模型,所以我們需要利用caffe自帶的工具將舊版的caffe模型轉換為新版的caffe模型後,在將新版本的模型轉換為NCNN模型.
舊版本caffe模型->新版本caffe模型->NCNN模型
2.1 舊版caffe模型轉新版caffe模型
我們執行如下命令.[要記得修改路徑]
ruyiwei@ruyiwei:~/code/ncnn/build/tools$ ~/caffe/build/tools/upgrade_net_proto_text deploy.prototxt new_deplpy.prototxt
ruyiwei@ruyiwei:~/code/ncnn/build/tools$ ~/caffe/build/tools/upgrade_net_proto_binary bvlc_alexnet.caffemodel new_bvlc_alexnet.caffemodel
上面的命令需要根據自己的caffe位置進行修改
執行後,就可以生成新的caffe模型.
因為我們每次檢測一張圖片,所以要對新生成的deploy.prototxt進行修改:第一個 dim 設為 1
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
}
2.2 新版caffe模型轉ncnn模型
ruyiwei@ruyiwei:~/code/ncnn/build/tools$./caffe2ncnn new_deplpy.prototxt new_bvlc_alexnet.caffemodel alexnet.param alexnet.bin
執行上面命令後就可以生成NCNN模型需要的param 與bin 檔案.
[email protected]:~/code/ncnn/build/tools$ ll
total 717492
drwxrwxr-x 3 ruyiwei ruyiwei 4096 7月 27 16:13 ./
drwxrwxr-x 6 ruyiwei ruyiwei 4096 7月 27 15:36 ../
-rw-rw-r-- 1 ruyiwei ruyiwei 243860928 7月 27 16:13 alexnet.bin
-rw-rw-r-- 1 ruyiwei ruyiwei 1583 7月 27 16:13 alexnet.param
-rw-rw-r-- 1 ruyiwei ruyiwei 243862414 7月 27 09:28 bvlc_alexnet.caffemodel
-rwxrwxr-x 1 ruyiwei ruyiwei 833720 7月 27 15:36 caffe2ncnn*
-rw-rw-r-- 1 ruyiwei ruyiwei 1102486 7月 27 15:36 caffe.pb.cc
-rw-rw-r-- 1 ruyiwei ruyiwei 894690 7月 27 15:36 caffe.pb.h
drwxrwxr-x 4 ruyiwei ruyiwei 4096 7月 27 15:36 CMakeFiles/
-rw-rw-r-- 1 ruyiwei ruyiwei 1018 7月 27 15:36 cmake_install.cmake
-rw-rw-r-- 1 ruyiwei ruyiwei 3629 6月 6 21:40 deploy.prototxt
-rw-rw-r-- 1 ruyiwei ruyiwei 9353 7月 27 15:36 Makefile
-rwxrwxr-x 1 ruyiwei ruyiwei 228032 7月 27 15:36 ncnn2mem*
-rw-rw-r-- 1 ruyiwei ruyiwei 243862660 7月 27 16:03 new_bvlc_alexnet.caffemodel
-rw-r--r-- 1 ruyiwei ruyiwei 3662 7月 27 16:03 new_deplpy.prototxt
3. 對模型引數加密
得到的alexnet.param是明文可見的,往往釋出的過程需要對這些檔案進行加密,NCNN提供了對應的加密工具,ncnn2mem,
ruyiwei@ruyiwei:~/code/ncnn/build/tools$ ./ncnn2mem alexnet.param alexnet.bin alexnet.id.h alexnet.mem.h
最後可以生成 alexnet.param.bin 這樣的二進位制加密檔案.
對於加密檔案的讀取也和原來不同,在原始碼中,非加密param讀取方式為
ncnn::Net net;
net.load_param("alexnet.param");
net.load_model("alexnet.bin");
加密param.bin讀取方式為
ncnn::Net net;
net.load_param_bin("alexnet.param.bin");
net.load_model("alexnet.bin");
4. 編譯NCNN例程
前面介紹瞭如何將caffe模型轉為NCNN模型並且加密,最後我們來編譯NCNN的例程,這樣可以更直觀的執行或者理解NCNN.
首先我們需要進入ncnn/examples目錄
新建一個makefile,內容如下,最重要的是,NCNN例程式只支援opencv2,不支援opencv3.
NCNN = /home/ruyiwei/code/ncnn
OPENCV = /home/ruyiwei/Downloads/opencv-2.4.13
INCPATH = -I${NCNN}/build/install/include \
-I${OPENCV}/modules/objdetect/include \
-I${OPENCV}/modules/highgui/include \
-I${OPENCV}/modules/imgproc/include \
-I${OPENCV}/modules/core/include
LIBS = -lopencv_core -lopencv_highgui -lopencv_imgproc \
-fopenmp -pthread
LIBPATH = -L${OPENCV}/build/lib
%:%.cpp
$(CXX) $(INCPATH) $(LIBPATH) $^ ${NCNN}/build/install/lib/libncnn.a $(LIBS) -o [email protected]
在當前目錄執行
ruyiwei@ruyiwei:~/code/ncnn/examples$ ./squeezenet test.jpg
可得到如下結果
ruyiwei@ruyiwei:~/code/ncnn/examples$ ./squeezenet test.jpg
404 = 0.990161
908 = 0.004498
405 = 0.004008
test.jpg 為下圖所示:
為了可以更直觀的顯示,我們修改程式碼如下:
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
#include "net.h"
static int detect_squeezenet(const cv::Mat& bgr, std::vector<float>& cls_scores)
{
ncnn::Net squeezenet;
squeezenet.load_param("squeezenet_v1.1.param");
squeezenet.load_model("squeezenet_v1.1.bin");
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 227, 227);
const float mean_vals[3] = {104.f, 117.f, 123.f};
in.substract_mean_normalize(mean_vals, 0);
ncnn::Extractor ex = squeezenet.create_extractor();
ex.set_light_mode(true);
ex.input("data", in);
ncnn::Mat out;
ex.extract("prob", out);
cls_scores.resize(out.c);
for (int j=0; j<out.c; j++)
{
const float* prob = out.data + out.cstep * j;
cls_scores[j] = prob[0];
}
return 0;
}
static int print_topk(const std::vector<float>& cls_scores, int topk, vector<int>& index_result, vector<float>& score_result)
{
// partial sort topk with index
int size = cls_scores.size();
std::vector< std::pair<float, int> > vec;
vec.resize(size);
for (int i=0; i<size; i++)
{
vec[i] = std::make_pair(cls_scores[i], i);
}
std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(), std::greater< std::pair<float, int> >());
// print topk and score
for (int i=0; i<topk; i++)
{
float score = vec[i].first;
int index = vec[i].second;
index_result.push_back(index);
score_result.push_back(score);
//fprintf(stderr, "%d = %f\n", index, score);
}
return 0;
}
static int load_labels(string path, vector<string>& labels)
{
FILE* fp = fopen(path.c_str(), "r");
while (!feof(fp))
{
char str[1024];
fgets(str, 1024, fp); //¶ÁÈ¡Ò»ÐÐ
string str_s(str);
if (str_s.length() > 0)
{
for (int i = 0; i < str_s.length(); i++)
{
if (str_s[i] == ' ')
{
string strr = str_s.substr(i, str_s.length() - i - 1);
labels.push_back(strr);
i = str_s.length();
}
}
}
}
return 0;
}
int main(int argc, char** argv)
{
const char* imagepath = argv[1];
vector<string> labels;
load_labels("synset_words.txt", labels);
cv::Mat m = cv::imread(imagepath, CV_LOAD_IMAGE_COLOR);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}
std::vector<float> cls_scores;
detect_squeezenet(m, cls_scores);
vector<int> index;
vector<float> score;
print_topk(cls_scores, 3, index, score);
for (int i = 0; i < index.size(); i++)
{
cv::putText(m, labels[index[i]], Point(50, 50 + 30 * i), CV_FONT_HERSHEY_SIMPLEX, 1.2, Scalar(0, 100, 200), 2, 8);
}
imshow("m", m);
imwrite("test_result.jpg", m);
waitKey(0);
return 0;
}
這樣就可以直觀的顯示出具體的類別,而不是數字,結果如下:
深度學習交流QQ群:116270156