caffe基本概念

在Caffe中,最基本的计算节点是层(Layer),所有计算关系都是基于层的。

  • 层(Layer)
  • 网络(Net)
  • 数据(Blob)
  • 梯度下降(Solver)

Caffe采用ptototxt文件来村塾网络结构和训练参数,并将网络参数存储在caffemodel文件中。以命令行方法启动Caffe训练,命令如下:

1
sudo sh caffe train --solver=solver.prototxt

solver.prototxt文件

solver.prototxt文件是用来告诉caffe如何训练网络的。
caffe提供了6中优化算法,在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”)

solver流程:

  1. 设计好要优化对象以及用于学习的训练网络、用于评估测试的网络
  2. 通过forward和backward迭代进行优化来更新参数
  3. 定期评价测试网络(可以设定多少次训练后进行一次测试)
  4. 在优化过程中显示模型和solver的状态

在每一次迭代中,solver做了一下工作:

  1. 调用forward来计算最终的输出值以及对应的loss
  2. 调用backward来计算每层的梯度
  3. 根据选用的solver方法,利用梯度进行参数更新
  4. 记录并保存每次迭代的学习率、快照以及对应的状态

示例文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
net: "/media/hl/新加卷/mycode/SegNet/SegNet/Models/segnet_train.prototxt" 
test_initialization: false
test_iter: 1 #测试迭代次数
test_interval: 10000000 # 测试间隔
base_lr: 0.001 # 基础学习率
lr_policy: "step" # 学习率变化规律
gamma: 1.0 # 学习率变化指数
stepsize: 10000000 # 学习率变化频率
display: 20 # 屏幕日志显示间隔,设置为0就不显示
momentum: 0.9 # 动量
max_iter: 40000 # 最大迭代次数
weight_decay: 0.0005 # 权值衰减,防止过拟合
snapshot: 1000 # 保存model的间隔
snapshot_prefix: "/media/hl/新加卷/mycode/SegNet/SegNet/Models/Training/segnet"
snapshot_diff: true # 是否保存梯度值,默认为false,不保存
snapshot_format: HDF5 # 保存的类型,HDF5和BINARYPROTO,默认为BINARYPROTO
solver_mode: GPU #默认GPU,若没有GPU需要改成CPU

  • net设置深度网络模型,每个模型就是一个net,需要在一个专门的配置文件中对net进行配置,每个net由许多layer组成。文件的路径要从caffe的根目录开始,其他配置也要求这样。
    也可用train_net和test_net来对训练模型和测试模型分别设定。例如:

    1
    2
    train_net: "/media/hl/新加卷/mycode/SegNet/SegNet/Models/segnet_train.prototxt"
    test_net: "/media/hl/新加卷/mycode/SegNet/SegNet/Models/segnet_test.prototxt"
  • lr_policy学习率调整策略。

    • fixed:保持base_lr不变
    • step:如果设置为step,则还需要设置一个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:学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter - stepsize))))
      下面是生成solver文件的代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      path='/media/hl/新加卷/mycode/SegNet/SegNet/Models/'
      solver_file=path+'segnet_solver.prototxt' #solver文件保存位置
      solver={}
      solver['train_net']='"'+path+'segnet_train.prototxt"' # 训练配置文件
      solver['test_net']='"'+path+'segnet_val.prototxt"' # 测试配置文件
      solver['test_iter']='1' # 测试迭代次数
      solver['test_interval']='782' # 测试间隔
      solver['base_lr']='0.001' # 基础学习率
      solver['display']='20' # 屏幕日志显示间隔
      solver['max_iter']='40000' # 最大迭代次数
      solver['lr_policy']='"step"' # 学习率变化规律
      solver['gamma']='0.1' # 学习率变化指数
      solver['momentum']='0.9' # 动量
      solver['weight_decay']='0.0005' # 权值衰减
      solver['stepsize']='10000000' # 学习率变化频率
      solver['snapshot']='1000' # 保存model间隔
      solver['snapshot_prefix']="snapshot" # 保存的model前缀
      solver['solver_mode']='GPU' # 是否使用gpu
      solver['solver_type']='SGD' # 优化算法
      def write_solver():
      with open(solver_file, 'w') as f:
      for key, value in sorted(solver.items()):
      if not(type(value) is str):
      raise TypeError('所有参数必须是字符串')
      f.write('%s: %s\n' % (key, value))
      if __name__ == '__main__':
      write_solver()

数据层参数

一个模型由多个layer构成,每个layer由许多参数组成。所有参数的定义都在prototxt的文件中。层有很多种类型,如Data、Convolution、Pooling,层之间的数据流动是以Blobs的方式进行。
数据层是每个模型的最底层,模型的入口,不仅提供数据的输入,也提供数据从Blobs转换成别的格式进行保存输出。通常数据预处理(如缩放、裁剪等),也在这一层设置参数实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
layer {
name: "data"
type: "DenseImageData"
top: "data"
top: "label"
dense_image_data_param {
source: "/media/hl/新加卷/mycode/SegNet/SegNet/CamVid/train.txt" # 数据源地址
batch_size: 1 # 每次处理的个数
shuffle: true % 随机打乱顺序,默认值为false
new_height: 256
new_width: 256
}
}

  • name:该层的名称
  • type:层类型,根据数据来源不同,数据层的类型也不同
  • top/bottom:每一层用bottom来输入数据,用top来输出数据.如果只有top没有bottom,则此层只有输出没有输入。如果有多个top或多个bottom,表示有多个blobs数据的输入和输出。
  • data与label:zai在数据层中,至少有一个名为data的top,=如果有第二个top,一般命名为label。这种(data,label)配对是分类模型所必须的。
  • new_height,new_width: 如果设置,则将图片进行resize

视觉层参数

Convolution层

卷积层,层类型为Convolution。

  • lr_mult:学习率系数,最终的学习率是这个数乘以solver文件中base_lr。如果有两个lr_mult,则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。
  • 在后面的convolution_param中,可以设定卷积层的特有参数。
    • 必选参数:
      • num_output: 卷积核(filter)的个数
      • kernel_size: 卷积核的大小。如果卷积核的长和宽不等,需要用kernel_h和kernel_w分别设定
    • 其他参数:
      • stride: 卷积核的步长,默认为1。也可以用stride_h和stride_w来设置。
      • pad: 边缘填充,默认为0,不填充。
      • weight_filler: 权值初始化。默认为”constant”,值全为0。常量初始化(constant)、高斯分布初始化(gaussian)、positive_unitball初始化、均匀分布初始化(uniform)、xavier初始化、msra初始化、双线性初始化(bilinear)
      • bias_filler: 偏置项的初始化。一般设置为”constant”,值全为0。
      • bias_term: 是否开启偏置项,默认为true, 开启
      • group: 分组,默认为1组。如果大于1,我们限制卷积的连接操作在一个子集内。如果我们根据图像的通道来分组,那么第i个输出分组只能与第i个输入分组进行连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
layer {
bottom: "data"
top: "conv1_1"
name: "conv1_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
weight_filler {
type: "msra"
}
bias_filler {
type: "constant"
}
num_output: 64
pad: 1
kernel_size: 3
}
}

输入:nc0w0h0
输出:n
c1w1h1
其中,c1就是参数中的num_output,生成的特征图个数

1
2
w1=(w0+2*pad-kernel_size)/stride+1;
h1=(h0+2*pad-kernel_size)/stride+1;

如果设置stride为1,前后两次卷积部分存在重叠。如果设置pad=(kernel_size-1)/2,则运算后,宽度和高度不变。

Pooling层

必选参数:

  • kernel_size: 池化的核大小。也可以用kernel_h和kernel_w分别设定。

其它参数:

  • pool: 池化方法,默认为MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
  • pad: 和卷积层的pad的一样,进行边缘扩充。默认为0
  • stride: 池化的步长,默认为1。一般我们设置为2,即不重叠。也可以用stride_h和stride_w来设置。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    layer {
    bottom: "conv1_2"
    top: "pool1"
    top: "pool1_mask"
    name: "pool1"
    type: "Pooling"
    pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
    }
    }

输入:ncw0h0
输出:n
cw1h1
和卷积层的区别就是其中的c保持不变

1
2
w1=(w0+2*pad-kernel_size)/stride+1;
h1=(h0+2*pad-kernel_size)/stride+1;

如果设置stride为2,前后两次卷积部分不重叠。100100的特征图池化后,变成5050

BN层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
layer {
bottom: "conv1_1"
top: "conv1_1"
name: "conv1_1_bn"
type: "BN"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 1
decay_mult: 0
}
bn_param {
scale_filler {
type: "constant"
value: 1
}
shift_filler {
type: "constant"
value: 0.001
}
}
}

激活层

从bottom得到一个blob数据输入,运算后,从top输出一个blob数据。
常用的激活函数有sigmoid, tanh,relu等

1
2
3
4
5
6
layer {
bottom: "conv1_1"
top: "conv1_1"
name: "relu1_1"
type: "ReLU"
}