操作系统:windows7, 显卡:NVIDIA GTX 780
Caffe是直接从官方GitHub上down下来的,之前安装教程推荐了Happynear的版本,但不知道为什么,我只要使用GPU模式电脑就直接黑屏重启,折腾了好久还以为显卡坏了(有人说是供电不足),但用官方的版本则没问题。
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和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文件
需要用到 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
对于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 } } } ...
输入数据格式改为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. 误差曲线
需要用到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