卷积神经网络中的填充、步幅与多通道处理
在卷积神经网络(CNN)中,输入尺寸、卷积核尺寸、填充(padding)和步幅(stride)共同决定了输出特征图的尺寸。
填充的作用
当进行多次卷积操作时,图像边缘的信息容易丢失。为缓解这一问题,可以通过在输入图像边缘补充零值(zero-padding)来保留更多原始信息。若在上下方向各填充 p_h 行,在左右方向各填充 p_w 列,则输出尺寸变为:
(n_h - k_h + p_h + 1) × (n_w - k_w + p_w + 1)
特别地,若设置 p_h = k_h - 1 且 p_w = k_w - 1,则输出与输入具有相同的高宽。此时若卷积核尺寸为奇数,可均匀分配上下或左右的填充量。
以下示例展示了使用 PyTorch 实现带填充的二维卷积:
import torch
import torch.nn as nn
def apply_conv2d(conv_layer, input_tensor):
input_tensor = input_tensor.unsqueeze(0).unsqueeze(0)
output = conv_layer(input_tensor)
return output.squeeze()
# 使用3x3卷积核并四周各填充1单位
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1)
x = torch.randn(8, 8)
output_shape = apply_conv2d(conv, x).shape
print(output_shape) # 输出: torch.Size([8, 8])
步幅的影响
步幅定义了卷积窗口移动的距离。较大的步幅可以降低输出尺寸,从而实现下采样效果。设垂直步幅为 s_h,水平步幅为 s_w,则输出尺寸公式如下:
⌊(n_h + p_h - k_h) / s_h⌋ + 1 × ⌊(n_w + p_w - k_w) / s_w⌋ + 1
例如,使用 3×3 卷积核、填充为 1、步幅为 2 的情况:
conv = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
又如使用 3×5 卷积核、垂直填充 0、水平填充 1、垂直步幅 3、水平步幅 4 的情形:
conv = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
多输入通道处理
彩色图像通常包含三个颜色通道(RGB),每个通道都需要独立参与卷积运算。假设输入通道数为 c_i,则卷积核也必须拥有同样数量的输入通道。具体而言,每个通道分别与对应卷积核子矩阵进行互相关运算后求和,得到一个二维输出。
示例代码演示了多通道输入的相关计算过程:
def multi_channel_corr2d(input_tensor, kernel):
return sum(torch.conv2d(x.unsqueeze(0), k.unsqueeze(0))
for x, k in zip(input_tensor, kernel))
input_data = torch.tensor([[[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]],
[[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]])
kernel_weights = torch.tensor([[[0., 1.],
[2., 3.]],
[[1., 2.],
[3., 4.]]])
result = multi_channel_corr2d(input_data, kernel_weights)
print(result)
多输出通道机制
网络可通过增加输出通道数提升表达能力。每一输出通道对应一组特定的卷积核权重,用于捕捉不同类型的特征。若输出通道数为 c_o,则总共有 c_o 组 c_i × k_h × k_w 形状的卷积核。
1×1 卷积的应用
虽然 1×1 卷积不具备空间感知能力,但它能在通道维度上进行线性变换,类似于全连接层的操作。这种结构常用于调整通道数量及控制模型复杂度。
下面是 1×1 卷积等价于全连接层的一个验证示例:
def conv1x1_equivalent(input_tensor, weights):
c_i, h, w = input_tensor.shape
c_o = weights.shape[0]
input_flat = input_tensor.view(c_i, -1)
weight_flat = weights.view(c_o, c_i)
output_flat = torch.mm(weight_flat, input_flat)
return output_flat.view(c_o, h, w)
input_sample = torch.randn(3, 4, 4)
weights_sample = torch.randn(6, 3, 1, 1)
output1 = conv1x1_equivalent(input_sample, weights_sample)
output2 = torch.conv2d(input_sample.unsqueeze(0), weights_sample).squeeze()
assert torch.allclose(output1, output2, atol=1e-6)
池化层的功能
池化层主要用于降低特征图的空间分辨率,增强模型对局部变化的鲁棒性。常见的两种类型包括最大池化和平均池化。
手动实现一个简单的池化操作:
def simple_pool(input_tensor, pool_size, mode='max'):
p_h, p_w = pool_size
out_height = input_tensor.shape[0] - p_h + 1
out_width = input_tensor.shape[1] - p_w + 1
result = torch.zeros(out_height, out_width)
for i in range(out_height):
for j in range(out_width):
region = input_tensor[i:i+p_h, j:j+p_w]
if mode == 'max':
result[i, j] = region.max()
elif mode == 'avg':
result[i, j] = region.mean()
return result
test_input = torch.tensor([[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]])
pooled_output = simple_pool(test_input, (2, 2), 'max')
print(pooled_output)
PyTorch 提供内置的池化模块支持自定义填充和步幅:
max_pool = nn.MaxPool2d(kernel_size=3, padding=1, stride=2)
总结
- 合理运用填充可维持特征图尺寸不变,便于设计深层网络。
- 通过调节步幅能够灵活控制输出大小,有助于构建高效的特征提取路径。
- 多通道机制使得 CNN 能够同时处理多种视觉模式。
- 1×1 卷积提供了一种轻量级的方式来进行通道间的特征融合。
- 池化层不仅减少了参数规模,还提升了模型泛化能力和抗噪性能。