【Caffe】网络参数详解(Alexnet举例)

本文为博主学习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.数据预处理。

 

你可能感兴趣的:(caffe)