Caffe--"deploy.prototxt" and "train_val.prototxt"

最近学习了一下Caffe,并且用迁移学习完成了一个简单的猫狗分类问题。在学习的过程中,踩了很多坑,这里主要记录一下关于几个prototxt配置文件的问题。

prototxt文件

  • train_val.prototxt:网络配置文件,用于训练
  • deploy.prototxt:网络配置文件,用于inference,测试
  • solver.prototxt:参数文件,用于训练,设置了学习率、优化方法等,slover函数生成

fine-tune

为了将在ImageNet上训练好的模型用于自己的数据集分类问题,我们需要对网络进行迁移学习或者微调。

下面以GoogleNet为例对几点需要注意的地方进行说明:

  1. 先准备好训练数据与测试数据。在train_val.prototxt文件中,如果输入的数据可以是图片格式,也可以是存储在数据库中的lmdb格式,可以加快读取速度。两种情况下数据层的配置有区别,如下:

Image格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
layer {
name: "data"
type: "ImageData"
top: "data"
top: "label"
include {
phase: TRAIN
}
image_data_param {
source: "train.txt"
new_height: 224
new_width: 224
}
}
layer {
name: "data"
type: "ImageData"
top: "data"
top: "label"
include {
phase: TEST
}
image_data_param {
source: "val.txt"
batch_size: 50
new_height: 224
new_width: 224
}
}

其中train.txt和val.txt分别是保存了训练集和测试集的路径的文件,直接以图片格式读入。

lmdb格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
crop_size: 224
mean_value: 104
mean_value: 117
mean_value: 123
}
data_param {
source: "examples/imagenet/ilsvrc12_train_lmdb"
batch_size: 32
backend: LMDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mirror: false
crop_size: 224
mean_value: 104
mean_value: 117
mean_value: 123
}
data_param {
source: "examples/imagenet/ilsvrc12_val_lmdb"
batch_size: 50
backend: LMDB
}
}

这个需要自己利用train.txt和val.txt文件从硬盘读取图像数据保存在LMDB数据库,该数据格式采用内存-映射,故I/O效率高。

  1. 需要改变最后一个fc层的名称(type:”InnerProduct”),并将输出维度改为我们的类别数,也就相当于重头开始学习一个新的层,这样在训练时,模型就会去fit我们的训练数据。

  2. 当自己的数据集够大时,可以在训练过程中对所有网络权重进行更新;但是本身任务的数据集较小时,为了避免过拟合,需要冻结一部分网络,一般是选择固定前面层的参数,不更新权重,只更新最后几层(迁移学习时冻结前面所有层,只对最后一个fc层进行权重更新,训练分类器)。这是因为网络的前几层得到的特征是比较基础local的特征,基本适用于全部任务(边缘,角点特征),而网络层数越高的层,和全局信息联系越紧密,local的信息是多数情况共享的,而global的信息和原图紧密相关。

具体的不更新权重的方法在caffe中就是将该层param中的学习率设为0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
layer {
name: "conv1/7x7_s2"
type: "Convolution"
bottom: "data"
top: "conv1/7x7_s2"
param {
lr_mult: 0
decay_mult: 1
}
param {
lr_mult: 0
decay_mult: 0
}
convolution_param {
num_output: 64
pad: 3
kernel_size: 7
stride: 2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}

  1. 在训练过程中一般将偏置项b的学习率设置为权重矩阵w的两倍,最终的学习率等于train_val.prototxt中的lr_mult*base_lr(slover.prototxt中设置)。在fine-tune时,base_lr要设置得很小,一般在1e-5数量级,而最后一层的lr_mult设置得较大,因为该层是重新学习,一般是其它层的10倍。

  2. solver参数都需要进行修改,原来的参数适用于training from scratch,学习率、步长、迭代次数都较大,我们都需要改得比较小。

1
2
3
4
5
6
7
8
9
10
11
12
#Based on https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenet
net: "train_val.prototxt"
display: 50
base_lr: 0.0001
lr_policy: "fixed"
max_iter: 15000
momentum: 0.9
weight_decay: 0.0002
snapshot_prefix: "transfer_learning"
solver_mode: CPU
test_iter: 100
test_interval: 1000
  1. 在做inference时,使用deploy.prototxt文件,其实是train_val.prototxt文件改动了前面的数据层以及最后的计算loss的部分。这时数据层很简单:
1
2
3
4
5
6
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 1 dim: 3 dim: 224 dim: 224 } }
}

而针对内积之后的softmax输出层,训练和测试时也有差别:

training:

1
2
3
4
5
6
7
8
layer {
name: "loss3/loss3"
type: "SoftmaxWithLoss"
bottom: "loss3/classifier_dc"
bottom: "label"
top: "loss3/loss3"
loss_weight: 1
}

inference:

1
2
3
4
5
6
layer {
name: "prob"
type: "Softmax"
bottom: "loss3/classifier_dc"
top: "prob"
}

因为测试时不需要计算误差进行bp,也就不需要label,故bottom层只有一个,type也不同。

------ Finished ------
0%