天天看點

Ubuntu16.04---騰訊NCNN架構入門到應用Ubuntu16.04—騰訊NCNN架構入門到應用

Ubuntu16.04—騰訊NCNN架構入門到應用

前言

兩天前騰訊釋出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 
drwxrwxr-x  ruyiwei ruyiwei     月   : ./
drwxrwxr-x  ruyiwei ruyiwei     月   : ../
-rwxrwxr-x  ruyiwei ruyiwei   月   : caffe2ncnn*
-rw-rw-r--  ruyiwei ruyiwei  月   : caffe.pb.cc
-rw-rw-r--  ruyiwei ruyiwei   月   : caffe.pb.h
drwxrwxr-x  ruyiwei ruyiwei     月   : CMakeFiles/
-rw-rw-r--  ruyiwei ruyiwei     月   : cmake_install.cmake
-rw-rw-r--  ruyiwei ruyiwei     月   : Makefile
-rwxrwxr-x  ruyiwei ruyiwei   月   : ncnn2mem*
           

2. 将caffe下Alexnet網絡模型轉換為NCNN模型

我們在測試的過程中需要caffemodel以及deploy.prototxt,是以我們再将caffe模型轉換為NCNN模型的時候,同樣也需要caffemodel以及deploy.prototxt這兩個檔案,為了友善,我們使用AlexNet為例講解。

alexnet 的 deploy.prototxt 可以在這裡下載下傳 https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet

alexnet 的 caffemodel 可以在這裡下載下傳 http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel

由于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:  dim:  dim:  dim:  } }
}
           

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 
drwxrwxr-x  ruyiwei ruyiwei       月   : ./
drwxrwxr-x  ruyiwei ruyiwei       月   : ../
-rw-rw-r--  ruyiwei ruyiwei  月   : alexnet.bin
-rw-rw-r--  ruyiwei ruyiwei       月   : alexnet.param
-rw-rw-r--  ruyiwei ruyiwei  月   : bvlc_alexnet.caffemodel
-rwxrwxr-x  ruyiwei ruyiwei     月   : caffe2ncnn*
-rw-rw-r--  ruyiwei ruyiwei    月   : caffe.pb.cc
-rw-rw-r--  ruyiwei ruyiwei     月   : caffe.pb.h
drwxrwxr-x  ruyiwei ruyiwei       月   : CMakeFiles/
-rw-rw-r--  ruyiwei ruyiwei       月   : cmake_install.cmake
-rw-rw-r--  ruyiwei ruyiwei       月    : deploy.prototxt
-rw-rw-r--  ruyiwei ruyiwei       月   : Makefile
-rwxrwxr-x  ruyiwei ruyiwei     月   : ncnn2mem*
-rw-rw-r--  ruyiwei ruyiwei  月   : new_bvlc_alexnet.caffemodel
-rw-r--r--  ruyiwei ruyiwei       月   : 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-.

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 $@
           

在目前目錄執行

ruyiwei@ruyiwei:~/code/ncnn/examples$ ./squeezenet test.jpg
           

可得到如下結果

ruyiwei@ruyiwei:~/code/ncnn/examples$ ./squeezenet test.jpg 
 = .
 = .98
 = .8
           

test.jpg 為下圖所示:

Ubuntu16.04---騰訊NCNN架構入門到應用Ubuntu16.04—騰訊NCNN架構入門到應用

為了可以更直覺的顯示,我們修改代碼如下:

#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, , );

    const float mean_vals[] = {, , };
    in.substract_mean_normalize(mean_vals, );

    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=; j<out.c; j++)
    {
        const float* prob = out.data + out.cstep * j;
        cls_scores[j] = prob[];
    }

    return ;
}

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=; 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=; 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 ;
}

static int load_labels(string path, vector<string>& labels)
{
    FILE* fp = fopen(path.c_str(), "r");

    while (!feof(fp))
    {
        char str[];
        fgets(str, , fp);  //¶ÁÈ¡Ò»ÐÐ
        string str_s(str);

        if (str_s.length() > )
        {
            for (int i = ; i < str_s.length(); i++)
            {
                if (str_s[i] == ' ')
                {
                    string strr = str_s.substr(i, str_s.length() - i - );
                    labels.push_back(strr);
                    i = str_s.length();
                }
            }
        }
    }
    return ;
}


int main(int argc, char** argv)
{
    const char* imagepath = argv[];
    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 -;
    }

    std::vector<float> cls_scores;
    detect_squeezenet(m, cls_scores);

    vector<int> index;
    vector<float> score;
    print_topk(cls_scores, , index, score);


    for (int i = ; i < index.size(); i++)
    {
       cv::putText(m, labels[index[i]], Point(,  +  * i), CV_FONT_HERSHEY_SIMPLEX, , Scalar(, , ), , );
    }

    imshow("m", m);
    imwrite("test_result.jpg", m);
    waitKey();

    return ;
}
           

這樣就可以直覺的顯示出具體的類别,而不是數字,結果如下:

Ubuntu16.04---騰訊NCNN架構入門到應用Ubuntu16.04—騰訊NCNN架構入門到應用

感謝

https://github.com/guozhongluo/ncnn-vs2015-examples-demo

https://github.com/Tencent/ncnn/wiki

繼續閱讀