卷积神经网络3-填充与步幅详解:理解卷积操作的关键

600 阅读2分钟

在卷积神经网络(CNN)中,卷积操作是最核心的部分之一。为了高效地计算并优化神经网络的性能,我们需要深入理解卷积操作中的两个重要概念:填充(padding)步幅(stride)。这篇博客将为你详细解释这两个概念,并通过实例帮助你更好地掌握它们。

1. 卷积操作回顾

在卷积神经网络中,卷积操作是通过一个卷积核(又称滤波器)与输入图像或特征图进行运算,生成输出特征图。这个输出特征图的尺寸(高度和宽度)不仅取决于输入图像的尺寸,还受到卷积核大小的影响。

如果我们设输入图像的尺寸为 Hin×WinH_{in} \times W_{in}(高度 HinH_{in} 和宽度 WinW_{in}),卷积核的尺寸为 Hk×WkH_{k} \times W_{k}(高度 HkH_{k} 和宽度 WkW_{k}),那么没有填充和步幅的情况下,卷积输出的尺寸(HoutH_{out}WoutW_{out})可以通过以下公式计算:

Hout=HinHk+1Wout=WinWk+1\begin{aligned} H_{\text{out}} &= H_{\text{in}} - H_{k} + 1 \\ W_{\text{out}} &= W_{\text{in}} - W_{k} + 1 \end{aligned}

从公式中我们可以看出,卷积操作会使输出的尺寸变小,特别是当卷积核较大时,输出的尺寸会显著缩小。

然而,在实际应用中,这种尺寸的变化并不总是我们想要的。因此,填充和步幅这两个概念应运而生。

2. 填充(Padding)

2.1 填充的概念

填充是指在输入图像的四周加上一层额外的像素(通常填充值为0)。这样做的主要目的是防止在卷积过程中丢失输入图像的边缘信息。具体来说,填充可以保证卷积核能够对输入图像的所有部分进行操作,特别是对于图像的边缘和角落。

2.2 填充的效果

假设我们有一个 8×88 \times 8 的输入图像,使用一个 3×33 \times 3 的卷积核。如果不进行填充,卷积操作将把输入图像缩小到 6×66 \times 6 的输出图像,丢失了图像的边缘信息。

为了避免这种情况,我们可以在输入图像的四周添加填充。例如,我们在每一边添加 1 个像素的填充,那么卷积后输出的尺寸仍然是 8×88 \times 8。这种方法被称为 零填充(Zero Padding)

2.3 填充的数学公式

如果在每一边填充 pp 行和列,输出的高度和宽度 HoutH_{out}WoutW_{out} 可以通过以下公式计算:

Hout=Hin+2pHks+1Wout=Win+2pWks+1\begin{aligned} H_{\text{out}} &= \frac{H_{\text{in}} + 2p - H_{k}}{s} + 1 \\ W_{\text{out}} &= \frac{W_{\text{in}} + 2p - W_{k}}{s} + 1 \end{aligned}

其中,pp 是填充的像素数,ss 是步幅。这个公式表示了填充如何影响输出的尺寸。

2.4 填充的示例

假设输入的尺寸为 8×88 \times 8,卷积核大小为 3×33 \times 3,并且填充为 1。这样,输出的尺寸仍然是 8×88 \times 8

import torch
from torch import nn

# 定义一个卷积层,卷积核大小为 3x3,填充为 1
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 3), padding=1)
X = torch.rand(size=(8, 8))
X = X.unsqueeze(0).unsqueeze(0)  # 添加批量维度和通道维度
output = conv2d(X)
print(output.shape)  # 输出为 torch.Size([1, 1, 8, 8])
  • X.unsqueeze(0) 用于在张量 X 的第 0 维(即最前面)插入一个新的维度,通常用于添加批量维度,使得原来的张量形状从 [C, H, W] 变为 [1, C, H, W],即添加一个大小为 1 的批量维度。

输出维度为 8×88 \times 8,证明了填充的有效性。

3. 步幅(Stride)

3.1 步幅的概念

步幅(Stride)是指卷积窗口在输入图像上滑动的步长。默认情况下,步幅为1,意味着每次卷积核滑动一个像素。然而,在某些情况下,为了减小输出的尺寸,或提高计算效率,我们可以使用较大的步幅。

3.2 步幅的效果

步幅的大小直接影响输出尺寸。如果步幅为 2,卷积核将每次跳过一个像素,输出的尺寸会减小。步幅的作用可以通过以下公式来计算输出尺寸:

Hout=Hin+2pHks+1Wout=Win+2pWks+1\begin{aligned} H_{\text{out}} &= \left\lfloor \frac{H_{\text{in}} + 2p - H_{k}}{s} \right\rfloor + 1 \\ W_{\text{out}} &= \left\lfloor \frac{W_{\text{in}} + 2p - W_{k}}{s} \right\rfloor + 1 \end{aligned}

其中,ss 是步幅,HinH_{in}WinW_{in} 是输入的高度和宽度,HkH_{k}WkW_{k} 是卷积核的高度和宽度。\left\lfloor \cdot \right\rfloor 表示向下取整操作,确保输出尺寸是整数。

3.3 步幅的示例

假设输入图像尺寸为 8×88 \times 8,卷积核为 3×33 \times 3,填充为 0,步幅为 2。我们可以看到,步幅为 2 将导致输出尺寸变为 3×33 \times 3

Hout=8+2×032+1=52+1=2+1=3Wout=8+2×032+1=52+1=2+1=3\begin{aligned} H_{\text{out}} = \left\lfloor \frac{8 + 2 \times 0 - 3}{2} \right\rfloor + 1 = \left\lfloor \frac{5}{2} \right\rfloor + 1 = 2 + 1 = 3 \\ W_{\text{out}} = \left\lfloor \frac{8 + 2 \times 0 - 3}{2} \right\rfloor + 1 = \left\lfloor \frac{5}{2} \right\rfloor + 1 = 2 + 1 = 3 \end{aligned}
# 定义卷积层,步幅为 2
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 3), stride=2)
output = conv2d(X)
print(output.shape)  # 输出为 torch.Size([1, 1, 3, 3])

4. 填充和步幅的综合应用

实际上,在实际的卷积神经网络中,填充和步幅通常是一起使用的,它们共同作用来调整输入图像经过卷积后的输出尺寸。接下来,我们将通过几个具体示例来探讨它们的综合应用。

4.1 填充和步幅的综合应用

假设输入图像的尺寸是 8×88 \times 8,卷积核的尺寸为 3×33 \times 3,步幅为 22,并且填充为 11。我们将使用上述公式来计算输出的尺寸。

# 定义卷积层,卷积核大小为 3x3,步幅为 2,填充为 1
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
output = conv2d(torch.rand(size=(8, 8)).unsqueeze(0).unsqueeze(0))
print(output.shape)  # torch.Size([1, 1, 4, 4])

4.2 不同填充和步幅的组合

现在,假设输入图像的尺寸仍然是 8×88 \times 8,但是我们将使用一个不同大小的卷积核和步幅。例如,卷积核的尺寸为 5×35 \times 3,步幅分别为 3(垂直方向)和 2(水平方向),填充为 2(上下各填充 2 行,左右各填充 1 列)。

Hout=8+2×253+1=73+1=2+1=3H_{\text{out}} = \left\lfloor \frac{8 + 2 \times 2 - 5}{3} \right\rfloor + 1 = \left\lfloor \frac{7}{3} \right\rfloor + 1 = 2 + 1 = 3
Wout=8+2×132+1=72+1=3+1=4W_{\text{out}} = \left\lfloor \frac{8 + 2 \times 1 - 3}{2} \right\rfloor + 1 = \left\lfloor \frac{7}{2} \right\rfloor + 1 = 3 + 1 = 4
# 定义卷积层,卷积核大小为 5x3,步幅为 (3, 2),填充为 (2, 1)
conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1), stride=(3, 2))
output = conv2d(torch.rand((8, 8)).unsqueeze(0).unsqueeze(0))
print(output.shape)  # 输出:torch.Size([1, 1, 3, 4])

5. 小结

填充和步幅是卷积操作中非常重要的两个概念。填充可以防止边缘信息丢失,而步幅则有助于减少输出尺寸或提高计算效率。通过合理设置填充和步幅,我们可以精确控制卷积层的输出形状,这对于构建深度神经网络至关重要。

  • 填充(Padding):通过在输入的四周增加零填充,可以防止信息丢失,确保卷积操作覆盖到边缘。
  • 步幅(Stride):通过增大步幅,可以减少输出尺寸,提高计算效率。

理解并掌握这些概念,你就能更好地设计卷积神经网络,优化计算过程,达到更高的性能。