deploy檔案的修改目是為了我們能夠真正使用訓練好的模型進行預測,輸出機率及最佳結果的重要過程。
問題背景:
如我們前面已經生成了一個預測手寫體識别的模型,我們現在如何預測某個人寫的數字是多少呢?預測的機率是多少呢?
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISOwUDNyITNyIjNwIDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
最終解決的結果:
為了達到以上的結果,我們需要做3步驟
第一步,修改mnist_deploy.prorotxt檔案
第二步,編寫test_mnist.cpp檔案
第三步,編譯運作輸出結果
第一步,修改prorotxt檔案:
我們首先要對之前propotxt進行修改,用來滿足我們的需求。
在修改propotxt之前我們可以對之前的網絡結構進行一個直覺的認識:可以使用http://ethereon.github.io/netscope/#/editor 這個網址。将propotxt檔案内容複制後會得到可視化模型,之前訓練模型如圖所示:
其實我們的目标就是使用模型的權重最後輸出類别機率,是以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"
}
新的結構生成一下可視化圖:
修改完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學習與應用》