天天看點

在ncnn上把玩mobileNet

ncnn是騰訊優圖最近開源的适合移動端的深度學習架構。mobileNet是谷歌在2017年4月份發表的論文MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications中提出的網絡。由于引入了depthwise convolution,mobileNet的模型非常小,1000類的分類模型隻有16.9M,很适合在移動端部署。本文嘗試在Mac上用ncnn來運作mobileNet。

1. 下載下傳并編譯ncnn

git clone https://github.com/Tencent/ncnn
cd ncnn
mkdir build && cd build
cmake ..
make -j
make install
           

編譯完成後在ncnn/build/tools 目錄下,可以看到已經生成了 caffe2ncnn和ncnn2mem這兩個可執行檔案。caffe2ncnn可的作用是将caffe模型生成ncnn 模型,ncnn2mem可對模型進行加密。

2. 下載下傳MobileNet的caffe模型和配置檔案

可從https://github.com/shicai/MobileNet-Caffe中下載下傳,下載下傳後得到mobilenet_deploy.prototxt和mobilenet.caffemodel兩個檔案。

3. 舊版caffe模型轉新版caffe模型

因為ncnn隻支援轉換新版的caffe模型,是以需要先把第二步下載下傳的caffe模型轉換為新版的caffe模型。新版caffe架構中自帶了轉換的工具,使用姿勢如下。

$ ~/caffe/build/tools/upgrade_net_proto_text mobilenet_deploy.prototxt mobilenet_deploy_new.prototxt
$ ~/caffe/build/tools/upgrade_net_proto_binary mobilenet.caffemodel mobilenet_new.caffemodel
           

4. 新版caffe模型轉ncnn模型

在第一步生成的ncnn/build/tools目錄下用caffe2ncnn來轉換新版的mobileNet模型。

注意生成的ncnn格式的模型中,.param可以了解為網絡的配置檔案,.bin可以了解為網絡的參數(各種權重)檔案。

若需要對模型進行加密,可用如下指令

最後可生成 mobilenet.param.bin 這樣的二進制加密檔案。ncnn對加密和非加密兩種檔案的讀取方式不一樣。

//load非加密的ncnn模型
ncnn::Net net;
net.load_param("mobilenet.param");
net.load_model("mobilenet.bin");
//load加密的ncnn模型
ncnn::Net net;
net.load_param_bin("mobilenet.param.bin");
net.load_model("mobilenet.bin");
           

5. 開工:使用Xcode編寫代碼運作

使用Xcode建立一個工程,并把第一步中編譯完成的ncnn庫導入工程中。編譯完成的ncnn lib在ncnn/build/install目錄下。

配置好ncnn庫後,可以借鑒example下面的squeezenet.cpp代碼進行mobileNet模型的部署。修改後的代碼如下

static int detect_mobileNet(const cv::Mat& bgr, std::vector<float>& cls_scores)
{
    ncnn::Net mobileNet;
    mobileNet.load_param("/Users/Guigu/Documents/projects/ncnn_mobileNet/mobilenet.param");
    mobileNet.load_model("/Users/Guigu/Documents/projects/ncnn_mobileNet/mobilenet.bin");

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, , );

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


    ncnn::Extractor ex = mobileNet.create_extractor();
    ex.set_light_mode(true);

    ex.input("data", in);

    ncnn::Mat out;
    ex.extract("fc7", out);  //此處與squeezenet不同

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

在main函數中調用該接口就OK了。

下面這張圖是我測試的圖

在ncnn上把玩mobileNet

mobileNet識别的結果如下:

detection time: ms
 =  ( comic book)
 =  ( mask)
 =  ( book jacket, dust cover, dust jacket, dust wrapper)
Program ended with exit code: 
           

細心的人可能觀察到耗時比較長,原因是用Mac自帶的編譯器編ncnn的時候不能把openmp編進去。另外,Mac上也不能通過arm neon來加速(畢竟平台不一樣嘛)。

完整的工程位址:https://github.com/Revo-Future/ncnn_mobileNet

Bonus

如果想編譯源代碼,可以把ncnn中src目錄下的檔案加上build/src下的platform.h layer_registry.h 和layer_declaration.h放到一起替換上面的ncnn lib進行源碼的編譯研究。

Reference:

http://blog.csdn.net/best_coder/article/details/76201275

繼續閱讀