深度学习中的残差网络架构解析

近年来,深度学习领域取得了巨大的进步,对于构建更深层次的网络架构以提高模型准确性充满期待。这些技术广泛应用于图像相关的任务,如分类、聚类或合成。虽然深层网络看起来很酷,但它们面临着一个称为退化的问题,这严重影响了模型的准确性。这也导致了梯度消失问题,这使得在反向传播步骤中无法正确更新权重。在反向传播过程中,使用链式法则,每一层的导数在网络中向下传播时会被相乘。如果使用深层网络并有像sigmoid这样的隐藏层,导数会在每一层被缩小到0.25以下。因此,当许多层的导数相乘时,梯度会呈指数级下降,得到一个非常小的值,这对于梯度计算来说是无用的。这促使微软研究院开发了ResNet,它使用跳过连接来避免退化。在本文中,将讨论如何使用Python中的Pytorch框架实现34层ResNet架构的实现。

正如上文所讨论的,下图显示了梯度消失问题。sigmoid函数的导数被缩小到0.25以下,这在更新梯度时丢失了很多信息。

平面网络的问题,如图2所示,看到了具有更深层的平面网络。在左侧,有训练误差和测试误差在右侧。可能会认为这是过拟合的一个案例,但在这里,56层网络的误差百分比在训练和测试数据上都更糟糕,这并不发生在模型过拟合时。这被称为退化。

ResNet-34的架构,如图3所示,最初有一个卷积层,有64个滤波器,核大小为7×7,这是第一次卷积,然后是一个最大池化层。指定了2的步长。接下来,在conv2_x中,有池化层和随后的卷积层。这些层通常成对分组,因为残差连接的方式(箭头显示每两层跳过)。在这里,有2层,核大小为3×3,num_filters为64,所有这些都重复了3次,对应于pool,/2和过滤器128层之间的层,总共6层(一对乘以3)。这2层核大小为3×3,num_filters为128,这些也重复了,但这次是4次。这一直持续到avg_pooling和softmax函数。每次滤波器数量翻倍时,可以看到第一层指定了num_filters/2。

以下是使用Pytorch定义ResNet-34的代码:

class ResNet34(nn.Module): def __init__(self): super(ResNet34, self).__init__() self.block1 = nn.Sequential( nn.Conv2d(1, 64, kernel_size=2, stride=2, padding=3, bias=False), nn.BatchNorm2d(64), nn.ReLU(True) ) self.block2 = nn.Sequential( nn.MaxPool2d(1, 1), ResidualBlock(64, 64), ResidualBlock(64, 64, 2) ) self.block3 = nn.Sequential( ResidualBlock(64, 128), ResidualBlock(128, 128, 2) ) self.block4 = nn.Sequential( ResidualBlock(128, 256), ResidualBlock(256, 256, 2) ) self.block5 = nn.Sequential( ResidualBlock(256, 512), ResidualBlock(512, 512, 2) ) self.avgpool = nn.AvgPool2d(2) self.fc1 = nn.Linear(512, 11) self.fc2 = nn.Linear(512, 168) self.fc3 = nn.Linear(512, 7) def forward(self, x): x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) x = self.block5(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x1 = self.fc1(x) x2 = self.fc2(x) x3 = self.fc3(x) return x1, x2, x3

这段代码为提供了ResNet-34模块的完整实现。现在让深入了解每一行代码是如何工作的。为此,让简化之前看到的架构。参考下面的34层图。可以看到,需要在5个区块中实现任何ResNet架构。第一个区块有64个滤波器,步长为2,后面是步长为2的最大池化。架构使用3的填充。由于存在内部协变量偏移,必须通过批量归一化来稳定网络。在最后使用ReLU激活。对于任何架构,第一个区块都是相同的,其余的区块对于不同的层会有所变化,并以特定的模式重复。第一个区块可以这样实现:

nn.Conv2d(1, 64, kernel_size=2, stride=2, padding=3, bias=False), nn.BatchNorm2d(64), nn.ReLU(True)

在这里,有一个跳过连接。这是残差网络的主要思想。之前看到的架构图上有跳过连接,用虚线和深色箭头显示。虚线就是在这里做的,因为正在进入一个比之前更大的通道。第二个区块有初始最大池的实现,核大小为3×3,两个残差块如下所示。这两个以64作为输入和输出通道,并重复3次,如架构所示。最后,最后一个以2的步长连接到下一个区块。

nn.MaxPool2d(1, 1), ResidualBlock(64, 64), ResidualBlock(64, 64, 2)

最后一个区块的内容重复了3次,在架构图中用深色箭头表示,输入和输出通道大小保持不变。架构图中的'/2'告诉使用的步长。第三个区块采用3×3的核大小,输入通道来自上一个区块的64,并给出128的输出通道。然后以128作为输入和输出通道。像之前架构图中显示的那样重复4次。

ResidualBlock(64, 128), ResidualBlock(128, 128, 2) ResidualBlock(128, 256), ResidualBlock(256, 256, 2) ResidualBlock(256, 512), ResidualBlock(512, 512, 2)
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485