參考連接:html
ubuntu下配置Caffe:https://blog.csdn.net/a_z666666/article/details/72853346ios
https://www.cnblogs.com/go-better/p/7161006.htmlgit
注意防坑點:Python和OpenCV版本都是採用的2.版本。(一開始在github上下載的,Python是3.6.4(因爲安裝了anaconda)和OpenCV和4.0版本,Caffe還不支持4.0的OpenCV;後來OpenCV降到3.1.0版本,安裝仍是各類坑。索性把Python和OpenCV都降到2.版本(先卸載了anaconda),我採用的Python是Ubuntu16.04自帶的2.7.12,OpenCV是2.4.11版本,一次即配置成功。)github
下面開始介紹Caffe實現物體分類的具體流程:ubuntu
前期準備參考上一篇博客連接:http://www.cnblogs.com/wmr95/p/9022415.html ,這裏train和test的lmdb準備仍是同樣(這裏test.txt沒有標籤,能夠先默認全部標籤都是1,而後生成lmdb,最後在acc層能夠來預測test的lmdb來獲得每一個的預測結果,能夠把預測結果寫入到一個txt中)。網絡
(這裏採用的是SENet,順便介紹下配置SENet的過程。)github連接:https://github.com/hujie-frank/SENet (Momenta大佬寫的)app
(採用其餘網絡如resnet也是同樣的步驟,並且不須要添加新的層。直接配置prototxt便可。)ide
1. 首先配置好senet的prototxt(SENet-50_train.prototxt,SENet-50_deploy.prototxt,SENet-50_solver.prototxt)測試
大部分的配置都在example下:ui
a. SENet-50_solver.prototxt的配置:
net: "examples/signboard/senet/SENet-50_train.prototxt" base_lr: 0.0001 lr_policy: "step" gamma: 0.1 stepsize: 500 display: 20 max_iter: 2000 momentum: 0.9 weight_decay: 0.0005 snapshot: 100 snapshot_prefix: "examples/signboard/model/senet" solver_mode: CPU
net:表示訓練網絡的路徑,solver_mode:表示CPU模式。
b. SENet-50_train.prototxt的配置改了下輸入和輸出:
輸入部分:
name: "SE-ResNet-50" layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TRAIN } data_param { source: "examples/signboard/train_lmdb" batch_size: 16 backend: LMDB } }
輸出部分:
layer { name: "pool5/7x7_s1" type: "Pooling" bottom: "conv5_3" top: "pool5/7x7_s1" pooling_param { pool: AVE kernel_size: 3 #注意這裏kersize改成3,這個多是根據你輸入圖像的大小,我這裏resize成224 stride: 1 } } layer { name: "fc" #這裏原來name爲classifier,報錯。問題連接:https://blog.csdn.net/raby_gyl/article/details/72357269 type: "InnerProduct" bottom: "pool5/7x7_s1" top: "fc" #這裏一樣將classifier改爲fc,下面bottom也要記得修改 inner_product_param { num_output: 100 #這裏改爲你本身的分類數目 } } layer { name: "accuracy" type: "Accuracy" bottom: "fc" bottom: "label" top: "accuracy" include { phase: TEST } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "fc" bottom: "label" top: "loss" }#
c. SENet-50_deploy.prototxt配置:
輸入部分:
name: "SE-ResNet-50" layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TEST } data_param { source: "examples/signboard/senet/test_lmdb" batch_size: 20 backend: LMDB } }
輸出部分:(這裏解釋同train.prototxt)
layer { name: "pool5/7x7_s1" type: "Pooling" bottom: "conv5_3" top: "pool5/7x7_s1" pooling_param { pool: AVE kernel_size: 3 stride: 1 } } layer { name: "fc" type: "InnerProduct" bottom: "pool5/7x7_s1" top: "fc" inner_product_param { num_output: 100 } } layer { name: "prob" type: "Softmax" bottom: "fc" top: "prob" } layer { name: "accuracy" type: "Accuracy" bottom: "fc" bottom: "label" top: "accuracy" include { phase: TEST } }
2. 接下來在Caffe中添加並編譯Axpy層,在Caffe中主要添加三個文件:caffe/include/caffe/layers文件夾下:axpy_layer.hpp;caffe/src/caffe/layers文件夾下:axpy_layer.cpp和axpy_layer.cu文件。這三個文件在git上都有:https://github.com/hujie-frank/SENet
添加完以後,在caffe/src/caffe/proto中的caffe.proto中message LayerParameter中爲新層添加參數:
這裏注意AxpyParameter的ID不能和現有的重複,我這裏取148。另外添加一個新的message,若是有參數就寫進去,沒有就空着。
3. 接下來在Caffe的路徑下進行make all就好了。執行命令:make all -j20
4. 接下來,編寫執行腳本進行訓練:
在caffe的路徑下新建一個train_senet.sh,輸入如下內容:
#!usr/bin/env sh ./build/tools/caffe train --solver=./examples/signboard/senet/SENet-50_solver.prototxt \ --weights=./examples/signboard/senet/SE-ResNet-50.caffemodel
執行train_senet.sh文件,便OK啦。
最後訓練模型完成以後,要進行test數據的預測,我這裏想把每一個圖片預測的分類寫到一個txt中。
1. 這裏只修改caffe/src/caffe/layers/的accuracy_layer.cpp中的Forward_cpu部分:(由於咱們在deploy.prototxt中寫了acc層,預測結果能夠從這裏獲取)
template <typename Dtype> void AccuracyLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { ofstream fout; fout.open("/home/wanglaotou/Caffe/caffe-master/out.txt", std::ios::out | std::ios::app); Dtype accuracy = 0;//準確率 const Dtype* bottom_data = bottom[0]->cpu_data();//100*10 const Dtype* bottom_label = bottom[1]->cpu_data();//100*1,100個圖像對應的類 const int dim = bottom[0]->count() / outer_num_;//dim=10 const int num_labels = bottom[0]->shape(label_axis_); //全鏈接後的blob數據都是2維的,label_axis_=1,因此shape就應該是N*D中的D(這塊能夠看我上一篇文章) //因此bottom[0]->shape(1)=10,也就是類別數 vector<Dtype> maxval(top_k_+1); vector<int> max_id(top_k_+1); if (top.size() > 1) { caffe_set(nums_buffer_.count(), Dtype(0), nums_buffer_.mutable_cpu_data()); caffe_set(top[1]->count(), Dtype(0), top[1]->mutable_cpu_data()); } int count = 0; //LOG(INFO) << "outer_num_: "<<outer_num_<<"\n"; //LOG(INFO) << "inner_num_: "<<inner_num_<<"\n"; for (int i = 0; i < outer_num_; ++i) {//outer_num_=100 for (int j = 0; j < inner_num_; ++j) {//inner_num_爲每一個圖像所對應的類別數,因此=1 const int label_value = static_cast<int>(bottom_label[i * inner_num_ + j]); //將bottom_label的值賦給label_value,[i * inner_num_ + j]其實就是一個圖像一個類嘛 if (has_ignore_label_ && label_value == ignore_label_) { continue; } if (top.size() > 1) ++nums_buffer_.mutable_cpu_data()[label_value]; DCHECK_GE(label_value, 0); DCHECK_LT(label_value, num_labels);//label_value(0~9)確定小於 num_labels(10) // Top-k accuracy // top_k爲取前k個最高評分(的預測標籤) std::vector<std::pair<Dtype, int> > bottom_data_vector; //這個主要就是用於對接下來兩步把測試評分與類別ID掛勾,並對評分排序(這裏我實際上是比較迷糊的,測試評分指的是bottom_data,難道說通過全鏈接後,獲得的向量就是測試評分? 迷糊中~~~) for (int k = 0; k < num_labels; ++k) { bottom_data_vector.push_back(std::make_pair( bottom_data[i * dim + k * inner_num_ + j], k)); //把測試評分與類別ID掛勾, } std::partial_sort( bottom_data_vector.begin(), bottom_data_vector.begin() + top_k_, bottom_data_vector.end(), std::greater<std::pair<Dtype, int> >());//排序 // check if true label is in top k predictions //LOG(INFO) << "top_k_: "<<top_k_<<"\n"; for (int k = 0; k < top_k_; k++) { //if (bottom_data_vector[k].second == label_value) { // ++accuracy; // .second指的是類別,若是跟label_value相等,那就說明準確 // if (top.size() > 1) ++top[1]->mutable_cpu_data()[label_value]; // break; //} LOG(INFO) << "label_value: "<<bottom_data_vector[k].second + 1<<"\n"; fout <<"label_value: "<<bottom_data_vector[k].second + 1<<"\n"; fout.flush(); } ++count; } } LOG(INFO) << "Accuracy: " << accuracy; top[0]->mutable_cpu_data()[0] = accuracy / count; if (top.size() > 1) { for (int i = 0; i < top[1]->count(); ++i) { top[1]->mutable_cpu_data()[i] = nums_buffer_.cpu_data()[i] == 0 ? 0 : top[1]->cpu_data()[i] / nums_buffer_.cpu_data()[i]; } } fout.close(); // Accuracy layer should not be used as a loss function. }
2. 編寫test.sh腳本文件進行測試:
#!usr/bin/env sh ./build/tools/caffe test --iterations=50 --model=./examples/signboard/senet/SENet-50_deploy.prototxt \ --weights=./examples/signboard/model/senet_iter_2000.caffemodel
3. 執行腳本完後會獲得一個out.txt文件,內容以下:
到此,大功告成~