【DL】复杂卷积神经网络Ⅱ

5. ResNet

ResNet是由微软研究院的 Kaiming He, Xiangyu Zhang, Shaoqing Ren 和 Jian Sun 在 2015 年提出的深度神经网络架构。ResNet斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名;COCO数据集中目标检测第一名,图像分割第一名。它的名字来源于残差网络(Residual Network),也就是 ResNet 架构中的神经网络块是建立在残差模块之上的。

ResNet 是目前依然非常流行的深度神经网络架构,在计算机视觉领域有着广泛的应用,并为之后的深度神经网络架构的提出和改进奠定了基础。截止目前为止,论文引用次数已经超过14万。

ResNet 是为了解决深度神经网络训练时网络退化问题而提出的,所谓的网络退化其实就是深层网络的效果反而比浅层网络更差。ResNet 的提出让我们可以训练出更深的网络,其深度突破了100层,甚至超过了1000层。

ResNet基本思想

ResNet 的基本思想是引入残差模块来解决深度神经网络训练时的网络退化问题。

残差模块的思想是将多层神经网络块看成一个整体,然后学习残差,即将输出与输入的差作为模型的预测目标。这样做的好处是可以使梯度流动更加稳定,从而使深度神经网络得以顺利训练。

假设将堆叠的几层神经元称为一个block,那么对于某个block,假设其拟合的函数是 F(x),如果期望潜在映射为 H(x),与其让 F(x) 直接拟合 H(x) ,不如去学习残差 H(x)−x,即 F(x)=H(x)−x,这样其前向路径就变成了 F(x)+x,即 H(x)=F(x)+x。其结构如下图所示。

ResNet结构

ResNet34的结构与VGG19的对比如下图所示:

其中实现实曲线表示残差连接,虚线表示包含升维。

不同深度的ResNet结构如下图所示:

上面的结构图对照上表就是三部分:

  1. 第一部分卷积层+最大池化层;
  2. 第二部分是4组不同数量和规格的残差模块;
  3. 第三部分平均池化层+全连接层。

ResNet代码实现

python 复制代码
class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super().__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)

        return out
python 复制代码
class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super().__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)

        return out

然后组装ResNet的网络结构:

python 复制代码
class ResNet(nn.Module):

    def __init__(self, block, layers, num_classes=1000):
        self.inplanes = 64
        super().__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * block.expansion, num_classes)

    # 生成网络结构的函数,根据传入的配置,拼接出对应的网络结构
    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)

        return x
  • 针对网络退化问题,采用残差结构解决(隔层相连,弱化每层之间的强联系),利用残差结构让网络能够更深、收敛速度更快、优化更容易,同时参数相对之前的模型更少、复杂度更低。
  • 对于很深的网络,ResNet使用了更高效的"bottleneck"结构极大程度上降低了参数计算量。
  • ResNet深度突破了100层,甚至超过了1000层,对后来的深层神经⽹络设计产⽣了深远影响。

6. DenseNet

DenseNet(Densely connected convolutional networks)是由Gao Huang等人在2017年提出的一种深度卷积神经网络,它在ResNet的基础上进行了改进,其中使用了联结不同层的稠密块(dense blocks)来构建模型。DenseNet的一个主要优势在于,它可以有效地减少模型的参数数量,同时保持或者提高模型的性能。这是通过在每个层之间使用"短接(shortcut connections)"来实现的,这些短接使得每个层都可以直接访问之前所有层的特征。

DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR 2017的最佳论文奖。

DenseNet基本思想

DenseNet核心思想在于建立了不同层之间的连接关系,充分利用了feature,进一步减轻了梯度消失问题,加深网络不是问题,而且训练效果非常好。另外通过利用bottleneck layer,Translation layer以及较小的growth rate使得网络变窄,参数减少,有效抑制了过拟合,同时计算量也减少了,在和ResNet的对比中优势还是比较明显。

相比ResNet,DenseNet提出了一个更激进的密集连接机制。即互相连接所有的层,具体来说就是每个层都会接受其前面所有层作为其额外的输入。其dense block结构如下图所示。

将一个个dense block结构拼接之后,DenseNet的密集连接机制如下图所示:

相比之下,ResNet是每个层与前面的某层短路连接在一起,连接方式是通过元素级相加。而在DenseNet中,每个层都会与前面所有层在channel维度上连接(concat)在一起,并作为下一层的输入。也就是说,对于一个 L 层的网络,DenseNet共包含 L(L+1)2 个连接,而不是传统意义上的L层连接。因为密集连接的特性,称其为DenseNet。

DenseNet结构

不同深度的DenseNet结构如下图所示:

上面的结构图对照上表就是几部分:

  1. 第一部分卷积层+最大池化层;
  2. 第二部分是4组不同数量和规格的Dense Block;
  3. 第三部分是3个Transition Layer,主要作用就是降维和调整特征图size;
  4. 第四部分平均池化层+全连接层。

DenseNet代码实现

首先是DenseBlock中的内部结构,这里是BN+ReLU+Conv1x1+BN+ReLU+Conv3x3的结构:

python 复制代码
class _DenseLayer(nn.Module):
    def __init__(self, num_input_features, growth_rate, bn_size):
        super().__init__()
        self.conv1 = nn.Sequential(
            nn.BatchNorm2d(num_input_features),
            nn.ReLU(inplace=True),
            nn.Conv2d(num_input_features, bn_size * growth_rate, kernel_size=1, stride=1, bias=False)
        )
        self.conv2 = nn.Sequential(
            nn.BatchNorm2d(bn_size * growth_rate),
            nn.ReLU(inplace=True),
            nn.Conv2d(bn_size * growth_rate, growth_rate, kernel_size=3, stride=1, padding=1, bias=False)
        )

    def forward(self, x):
        out = self.conv1(x)
        out = self.conv2(out)
        return torch.cat([x, out], 1)

然后实现DenseBlock模块,内部是密集连接方式(输入特征数线性增长):

python 复制代码
class _DenseBlock(nn.Module):
    def __init__(self, num_layers, num_input_features, bn_size, growth_rate):
        super().__init__()
        
        layers = []
        for i in range(num_layers):
            layer = _DenseLayer(num_input_features + i * growth_rate, growth_rate, bn_size)
            layers.append(layer)
        self.block = nn.Sequential(*layers)
        
    def forward(self, x):
        return self.block(x)

接下来实现Transition层,它是BN+ReLU+Conv1x1+Pool的结构:

python 复制代码
class _Transition(nn.Module):
    def __init__(self, num_input_features, num_output_features):
        super().__init__()
        self.trans = nn.Sequential(
            nn.BatchNorm2d(num_input_features),
            nn.ReLU(inplace=True),
            nn.Conv2d(num_input_features, num_output_features, kernel_size=1, stride=1, bias=False),
            nn.AvgPool2d(kernel_size=2, stride=2)
        )
    
    def forward(self, x):
        return self.trans(x)

然后组装DenseNet的网络结构:

python 复制代码
class DenseNet(nn.Module):
    def __init__(self, block_config, growth_rate=32, num_init_features=64, bn_size=4, num_classes=1000):

        super().__init__()

        # First convolution
        self.features = nn.Sequential(
            nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False),
            nn.BatchNorm2d(num_init_features),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )

        # Each denseblock
        num_features = num_init_features
        layers = []
        for i, num_layers in enumerate(block_config):
            block = _DenseBlock(num_layers=num_layers, num_input_features=num_features, bn_size=bn_size, growth_rate=growth_rate)
            layers.append(block)
            num_features = num_features + num_layers * growth_rate
            if i != len(block_config) - 1:
                trans = _Transition(num_input_features=num_features, num_output_features=num_features // 2)
                layers.append(trans)
                num_features = num_features // 2
        layers.append(nn.BatchNorm2d(num_features))
        self.denseblock = nn.Sequential(*layers)

        # Linear layer
        self.classifier = nn.Linear(num_features, num_classes)

    def forward(self, x):
        features = self.features(x)
        features = self.denseblock(features)
        out = F.relu(features, inplace=True)
        out = F.avg_pool2d(out, kernel_size=7, stride=1).view(features.size(0), -1)
        out = self.classifier(out)
        return out
  • DenseNet 和 ResNet 非常的相似,简单来看,可以理解为 ResNet 的特征图是按位置相加,而DenseNet的特征图是进行concat拼接。但实际上基于密集连接的结构,能够形成更复杂的特征模式。
  • DenseNet 可以有效地减少模型的参数数量,保持或者提高模型的性能,同时通过特征在channel上的连接来实现特征重用。
  • DenseNet 结构可以有效环节梯度消失和模型退化的问题。
相关推荐
在所不辞兄3 小时前
【零基础学智能仿真-63】ABACUS作业工具与MCP接口
人工智能·深度学习·神经网络·机器学习·智能仿真
楚山之石3 小时前
【Medical Image Analysis】深度学习医学图像合成综述:伪CT、合成MRI与合成PET的架构、性能与临床边界
图像处理·人工智能·学习·mr·mri
blackappleD3 小时前
百度 Unlimited OCR 2 拆解:SR-SWA 如何让百页文档的 KV 缓存不再涨
人工智能·llm
数字新视界3 小时前
国产动环数据库在智能机房环境监控中的应用与优势分析
大数据·人工智能·数据中心·机房管理·模块化机房
holly就是我3 小时前
AI 编程助手为什么跑偏?用开源 ccglass 看清 Prompt、工具调用和 Token 消耗
人工智能·后端
雨中飘荡的记忆3 小时前
额度又见底了?这个开源项目,给 AI 编程助手接上 61 家"加油站"
人工智能
天远Date Lab3 小时前
零信任架构实战:基于天远企业年报信息核验构建自动化商业保理财务与社保穿透评估网关
人工智能·架构·自动化
EnCi Zheng4 小时前
后端怎么把AI接口稳定安全低成本接进业务:超时重试、密钥隔离与成本熔断清单
人工智能·后端·安全·架构·api
loulanyue_4 小时前
给概率大模型套上确定性缰绳:GraphRAG、神经符号与知识超图重构可信AI底座——读陈华钧杨博等CNCC2026第十届知识图谱论坛实录
人工智能·rag
古法老登瞎折腾4 小时前
大数据智能运维实战(九):让 AI 查日志——找到异常,也保留上下文
java·大数据·运维·人工智能·spring ai·mcp