<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
<div class="htmledit_views" id="content_views">
<p>本文为博主学习caffe的相关笔记。</p>
在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.数据预处理。