前言

特征图大小计算式卷积神经网络中一个很基础的问题,也是一个必须理解的问题。卷到最后我们要知道提取的特征的维度的大小,所以我们必须知道,卷积后特征图的大小。这里我们讲解一下卷积的不同方式以及特征图大小计算的公式。

基本公式

给特征图加权重 特征图大小_卷积核

卷积的不同方式

常用的卷积(不包含反卷积/转置卷积)的方式有两种,一种是Valid,一种是SAME。阅读过深度学习开源代码的小伙伴,估计知道这两个参数。比如在使用tensorflow的tf.nn.conv2d函数卷积时padding参数就可以指定为VALID或者SAME,下面我们就讲解一下这两个参数取值的意义。

VALID

valid是卷积方式的一种,卷积的示例如下图:

给特征图加权重 特征图大小_卷积_02


可以看到,使用valid方式卷积时,不会做填充,卷积核不会有超出特征图的范围。如果在卷积时剩下的部分不能被卷积核完全覆盖,则这部分将会被放弃,不参与卷积。具体如下图:

这是一个2X3的特征图,卷积核的大小是2X2,步长是2,则只能进行一次卷积,最后一列将被对其丢弃,不参与卷积。

给特征图加权重 特征图大小_给特征图加权重_03

对于valid方式卷积后特征图大小的计算,同样可以使用第二部分中的公式,此时P=0:
width = (W - F) / S + 1 不能整除时向下取整
height = (H - F) / S + 1 不能整除时向下取整

SAME

same也是卷积方式的一种,如果卷积核的大小是3X3,卷积的示例如下图:

给特征图加权重 特征图大小_取整_04

可以看到,使用same方式卷积时,会做填充,填充圈数的公式为P = (F-1)/2。这样可以保证所有的内容都参与卷积,不会丢弃任何列。

同样对于一个2X3的特征图,卷积核的大小是2X2,步长是2,将进行padding,这样将可以进行两次卷积操作。

给特征图加权重 特征图大小_取整_05

对于same方式卷积后特征图大小的计算,同样可以使用第二部分中的公式:
width = (W - F + 2P) / S + 1 不能整除时向下取整数
height = (H - F + 2P) / S + 1 不能整除时向下取整数
其中,P = (F-1)/2。
注意

  1. 需要注意的是,SAME方式并不是保证卷积出的特征图和输入的特征图大小是等大的。只有stride为1时,并且卷积的方式是SAME输出的特征图才和输入的特征图是等大的。
  2. 所以SAME方式还有卷积的特征图的大小的计算还有一个公式,这个公式更加常用和通用,并且不需要计算P的大小。有时候P的大小是不容易确定的,如上面的2X3的卷积,在使用SAME卷积的时候,只在右侧填充了一列。所以SAME卷积的特征图的大小的计算一般使用如下公式:
    width = W / S 向上取整
    height = H / S 向上取整

实验

是在不太明白的怎么回事,或者对结果不太确定的话,我们可以通过实验验证,如果使用tensorflow实验的话会用到tf.nn.conv2d函数:

tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    dilations=[1, 1, 1, 1],
    name=None
)

其中data_format这个参数我们很少了解,其实它是固定你input的格式,NHWC对应着你的输入数据的维度时[batch_size,Heigth,Width,Channel],NCHW对应[batch_size,Channel,Heigth,Width]。

其实strides里面的参数和这个data_format是一一对应的,什么意思呢?就是说,如果我们采用NHWC的方式和strides=[a,b,c,d]的情况下,计算卷积核移动过程是怎么样的呢?它会在batch的维度上按照 step=a的大小进行移动计算,同理,分别在HWC的维度上移动的step为b c d。因为我们需要对每一个batch和channel都需要进行计算,所以这里 a,d 的值为1。b c的值可以根据实际情况进行改变,他们的值直接关系到输出的feature map 的维度。

import tensorflow as tf

x = tf.constant([[1., 2., 3.],
                 [4., 5., 6.]])
x = tf.reshape(x, [1, 2, 3, 1])  # give a shape accepted by tf.nn.max_pool

valid_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='VALID')
same_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='SAME')

print(valid_pad.get_shape())
print(same_pad.get_shape())

输出结果为:

(1, 1, 1, 1)
(1, 1, 2, 1)