以前一直用TensorFlow,但是发现好多大牛写的文章都是用caffe实现的,因此不得不入门caffe。caffe的安装参考这里,包括Ubuntu和window下的安装。在我们安装好caffe以后,我们可以在caffe内置的目录下进行mnist的测试,测试步骤如下:
1、下载mnist数据库
cd ~/caffe
sduo ./data/mnist/get_mnist.sh
执行之后,在./data/mnist文件夹下生成4个文件,分别是测试、训练数据库和测试、训练标签:
2、二进制数据库文件转换成lmdb数据库格式
sudo ./examples/mnist/create_mnist.sh
执行之后,在 ./examples/mnist文件夹下生成两个文件夹,一个是 mnist_train_lmdb 和 mnist_test_lmdb,分别存放了训练和测试数据:
3、训练lenet网络:
如果只是用CPU训练的话,需要先在 lenet_solver.prototxt 文件中修改训练模式为CPU,修改文件最后一行的 solver_mode的配置为 CPU。以下指令执行训练:
sudo ./examples/mnist/train_lenet.sh
训练结果的分类文件 lenet_iter_10000.caffemodel 文件保存在 ./examples/mnist路径下。
------------------------------------------------------------------------------------------------------
接下来我们介绍caffe训练模型需要三个文件,分别是solver.prototxt、train_val.prototxt、train.sh,下面我们依次介绍。
4、solver.prototxt
solver.prototxt这个文件主要存放模型训练所用到的一些超参数:
net := 指定待训练模型结构文件,即train_val.prototxt
test_interval := 测试间隔,即每隔多少次迭代进行一次测试。
test_initialization := 指定是否进行初始测试,即模型未进行训练时的测试
test_iteration := 指定测试时进行的迭代次数,即test_iter* batchsize(测试集的)=测试集的大小,测试集的 batchsize可以在prototx文件里设置。
base_lr := 指定基本学习率
lr_policy := 学习率变更策略,这个参数代表的是learning rate应该遵守什么样的变化规则,这个参数对应的是字符串,选项及说明如下:
“step” - 需要设置一个stepsize参数,返回base_lr * gamma ^ ( floor ( iter / stepsize ) ),iter为当前迭代次数
“multistep” - 和step相近,但是需要stepvalue参数,step是均匀等间隔变化,而multistep是根据stepvalue的值进行变化
“fixed” - 保持base_lr不变
“exp” - 返回base_lr * gamma ^ iter, iter为当前迭代次数
“poly” - 学习率进行多项式误差衰减,返回 base_lr ( 1 - iter / max_iter ) ^ ( power )
“sigmoid” - 学习率进行sigmod函数衰减,返回 base_lr ( 1/ 1+exp ( -gamma * ( iter - stepsize ) ) )
gamma := 学习率变更策略需要用到的参数
power := 同上
stepsize := 学习率变更策略Step的变更步长(固定步长),每stepsize次迭代减少学习率,这一项和lr_policy有关
stepvalue := 学习率变更策略Multistep的变更步长(可变步长)
iter_size: iter_size,iter_size:iter_size*batch size=实际使用的batch size。 相当于读取batchsize*itersize个图像才做一下gradient decent。 这个参数可以规避由于gpu不足而导致的batchsize的限制 因为你可以用多个iteration做到很大的batch 即使单次batch有限
max_iter := 模型训练的最大迭代次数
momentum := 动量,这是优化策略(Adam, SGD, … )用到的参数,灵感来自于牛顿第一定律,基本思路是为寻优加入了“惯性”的影响,这样一来,当误差曲面中存在平坦区的时候,SGD可以更快的速度学习。
momentum2 := 优化策略Adam用到的参数
weight_decay := 权重衰减率,用于防止过拟合。使用方式:样本越多,该值越小,模型参数越多,该值越大。一般建议值:weight_decay: 0.0005。
clip_gradients := 固定梯度范围
display := 每隔几次迭代显示一次结果
snapshot := 快照,每隔几次保存一次模型参数
snapshot_prefix := 保存模型文件的前缀,可以是路径
type := solver优化策略,即SGD、Adam、AdaGRAD、RMSProp、NESTROVE、ADADELTA等,在solver配置文件中,通过设置type类型来选择:
Stochastic Gradient Descent (type: "SGD"),
AdaDelta (type: "AdaDelta"),
Adaptive Gradient (type: "AdaGrad"),
Adam (type: "Adam"),
Nesterov’s Accelerated Gradient (type: "Nesterov") and
RMSprop (type: "RMSProp")
solver_mode := 指定训练模式,即GPU/CPU
debug_info := 指定是否打印调试信息,这里有对启用该功能的输出作介绍
device_id := 指定设备号(使用GPU模式),选择几块GPU,默认为0
5、train_val.prototxt
train_val文件是用来存放模型结构的地方,模型的结构主要以layer为单位来构建。下面我们以LeNet为例介绍网络层的基本组成:
name: "LeNet"
layer { # 这里为训练的数据层
name: "mnist" # 网络层名称
type: "Data" # 网络层类型,数据层
top: "data" # 这一层的输出,数据
top: "label" # 这一层的输出,标签
include { phase: TRAIN } # TRAIN:=用于训练,TEST:=用于测试,表明这是在训练阶段才包括进去
transform_param {
scale: 0.00390625 # 对数据进行scale
mirror: true # 是否做镜像
crop_size: 227 # 对图像做裁剪
mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" # 减去均值文件
}
data_param { # 数据层配置
source: "examples/mnist/mnist_train_lmdb" # 数据存放路径
batch_size: 64 # 指定batch大小
backend: LMDB # 指定数据库格式,LMDB/LevelDB
}
}
layer { # 这里为测试的数据层
name: "mnist" # 网络层名称
type: "Data" # 网络层类型,数据层
top: "data" # 这一层的输出,数据
top: "label" # 这一层的输出,标签
include { phase: TEST } #TRAIN:=用于训练,TEST:=用于测试,表明这是在测试阶段才包括进去
transform_param {
scale: 0.00390625 # 对数据进行scale
mirror: true # 是否做镜像
crop_size: 227 # 对图像做裁剪
mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" # 减去均值文件
}
data_param { # 数据层配置
source: "examples/mnist/mnist_test_lmdb" # 数据存放路径
batch_size: 100 # 指定batch大小
backend: LMDB # 指定数据库格式,LMDB/LevelDB
}
}
layer{ # 这里为第一个卷积层
name:"conv1" # 卷积层名
type:"Convolution" # 卷积层类型:卷积层
bottom:"data" # 上一层的输出作为输入
top:"conv1" # 这一层的输出,conv1
param{
name:"conv1_w" # 卷积层参数w(权重)的名称、
lr_mult:1 # 卷积层参数w学习率,最终的学习率需要乘以 solver.prototxt 配置文件中的base_lr
decay_mult:1 # 卷积层参数w的衰减率,最终的衰减率需要乘以 solver.prototxt 配置文件中的weight_decay
}
param{
name:"conv1_b" # 卷积层参数b(偏置)的名称,学习率和衰减率
lr_mult:2 # 偏置设为2倍学习率,能够加速收敛
decay_mult:0}
convolution_param{
num_output:20 # 卷积层输出的feature map数量
kernel_size:5 # 卷积层的大小
pad:0 # 卷积层的填充大小
stride:1 # 进行卷积的步长
weight_filler{type:"xavier" } # 参数w的初始话策略
weight_filler{type:"constant" value:0.1} # 参数b的初始化策略
}
}
layer { # BatchNorm层,对feature map进行批规范化处理
name:"bn1"
type:"BatchNorm"
bottom:"conv1"
top:"conv1"
batch_norm_param{ use_global_stats:false} #训练时为false,测试时为true
}
layer { # 池化层,即下采样层
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX # 最大值池化,还有AVE均值池化
kernel_size: 2
stride: 2
}
} # 第二个卷积层配置
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param { lr_mult: 1 }
param { lr_mult: 2 }
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler { type: "xavier" }
bias_filler { type: "constant" }
}
}
layer { # 第二个batchnorm层
name:"bn2"
type:"BatchNorm"
bottom:"conv2"
top:"conv2"
batch_norm_param{ use_global_stats:false}
}
layer { # 第二个池化层
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer { #全连接层
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
param { lr_mult: 1 }
param { lr_mult: 2 }
inner_product_param {
num_output: 500
weight_filler { type: "xavier" }
bias_filler { type: "constant" }
}
}
layer { # 激活函数层,提供非线性能力
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
layer { # 第二个全连接层
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param { lr_mult: 1 }
param { lr_mult: 2 }
inner_product_param {
num_output: 10
weight_filler { type: "xavier" }
bias_filler { type: "constant" }
}
}
layer { # 损失函数层
name: "prob"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "prob"
}
6、train.sh
这个脚本文件可写,可不写。每次运行需要写一样的命令,所以建议写一下。以下是最简单训练脚本:
#!/usr/bin/env sh
set -e
caffe/build/tools/caffe train --solver=xxxxx/solver.prototxt --weights=xxxxx/xxxxx.caffemode
其中, caffe程序的命令行执行格式如下:
caffe <command> <args>
其中的<command>有这样四种:
train : 训练或finetune模型(model),
test :测试模型
device_query :显示gpu信息
time : 显示程序执行时间
其中的<args>参数有:
-solver : 指定sovler.prototxt文件,在train的时候需要这个参数
-gpu :可选;给定时运行GPU模式,用’ , ’分隔开不同的gpu,‘-gpu all’表示运行在所有可用的gpu设备上,此时有效训练批量大小就是gpu设备数乘以batch_size.如使用第二块gpu运行:-gpu 2
-snapshot :可选,恢复训练时指定上次中止的快照,就是比如训练到一般按Ctrl+C终止训练(Linux中这个Ctrl+C不是copy,而是终止当前操作),就会得到一个solverstate文件,下次恢复训练时就可以指定这个.
-weights : 指定用于微调的预训练权值,也即 训练后得到的**.caffemodel文件,不可与snapshot同时出现.
-iteration :循环迭代次数,默认为50.
-model :指定模型定义文本文件名,xxx.prototxt,也可以在solver配置文件中指定。
-sighup_effect :用来设定当程序发生挂起事件时,执行的操作,可以设置为snapshot, stop或none, 默认为snapshot.
-sigint_effect :可选参数。用来设定当程序发生键盘中止事件时(ctrl+c), 执行的操作,可以设置为snapshot, stop或none, 默认为stop
折腾了很久,发现caffe和TensorFlow区别还是好大的,首先是安装问题,TensorFlow安装简单快捷,只需要一条命令pip install TensorFlow,而caffe安装非常复杂,需要安装各种依赖包,然后还需要自己编译,如果是第一次安装,通常会折腾很久才能装上。其次,TensorFlow训练神经网络时整个训练过程需要自己动手实现,包括如何进行数据预处理(如何生成TFrecord文件高效利用内存)、一层一层的调用TensorFlow的函数搭建网络结构、然后定义损失函数和优化器、最后进行训练和保存模型。而caffe在进行网络训练的过程中,前面安装很复杂,但是成功安装以后训练过程就非常简单了。首先是用原始图像制作caffe所支持的数据集文件(通常是lmdb或leveldb,使用一个脚本文件生成),然后是定义好网络结构的文件train.prototxt,然后定义训练过程中的各种参数的文件solver.prototxt,最后就是写一个训练脚本train.sh,在这个脚本中调用编译生成caffe.exe传入各种参数就开始训练了。相对于TensorFlow而言,caffe进行神经网络的训练相对更加抽象,只需要在prototxt文件定义网络层各种参数,caffe从这个文件读取参数然后调用底层的函数帮你创建模型训练,而TensorFlow整个网络结构需要你自己调用TF的函数来创建。