趋近智
当你开始构建卷积神经网络 (neural network)(CNN)时,最常见的实际问题之一是确保一个层的输出形状能正确匹配下一个层的预期输入形状。与只需要考虑一个维度的简单全连接层不同,卷积层和池化层对多维网格状数据(如图像)进行操作,涉及高度、宽度和通道维度。了解这些维度如何变化对于构建有效的CNN架构非常重要。
让我们看一个用于二维CNN层(如nn.Conv2d或nn.MaxPool2d)的典型输入张量。它通常有四个维度:
批维度 通常保持不变。主要的变换发生在通道 ()、高度 () 和宽度 () 上。
nn.Conv2d)torch.nn.Conv2d层对由多个输入平面组成的输入信号应用二维卷积。影响输出形状最重要的参数 (parameter)是:
in_channels (): 必须与输入张量中的通道数匹配。out_channels (): 决定卷积产生的通道数。这是该层学习的滤波器数量。kernel_size: 卷积核(滤波器)的大小。可以是一个整数用于方形卷积核(例如,3表示3x3),也可以是一个元组(kH, kW)用于指定高度和宽度。stride: 卷积核在输入特征图上滑动时的步长。默认为1。可以是一个整数或一个元组(sH, sW)。较大的步长会导致输出特征图尺寸更小。padding: 输入边缘添加的零填充量。默认为0。可以是一个整数或一个元组(padH, padW)。填充有助于控制输出的空间维度,并能保留边界信息。dilation: 卷积核元素之间的间距。默认为1。较大的空洞(扩张)允许卷积核覆盖输入更广的区域,而不会增加参数数量(空洞卷积)。输出形状 如下确定:
通道数 (): 这由nn.Conv2d层的out_channels参数直接设置。每个滤波器产生一个输出通道(特征图)。
高度 () 和宽度 (): 这些取决于输入维度 () 和层的参数。计算输出高度的公式是:
对于宽度 () 也是类似的:
注意:如果padding、dilation、kernel_size或stride被指定为单个整数,它们将应用于高度和宽度两个维度(例如,padding[0] = padding[1] = padding)。符号 表示向下取整函数(向下舍入到最接近的整数)。
让我们看一个当dilation = 1的常见情况。公式简化为:
示例:
假设我们有一个形状为(16, 3, 32, 32)的输入张量(批=16,通道=3,高=32,宽=32)。我们将其通过一个定义如下的nn.Conv2d层:
import torch
import torch.nn as nn
conv_layer = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
# 输入: N=16, Cin=3, Hin=32, Win=32
input_tensor = torch.randn(16, 3, 32, 32)
# 参数: K=3, S=1, P=1, D=1 (默认)
# H_out = floor((32 + 2*1 - 1*(3-1) - 1)/1 + 1) = floor((32 + 2 - 2 - 1)/1 + 1) = floor(31/1 + 1) = 32
# W_out = floor((32 + 2*1 - 1*(3-1) - 1)/1 + 1) = floor((32 + 2 - 2 - 1)/1 + 1) = floor(31/1 + 1) = 32
# 简化公式 (D=1):
# H_out = floor((32 + 2*1 - 3)/1 + 1) = floor(31/1 + 1) = 32
# W_out = floor((32 + 2*1 - 3)/1 + 1) = floor(31/1 + 1) = 32
output_tensor = conv_layer(input_tensor)
print(output_tensor.shape)
# 预期输出: torch.Size([16, 64, 32, 32])
在此示例中,使用kernel_size=3、stride=1和padding=1是一种常见组合,它保持了输入的高度和宽度(32x32 -> 32x32),同时将通道数从3变为64。这有时被称为“相同”填充,尽管PyTorch不像其他一些框架那样有明确的'same'选项;你可以通过正确设置参数来实现。
如果我们将步长改为2(stride=2),输出维度将会减小:
conv_layer_s2 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=2, padding=1)
# H_out = floor((32 + 2*1 - 3)/2 + 1) = floor(31/2 + 1) = floor(15.5 + 1) = floor(16.5) = 16
# W_out = floor((32 + 2*1 - 3)/2 + 1) = floor(31/2 + 1) = floor(15.5 + 1) = floor(16.5) = 16
output_tensor_s2 = conv_layer_s2(input_tensor)
print(output_tensor_s2.shape)
# 预期输出: torch.Size([16, 64, 16, 16])
nn.MaxPool2d)池化层,例如nn.MaxPool2d,用于减小特征图的空间维度(下采样),使表示更紧凑且对小的平移具有鲁棒性。它们在每个通道上独立操作。
影响形状的主要参数 (parameter)与nn.Conv2d相似,但没有out_channels这个参数,因为池化不会改变通道数量:
kernel_size: 池化窗口的大小。stride: 窗口的步长。通常设置为与kernel_size相等,以实现不重叠的池化(默认值是kernel_size)。padding: 添加的零填充量。dilation: 控制池化元素之间的间距。输出形状 中 和 的计算遵循与卷积层完全相同的公式:
重要区别: 池化层不改变通道数量。因此,。
示例:
让我们使用我们第一个conv_layer的输出(形状为[16, 64, 32, 32])并将其通过一个常见的最大池化层:
# 来自前一个卷积层的输入: N=16, Cin=64, Hin=32, Win=32
pool_layer = nn.MaxPool2d(kernel_size=2, stride=2, padding=0) # 常见设置
# 参数: K=2, S=2, P=0, D=1 (默认)
# H_out = floor((32 + 2*0 - 1*(2-1) - 1)/2 + 1) = floor((32 - 1 - 1)/2 + 1) = floor(30/2 + 1) = floor(15 + 1) = 16
# W_out = floor((32 + 2*0 - 1*(2-1) - 1)/2 + 1) = floor((32 - 1 - 1)/2 + 1) = floor(30/2 + 1) = floor(15 + 1) = 16
pooled_output = pool_layer(output_tensor)
print(pooled_output.shape)
# 预期输出: torch.Size([16, 64, 16, 16])
在此,具有2x2卷积核和步长为2的池化层将高度和宽度维度减半(32x32 -> 16x16),而通道数量保持不变(64)。
张量维度通过一个示例卷积和池化层序列的流向。
在构建复杂的CNN时,手动计算形状会变得繁琐且容易出错。这里有一些实用建议:
print(x.shape)语句以验证维度。torchinfo或pytorch-summary)可以自动总结你的模型,显示给定输入尺寸下每个层的输出形状。掌握形状计算是在设计和调试CNN时必要的一步。通过理解kernel_size、stride、padding和dilation如何影响空间维度,以及out_channels如何决定深度,你可以放心地堆叠层来构建有效的深度学习 (deep learning)模型。
这部分内容有帮助吗?
nn.Conv2d层的官方细节和计算公式,涵盖参数和输出形状计算。nn.MaxPool2d层的官方细节和计算公式,涵盖参数和输出形状计算。© 2026 ApX Machine LearningAI伦理与透明度•