跳到主要内容

填充和步幅

当我们在卷积神经网络(CNN)中使用卷积操作时,填充(Padding)和步幅(Stride)是两个关键参数。它们决定了如何将卷积核应用于输入数据。

填充 (Padding)

填充在卷积神经网络中主要是为了解决以下几个问题:

1. 保持空间维度

当我们对输入数据应用卷积操作时,输出的空间维度(例如,图像的宽度和高度)通常会减小。这是因为卷积核通常不能完全适应输入数据的边界。为了避免这种空间信息的损失,我们可以在输入数据的边界周围添加填充。

例子:考虑一个 5×55 \times 5 的输入图像和一个 3×33 \times 3 的卷积核。如果不使用填充,输出图像的大小将是 3×33 \times 3。但是,如果我们在输入图像周围添加一个填充,输出图像的大小将保持为 5×55 \times 5

2. 保护边界信息

在没有填充的情况下,输入数据的边界像素只会被少数几个输出像素使用。这意味着边界信息可能会在卷积操作中丢失。通过添加填充,我们可以确保边界像素在输出中得到更多的表示。

例子:考虑你有一排五个数字,代表一个 1×51 \times 5 的输入数据:1,2,3,4,51, 2, 3, 4, 5。你还有一个 1×31 \times 3 的卷积核,这意味着你每次只看这五个数字中的三个。

如果你从左到右移动这个卷积核,不使用填充,你会得到三个输出:

  1. 从数字 1, 2, 3 得到的输出。
  2. 从数字 2, 3, 4 得到的输出。
  3. 从数字 3, 4, 5 得到的输出。

在这种情况下,你可以看到数字 1 和 5 只被用于一个输出。

但是,如果我们在这五个数字的两端各添加一个填充(例如,0),那么数据变为:0,1,2,3,4,5,00, 1, 2, 3, 4, 5, 0

现在,使用相同的卷积核,你会得到五个输出:

  1. 从数字 0, 1, 2 得到的输出。
  2. 从数字 1, 2, 3 得到的输出。
  3. 从数字 2, 3, 4 得到的输出。
  4. 从数字 3, 4, 5 得到的输出。
  5. 从数字 4, 5, 0 得到的输出。

在这种情况下,每个数字(除了填充的 0)都被用于两个输出。

这就是为什么填充可以帮助保护边界信息:它确保边界像素(或数据点)在输出中得到更多的表示。

3. 允许更多的卷积层

在深度卷积神经网络中,我们可能希望有多个连续的卷积层。如果在每一层中都不使用填充,那么空间维度将迅速减小,这可能会限制我们可以添加的卷积层的数量。通过使用填充,我们可以保持空间维度并允许更多的卷积层。

例子:考虑一个 32×3232 \times 32 的输入图像和一个 3×33 \times 3 的卷积核。如果我们不使用填充并应用卷积操作,输出图像的大小将是 30×3030 \times 30,因为卷积核不能完全适应输入图像的边界。

如果我们在这个 30×3030 \times 30 的输出上再次应用一个 3×33 \times 3 的卷积核(仍然不使用填充),输出的大小将是 28×2828 \times 28

继续这个过程,经过 5 个连续的 3×33 \times 3 的卷积层(每层都不使用填充),输出的大小将是:

  1. 第一层:30×3030 \times 30
  2. 第二层:28×2828 \times 28
  3. 第三层:26×2626 \times 26
  4. 第四层:24×2424 \times 24
  5. 第五层:22×2222 \times 22

所以,经过 5 个连续的 3×33 \times 3 的卷积层,输出的大小将是 22×2222 \times 22

总之,填充在卷积神经网络中是一个重要的技术,它允许我们更灵活地设计网络结构,同时保护输入数据的空间信息。

在 PyTorch 里面使用填充

在 PyTorch 中,当使用 nn.Conv2d 进行卷积操作时,可以直接设置 padding 参数来为输入数据添加填充。

以下是如何在 nn.Conv2d 中使用填充的示例:

import torch
import torch.nn as nn

# 定义一个卷积层,其中输入和输出通道数都为 1,卷积核大小为 3x3,步幅为 1,填充为 1
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1)

# 创建一个示例的输入数据,大小为 1x1x5x5(批大小为 1,通道数为 1,高度和宽度都为 5)
input_data = torch.randn(1, 1, 5, 5)

# 应用卷积层
output_data = conv_layer(input_data)

# 输出数据的大小为 1x1x5x5,因为我们使用了填充
print(output_data.shape)

在上面的示例中,我们定义了一个卷积层 conv_layer,其中使用了 padding=1 参数。这意味着在应用卷积操作之前,输入数据的每个边都会添加一个像素的零填充。因此,尽管我们使用了一个 3×33 \times 3 的卷积核,输出数据的空间维度仍然与输入数据相同。

请注意,padding 参数可以是一个整数(在这种情况下,它将在高度和宽度上都应用相同的填充)或一个二元组(分别指定高度和宽度的填充)。例如,padding=(2, 3) 将在高度上添加 2 个像素的填充,在宽度上添加 3 个像素的填充。

步幅 (Stride)

步幅(Stride)是卷积神经网络中的一个关键参数,它决定了卷积核在输入数据上移动的步长。步幅主要用于以下目的:

1. 调整输出的空间维度

通过增加步幅,我们可以减少输出特征图的大小。这在某些情况下是有用的,特别是当我们希望减少计算量或减少模型的参数数量时。

例子:考虑一个 5×55 \times 5 的输入图像和一个 3×33 \times 3 的卷积核。如果步幅为 1,输出图像的大小将是 3×33 \times 3。但是,如果步幅为 2,输出图像的大小将是 2×22 \times 2

2. 增加感受野

增加步幅可以有效地增加神经元的感受野,这意味着每个输出特征可以覆盖更大的输入区域。这在某些情况下是有用的,特别是当我们希望模型能够捕捉更大范围的上下文信息时。

例子:在一个 5×55 \times 5 的输入图像上,使用 3×33 \times 3 的卷积核和步幅为 1,每个输出特征的感受野是 3×33 \times 3。但是,如果步幅为 2,尽管输出特征的数量减少,但每个特征的感受野实际上增加了,因为它们之间有更大的间隔。

3. 降采样

步幅通常与池化层一起用作降采样方法,以减少特征图的空间维度。通过降采样,我们可以减少计算量和内存使用,同时增加模型的感受野。

例子:考虑一个 8×88 \times 8 的输入图像。如果我们使用 2×22 \times 2 的卷积核和步幅为 2,输出图像的大小将是 4×44 \times 4。这实际上是一个降采样操作,因为我们从 8×88 \times 8 的输入得到了 4×44 \times 4 的输出。

总的来说,步幅是一个强大的工具,它允许我们控制输出特征图的大小、增加感受野并进行降采样。在设计卷积神经网络时,选择合适的步幅是很重要的,因为它会影响模型的性能和计算效率。