如何在简单的pytorch模型中重构cnn层的输出张量以供线性层使用

Posted

技术标签:

【中文标题】如何在简单的pytorch模型中重构cnn层的输出张量以供线性层使用【英文标题】:How to restructure the output tensor of a cnn layer for use by a linear layer in a simple pytorch model 【发布时间】:2021-01-23 13:53:07 【问题描述】:

给定一个带有维度的pytorch 输入数据集:

dat.shape = torch.Size([128, 3, 64, 64])

这是一个监督学习问题:我们有一个单独的 labels.txt 文件,其中包含每个输入观察的 C 类之一。 C 的值由标签文件中不同值的数量计算得出,目前为个位数。

我可以帮助我了解如何对执行多类分类的卷积层和线性层的简单混合网络的层进行网格划分。意图是通过:

两个 cnn 层,每个层后都有 maxpooling 线性“读出”层 在输出/标签之前激活softmax

这是我(故障/损坏)网络的核心。我无法确定以下所需的正确尺寸/形状:

 Output of Convolutional layer -> Input of Linear [Readout] layer
class CNNClassifier(torch.nn.Module):

    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, 3)
        self.maxpool = nn.MaxPool2d(kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3)
        self.linear1 = nn.Linear(32*16*16, C)
        self.softmax1 = nn.LogSoftmax(dim=1)

    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool(F.leaky_relu(x))
        x = self.conv2(x)
        x = self.maxpool(F.leaky_relu(x))
        x = self.linear1(x)  # Size mismatch error HERE 
        x = self.softmax1(x)
        return x

模型的训练开始于:

        Xout = model(dat)

这会导致:

RuntimeError:尺寸不匹配,m1:[128 x 1568],m2:[8192 x 6]

linear1 输入。这里需要什么?注意我已经看到了通配符输入大小的使用,例如通过view:

    ..
    x = x.view(x.size(0), -1)
    x = self.linear1(x)  # Size mismatch error HERE 

如果包含,则错误更改为

RuntimeError:尺寸不匹配,m1:[28672 x 7],m2:[8192 x 6]

非常感谢有关如何考虑和计算 cnn 层/线性层输入/输出大小的一些指示。

【问题讨论】:

【参考方案1】:

错误

您错误地计算了卷积堆栈的输出大小。实际上是[batch, 32, 7, 7] 而不是[batch, 32, 16, 16]

你必须使用reshape(或view)作为Conv2d的输出有4个维度([batch, channels, width, height]),而nn.Linear的输入需要有2个维度([batch, features])。

将此用于nn.Linear

self.linear1 = nn.Linear(32 * 7 * 7, C)

这个在forward:

x = self.linear1(x.view(x.shape[0], -1))

其他可能性

当前的新架构使用跨渠道池(通常称为全局池)。在 PyTorch 中有一个 torch.nn.AdaptiveAvgPool2d(或 Max 池)。使用这种方法,您可以拥有可变大小的输入图像的高度和宽度,因为每个通道只有一个值用作nn.Linear 的输入。看起来是这样的:

class CNNClassifier(torch.nn.Module):
    def __init__(self, C=10):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, 3)
        self.maxpool = nn.MaxPool2d(kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3)
        self.pooling = torch.nn.AdaptiveAvgPool2d(output_size=1)
        self.linear1 = nn.Linear(32, C)
        self.softmax1 = nn.LogSoftmax(dim=1)

    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool(F.leaky_relu(x))
        x = self.conv2(x)
        x = self.maxpool(F.leaky_relu(x))
        x = self.linear1(self.pooling(x).view(x.shape[0], -1))
        x = self.softmax1(x)
        return x

所以现在torch.Size([128, 3, 64, 64])torch.Size([128, 3, 128, 128]) 的图像可以传递到网络。

【讨论】:

添加AdaptiveAvgPool2d 非常适合您可能有多种图像尺寸的情况;我想自己添加它。 +1 谢谢!对于第一种方法和大小32x7x7 你是通过O = (W - K + 2*P)/S + 1 计算出来的吗? @javadba 您可以在 PyTorch 文档中找到确切的公式。这次我在linear 之前添加了printed shape 属性。此外,通常,在 Conv 中使用 kernel_size=3 时,您使用 padding=1,因此形状保持不变(整个事情更容易理解)。【参考方案2】:

所以问题在于您定义nn.Linear 的方式。您将输入大小设置为32*16*16,这不是输出图像的形状,但数字32/16 表示Conv2d 期望输入的“通道”暗淡的数量以及它将输出什么.

如果你在全连接层入口前加上print(x.shape)你会得到:

torch.Size([Batch, 32, 7, 7])

所以你的计算应该是7*7*32:

self.linear1 = nn.Linear(32*7*7, C)

然后使用:

x = x.view(x.size(0), -1)
x = self.linear1(x)

可以正常工作。您可以阅读view 的作用:How does the "view" method work in PyTorch?

【讨论】:

以上是关于如何在简单的pytorch模型中重构cnn层的输出张量以供线性层使用的主要内容,如果未能解决你的问题,请参考以下文章

计算CNN模型中Conv层的输出大小

pytorch 中实现CNN,对CNN的理解心得

pytorch 中实现CNN,对CNN的理解心得

最后一层的张量输出在 PyTorch 中的形状错误

ResNet——CNN经典网络模型详解(pytorch实现)

取出预训练模型中间层的输出(pytorch)