天天看點

Caffe學習筆記5--deploy檔案的修改與使用第一步,修改prorotxt檔案:第二步,編寫運作代碼 test_mnist.cpp:第三步,編譯運作:

deploy檔案的修改目是為了我們能夠真正使用訓練好的模型進行預測,輸出機率及最佳結果的重要過程。

問題背景:

如我們前面已經生成了一個預測手寫體識别的模型,我們現在如何預測某個人寫的數字是多少呢?預測的機率是多少呢?

Caffe學習筆記5--deploy檔案的修改與使用第一步,修改prorotxt檔案:第二步,編寫運作代碼 test_mnist.cpp:第三步,編譯運作:

最終解決的結果:

Caffe學習筆記5--deploy檔案的修改與使用第一步,修改prorotxt檔案:第二步,編寫運作代碼 test_mnist.cpp:第三步,編譯運作:

為了達到以上的結果,我們需要做3步驟

第一步,修改mnist_deploy.prorotxt檔案

第二步,編寫test_mnist.cpp檔案

第三步,編譯運作輸出結果

第一步,修改prorotxt檔案:

我們首先要對之前propotxt進行修改,用來滿足我們的需求。

在修改propotxt之前我們可以對之前的網絡結構進行一個直覺的認識:可以使用http://ethereon.github.io/netscope/#/editor 這個網址。将propotxt檔案内容複制後會得到可視化模型,之前訓練模型如圖所示:

Caffe學習筆記5--deploy檔案的修改與使用第一步,修改prorotxt檔案:第二步,編寫運作代碼 test_mnist.cpp:第三步,編譯運作:

其實我們的目标就是使用模型的權重最後輸出類别機率,是以loss,label我們都不要了。根據上面的圖我們對原始結構進行修改,如下:

name: "LeNet"
input: "data"
input_shape {
  dim: 1 # batchsize
  dim: 1 # number of colour channels - rgb
  dim: 28 # width
  dim: 28 # height
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "prob"
  type: "Softmax"
  bottom: "ip2"
  top: "prob"
}
           

新的結構生成一下可視化圖:

Caffe學習筆記5--deploy檔案的修改與使用第一步,修改prorotxt檔案:第二步,編寫運作代碼 test_mnist.cpp:第三步,編譯運作:

修改完propotxt後,我們命名為mnist_deploy.prototxt。

第二步,編寫運作代碼 test_mnist.cpp:

#include "opencv2/dnn.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace cv::dnn;

#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;

/* Find best class for the blob (i. e. class with maximal probability) */ 
void getMaxClass(dnn::Blob &probBlob, int *classId, double *classProb)
{
    Mat probMat = probBlob.matRefConst().reshape(1, 1); //reshape the blob to 1x1000 matrix
    Point classNumber;
    minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
    *classId = classNumber.x;
}


int main(int argc,char* argv[]){

    String modelTxt = "mnist_deploy.prototxt";
    String modelBin = "lenet_iter_10000.caffemodel";
    String imageFile = (argc > 1) ? argv[1] : "5.jpg";

    //! [Create the importer of Caffe model] 導入一個caffe模型接口 
    Ptr<dnn::Importer> importer; 
    importer = dnn::createCaffeImporter(modelTxt, modelBin);
  
    if (!importer){
        std::cerr << "Can't load network by using the following files: " << std::endl;
        std::cerr << "prototxt:   " << modelTxt << std::endl;
        std::cerr << "caffemodel: " << modelBin << std::endl;
        exit(-1);
    }

    //! [Initialize network] 通過接口建立和初始化網絡
    Net net;
    importer->populateNet(net);  
    importer.release();

    //! [Prepare blob] 讀取一張圖檔并轉換到blob資料存儲
    Mat img = imread(imageFile,0); //[<Important>] "0" for 1 channel, Mnist accepts 1 channel
    if (img.empty())
    {
        std::cerr << "Can't read image from the file: " << imageFile << std::endl;
        exit(-1);
    }
    resize(img, img, Size(28, 28));                   //[<Important>]Mnist accepts only 28x28 RGB-images

    dnn::Blob inputBlob = cv::dnn::Blob(img);   //Convert Mat to dnn::Blob batch of images

    //! [Set input blob] 将blob輸入到網絡
    net.setBlob(".data", inputBlob);        //set the network input

    //! [Make forward pass] 進行前向傳播
    net.forward();                          //compute output

    //! [Gather output] 擷取機率值
    dnn::Blob prob = net.getBlob("prob");   //[<Important>] gather output of "prob" layer
    int classId;
    double classProb;
    getMaxClass(prob, &classId, &classProb);//find the best class

    //! [Print results] 輸出結果
    std::cout << "Best class: #" << classId << "'" << std::endl;
    std::cout << "Probability: " << classProb * 100 << "%" << std::endl;
    
    return 0;
}
           

第三步,編譯運作:

目前檔案需要确認的一共有4樣:5.jpg(測試資料)  lenet_iter_10000.caffemodel(上一篇文章生成的模型) mnist_deploy.prototxt(這次修改的prototxt檔案) test_mnist.cpp (編譯檔案)

随後執行指令:

g++-o test_mnist test_mnist.cpp -lopencv_dnn -lopencv_highgui -lopencv_imgcodecs-lopencv_imgproc -lstdc++ -lopencv_core

./test_mnist

恭喜你就能看到最終結果

參考内容

------------------

《Dataguru課程-caffe學習與應用》

繼續閱讀