pytorch 为什么这种深度学习卷积模型不能推广?

dtcbnfnu  于 2023-02-08  发布在  其他
关注(0)|答案(1)|浏览(140)

我正在使用pytorch训练一个卷积网络,该网络用于3D医学光栅图像(.nrrd文件),以从非常嘈杂的超声图像中获得估计的体积测量值。
我有30个病人的大约200个单独的光栅图像,并在所有3个轴(随机选择)上应用各种变换和噪声将它们增加到5000多个。所有光栅在使用前都被调整为128x128x128。
我正在进行6重交叉验证,确保验证集由与训练集完全不同的患者组成,我认为这有助于了解模型是否真的具有泛化能力,是否能够估计看不见的患者的栅格。
问题是,模型根本无法泛化或学习。请参见我所做的2次测试运行(每次处理10个小时)的结果:
First Training Failure
Second Training Failure
所使用的架构只是6个卷积层,后面是2个紧密连接的层,没有什么太花哨的。是什么原因导致了这种情况?会不会是我没有足够的数据让我的模型学习?
我试过降低学习率和提高权重衰减,没有运气。我还没有尝试使用其他标准和优化器(目前使用MSE损失和亚当)。

  • 编辑:添加代码:
class RasterNet(nn.Module):
    def __init__(self):
        super(RasterNet, self).__init__()

        self.conv0 = nn.Sequential( # 128x128x128 -> 256x32x32
            nn.Conv2d(128, 256, kernel_size=7, stride=2, padding=3),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.conv1 = nn.Sequential( # 256x32x32 -> 512x16x16
            nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.conv2 = nn.Sequential( # 512x16x16 -> 1024x8x8
            nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(1024),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.conv3 = nn.Sequential( # 1024x8x8 -> 2048x4x4
            nn.Conv2d(1024, 2048, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(2048),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.conv4 = nn.Sequential( # 2048x4x4 -> 4096x2x2
            nn.Conv2d(2048, 4096, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(4096),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.conv5 = nn.Sequential( # 4096x2x2 -> 8192x1x1
            nn.Conv2d(4096, 8192, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(8192),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.linear = nn.Sequential(
            nn.Linear(8192, 4096),
            nn.ReLU(),
            nn.Linear(4096, 1)
        )

    def forward(self, base):
        base = base.squeeze().float().to(dml)

        # View from y axis (Coronal, as this is the clearest view)
        base = torch.transpose(base, 2, 1)

        x = self.conv0(base)
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = x.view(x.size(0), -1)
        return self.linear(x)
g6ll5ycj

g6ll5ycj1#

好的,下面是一些注解,它们本身不是“答案”,但过于冗长,无法发表评论:
首先,您的训练损失收敛到一个低值,但验证损失较高,这意味着您的模型过拟合训练分布。这可能意味着:
1.您的模型架构表达能力不足,无法从低级别(像素/体素)信息中有意义地提取高级别信息,因此需要学习使损失相对较低的训练集宽偏差项。这可能表明您的验证和训练分割来自不同的分布,或者您的损失函数未针对任务进行适当选择。
1.您的模型表达性太强(方差高),以至于无法学习精确的训练示例(经典过拟合)
其次,神经网络训练中一个几乎无处不在的技巧是使用运行时数据扩充。这意味着,不是在训练前生成一组扩充图像,而是生成一组随机应用数据转换的扩充函数。这组函数用于在每个训练时期转换数据批,这样模型就不会看到两次完全相同的数据示例。
第三,这个模型架构相对简单(比第一个现代深度CNN AlexNet简单)。通过构建更深的架构并使用剩余层(参见ResNet)来处理梯度消失问题,已经获得了更高的性能。如果你能用这个架构在这个任务上获得良好的性能,我会有点惊讶。
验证损失的平均值高于训练损失是正常的。您的模型可能正在某种程度上学习,但与(可能过拟合)训练曲线相比,损失曲线相对较浅。我建议还计算整个时期的验证 * 准确度 * 并跨时期报告此值。您应该看到训练准确度增加,验证准确度也可能增加。
请注意,交叉验证并不完全是为了确定模型是否推广到看不见的患者。这是 * 验证集 * 的目的。相反,交叉验证确保训练验证性能在多个数据分区上有效,而不仅仅是选择“简单”验证集的结果。
纯粹为了速度/简单性,我建议先训练模型,而不使用交叉验证(即使用单个训练-测试分区)。一旦您在整个数据集上实现了良好的性能,您可以使用k-fold重新训练以确保上述性能,但这应该会使您的调试周期更快一些。

相关问题