零填充与步长对输出尺寸的影响
1. 引言
在卷积神经网络中,零填充(Padding)和步长(Stride)是两个非常重要的参数,它们直接影响卷积操作的输出尺寸。理解这两个参数的作用以及它们如何影响输出尺寸,对于设计和调试卷积神经网络至关重要。
本教程将详细介绍零填充和步长的概念,以及它们如何共同影响卷积操作的输出尺寸,包括具体的计算公式、示例和代码实现。
2. 零填充(Padding)的概念
2.1 什么是零填充
零填充是指在输入特征图的周围添加零值像素,以控制输出特征图的尺寸和边界信息的处理。
输入特征图 (3x3):
1 2 3
4 5 6
7 8 9
添加1圈零填充后:
0 0 0 0 0
0 1 2 3 0
0 4 5 6 0
0 7 8 9 0
0 0 0 0 02.2 零填充的作用
零填充的主要作用包括:
- 保持特征图尺寸:通过添加适当的填充,可以使输出特征图的尺寸与输入特征图的尺寸相同
- 保留边界信息:防止边缘像素在卷积过程中被忽略
- 控制感受野:影响后续层的感受野大小
- 减少信息损失:特别是在深层网络中,减少边界信息的损失
2.3 常见的填充方式
常见的填充方式包括:
- Valid填充:不添加任何填充,输出尺寸小于输入尺寸
- Same填充:添加足够的填充,使输出尺寸等于输入尺寸
- 自定义填充:根据需要添加特定数量的填充
3. 步长(Stride)的概念
3.1 什么是步长
步长是指卷积核在输入特征图上移动的距离。步长越大,输出特征图的尺寸越小。
步长 = 1: 卷积核每次移动1个像素
步长 = 2: 卷积核每次移动2个像素3.2 步长的作用
步长的主要作用包括:
- 控制输出尺寸:步长越大,输出尺寸越小
- 减少计算量:通过减少输出尺寸,减少后续层的计算量
- 实现下采样:步长大于1时,可以实现类似池化层的下采样效果
- 增加感受野:步长越大,感受野增长越快
4. 输出尺寸的计算公式
4.1 基本计算公式
对于二维卷积,输出特征图的尺寸可以通过以下公式计算:
$$
H_{out} = \left\lfloor \frac{H_{in} + 2 \times P - K}{S} \right\rfloor + 1
$$
$$
W_{out} = \left\lfloor \frac{W_{in} + 2 \times P - K}{S} \right\rfloor + 1
$$
其中:
- $H_{in}$ 和 $W_{in}$ 是输入特征图的高度和宽度
- $H_{out}$ 和 $W_{out}$ 是输出特征图的高度和宽度
- $K$ 是卷积核的大小
- $P$ 是填充的大小
- $S$ 是步长
- $\lfloor \cdot \rfloor$ 表示向下取整
4.2 Valid填充的输出尺寸
对于 Valid 填充($P=0$),输出尺寸的计算公式简化为:
$$
H_{out} = \left\lfloor \frac{H_{in} - K}{S} \right\rfloor + 1
$$
$$
W_{out} = \left\lfloor \frac{W_{in} - K}{S} \right\rfloor + 1
$$
4.3 Same填充的输出尺寸
对于 Same 填充,填充的大小 $P$ 计算为:
$$
P = \left\lceil \frac{(S-1) \times H_{in} - S + K}{2} \right\rceil
$$
这样可以确保输出尺寸等于输入尺寸:
$$
H_{out} = H_{in}
$$
$$
W_{out} = W_{in}
$$
5. 示例分析
5.1 示例1:不同填充方式的影响
输入参数:
- 输入尺寸:5x5
- 卷积核大小:3x3
- 步长:1
输出尺寸:
- Valid填充 (P=0):3x3
- Same填充 (P=1):5x5
- 自定义填充 (P=2):7x7
5.2 示例2:不同步长的影响
输入参数:
- 输入尺寸:7x7
- 卷积核大小:3x3
- 填充:1
输出尺寸:
- 步长=1:7x7
- 步长=2:4x4
- 步长=3:3x3
5.3 示例3:综合影响分析
输入参数:
- 输入尺寸:224x224
- 卷积核大小:3x3
输出尺寸:
| 填充方式 | 步长 | 输出尺寸 |
|---|---|---|
| Valid | 1 | 222x222 |
| Same | 1 | 224x224 |
| Valid | 2 | 111x111 |
| Same | 2 | 112x112 |
6. 代码示例:填充和步长的实现
6.1 使用 TensorFlow 实现不同填充方式
import tensorflow as tf
# 创建输入张量 (batch_size, height, width, channels)
input_tensor = tf.constant([[[[1.0], [2.0], [3.0], [4.0], [5.0]],
[[6.0], [7.0], [8.0], [9.0], [10.0]],
[[11.0], [12.0], [13.0], [14.0], [15.0]],
[[16.0], [17.0], [18.0], [19.0], [20.0]],
[[21.0], [22.0], [23.0], [24.0], [25.0]]]])
# 创建卷积核 (height, width, in_channels, out_channels)
kernel = tf.constant([[[[1.0]], [[1.0]], [[1.0]]],
[[[1.0]], [[1.0]], [[1.0]]],
[[[1.0]], [[1.0]], [[1.0]]]])
# 测试不同填充方式
print("Valid填充:")
output_valid = tf.nn.conv2d(input_tensor, kernel, strides=[1, 1, 1, 1], padding='VALID')
print(f"输出尺寸: {output_valid.shape}")
print(output_valid.numpy())
print("\nSame填充:")
output_same = tf.nn.conv2d(input_tensor, kernel, strides=[1, 1, 1, 1], padding='SAME')
print(f"输出尺寸: {output_same.shape}")
print(output_same.numpy())6.2 使用 TensorFlow 实现不同步长
import tensorflow as tf
# 创建输入张量 (batch_size, height, width, channels)
input_tensor = tf.constant([[[[1.0], [2.0], [3.0], [4.0], [5.0], [6.0], [7.0]],
[[8.0], [9.0], [10.0], [11.0], [12.0], [13.0], [14.0]],
[[15.0], [16.0], [17.0], [18.0], [19.0], [20.0], [21.0]],
[[22.0], [23.0], [24.0], [25.0], [26.0], [27.0], [28.0]],
[[29.0], [30.0], [31.0], [32.0], [33.0], [34.0], [35.0]],
[[36.0], [37.0], [38.0], [39.0], [40.0], [41.0], [42.0]],
[[43.0], [44.0], [45.0], [46.0], [47.0], [48.0], [49.0]]]])
# 创建卷积核 (height, width, in_channels, out_channels)
kernel = tf.constant([[[[1.0]], [[1.0]], [[1.0]]],
[[[1.0]], [[1.0]], [[1.0]]],
[[[1.0]], [[1.0]], [[1.0]]]])
# 测试不同步长
print("步长=1:")
output_stride1 = tf.nn.conv2d(input_tensor, kernel, strides=[1, 1, 1, 1], padding='SAME')
print(f"输出尺寸: {output_stride1.shape}")
print("\n步长=2:")
output_stride2 = tf.nn.conv2d(input_tensor, kernel, strides=[1, 2, 2, 1], padding='SAME')
print(f"输出尺寸: {output_stride2.shape}")
print("\n步长=3:")
output_stride3 = tf.nn.conv2d(input_tensor, kernel, strides=[1, 3, 3, 1], padding='SAME')
print(f"输出尺寸: {output_stride3.shape}")7. 填充和步长的选择策略
7.1 填充的选择策略
- 需要保持特征图尺寸时:使用 Same 填充
- 需要减少特征图尺寸时:使用 Valid 填充
- 需要保留边界信息时:使用 Same 填充
- 深层网络:通常使用 Same 填充,以减少信息损失
- 浅层网络:可以根据具体任务选择合适的填充方式
7.2 步长的选择策略
- 需要保留细节信息时:使用步长=1
- 需要下采样时:使用步长=2 或结合池化层
- 需要快速减少特征图尺寸时:使用较大的步长
- 计算资源有限时:使用较大的步长减少计算量
- 需要增加感受野时:使用较大的步长
8. 案例分析:不同参数组合的效果
8.1 实验设置
- 输入图像:224x224 彩色图像
- 卷积核:3x3 卷积核,64个
- 参数组合:
- 步长=1, Valid填充
- 步长=1, Same填充
- 步长=2, Valid填充
- 步长=2, Same填充
8.2 实验结果
| 参数组合 | 输出尺寸 | 参数数量 | 计算量 | 特征提取能力 |
|---|---|---|---|---|
| 步长=1, Valid填充 | 222x222 | 1,792 | 高 | 中 |
| 步长=1, Same填充 | 224x224 | 1,792 | 高 | 高 |
| 步长=2, Valid填充 | 111x111 | 1,792 | 中 | 低 |
| 步长=2, Same填充 | 112x112 | 1,792 | 中 | 中 |
8.3 结果分析
- 参数数量:所有组合的参数数量相同,因为参数数量只与卷积核大小、输入通道数和输出通道数有关
- 计算量:步长越大,计算量越小;填充方式对计算量的影响较小
- 特征提取能力:Same填充的特征提取能力优于Valid填充,步长=1的特征提取能力优于步长=2
- 输出尺寸:步长和填充方式共同决定输出尺寸
9. 特殊情况的处理
9.1 非对称填充
在某些情况下,可能需要使用非对称填充,特别是当输入尺寸、卷积核大小和步长的组合无法通过对称填充得到整数输出尺寸时。
9.2 分数步长
分数步长(也称为反卷积或转置卷积)用于增大特征图尺寸,常用于生成模型和语义分割任务。
import tensorflow as tf
# 创建输入张量
input_tensor = tf.constant([[[[1.0], [2.0]],
[[3.0], [4.0]]]])
# 创建转置卷积核
kernel = tf.constant([[[[1.0]], [[1.0]]],
[[[1.0]], [[1.0]]]])
# 执行转置卷积
output = tf.nn.conv2d_transpose(input_tensor, kernel, output_shape=[1, 4, 4, 1], strides=[1, 2, 2, 1], padding='SAME')
print(f"输出尺寸: {output.shape}")
print(output.numpy())10. 总结与展望
10.1 关键知识点总结
- 零填充的作用:保持特征图尺寸、保留边界信息、控制感受野、减少信息损失
- 步长的作用:控制输出尺寸、减少计算量、实现下采样、增加感受野
- 输出尺寸的计算:通过输入尺寸、卷积核大小、步长和填充计算输出尺寸
- 参数选择策略:根据任务需求和计算资源选择合适的填充方式和步长
- 特殊情况处理:非对称填充和分数步长的应用
10.2 未来展望
随着深度学习的发展,填充和步长的使用策略也在不断演变:
- 自适应填充:根据输入内容动态调整填充方式
- 混合步长:在不同区域使用不同的步长
- 可变形卷积:结合填充和步长的优点,实现更灵活的特征提取
- 轻量级网络设计:通过优化填充和步长策略,设计更高效的网络结构
11. 思考与练习
思考:为什么在深层网络中通常使用 Same 填充?
练习:计算以下参数组合的输出尺寸:
- 输入尺寸:128x128
- 卷积核大小:5x5
- 步长:2
- 填充:2
思考:步长大于1时,如何确保输出尺寸的计算结果为整数?
练习:使用 TensorFlow 实现一个卷积层,要求:
- 输入尺寸:64x64x3
- 输出尺寸:32x32x16
- 选择合适的卷积核大小、步长和填充
思考:在语义分割任务中,为什么通常使用转置卷积而不是直接使用大的步长?
通过本教程的学习,相信你已经掌握了零填充和步长对输出尺寸的影响,以及如何根据任务需求选择合适的参数组合。在接下来的教程中,我们将进一步探讨多通道卷积的详细计算过程。