我已经为 mnist 实现了 CNN 模型。我能够理解如何计算 CNN 不同层的参数和形状,但出于好奇,我只是想了解如何确定分类器部分中的 in_features 和 out_features,特别是 nn.Linear()。我也很难理解如何在 nn.Conv2d 中选择 in_channels,out_channels 。如果我能澄清这些问题,我将不胜感激。
class CNNclf(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3),
nn.ReLU(),
nn.MaxPool2d((2, 2), stride=2),
nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3),
nn.ReLU(),
nn.MaxPool2d((2, 2), stride=3),
nn.Conv2d(in_channels=128, out_channels=64, kernel_size=3),
nn.ReLU(),
nn.MaxPool2d((2, 2), stride=2))
self.clf = nn.Sequential(
nn.Flatten(),
nn.Linear(64, 20, bias=True),
nn.ReLU(),
nn.Linear(20, 10, bias=True))
def forward(self, x):
x = self.net(x)
x = self.clf(x)
return x
这是一个关于 CNN 的典型问题,有很多类似的帖子,用户都遇到了源于此的相同类型的错误。
运行时错误:mat1 和 mat2 形状无法相乘(
xa
和b
xc
)d
我将在这里提供规范的答案。
nn.Conv2d
时,中间张量将是四维的:(batch size, channel, height, width)
。卷积在空间上工作,它们在高度和宽度维度上跨张量移动(对于 2D 卷积)。输出通道的数量由卷积层中相互独立运行的滤波器的数量决定。您可以阅读有关卷积层和大小的更多信息:了解卷积层形状。
对于 CNN 架构,从卷积部分(特征提取器)转移到全连接层(分类器)时,您必须适应维度的变化。一般情况下,张量形状从 4D 变为 2D。这需要某种形式的空间减少:要么
(b, c*h*w)
的形状。这可以使用 nn.Flatten
;nn.MaxPool2d
或平均池化nn.AdaptiveAvgPool2d
,导致(b, c, h', w')
的形状缩小。如果 h'
和 w'
不是单例,仍然需要展平操作。最终最后一个卷积层的输出形状取决于两件事:输入形状以及其前面的卷积的数量和大小。 上述错误是指 CNN 的输出与线性层期望的形状之间的形状不匹配。
a
是批量大小,b
是实际展平特征长度,c
是第一个线性层的in_features
,d
是其out_features
。因此,如果您收到此错误,您已经知道要使用哪个 in_features
!
为了预测此错误并避免在调试架构时抛出它,确定
in_features
的另一种方法是截断模型(删除所有线性层)并使用虚拟数据执行推理。观察该推理的输出形状将告诉您要采用的in_features
。
>>> CNNclf().net(torch.rand(3,1,100,100)).shape # adapt with your input shape
torch.Size([3, 64, 7, 7])
因此空间维度为
7x7
,通道数为 64
,因此特征维度为 64*7*7 = 3136
。在这种情况下,第一个线性层必须初始化为nn.Linear(3136, 20, bias=True)
。
从 1.8 版本开始,存在一个类
nn.LazyLinear
,它会在运行时(在模型的第一次推理期间)自动推断 in_features
。在这种情况下,不需要自己执行虚拟推理,只需使用 nn.LazyLinear(20, bias=True)