環境配置
Microsoft/caffe-master + windows
我的電腦比較渣,獨立顯卡計算能力低于要求,所以并沒有使用GPU+Cudnn加速。首先,依照README.md中的指示在Makefile.config依次配置python,matlab環境。其中,在配置Miniconda2環境時,需要將E:\ProgramData\Miniconda2 & E:\ProgramData\Miniconda2\Scripts寫入系統路徑,然后才可以使用pip命令安裝numpy和protobuf,安裝指令為
pip install numpy
conda install --yes numpy scipy matplotlib scikit-image pip
pip install protobuf
環境配置完成后,修改windows下的CommonSettings.props文件,然后編譯caffe for windows源碼,記得將roi_pooling_layer和smooth_l1_loss_layer加到解決方案中來,這兩個層默認是沒有加進來的。如果只用CPU的話,在運行faster-rcnn代碼時,將和GPU有關的代碼屏蔽掉即可。
BVLC/caffe + ubuntu
首先下載caffe源碼: git clone git://github.com/BVLC/caffe.git
配置第三方庫的過程請參考博客:www.cnblogs.com/yaoyaoliu/p/5850993.html
安裝Cudnn
下載地址:developer.nvidia.com/cudnn,需要先注冊賬號,下載的時候需要注意cuda和cudnn版本的兼容性,我的是cuda7.5 & cudnn-7.5-linux-x64-v5.1。解壓之后,需要將相應的頭文件和庫文件拷貝到cuda的路徑下以便調用,指令如下:
sudo tar xvf cudnn-7.5-linux-x64-v5.1.tgz
cd cuda
sudo cp include/*.h /usr/local/cuda/include/
sudo cp lib64/lib* /usr/local/cuda/lib64
cd /usr/local/lib
sudo chmod +r libcudnn.so.5.1.10
sudo ln -sf licd bcudnn.so.5.1.10 libcudnn.so.5
sudo ln -sf libcudnn.so.5 libcudnn.so
sudo ldconfig
修改配置文件
# cuDNN acceleration switch (uncomment to build with cuDNN).
USE_CUDNN := 1
# CPU-only switch (uncomment to build without GPU support).
# CPU_ONLY := 1
USE_OPENCV := 1
USE_LEVELDB := 1
USE_LMDB := 1
OPENCV_VERSION := 3
CUDA_DIR := /usr/local/cuda
CUDA_ARCH := -gencode arch=compute_20,code=sm_20 \
-gencode arch=compute_20,code=sm_21 \
-gencode arch=compute_30,code=sm_30 \
-gencode arch=compute_35,code=sm_35 \
-gencode arch=compute_50,code=sm_50 \
-gencode arch=compute_52,code=sm_52 \
-gencode arch=compute_52,code=compute_52
BLAS := atlas
MATLAB_DIR := /usr/local/MATLAB/R2014a
PYTHON_INCLUDE := /usr/include/python2.7 \
/usr/lib/python2.7/dist-packages/numpy/core/include
PYTHON_LIB := /usr/lib
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib
BUILD_DIR := build
DISTRIBUTE_DIR := distribute
TEST_GPUID := 0
Q ?= @
開始編譯
在caffe路徑下執行make all -j8編譯源碼。在編譯matcaffe時可能會出現問題,如果是g++版本的問題,可以這樣解決:在Makefile文件中加上一句話
CXXFLAGS += -MMD -MP
CXXFLAGS += -std=c++11
如果報出缺少caffe.pb.h的錯誤,那么可以用 protoc caffe.proto --cpp_out=./ 指令生成caffe.pb.h文件,然后拷貝到 caffe/include/caffe/proto/路徑下。這樣編譯之后的caffe在faster-rcnn中仍然會報錯,原因是缺少roi_pooling和smooth_l1_loss層,所以要在源碼中加入這兩個層,之后還要修改caffe.proto文件,要分別在LayerParameter和V1layerParameter中添加:
optional ROIPoolingParameter roi_pooling_param = 150
optional SmoothL1LossParameter smooth_l1_loss_param = 151;
另外,需要用如下方法來聲明這兩個層:
message ROIPoolingParameter {
optional uint32 pooled_h = 1 [default = 0]; // The pooled output height
optional uint32 pooled_w = 2 [default = 0]; // The pooled output width
optional float spatial_scale = 3 [default = 1];
}
message SmoothL1LossParameter {
optional float sigma = 1 [default = 1];
}
如此配置之后的caffe在faster-rcnn中仍會報錯,這其中包括dropout train scale沒有聲明以及caffe缺少成員函數reshape_as_input等錯誤。說明faster-rcnn的caffe在BVLC版caffe的基礎上有了較大改動,所以我決定使用shaoqingren提供的caffe代碼重新編譯。
shaoqingren/caffe + ubuntu
作者提供的caffe由于是基于cuda6.5實現的,所以可能和自己裝的cuda版本不一致,和cudnn相關的所有代碼將出現不兼容的問題,并不想重裝cuda,所有我嘗試用caffe-master的代碼重新編譯。
Microsoft/caffe-master + ubuntu
這個過程還算順利,只需要去掉box_annotator_ohem_layer.cpp 這個文件,然后使用之前修改好的Makefile.config就可以了,大功告成!但是高興太早,這個版本的caffe只能跑跑demo,訓練時候仍然會報錯,所以想要訓練自己的數據還是要編譯shaoqingren提供的caffe源碼。
代碼解讀
整體框架
- faster-rcnn 把整張圖片輸入CNN, 進行特征提取
- RPN網絡生成建議窗口(Proposals),每張圖片大概300個
- fast-rcnn網絡把建議窗口映射到CNN的最后一層卷積feature map上
- ROI pooling層使每個ROI生成固定尺寸的feature map
- fast-rcnn網絡利用softmax loss和smooth L1 loss對分類概率和邊框回歸聯合訓練
網絡結構
RPN網絡:
layer | input | size_kernal | pad | stride | output |
---|---|---|---|---|---|
conv1 | 3@800×600 | 7 × 7 | 3 | 2 | 96@401 × 301 |
pool1 | 96@401 × 301 | 3 × 3 | 1 | 2 | 96@201 × 152 |
conv2 | 96@201 × 152 | 5 × 5 | 2 | 2 | 256@101 × 77 |
pool2 | 256@101 × 77 | 3 × 3 | 1 | 2 | 256@51 × 39 |
conv3 | 256@51 × 39 | 3 × 3 | 1 | 1 | 384@51 × 39 |
conv4 | 384@51 × 39 | 3 × 3 | 1 | 1 | 384@51 × 39 |
conv5 | 384@51 × 39 | 3 × 3 | 1 | 1 | 256@51 × 39 |
conv_prososal1 | 256@51 × 39 | 3 × 3 | 1 | 1 | 256@51 × 39 |
proposal_bbox_pred | 256@51 × 39 | 1 × 1 | 0 | 1 | 36@51 × 39 |
proposal_cls_score | 256@51 × 39 | 1 × 1 | 0 | 1 | 18@51 × 39 |
proposal_cls_score_reshape | 18@51 × 39 | - | - | - | 2 ×@51 × 351 |
fast-rcnn網絡:
訓練代碼
- 參數配置
需要配置的參數包括model,dataset,conf_proposal,conf_fast_rcnn。model包含了圖像均 值、預訓練網絡、stage1_rpn、stage1_fast_rcnn、stage2_rpn、stage2_fast_rcnn、pre_trainde_net_file。dataset經訓練數據組織成imdb和roidb的形式,imdb文件是一個matlab的表結構,表的每一行是一幅圖像,分別包含如下信息:圖像的路徑,編號,大小,groundtruth等。conf_proposal以及conf_fast_rcnn配置了RPN和fast-rcnn網絡的基本參數。
model = Model.ZF_for_Faster_RCNN_VOC0712;
dataset = Dataset.voc0712_trainval(dataset, 'train', use_flipped);
dataset = Dataset.voc2007_test(dataset, 'test', false);
conf_proposal = proposal_config('image_means', model.mean_image, 'feat_stride', model.feat_stride);
conf_fast_rcnn = fast_rcnn_config('image_means', model.mean_image);
- 產生anchor
首先產生輸入圖像大小和conv5_3大小的對應關系map;然后產生9個基本anchors。
[conf_proposal.anchors, conf_proposal.output_width_map, conf_proposal.output_height_map]
= proposal_prepare_anchors(conf_proposal, model.stage1_rpn.cache_name, model.stage1_rpn.test_net_def_file);
- 開始訓練
訓練采取分步訓練的方式,共分為4個階段:訓練RPN網絡;RPN網絡提取的Proposal作為輸入訓練fast-rcnn網絡;用前一階段訓練得到的網絡作為初始網絡,繼續訓練RPN網絡;用第二階段訓練的網絡作為初始網絡,前一階段訓練的RPN網絡提取的Propsol作為輸入訓練fast-RCNN網絡。
%% stage one proposal
% train
model.stage1_rpn = Faster_RCNN_Train.do_proposal_train(conf_proposal, dataset, model.stage1_rpn, opts.do_val);
% test
dataset.roidb_train = cellfun(@(x, y) Faster_RCNN_Train.do_proposal_test(conf_proposal, model.stage1_rpn, x, y), dataset.imdb_train, dataset.roidb_train, 'UniformOutput', false);
dataset.roidb_test = Faster_RCNN_Train.do_proposal_test(conf_proposal, model.stage1_rpn, dataset.imdb_test, dataset.roidb_test);
%% stage one fast rcnn
% train
model.stage1_fast_rcnn = Faster_RCNN_Train.do_fast_rcnn_train(conf_fast_rcnn, dataset, model.stage1_fast_rcnn, opts.do_val);
% test
opts.mAP = Faster_RCNN_Train.do_fast_rcnn_test(conf_fast_rcnn, model.stage1_fast_rcnn, dataset.imdb_test, dataset.roidb_test);
%% stage two proposal
% net proposal
% train
model.stage2_rpn.init_net_file = model.stage1_fast_rcnn.output_model_file;
model.stage2_rpn = Faster_RCNN_Train.do_proposal_train(conf_proposal, dataset, model.stage2_rpn, opts.do_val);
% test
dataset.roidb_train = cellfun(@(x, y) Faster_RCNN_Train.do_proposal_test(conf_proposal, model.stage2_rpn, x, y), dataset.imdb_train, dataset.roidb_train, 'UniformOutput', false);
dataset.roidb_test = Faster_RCNN_Train.do_proposal_test(conf_proposal, model.stage2_rpn, dataset.imdb_test, dataset.roidb_test);
%% stage two fast rcnn
% train
model.stage2_fast_rcnn.init_net_file = model.stage1_fast_rcnn.output_model_file;
model.stage2_fast_rcnn = Faster_RCNN_Train.do_fast_rcnn_train(conf_fast_rcnn, dataset, model.stage2_fast_rcnn, opts.do_val);
do_proposal_train代碼如下,采樣時并不是整張圖像的每個位置都參與梯度反傳,而是通過隨機采樣的方式決定哪些前景或背景的rois參與訓練,參與訓練權重為1,否則置為0。這樣可以保證背景和前景的數量不至于懸殊太大。
% Preparing training data
[image_roidb_train, bbox_means, bbox_stds]...
= proposal_prepare_image_roidb(conf, opts.imdb_train, opts.roidb_train);
while (iter_ < max_iter)
caffe_solver.net.set_phase('train');
% generate minibatch training data
[shuffled_inds, sub_db_inds] = generate_random_minibatch(shuffled_inds, image_roidb_train, conf.ims_per_batch);
[net_inputs, scale_inds] = proposal_generate_minibatch_fun(conf, image_roidb_train(sub_db_inds));
caffe_solver.net.reshape_as_input(net_inputs);
% one iter SGD update
caffe_solver.net.set_input_data(net_inputs);
caffe_solver.step(1);
iter_ = caffe_solver.iter();
end
檢測代碼
- 初始化模型
model_dir = fullfile(pwd, 'output', 'faster_rcnn_final', 'faster_rcnn_VOC0712_ZF');
proposal_detection_model = load_proposal_detection_model(model_dir);
proposal_detection_model.conf_proposal.test_scales = opts.test_scales;
proposal_detection_model.conf_detection.test_scales = opts.test_scales;
rpn_net = caffe.Net(proposal_detection_model.proposal_net_def, 'test');
rpn_net.copy_from(proposal_detection_model.proposal_net);
fast_rcnn_net = caffe.Net(proposal_detection_model.detection_net_def, 'test');
fast_rcnn_net.copy_from(proposal_detection_model.detection_net);
- 提取proposal
首先將輸入圖像縮放到目標尺度,然后調整圖像的維度順序,輸入送進網絡得到輸出,通過bbox的損失預測bbox的位置,最后對每個bbox的分數排序,取高分bbox作為proposal。
[im_blob, im_scales] = get_image_blob(conf, im);
im_size = size(im);
scaled_im_size = round(im_size * im_scales);
im_blob = im_blob(:, :, [3, 2, 1], :); % from rgb to brg
im_blob = permute(im_blob, [2, 1, 3, 4]);
im_blob = single(im_blob);
net_inputs = {im_blob};
caffe_net.reshape_as_input(net_inputs);
output_blobs = caffe_net.forward(net_inputs);
box_deltas = output_blobs{1};
featuremap_size = [size(box_deltas, 2), size(box_deltas, 1)];
box_deltas = permute(box_deltas, [3, 2, 1]);
box_deltas = reshape(box_deltas, 4, [])';
anchors = proposal_locate_anchors(conf, size(im), conf.test_scales, featuremap_size);
pred_boxes = fast_rcnn_bbox_transform_inv(anchors, box_deltas);
scores = output_blobs{2}(:, :, end);
scores = reshape(scores, size(output_blobs{1}, 1), size(output_blobs{1}, 2), []);
scores = permute(scores, [3, 2, 1]);
scores = scores(:);
[scores, scores_ind] = sort(scores, 'descend');
pred_boxes = pred_boxes(scores_ind, :);
- 開始檢測
檢測階段,將rpn網絡的最后一個卷積層和rois作為輸入送入fast-rcnn網絡,輸出結果為類標簽和回歸之后的bbox。
caffe_net.blobs('data').copy_data_from(conv_feat_blob);
net_inputs = {[], sub_rois_blob};
% Reshape net's input blobs
caffe_net.reshape_as_input(net_inputs);
output_blobs = caffe_net.forward(net_inputs);
pred_boxes = fast_rcnn_bbox_transform_inv(boxes, box_deltas);
pred_boxes = clip_boxes(pred_boxes, size(im, 2), size(im, 1));
【參考文獻】
http://blog.csdn.net/Seven_year_Promise/article/details/60954553
http://blog.csdn.net/mllearnertj/article/details/53709766