卷积计算是 CNN 最核心的部分,而随着网络结构不断推陈出新(LeNet-5、AlexNet、GoogLeNet、VGG、ResNet、DenseNet、SENet、Xception、ResNeXt、MobileNet、ShuffleNet、NASNet……),卷积类型越来越丰富(二维卷积、三维卷积、空洞卷积、空间可分离卷积、深度可分离卷积、反卷积、变形卷积……)。设计网络时需要特别留意 padding 策略。
在 TensorFlow 中,padding 参数可选项有两种:'SAME' 和 'VALID'。在 TF 1.7 API 中 conv2d 是长这样的:
我们先看一个例子:假设输入图片尺寸为 5x5,卷积核尺寸为 3x3,做二维卷积时动图如下:
其中虚线格子就表示 padding 的像素(值为 0)。经过 padding,实际输入图片尺寸变成了 7x7,卷积核在 7x7 范围内滑动,分别每个位置产生一个输出像素,最终输出结果尺寸为 5x5。我们发现,输出尺寸与原输入尺寸相同,也就是所谓 'SAME' 方式 padding 名称的由来。为了保证输出与输入尺寸一致,我们可以推出 'SAME' padding 计算公式:
SAME PADDING 计算公式
output_h = input_h
output_w = input_w
pad_h = (kernel_h - 1)/2
pad_w = (kernel_w - 1)/2
前面例子中 kernel_h = kernel_w = 3,所以 pad_h = pad_w = (3 - 1)/2 = 1。
如前所述,使用 'SAME' padding 会在输入图片边界处“无中生有”地加入了一些像素,这很可能破坏原始图片的内容。而 'VALID' padding 则非常守规矩, 卷积核只在“有效的”输入像素点范围内滑动 。用公式表达就是:
VALID PADDING 计算公式
pad_h = 0
pad_w = 0
output_h = input_h - kernel_h + 1
output_w = input_w - kernel_w + 1
下图为一个 'VALID' padding 例子,输入图片尺寸为 4x4,卷积核尺寸为 3x3,输出尺寸为 2x2。
以上讨论都是在 stride == 1 和 dilation == 1 时的情况。
当 dilation > 1 时,先计算扩张卷积核尺寸:
dilation_kernel_h = (kernel_h - 1) * dilation_h + 1
dilation_kernel_w = (kernel_w - 1) * dilation_w + 1
之后再将 dilation_kernel_h、dilation_kernel_w 代入前面不同 padding 公式即可得到对应的 padding 尺寸和输出尺寸。一个例子如下:
输入图片尺寸为 7x7,卷积核尺寸为 3x3,扩张率为 2x2,使用 'VALID' padding 方法。首先得到扩张后卷积核尺寸为 (3 - 1) * 2 + 1 = 5,即等效为 5x5 卷积核。将该值代入 'VALID' padding 公式,得到输出尺寸为:7 - 5 + 1 = 3,即 3x3,与图中显示一致。
当 stride > 1 时,即使使用 'SAME' padding 方式,输出尺寸依然小于输入尺寸,只是沿用了 stride == 1 的称谓。
SAME PADDING 计算公式(stride > 1)
output_h = ceil(input_h / stride_h)
output_w = ceil(input_w / stride_w)
pad_h = ((output_h - 1) * stride_h + kernel_h - input_h) / 2
pad_w = ((output_w - 1) * stride_w + kernel_w - input_w) / 2
如上图为 stride = 2 的情况,输入尺寸为 5x5,卷积核尺寸 3x3。首先通过公式计算得到输出尺寸:output_h = output_w = ceil(5/2) = ceil(2.5) = 3,即 3x3;然后利用公式计算 pad_h = pad_w = ((3 - 1) * 2 + 3 - 5)/2 = 1,与图中一致。
当 stride > 1 时,使用 'VALID' padding 方式,计算过程仍满足卷积核只在“有效的”输入像素点范围内滑动 的约束。
VALID PADDING 计算公式(stride > 1)
pad_h = 0
pad_w = 0
output_h = ceil((input_h - kernel_h + 1) / stride_h)
output_w = ceil((input_w - kernel_w + 1) / stride_w)
上图中 input : 5x5,kernel : 3x3,stride : 2x2,output : 2x2,请读者自行核算。
另外,MaxPool 和 AvePool 的输出尺寸计算公式也可从上面公式推导得到。
参考:
【1】 https://github.com/vdumoulin/conv\_arithmetic
【2】 TensorFlow 源码 v1.7
