微調網絡,通常我們有一個初始化的模型參數文件,這裏是不同於training from scratch,scrachtch指的是我們訓練一個新的網絡,在訓練過程中,這些參數都被隨機初始化,而fine-tuning,是我們可以在ImageNet上1000類分類訓練好的參數的基礎上,根據我們的分類識別任務進行特定的微調
這裏我以一個車的識別爲例,假設我們有1種車需要識別,我的任務對象是車,現在有ImageNet的模型參數文件,在這裏使用的網絡模型是CaffeNet,是一個小型的網絡,其實別的網絡如GoogleNet也是一樣的原理。那麼這個任務的變化可以表示爲:
任務:分類 類別數目:1000(ImageNet上1000類的分類任務)------> 1(自己的特定數據集的分類任務車)
那麼在網絡的微調中,我們的整個流程分爲以下幾步:
1. 依然是準備好我們的訓練數據和測試數據
2. 計算數據集的均值文件,因爲集中特定領域的圖像均值文件會跟ImageNet上比較General的數據的均值不太一樣
3. 修改網絡最後一層的輸出類別,並且需要加快最後一層的參數學習速率
4. 調整Solver的配置參數,通常學習速率和步長,迭代次數都要適當減少
5. 啓動訓練,並且需要加載pretrained模型的參數
1.準備數據集
這一點就不用說了,準備兩個txt文件,放成list的形式,可以參考caffe下的example,圖像路徑之後一個空格之後跟着類別的ID,如下,這裏記住ID必須從0開始,要連續,否則會出錯,loss不下降,按照要求寫就OK。
這個是訓練的圖像label,測試的也同理
1. 創建lmdb文件,使用caffe下的convert_imageset 工具,具體命令如下:
./build/tools/convert_imageset /media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/ data/cartest/carlist.txt data/cartest/train_car_lmdb -resize_width=227 -resize_height=227 -check_size -shuffle true
其中第一個參數是基地址路徑用來拼接的,第二個是label的文件,第三個是生成的數據庫文件支持leveldb或者lmdb,接着是resize的大小,最後是否隨機圖片順序
計算均值,使用caffe下的convert_imageset 工具,具體命令
./build/tools/compute_image_mean /media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/train_car_lmdb/ data/carmean.binaryproto
第一個參數是基地址路徑用來拼接的,第二個是lmdb文件,第三個是生成的均值文件carmean.binaryproto
3.調整網絡層參數
參照Caffe上的例程,我用的是CaffeNet,首先在輸入層data層,修改我們的source 和 meanfile, 根據之前生成的lmdb 和mean.binaryproto修改即可。
最後輸出層是fc8,
1.首先修改名字,這樣預訓練模型賦值的時候這裏就會因爲名字不匹配從而重新訓練,也就達成了我們適應新任務的目的。
2.調整學習速率,因爲最後一層是重新學習,因此需要有更快的學習速率相比較其他層,因此我們將,weight和bias的學習速率加快10倍。
修改./models/bvlc_reference_caffenet/train_cal_resnet_lily.prototxt中 train和test對應的相關路徑
mean_file: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/carmean.binaryproto
source: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/train_car_lmdb"
修改./models/bvlc_reference_caffenet/solver_resnet_lily.prototxt
net: "models/bvlc_reference_caffenet/train_val_resnet_lily.prototxt"
test_iter: 100
test_interval: 1000
base_lr: 0.001
lr_policy: "step"
gamma: 0.1
stepsize: 20000
display: 20
max_iter: 50000
momentum: 0.9
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "models/bvlc_reference_caffenet/caffenet_resnet_model_lily"
solver_mode: GPU
原來是fc8,記得把跟fc8連接的名字都要修改掉,修改修改./models/bvlc_reference_caffenet/train_val_resnet_lily.prototxt 後如下
layer {
name: "fc8_comp_model"
type: "InnerProduct"
bottom: "fc7"
top: "fc8_comp_model"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8_comp_model"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc8_comp_model"
bottom: "label"
top: "loss"
}
主要的調整有:test_iter從1000改爲了100,因爲數據量減少了,base_lr從0.01變成了0.001,這個很重要,微調時的基本學習速率不能太大,學習策略沒有改變,步長從原來的100000變成了20000,最大的迭代次數也從450000變成了50000,動量和權重衰減項都沒有修改,依然是GPU模型,網絡模型文件和快照的路徑根據自己修改
train_val_resnet_lily.prototxt完整文件爲:
name: "CaffeNet"
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
crop_size: 227
mean_file: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/carmean.binaryproto"
}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: true
# }
data_param {
source: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/train_car_lmdb"
batch_size: 256
backend: LMDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mirror: false
crop_size: 227
mean_file: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/carmean.binaryproto"
}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: false
# }
data_param {
source: "/media/***/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/data/cartest/train_car_lmdb"
batch_size: 50
backend: LMDB
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96
kernel_size: 11
stride: 4
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm1"
type: "LRN"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "norm1"
top: "conv2"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc8_comp_model"
type: "InnerProduct"
bottom: "fc7"
top: "fc8_comp_model"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8_comp_model"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc8_comp_model"
bottom: "label"
top: "loss"
}
訓練的指令如下:
./build/tools/caffe train --solver ./models/bvlc_reference_caffenet/solver_resnet_lily.prototxt --weights ./models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel --gpu 0
測試指令:
python ./python/classify02.py --model_def ./models/bvlc_reference_caffenet/train_val_test_resnet_lily.prototxt --pretrained_model ./models/bvlc_reference_caffenet/caffenet_resnet_model_lily_iter_50000.caffemodel --labels_file ./data/cartest/cartest.txt --center_only ./data/cartest/JPEGImages/crk201706301341.jpg foo
注意這裏的train_val_test_resnet_lily.prototxt文件與訓練時的文件train_val_resnet_lily.prototxt文件是不一樣的。
train_val_resnet_lily.prototxt文件爲:
name: "train_resnet_lily"
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96
kernel_size: 11
stride: 4
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm1"
type: "LRN"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "norm1"
top: "conv2"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc8_comp_model"
type: "InnerProduct"
bottom: "fc7"
top: "fc8_comp_model"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "fc8_comp_model"
top: "prob"
}
報錯及解決方案
在最後一步測試的時候運行報錯
報錯:
File "python/classify.py", line 138, in <module>
main(sys.argv)
File "python/classify.py", line 110, in main
channel_swap=channel_swap)
File "/media/futurus/801328a5-39c6-4e08-b070-19fc662a5236/resnet/caffe/python/caffe/classifier.py", line 29, in __init__
in_ = self.inputs[0]
IndexError: list index out of range
參考解決方案;
加入:
net: "train_resnet_lily"
input: "data"
input_shape {
dim: 10
dim: 3
dim: 224
dim: 224
}
加入之後 又報了其他錯誤:
[libprotobuf ERROR google/protobuf/text_format.cc:274] Error parsing text-format caffe.NetParameter: 1:4: Message type "caffe.NetParameter" has no field named "net".
F0125 11:48:14.708683 42586 upgrade_proto.cpp:88] Check failed: ReadProtoFromTextFile(param_file, param) Failed to parse NetParameter file: ./models/bvlc_reference_caffenet/train_val_test_resnet_lily.prototxt
*** Check failure stack trace: ***
Aborted (core dumped)
根據網友的博客修改加入爲
net: "train_resnet_lily"
input: "data"
input_dim: 10
input_dim: 3
input_dim: 224
input_dim: 224
測試還不不行,還是報錯。
最終解決方案如下,運行通過。加入
name: "train_resnet_lily"
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
}
Reference:
https://www.cnblogs.com/louyihang-loves-baiyan/p/5038758.html
http://blog.csdn.net/sunshine_in_moon/article/details/49472901