1. 环境
操作系统:windows7, 显卡:NVIDIA GTX 780
Caffe是直接从官方GitHub上down下来的,之前安装教程推荐了Happynear的版本,但不知道为什么,我只要使用GPU模式电脑就直接黑屏重启,折腾了好久还以为显卡坏了(有人说是供电不足),但用官方的版本则没问题。
2. 数据准备
1)将带标签图像数据按6:2:2(根据Andrew机器学习课程笔记(2)——神经网络、机器学习Tips中的推荐)分为 train,test,validate 3类,train和test在模型训练中使用,validate在模型精度测试中使用
../../../FaceDetection_CNN-master/crop_images/face/image12155_8.jpg 1
../../../FaceDetection_CNN-master/crop_images/face/image12155_9.jpg 1
../../../FaceDetection_CNN-master/crop_images/non-face/image04210_10.jpg 0
../../../FaceDetection_CNN-master/crop_images/non-face/image04210_13.jpg 0
../../../FaceDetection_CNN-master/crop_images/non-face/image04210_14.jpg 0
../../../FaceDetection_CNN-master/crop_images/face/image04210_16.jpg 1
如上所示为train.list的部分内容,后面的数字表示该样本所属类别(从0开始)。
根据实际情况可能还需要对train和test正负样本的比例做调整(我都调整为1:1,直接用copy的方式做填充),之后再将记录随机打乱以避免同类样本扎堆的情况
数据准备这块推荐使用Python实现,太方便了
2)Caffe接受直接图像数据(jpg,png等)、LevelDB和LMDB 3中数据格式,如果要使用后两种,还需将上一步的数据做转换
转换代码在 ./tools/convert_imageset.cpp,直接将其作为工程main文件即可
LMDB格式似乎在windows上没法用
3)一般来说还需生成一个mean.binaryproto的均值文件,其实就是统计所有训练样本的均值,将其作为样本的背景,以后在训练和测试中首先将样本减去该均值以排除背景干扰
相应代码在 ./tools/compute_image_mean.cpp,但这里只接受LevelDB或LMDB作为原始数据输入,因此如果要均值文件首先必须将数据转为LevelDB或LMDB格式
我在训练自己的数据时使用jpg格式,因此也就没有用均值文件(主要是之前测试发现数据格式基本不影响训练速度,而转为LevelDB格式所占的空间大很多,此外,没有用mean文件似乎对最终精度影响也不大,虽然从理论上mean是必要的)
Fine-tuning时使用初始模型对应的mean文件
3. 训练
需要用到 solver.prototxt 和 train.prototxt 两个定义文件,前者决定训练的一些参数,后者决定训练采用的CNN网络结构
solver.prototxt文件示例:
net: "../../../FaceDetection_CNN-master/train_val.prototxt"
#test迭代次数 如果batch_size =100,则100张图一批,训练157次,则可以覆盖15700张图的需求
test_iter: 157
#训练迭代500次,测试1次
test_interval: 500
base_lr: 0.01 #<span style="color: rgb(68, 68, 68); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 20.3px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">开始学习速率</span>
lr_policy: "step" #<span style="color: rgb(68, 68, 68); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 20.3px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">学习策略: 每stepsize次迭代之后,将α乘以gamma</span>
gamma: 0.1 #<span style="color: rgb(68, 68, 68); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 20.3px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">学习速率变化因子</span>
stepsize: 20000
display: 20 #训练20次显示一次精度
max_iter: 100000 #<span style="color: rgb(68, 68, 68); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 20.3px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">训练的最大迭代次数</span>
momentum: 0.9
weight_decay: 0.0005
snapshot: 10000 #每训练10000次保存一次模型
snapshot_prefix: "../../../FaceDetection_CNN-master/tune_model/gpu_alexNet_"
solver_mode: GPU
如果训练时发现精度发散,可以尝试减小学习率base_lr(默认为0.01),同样对于fine-tuning,也应设置个较低的base_lr(0.001)
对于fine-tuning,还应适当降低stepsize的值(20000),其实就是避免初始模型变得太快而导致发散
train.prototxt部分示例:
name: "CaffeNet"
layer {
name: "data"
type: "ImageData"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
#crop_size: 227
#mean_file: "../../../IMG/img_mean.binaryproto"
}
# data_param {
# source: "../../../IMG/trainLevelDB"
# batch_size: 80
# backend: LEVELDB
# }
<span style="white-space:pre"> </span>image_data_param {
source: "../../../IMG/train.list"
batch_size: 50
new_height: 250
new_width: 340
}
}
layer {
name: "data"
type: "ImageData"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mirror: false
#crop_size: 227
#mean_file: "../../../IMG/img_mean.binaryproto"
}
# data_param {
# source: "../../../IMG/testLevelDB"
# batch_size: 50
# backend: LEVELDB
# }
<span style="white-space:pre"> </span>image_data_param {
source: "../../../IMG/test.list"
batch_size: 30
new_height: 250
new_width: 340
}
}
...
layer {
name: "fc8"
type: "InnerProduct"
bottom: "fc7"
top: "fc8"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
...
这里以官方的caffeNet网络结构做细微修改
输入数据格式改为imagedata,相应的data_param也改为image_data_param,mean_file也不用了,此外由于图像尺寸较大(340*250),需要适当缩小batch_size的值,否则使用GPU时可能超出其缓存大小而报错
最后一层的全连接层的输出改为2(因为这个例子是2元分类),对于fine-tuning,这一层的lr_mult需要适当增加(10),因为这一层在fine-tuning中需要变的最多
主程序部分
./tools/caffe.cpp,无需修改,直接拿来用就成。
在slover.cpp里添加了误差记录代码,每次test画出其误差曲线(图1)
图1. 误差曲线
4. 分类(验证)
需要用到deploy.prototxt,其主体与train.prototxt类似,都是定义CNN模型结构,但输入形式不一样(这里只是针对一个样本,而train这对一批样本),此外train最后的accuracy层和loss层也需换成prob层
deploy.prototxt部分示例:
name: "CaffeNet"
input: "data"
input_dim: 10
input_dim: 3
input_dim: 250 #height
input_dim: 340 #width
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: "prob"
type: "Softmax"
bottom: "fc8"
top: "prob"
}
主要参考 ./examples/cpp_classification/classification.cpp
如果是用作模型精度测试,建议添加查全率和查准率的计算,避免测试样本不平衡的影响
P.S. 训练和分类都要用同一种计算模式(CPU或GPU),否则会出错(估计是两种模式生成的caffemodel文件不同)
P.P.S 一个不错的参考:Caffe学习:Solver