caffe学习-模型定义   2016-10-26


本文用来整理在学习caffe时遇到的一些基础概念。希望通过整理能加深自己的理解。

1、数据层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
layers {
name: "mnist"
type: DATA
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
mean_file: "examples/mnist/mean.binaryproto"
mirror: 1
crop_size: 227
}
data_param {
source: "examples/mnist/mnist_train_lmdb"
backend: LMDB
batch_size: 64
scale: 0.00390625
}
}

这是第一个层。以下是参数定义:

  • name指定当前层的名称。名字可以随便取,不重复即可。
  • type指定当前层的类型,这里就是输入层,所以指定为DATA。
  • top或bottom: 每一层用bottom来输入数据,用top来输出数据。如果只有top没有bottom,则此层只有输出,没有输入。反之亦然。如果有多个 top或多个bottom,表示有多个blobs数据的输入和输出。上例中的第一个top是指定往后传递data,第二个是指定往后传递label.
  • include: 一般训练和测试的时候,模型的层是不一样的。include来用指定该层(layer)是属于训练阶段的层,还是属于测试阶段的层。如果没有include参数,则表示该层既在训练模型中,又在测试模型中。
  • transform_param中的参数用来对数据进行预处理。
    • scale是将数据进行尺寸变换,比如这里设置的0.00390625,就是1/255,用来将输入数据由0-255归一化到0-1之间。
    • mean_file用来指定配置文件进行均值操作
    • mirror值为1表示开启镜像,值为0表示关闭,也可用ture和false来表示
    • crop_size表示裁剪的大小。上例中的227表示剪裁一个227*227的图块,在训练阶段随机剪裁,在测试阶段从中间裁剪。
  • data_param指定当前层输入数据相关的参数,其中:
    • source就是输入数据的路径(可以写绝对路径)
    • backend就是数据类型
    • batch_size是指定运行时的batch大小

根据data_param指定的输入数据来源不同,可以分为以下几种。

1.1 数据来自于数据库(如LevelDB和LMDB)

1
2
3
4
5
6
7
8
9
10
layer {
name: "mnist"
type: "Data"
...
data_param {
source: "examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}

可以看到,type必须指定为Data,而data_param中中指定LevelDB和LMDB的文件路径,并指定backend为LevelDB或LMDB。

1.2 数据来自于内存

1
2
3
4
5
6
7
8
9
10
11
layer {
name: "mnist"
type: "MemoryData"
...
memory_data_param {
batch_size: 64
height: 100
width: 100
channels: 1
}
}

可以看到,type必须指定为MemoryData。memory_data_param:

  • channels表示通道数
  • height表示高度
  • width表示宽度

1.3 数据来自于HDF5

1
2
3
4
5
6
7
8
9
layer {
name: "mnist"
type: "HDF5Data"
...
hdf5_data_param {
batch_size: 10
source: "examples/hdf5_classification/data/train.txt"
}
}

可以看到,type必须指定为HDF5Data。

1.4 数据来自于图片

1
2
3
4
5
6
7
8
9
10
11
layer {
name: "mnist"
type: "ImageData"
...
image_data_param {
batch_size: 10
source: "examples/_temp/file_list.txt"
new_height: 256
new_width: 256
}
}

可以看到,type必须指定为ImageData。此外,image_data_param有一些可选参数:

  • rand_skip: 在开始的时候,路过某个数据的输入。通常对异步的SGD很有用。
  • shuffle: 随机打乱顺序,默认值为false
  • new_height,new_width: 如果设置,则将图片进行resize

1.5 数据来源于Windows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
layer {
name: "mnist"
type: "WindowData"
...
window_data_param {
source: "examples/finetune_pascal_detection/window_file_2007_trainval.txt"
batch_size: 128
fg_threshold: 0.5
bg_threshold: 0.5
fg_fraction: 0.25
context_pad: 16
crop_mode: "warp"
}
}

可以看到,type必须指定为WindowData。

2、卷积层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
layers {
name: "conv1"
type: CONVOLUTION
blobs_lr: 1.
blobs_lr: 2.
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "data"
top: "conv1"
}

这里的type就是CONVOLUTION了。

  • blobs_lr: 1. 就是指定weight的学习率的倍数,这里就是1.0了
  • blobs_lr: 2. 就是指定bias学习率的倍数。
  • convolution_param的参数含义如下:
    • num_output指定输出数据个数
    • kernelsize是指定卷积模板的大小,也就卷积核的矩阵大小。
    • stride就是指定卷积的步长,默认为1。也可以用stride_h和stride_w来设置。
    • pad: 扩充边缘,默认为0,不扩充。 扩充的时候是左右、上下对称的,比如卷积核的大小为5*5,那么pad设置为2,则四个边缘都扩充2个像素,即宽度和高度都扩充了4个像素,这样卷积运算之后的特征图就不会变小。也可以通过pad_h和pad_w来分别设定。
    • weight_filler是指定weight初始化,其中type是指定初始化的方式,这里用的是xavier算法(根据输入输出的神经元个数自动决定初始化的尺度)。
    • bias_filler是类似的,constant就是说指定为常数了,默认为0.
    • bias_term: 是否开启偏置项,默认为true, 开启
    • group: 分组,默认为1组。如果大于1,我们限制卷积的连接操作在一个子集内。如果我们根据图像的通道来分组,那么第i个输出分组只能与第i个输入分组进行连接。
  • bottom就是指定这一层的输入数据,显然就是数据层传来的那个data,top就是输出数据。

在卷积层中:
输入:n*c0*w0*h0
输出:n*c1*w1*h1
其中,c1就是参数中的num_output,即生成的特征图个数
$$w1=\frac{(w0+2*pad-kernelsize)}{stride}+1;$$

$$h1=\frac{(h0+2*pad-kernelsize)}{stride}+1;$$

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

3、pooling层

也叫池化层,为了减少运算量和数据维度而设置的一种层。

1
2
3
4
5
6
7
8
9
10
11
layers {
name: "pool1"
type: POOLING
pooling_param {
kernel_size: 2
stride: 2
pool: MAX
}
bottom: "conv1"
top: "pool1"
}

必须设置的参数:

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

其它参数:

  • pool: 池化方法,默认为MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
  • pad: 和卷积层的pad的一样,进行边缘扩充。默认为0
  • stride: 池化的步长,默认为1。一般我们设置为2,即不重叠。也可以用stride_h和stride_w来设置。

pooling_param就是设置这个pooling层的参数,pool的方式就是max pooling。值得一提的是这里的kernel_size和stride的设置,这里恰好就相等,所以所有的pooling都不会出现重叠,一般来说,kernel_size的尺寸不小于stride的。

pooling层的运算方法基本是和卷积层是一样的。
输入:n*c*w0*h0
输出:n*c*w1*h1
和卷积层的区别就是其中的c保持不变
$$w1=\frac{(w0+2*pad-kernelsize)}{stride}+1;$$

$$h1=\frac{(h0+2*pad-kernelsize)}{stride}+1;$$

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

4、Local Response Normalization (LRN)层

此层是对一个输入的局部区域进行归一化,达到“侧抑制”的效果。可去搜索AlexNet或GoogLenet,里面就用到了这个功能。

1
2
3
4
5
6
7
8
9
10
11
layers {
name: "norm1"
type: LRN
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}

参数:全部为可选,没有必须

  • local_size: 默认为5。如果是跨通道LRN,则表示求和的通道数;如果是在通道内LRN,则表示求和的正方形区域长度。
  • alpha: 默认为1,归一化公式中的参数。
  • beta: 默认为5,归一化公式中的参数。
  • norm_region: 默认为ACROSS_CHANNELS。有两个选择,ACROSS_CHANNELS表示在相邻的通道间求和归一化。WITHIN_CHANNEL表示在一个通道内部特定的区域内进行求和归一化。与前面的local_size参数对应。

归一化公式:对于每一个输入, 去除以$(1+(\frac{\alpha}{n})\sum_{i}{x_i}^2 )^{\beta}$得到归一化后的输出

5、im2col层

im2col先将一个大矩阵,重叠地划分为多个子矩阵,对每个子矩阵序列化成向量,最后得到另外一个矩阵。
如下图所示:

在caffe中,卷积运算就是先对数据进行im2col操作,再进行内积运算(inner product)。这样做,比原始的卷积操作速度更快。
对比一下两种卷积操作的异同:

6、激活层(Activiation Layers)及参数

在激活层中,对输入数据进行激活操作(实际上就是一种函数变换),是逐元素进行运算的。从bottom得到一个blob数据输入,运算后,从top输入一个blob数据。在运算过程中,没有改变数据的大小,即输入和输出的数据大小是相等的。
输入:n*c*h*w
输出:n*c*h*w

常用的激活函数有sigmoid, tanh,relu等,下面分别介绍。

6.1 Sigmoid层

对每个输入数据,利用sigmoid函数执行操作。这种层设置比较简单,没有额外的参数。
$$s(x)=\frac{1}{1+e^{-x}}$$
示例:

1
2
3
4
5
6
layer {
name: "sigmode"
bottom: "encode1"
top: "encode1neuron"
type: "Sigmoid"
}

6.2 ReLU层

ReLU是目前使用最多的激活函数,主要因为其收敛更快,并且能保持同样效果。
标准的ReLU函数为max(x, 0),当x>0时,输出x; 当x<=0时,输出0
$$f(x)=max(x,0)$$

1
2
3
4
5
6
layers {
name: "relu1"
type: RELU
bottom: "ip1"
top: "ip1"
}

这里有趣的地方在于输入和输出是同一个,官网上说只是因为这个ReLU操作可以原地操作(in place),能够节省内存。ReLU是一个替换sigmoid units的一个函数,全称是Rectified Linear Unit,是一个激活函数。wiki)和豆瓣有些资料,可以自己看看。

6.3 TanH/Hyperbolic Tangent

利用双曲正切函数对数据进行变换。
$$tanh x = \frac{\sinh x}{\cosh x} = \frac{e^x - e^{ - x}}{e^x + e^{ - x}}$$

1
2
3
4
5
6
layer {
name: "layer"
bottom: "in"
top: "out"
type: "TanH"
}

6.4 Absolute Value

求每个输入数据的绝对值。
$$f(x)=abs(x)$$

1
2
3
4
5
6
layer {
name: "layer"
bottom: "in"
top: "out"
type: "AbsVal"
}

6.5 Power

对每个输入数据进行幂运算
$$f(x)= (shift + scale * x) ^ {power}$$

可选参数:

  • power: 默认为1
  • scale: 默认为1
  • shift: 默认为0
1
2
3
4
5
6
7
8
9
10
11
layer {
name: "layer"
bottom: "in"
top: "out"
type: "Power"
power_param {
power: 2
scale: 1
shift: 0
}
}

6.6 BNLL

binomial normal log likelihood的简称
$$f(x)=log(1 + e ^ x)$$

1
2
3
4
5
6
layer {
name: "layer"
bottom: "in"
top: "out"
type: “BNLL”
}

7、完全连接层

全连接层,把输入当作成一个向量,输出也是一个简单向量(把输入数据blobs的width和height全变为1)。
输入: n*c0*h*w
输出: n*c1*1*1
全连接层实际上也是一种卷积层,只是它的卷积核大小和原数据大小一致。因此它的参数基本和卷积层的参数一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
layers {
name: "ip2"
type: INNER_PRODUCT
blobs_lr: 1.
blobs_lr: 2.
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "ip1"
top: "ip2"
}

8、Loss层

softmax-loss层和softmax层计算大致是相同的。softmax是一个分类器,计算的是类别的概率(Likelihood),是逻辑回归(Logistic Regression) 的一种推广。逻辑回归只能用于二分类,而softmax可以用于多分类。

softmax与softmax-loss的区别:

softmax计算公式:

$$\sigma_{i}(z)=\frac{exp(z_{i})}{\sum_{j=1}^{m}{exp(z_j)}}, i=1,…m$$

而softmax-loss计算公式:

$$L(y,z)=-log(\frac{exp(z_y)}{\sum_{j=1}^{m}exp(z_j)})=log({\sum_{j=1}^{m}exp(z_j)})-z_y$$

用户可能最终目的就是得到各个类别的概率似然值,这个时候就只需要一个Softmax层,而不一定要进行softmax-Loss 操作;或者是用户有通过其他什么方式已经得到了某种概率似然值,然后要做最大似然估计,此时则只需要后面的softmax-Loss而不需要前面的Softmax 操作。因此提供两个不同的Layer结构比只提供一个合在一起的Softmax-Loss Layer要灵活许多。

不管是softmax层还是softmax-loss层,都是没有参数的,只是层类型不同而已。

softmax-loss层:输出loss值

1
2
3
4
5
6
7
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip1"
bottom: "label"
top: "loss"
}

到这里也解开了我们心头的一个疑惑哈,为什么第一层的top有两个,哈哈,label是传递到这儿来的。

softmax层: 输出似然值

1
2
3
4
5
6
layers {
bottom: "cls3_fc"
top: "prob"
name: "prob"
type: “Softmax"
}

9、accuracy层

输出分类(预测)精确度,只有test阶段才有,因此需要加入include参数。

1
2
3
4
5
6
7
8
9
10
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}

10、reshape层

在不改变数据的情况下,改变输入的维度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
layer {
name: "reshape"
type: "Reshape"
bottom: "input"
top: "output"
reshape_param {
shape {
dim: 0
dim: 2
dim: 3
dim: -1
}
}
}

有一个可选的参数组shape, 用于指定blob数据的各维的值(blob是一个四维的数据:n*c*w*h)。

  • dim:0 表示维度不变,即输入和输出是相同的维度。
  • dim:2 或 dim:3 将原来的维度变成2或3
  • dim:-1 表示由系统自动计算维度。数据的总量不变,系统会根据blob数据的其它三维来自动计算当前维的维度值 。

假设原数据为:64*3*28*28,表示64张3通道的28*28的彩色图片
经过reshape变换:

1
2
3
4
5
6
7
8
reshape_param {
shape {
dim: 0
dim: 0
dim: 14
dim: -1
}
}

输出数据为:64*3*14*56

11、Dropout层

Dropout是一个防止过拟合的手段。可以随机让网络某些隐含层节点的权重不工作。

1
2
3
4
5
6
7
8
9
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7-conv"
top: "fc7-conv"
dropout_param {
dropout_ratio: 0.5
}
}

只需要设置一个dropout_ratio就可以了。


分享到:


  如果您觉得这篇文章对您的学习很有帮助, 请您也分享它, 让它能再次帮助到更多的需要学习的人. 您的支持将鼓励我继续创作 !
本文基于署名4.0国际许可协议发布,转载请保留本文署名和文章链接。 如您有任何授权方面的协商,请邮件联系我。

目录

  1. 1. 1、数据层
    1. 1.1. 1.1 数据来自于数据库(如LevelDB和LMDB)
    2. 1.2. 1.2 数据来自于内存
    3. 1.3. 1.3 数据来自于HDF5
    4. 1.4. 1.4 数据来自于图片
    5. 1.5. 1.5 数据来源于Windows
  2. 2. 2、卷积层
  3. 3. 3、pooling层
  4. 4. 4、Local Response Normalization (LRN)层
  5. 5. 5、im2col层
  6. 6. 6、激活层(Activiation Layers)及参数
    1. 6.1. 6.1 Sigmoid层
    2. 6.2. 6.2 ReLU层
    3. 6.3. 6.3 TanH/Hyperbolic Tangent
    4. 6.4. 6.4 Absolute Value
    5. 6.5. 6.5 Power
    6. 6.6. 6.6 BNLL
  7. 7. 7、完全连接层
  8. 8. 8、Loss层
  9. 9. 9、accuracy层
  10. 10. 10、reshape层
  11. 11. 11、Dropout层