Caffe自帶例程classification詳解

構造Classifier類


初始化net_并讀入網絡模型文件.prototxt

net_.reset(new Net<float>(model_file, TEST));  //model_file = models/bvlc_reference_caffenet/deploy.prototxt
net_->CopyTrainedLayersFrom(trained_file);  //trained_file = models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

通過net初始化來了解Net類

blob_names_ —— 讀取各層blob名字

image.png

blob_names_index —— map類存儲的網絡名稱和對應編號
image.png

blob_need_backward_ —— 是否需要反向傳播梯度
image.png

blobs —— 用于存儲數據和梯度std::vector<boost::shared_ptr<caffe::Blob<float>>>
image.png

bottom_id_vecs_ —— 存儲每一層bottomvector

這里注意要配合layer_names_來理解,[0]data層,故其沒有bottom,[2]-relu1 [3]-pool1 由于[2]-relu1in-place存儲,所以其bottom[3]-pool1相同

image.png

bottom_vecs_ —— 存儲對應bottom的地址
image.png

layer_names_ —— 各層名稱

image.png

layer_names_index_ —— 各層順序
image.png

存儲每一層topvector
image.png


讀取輸入信息并設置輸出blob

num_input() num_output()函數分別返回net_input_blobs_.size() net_output_blobs_.size()

CHECK_EQ(net_->num_inputs(), 1) << "Network should have exactly one input.";
CHECK_EQ(net_->num_outputs(), 1) << "Network should have exactly one output.";

可以看到初始網絡輸入維度為10×3*227*227 輸出維度為10*1000(注意這里只是讀取model_file網絡的輸入輸出維度,網絡至少要有一個輸入和輸出

image.png

設置一個指向輸入層的Blob(10×3×227*227)指針

Blob<float>* input_layer = net_->input_blobs()[0];  //input_blobs()函數返回net_input_blobs_
num_channels_ = input_layer->channels();  //顯然,這里的num_channels_為輸入的channel數 = 3
input_geometry_ = cv::Size(input_layer->width(), input_layer->height());  //input_geometry_ = (227,227)

加載均值文件

SetMean(mean_file);

讀入類別及對應的編號

std::ifstream labels(label_file.c_str());
image.png

同樣的,設置一個指向輸出層的Blob(10×1000)指針用于讀取網絡運行結果

Blob<float>* output_layer = net_->output_blobs()[0];  //10*1000的blob

Classify函數

std::vector<float> output = Predict(img);

Predict函數 Predict(const cv::Mat& img)

Blob<float>* input_layer = net_->input_blobs()[0];  //與上文中的input_layer相同
input_layer->Reshape(1, num_channels_, input_geometry_.height, input_geometry_.width);
//10×3*227*227→1*3*227*227

接下來對net_進行reshape,結果就是所有blob的第一個維度由10變?yōu)?

image.png

net_->Reshape();
WrapInputLayer(&input_channels);

WrapInputLayer函數

Blob<float>* input_layer = net_->input_blobs()[0];  //同上問一樣,只是第一個維度為1
int width = input_layer->width();  //227
int height = input_layer->height();  //227
float* input_data = input_layer->mutable_cpu_data();  //input_data為指向數據的指針
for (int i = 0; i < input_layer->channels(); ++i) {
   cv::Mat channel(height, width, CV_32FC1, input_data);   //32位浮點型單通道
   //在input_data處創(chuàng)建一個Mat用于Preprocess函數中通道分離之后存儲數據
   input_channels->push_back(channel);    //注意是指針,channel就是在CPU中為輸入圖像預留的空間
   input_data += width * height;
 }
Preprocess(img, &input_channels);

Preprocess函數

else
  sample = img;  //(360×480)
 
cv::Mat sample_resized;
if (sample.size() != input_geometry_)    //判斷輸入圖像尺寸是否和網絡輸入尺寸大小相同
  cv::resize(sample, sample_resized, input_geometry_);  //調整為網絡輸入尺寸大小(227*227)
else
  sample_resized = sample;
cv::Mat sample_float;
if (num_channels_ == 3)
  sample_resized.convertTo(sample_float, CV_32FC3);  //轉化為CV_32FC3
else
  sample_resized.convertTo(sample_float, CV_32FC1);

cv::Mat sample_normalized;
cv::subtract(sample_float, mean_, sample_normalized);  //減去均值操作
/* This operation will write the separate BGR planes directly to the
  * input layer of the network because it is wrapped by the cv::Mat
  * objects in input_channels. */
cv::split(sample_normalized, *input_channels);  //見《OpenCV3》書籍P125、分成B、G、R三個單獨通道
//同時也將sample_normalized讀入了*input_channels,即讀入到input_layer->mutable_cpu_data();中

執(zhí)行前向傳播

net_->Forward();

Blob<float>* output_layer = net_->output_blobs()[0];
const float* begin = output_layer->cpu_data();
const float* end = begin + output_layer->channels();
return std::vector<float>(begin, end);
image.png

輸出結果

為什么輸出5個結果詳見Classify構造函數設置了變量N

image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容