本文为博主学习caffe的相关笔记。
在caffe框架中,网络的各个层的参数可以自己配置。文件名一般是train_val.prototxt。
一、Alexnet:
1. 数据层。
数据层一般作为网络的第一层,由cpu读取数据库中的数据,后面的层的计算由gpu进行。
#数据层(第一层)
layer {
name: "cifar"
type: "Data"
top: "data" #一般用bottom表示输入,top表示输出,第一层没有输入
top: "label" #多个top多个输出
include {
phase: TRAIN #训练网络分为训练和测试阶段,如果没写include则表示该层在测试中,又在训练中
}
transform_param {#计算所有数据RGB通道上的均值mean
#scale:0.00390625,为1/255,把1到255压缩到0到1,这行可以不写
mirror: true #mirror:1 #1表示开启镜像,0表示关闭,也可以用true和false来表示
crop_size: 227 #假设原始数据都resize成256*256,可以随机裁227*227,也相当于做数据增强,随机crop出一系列小区域
mean_file: "examples/imagenet/project/imagenet_mean.binaryproto"#用一个配置文件来计算均值
}
data_param {
source: "examples/imagenet/project/train_lmdb" #数据库来源路径,支持lmdb,hdf5,方便caffe读取。数据是由cpu从数据库取,如果取的太慢,gpu的计算即使再快,整体速度也是慢的
batch_size: 64 #每次批处理的个数,一般是越大越好,越大的话过拟合越小,大的更有共性,一般被显存限制
backend: LMDB #选用数据的名称
}
}
caffe一般处理的数据是lmdb或hdf5格式,上面的注释详细解释了每段代码的含义,使用的数据是lmdb格式。
如果使用HDF5数据源:
###使用HDF5数据源
layer{
name:"data"
type: "HDF5Data"
top: "data"
top: "label"
hdf5_data_param{
source: "examples/imagenet/project/train.txt"
batch_size:16
}
}
如果数据直接来源于图片(不推荐,速度慢):
###数据直接来源于图片
/path/to/images/img3423.jpg 2 #文件的路径和类别
/path/to/images/img3424.jpg 13
/path/to/images/img3425.jpg 8
###不推荐,速度慢。把图片做成txt文件,前面写路径,后面写类别,中间用空格隔开
layer{
name:"data"
type:"ImageData" #类型
top:"data"
top:"label"
transform_param{
mirror:false
crop_size:227
mean_file:"examples/imagenet/project/imagenet_mean.binaryproto"
}
image_data_param{
source:"examples/imagenet/project/file_list.txt"
batch_size:16
new_height:256 #对图片进行resize操作
new_width:256
}
}
2.卷积层。
#卷积层
layer {
name: "conv1"
type: "Convolution"
bottom: "data" #前面输入是data层
top: "conv1" #输出卷积后的结果
param {
lr_mult: 1 #lr_mult:学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult,则第一个表示权值的学习率,第二个表示偏置项对学习率。一般偏置项对学习率是权值学习率的两倍。
decay_mult: 1
}
param {
lr_mult: 2 #如果要让某一层不做更新,就把这两个lr_mult改成0
decay_mult: 0
}
convolution_param {
num_output: 96 #卷积核(filter)的个数
kernel_size: 11 #卷积核的大小
stride: 4 #卷积核的步长,默认为1
#pad:0 #边缘扩充,默认为0,不扩充(因为扩充的值本来是没有的,设为0对本来不干扰)
weight_filler {
type: "gaussian" #权值初始化。默认为“constant”,值全为0。可以用“xavier”或“gaussian”算法
std: 0.01
}
bias_filler {#偏置项的初始化,一般为“constant”,值全为0
type: "constant"
value: 0
}
}
}
输入:n*c0*w0*h0
输出:n*c1*w1*h1
其中,c1就是参数中的num_output,生成的特征图个数
w1=(w0+2*pad-kernel_size)/stride+1
h1=(h0+2*pad-kernel_size)/stride+1
3.激活层。
在激活层中,对输入数据进行激活操作,是逐元素进行运算的,在运算过程中,没有改变数据的大小,即输入和输出的数据大小是相等的。
###Sigmoid
layer{
name:"test"
bottom:"conv"
top:"test"
type:"Sigmoid"
}
Sigmoid可能会发生杀死梯度的现象,用的少。
ReLU是目前使用最多的激活函数,因为其收敛更快,并且能保持同样的效果。标准的ReLU函数为max(x,0),当x>0时,输出x;当x<=0时,输出0。
#激活函数
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
4.池化层。
#池化层
layer {
name: "pool1"
type: "Pooling"
bottom: "norm1"
top: "pool1"
pooling_param {
pool: MAX #池化方法,默认为MAX。目前可用的方法有MAX,AVE
#MAX pooling为,假设一个pool中的值有1,2,5,7,保留最大的7
kernel_size: 3 #池化的核大小
stride: 2 #池化的步长,默认为1.一般设置成2,即不重叠
}
}
5.全连接层
输出的是一个简单向量,参数跟卷积层一样。
相当于做一个特征代提取的过程,最后需要做的任务都是通过全连接这个向量做的。
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: 0.1
}
}
}
6.Accuracy。
测试的时候输入准确率,看当前迭代的准确率。
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8" #分类的结果
bottom: "label" #label
#label和分类的结果需要看是否匹配
top: "accuracy"
include {
phase: TEST
}
}
#softmax·loss layer:输出loss值
layer {
name: "loss"
type: "SoftmaxWithLoss"#计算loss
bottom: "fc8" #由全连接层的计算结果和label输出loss
bottom: "label"
top: "loss"
}
#softmax layer:输出似然值
#期望输出的是准确率的值,属于某一类的概率值
layers{
bottom:"cls3_fc"
top:"prob"
name:"prob"
type:"Softmax"
}
7.reshape层
有的时候需要在不改变数据的情况下,改变输入的维度。
假设原来的输入是32*3*28*28,代表批处理有32张图片,3个通道,w和h值为28和28。如果想把28*28变成14*56(改变长宽)。在不改变原值,只改变位置的时候,用到reshape。
layer{
name:"reshape"
type:"Reshape"
bottom:"input"
top:"output"
reshape_param{
shape{
dim:0 #copy the dimesion from below 等于0表示不变
dim:2 #等于一个值为指定值
dim:3
dim:-1 #infer it from the other dimensions,等于-1表示自己推断
}
}
}
有一个可选的参数组shape,用于指定blob数据的各维的值(blob是一个四维的数据:n*c*w*h)
dim:0 表示维度不变,即输入和输出是相同的维度
dim:2或dim:3 将原来的维度变成2或3
dim:-1 表示由系统自动计算维度。数据的总量不变,系统会根据blob数据的其它三维来自动计算当前的维度值。
假设原数据为32*3*28*28,代表批处理有32张彩色图片,3个通道,w和h值为28和28。
shape{
dim:0
dim:0
dim:14
dim:-1
}
输出数据为:32*3*14*56
8.Dropout层
Dropout是一个防止过拟合的层,只需要设置一个dropout_ratio就可以。(即杀死某些神经元)
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5 #一次迭代杀死50%
}
}
二、mnist:lenet_train_test.prototxt(caffe自带,路径为/caffe / examples / mnist/)
分析接下来整体分析一下mnist中的网络。
name: "LeNet"
#两个数据层,一个TRAIN,一个TEST,所以要指定两个数据源,一个用于训练一个用于测试。
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_test_lmdb"
batch_size: 100
backend: LMDB
}
}
#数据层后接卷积层
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
#卷积层后接池化层
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
#池化层后接了卷积层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"
}
}
}
#卷积层2后接池化层2
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"
}
}
}
#Accuracy在test阶段,衡量label和分类结果
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
#loss与最后的一个全连接层和label相连
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
可以根据自己的逻辑,设计各层的连接,组合后写在配置文件,就可以定义网络。
三、超参数配置
一般超参数在solver.prototxt中配置。
net: "examples/imagenet/project/train_val.prototxt" #网络配置文件的路径,如果使用不同的网络进行训练,可以指定train_net和test_net,但是不推荐这么做
test_iter: 1000 #一次迭代的样本数。在训练和测试中,是按batch数进行训练的,要测试100*batch,假设batch=32,一次就测试100*32个样本。假设有3200个样本,batch=32,iter值就等于3200/32
test_interval: 1000 #测试间隔。每训练1000次,进行一次测试。
base_lr: 0.01 #基础学习率,实际学习率=每一层指定的学习率*base学习率,训练时主要就改这个。如果网络不收敛或过拟合,一般是base值太高,需要改低。
#迭代时,刚开始希望学习率较高,迭代速率能快些。希望学习率能不断降低(训练到后期希望学习率小调整)
lr_policy: "step" #学习策略
gamma: 0.1
stepsize: 100000
display: 20 #每迭代20次打印结果看一看
max_iter: 10000 #最大迭代次数
momentum: 0.9 #动量,为了能更快的朝最优解方向移动,不推荐修改
weight_decay: 0.0001
snapshot: 1000 #快照,每迭代1000次保存模型,保存的模型多利于后面做分析。因为loss一般是震荡下降。
snapshot_prefix: "examples/imagenet/project" #model保存的文件夹
solver_mode: CPU #设置运行模式,CPU或GPU
1. type
caffe中自带的Alexnet中的solver.prototxt没有给type。由于loss function是非凸的,没有解析解,所以需要通过优化方法求解。caffe提供了六种优化算法来求解最优擦单数,在solver配置文件中,通过设置type类型来选择。
六种优化算法:
Stochastic Gradient Descent(type:"SGD"),
AdaDelta(type:AdaDelta),
Adaptive Gradient(type:"AdaGrad"),
Adam(type:"Adam"),
Nesterov's Accelerated Gradient(type:"Nesterov")
RMSprop(type:"RMSProp")
一般SGD,SGD不能优化的,别的算法也不太好用。
2. 学习策略(lr_policy)
caffe也提供了几种学习策略可供选择:
fixed:保持base_lr不变
step:如果设置为stepm,则还需要设置一个stepsize,返回base_lr*gamma^(floor(Iter/stepsize)),其中iter表示当前的迭代次数
exp:返回base_lr*gamma^iter,iter为当前迭代次数
inv:如果设置成inv,还需设置一个power,返回base_lr*(1+gamma*iter)^(-power)
multistep:如果设置为multistep,则还需要设置成一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep是根据stepvalue值变化
poly:学习率进行多项式误差,返回base_lr(1-iter/max_iter)^(power)
sigmoid:学习率进行sigmoid衰减,返回base_lr(1/(1+exp(-gamma*(iter-stepsize))))
在实际训练中,如果网络不收敛,如果被认为是学习率的问题,可以调整学习率,一般用inv。
3. 其它参数的具体用法见注释
四、过拟合
发生原因:数据不符合网络要求。一般是网络架构层次太深。
解决方法:
1.修改学习率。比如从第20000次开始发生过拟合现象,就拿第20000次的结果来当成第一次,调整学习率(调小)。
2.调整网络架构。可以选择其它架构。
3.数据预处理。