关于Matconvnet中Conv-ReLU-Pool-NormBatch的总结与思考

最近一直在思考是出国还是找工作的事,和三年前一样又到了做决定的时候。真的很心累,捡起了我的MatConvNet,重新看一了一下牛津派的编程艺术。只有沉浸式的工作,才能安静下来吧。

1. vl_nncov - CNN的卷积操作

Y = VL_NNCONV(X, F, B)计算图像X与滤波器组F/偏置B之间的卷积操作。如果B是一个空矩阵,就是没有偏置参与;如果F是一个空矩阵,图像并不进行卷积操作,但是仍然会执行添加偏置B、下采样stride、以及边缘补充padding。

  • X 图像是一个四维数据结构 =  H x W x C x N。其中 (H,W) 是一个图像通道的宽度和高度。 C 是特征通道的数量, N是Batchsize中图像的数量。
  • F 滤波器组是一个四维数据结构 = FW x FH x FC x K。其中 (FH,FW) 是滤波器的高度和宽度,  K是滤波器组中滤波器的个数. FC 是每个滤波器接收到特征图的数量,FC必须与图像X中的特征通道的数量相匹配。 Alternatively, FC can
  •                 divide* the C; in this case, filters are assumed to form G=C/FC
  •                 groups* of equal size (where G must divide K). Each group of filters works on a consecutive subset of feature channels of the input array X.

[DX, DF, DB] = VL_NNCONV(X, F, B, DY) 计算导数。DX, DF, DB, DY和 X, F, B, and Y具有相同的维度。 特别地,如果B是空矩阵,那么DB也是空矩阵。

VL_NNCONV() 实现了一个特殊的全连接模式。

关于VL_NNCONV的可选项参数问题,主要指跨步卷积边缘填充卷积核膨胀选项:

VL_NNCONV(..., 'option', value, ...)

  • 跨步卷积和默认值 = stride [1] 可以看成是降采样步骤,stride=1表示没有降采样. 如果设置stride=2,意味着横向和纵向都进行1/2采样操作;如果设置stride=[2,4], 意味着水平进行了1/2的降采样,垂直进行了1/4的降采样。

  • 边缘填充和默认值 = Pad [0]为输入图像进行边缘填充。该步骤在卷积操作之前执行,填充的值都是0。[TOP BOTTOM LEFT RIGHT]指为上下左右填充不同数目的0. 如果指定 pad=1,意味着上下左右都填充单位为1的0占位。大多数情况下我是反对边缘填充的。

  • 卷积核膨胀及默认值 = Dilate [1] 将卷积核进行膨胀运算,膨胀之后0占位,具体如下所示。通过设置 dilate = [dilate_y, dilate_x]对卷积核不同方向进行膨胀。大多数情况下我也反对卷积核膨胀运算,虽然说大尺寸卷积核有着更好的感知域,但是14年YZ的研究表明,卷积核越小,深度模型拟合得越好。

 [1 3]     if dilate = 2;     [1 0 3]
 [2 4]                        [0 0 0]
                              [2 0 4]

卷积之后图像尺寸的计算公式为:

format for padding and stride:
   YH = floor((H + (PADTOP+PADBOTTOM) - FH)/STRIDEY) + 1
   YW = floor((W + (PADLEFT+PADRIGHT) - FW)/STRIDEX) + 1


format for padding , stride, and dilate:
   YH = floor((H + (PADTOP+PADBOTTOM) - FH*(DILATEY-1) -1)/STRIDEY) + 1
   YW = floor((W + (PADLEFT+PADRIGHT) - FW*(DILATEX-1) -1)/STRIDEX) + 1

2. vl_nnReLU - CNN的线性整流单元

Y = VL_NNRELU(X) 对数据X应用线性整流单元函数。X可以是人任意大小。

DZDX = VL_NNRELU(X, DZDY) 计算块投影到DZDY上的导数. DZDX,DZDY 与 X , Y 具有相同的维度。

VL_NNRELU(...,'OPT',VALUE,...) 具有以下选项:

  • 泄露因子 = leak [0] 设置泄露因子,他是一个非负数. 如果X大于等于0,那么Y=X; 否则, Y=X*leak. 默认情况下,我们使用leak=0就够了。

3. vl_nnpool - CNN的池化处理

Y = VL_NNPOOL(X, POOL) 对数据的所有通道施加池化操作。X是四维的数据结构 H x W x D x N,其中 (H,W) 是一层的宽度和高度,D是图像深度,可以理解为特征通道数。N是图像数目。

Y = VL_NNPOOL(X, [POOLY, POOLX]) 使用矩形窗进行池化,矩形窗的尺寸为[pool_y, pool_x].

DZDX = VL_NNPOOL(X, POOL, DZDY)计算模块在DZDY方向上的偏导数。 DZDX,DZDY和 X,Y各自维度对齐。

VL_NNPOOL(..., 'option', value, ...)具有下面的选项参数:

  • 跨步池化 = stride [1] 降采样操作.可以设置为同质的标量 stride = [2,2]; 也可以设置为异质的矢量stride = [2,3].

  • 边缘填充 = pad [0]:和卷积里面的操作一样的

  • 池化方法 = method ['max']: 指定池化的方法。可以选择最大池化 'method' = 'max'; 也可以选择平局池化 ‘method’ = ‘avg’

4. vl_nnnormalize - CNN局部响应归一化

LRN是一种提高深度学习准确度的技术方法。LRN一般是在激活、池化函数后的一种方法。 在ALexNet中,提出了LRN层,对局部神经元的活动创建竞争机制,使其中响应比较大对值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

在2012的Alexnet网络中具体计算公式如下:

但是,这个是也很有争议,2015年针对更深更大的图像识别模型,有研究学者报道LRN并没啥用...反而增加了内存的消耗,还浪费时间....

dagnn.LRN('param',[5 1 0.0001/5 0.75]) ;

5. vl_nnbnorm - CNN批归一化

BatchNorm也是一种旨在提高深度学习精度的方法。与LRN相比,BatchNorm具有容易理解的数学意义。

Y = VL_NNBNORM(X,G,B) 对输入数据X执行批归一化操作。批归一化定义如下:

Y(i,j,k,t) = G(k) * (X(i,j,k,t) - mu(k)) / sigma(k) + B(k)
% where:
mu(k) = mean_ijt X(i,j,k,t)                  % 均值
sigma2(k) = mean_ijt (X(i,j,k,t) - mu(k))^2  % 方差
sigma(k) = sqrt(sigma2(k) + EPSILON)         % 标准差

G(k) 和B(k)分别相乘因子和相加因子,用于处理各通道中的尺度问题,实际中可以设置他们为常数。

均值和方差通过所有的4D张量X逐渐累积。 常数EPSILON用于正则化sigma(k)和避免零除情况。

[DZDX,DZDG,DZDB] = VL_NNBNORM(X,G,B,DZDY) computes the derviatives of the block projected onto DZDY. DZDX, DZDG, DZDB and DZDY have the same dimensions as X, G, B, and Y respectivey.

VL_NNBNROM(..., 'Option', value)的可选项设置:

  • 微量 = epsilon [1e-4] 指定微量值小一点就好.

  • 动量匹配 = moments [unspecified]:为了匹配mu和sigma,这是一个很有用的技巧在测试阶段,用于禁止批归一化

  • CuDNN [specified] 如果指定,使用CuDNN. 默认情况下,CuDNN都是开启的

  • NoCuDNN [not specified] 如果指定,批归一化就不采用CuDNN.

具体的BatchNorm的原理细节需要单独再弄一下。

猜你喜欢

转载自blog.csdn.net/shenziheng1/article/details/81239213