PyTorch实战——ResNet与DenseNet详解

PyTorch实战------ResNet与DenseNet详解

    • [0. 前言](#0. 前言)
    • [1. ResNet](#1. ResNet)
    • [2. DenseNet](#2. DenseNet)
    • 相关链接

0. 前言

我们已经学习了Inception 模型,这些模型通过 1x1 卷积和全局平均池化减少了模型参数的数量,从而避免了随着层数的增加可能导致的参数爆炸问题。此外,还通过辅助分类器缓解了梯度消失问题。在本节中,我们将讨论 ResNetDenseNet 模型。

1. ResNet

ResNet 引入了跳跃连接 (skip connections) 的概念。这种简单而有效的技巧克服了参数爆炸和梯度消失的问题。其基本思想如下图所示,输入首先经过非线性变换(卷积后跟非线性激活),然后将该变换的输出(称为残差)与原始输入相加。每个这样的计算块称为残差块,因此该模型称为残差网络 (Residual Network, ResNet):

通过使用跳跃连接(也称捷径连接),ResNet-50 (50 层)的参数数量为 2600 万。由于参数数量有限,即使层数增加到 152 层( ResNet-152),ResNet 也能很好地泛化而不会过拟合。下图展示了 ResNet-50 的架构:

ResNet 中有两种残差块:卷积残差块和恒等残差块,两者都包含跳跃连接。对于卷积残差块,额外添加了一个 1x1 的卷积层,以进一步减少维度。使用 PyTorch 实现残差块:

python 复制代码
class BasicBlock(nn.Module):
    multiplier=1
    def __init__(self, input_num_planes, num_planes, strd=1):
        super(BasicBlock, self).__init__()
        self.conv_layer1 = nn.Conv2d(in_channels=input_num_planes, out_channels=num_planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.batch_norm1 = nn.BatchNorm2d(num_planes)
        self.conv_layer2 = nn.Conv2d(in_channels=num_planes, out_channels=num_planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.batch_norm2 = nn.BatchNorm2d(num_planes)
 
        self.res_connnection = nn.Sequential()
        if strd > 1 or input_num_planes != self.multiplier*num_planes:
            self.res_connnection = nn.Sequential(
                nn.Conv2d(in_channels=input_num_planes, out_channels=self.multiplier*num_planes, kernel_size=1, stride=strd, bias=False),
                nn.BatchNorm2d(self.multiplier*num_planes)
            )
    def forward(self, inp):
        op = F.relu(self.batch_norm1(self.conv_layer1(inp)))
        op = self.batch_norm2(self.conv_layer2(op))
        op += self.res_connnection(inp)
        op = F.relu(op)
        return op

要快速开始使用 ResNet,我们可以直接使用 PyTorch 提供的预训练模型:

python 复制代码
import torchvision.models as models
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)

ResNet 使用恒等函数(直接将输入连接到输出)在反向传播中保留梯度(因为梯度为 1)。然而,对于极深的网络,这一原则可能不足以将强梯度从输出层传递回输入层。接下来,将讨论的 CNN 模型 (DenseNet) 旨在确保强梯度流动,并进一步减少所需的参数数量。

2. DenseNet

ResNet 的跳跃连接将残差块的输入直接连接到其输出。然而,残差块之间的连接依然是顺序的;也就是说,残差块 3 与块 2 直接连接,但与块 1 没有直接连接。
DenseNet 通过密集连接进一步优化了梯度流动和参数效率。在稠密块内部,每个卷积层都与所有后续层直连;在整个网络中,每个稠密块也与其他所有稠密块相连。一个稠密块由两个 3x3 的密集连接卷积层组成。

这种密集连接确保网络中各层都能获取所有前置层的特征信息,从而形成从末层到首层的强梯度流。这种结构反而能减少参数量------由于每层都能接收前面所有层的特征图,所需通道数(深度)可以大幅降低。在传统模型中,增加深度是为了累积早期层的信息,而全网络的 DenseNet 连接不再需要这种方式,因为网络中的每一层都通过密集连接进行交互。
ResNetDenseNet 的一个关键区别是,ResNet 采用跳跃连接将输入与输出相加,而 DenseNet 是在深度维度上将前面所有层的输出与当前层输出拼接。

这可能会引发,关于随着网络层数增加输出大小是否会爆炸增长的问题。为了应对这种积累效应,DenseNet 专门设计了过渡块结构。过渡块由一个 1x1 的卷积层和一个 2x2 的池化层组成,这个模块标准化或重置深度维度的大小,以便这个模块的输出可以传递到后续的稠密块。下图展示了 DenseNet 的架构:

DenseNet 由两类模块构成:稠密块 (dense block) 和过渡块 (transition block)。使用 PyTorch 实现这两类模块:

python 复制代码
class DenseBlock(nn.Module):
    def __init__(self, input_num_planes, rate_inc):
        super(DenseBlock, self).__init__()
        self.batch_norm1 = nn.BatchNorm2d(input_num_planes)
        self.conv_layer1 = nn.Conv2d(in_channels=input_num_planes, out_channels=4*rate_inc, kernel_size=1, bias=False)
        self.batch_norm2 = nn.BatchNorm2d(4*rate_inc)
        self.conv_layer2 = nn.Conv2d(in_channels=4*rate_inc, out_channels=rate_inc, kernel_size=3, padding=1, bias=False)
    def forward(self, inp):
        op = self.conv_layer1(F.relu(self.batch_norm1(inp)))
        op = self.conv_layer2(F.relu(self.batch_norm2(op)))
        op = torch.cat([op,inp], 1)
        return op

class TransBlock(nn.Module):
    def __init__(self, input_num_planes, output_num_planes):
        super(TransBlock, self).__init__()
        self.batch_norm = nn.BatchNorm2d(input_num_planes)
        self.conv_layer = nn.Conv2d(in_channels=input_num_planes, out_channels=output_num_planes, kernel_size=1, bias=False)
    def forward(self, inp):
        op = self.conv_layer(F.relu(self.batch_norm(inp)))
        op = F.avg_pool2d(op, 2)
        return op

通过交替堆叠稠密块与过渡块,并配合输入端的固定 7×7 卷积层和输出端的全连接层,可构建 DenseNet121/161/169/201 等不同深度的变体(数字代表总层数)。PyTorch 提供了所有变体的预训练模型:

python 复制代码
import torchvision.models as models
densenet121 = models.densenet121(weights=models.DenseNet121_Weights.DEFAULT)
denseneti61 = models.densenet161(weights=models.DenseNet161_Weights.DEFAULT)
densenet169 = models.densenet169(weights=models.DenseNet159_Weights.DEFAULT)
densenet201 = models.densenet201(weights=models.DenseNet201_Weights.DEFAULT)

通过组合不同网络的创新点,还发展出 Inception-ResNetResNeXt 等混合架构。下面的图展示了 ResNeXt 架构:

可以看到,ResNeXt 残差块中包含大量并行卷积分支,可视为 ResNetInception 的加宽混合体。

相关链接

PyTorch实战(1)------深度学习概述
PyTorch实战(2)------使用PyTorch构建神经网络
PyTorch实战(3)------PyTorch vs. TensorFlow详解
PyTorch实战(4)------卷积神经网络(Convolutional Neural Network,CNN)
PyTorch实战(5)------深度卷积神经网络
PyTorch实战(6)------模型微调详解
PyTorch实战------GoogLeNet与Inception详解

相关推荐
aqi00几秒前
15天学会AI应用开发(六)使用离线大模型对文本生成摘要
人工智能·python·ai编程
qq_411262422 分钟前
AI-02模组架构与Coze智能体接入说明
人工智能·ai·架构·esp32-c3·coze·四博
果丁智能5 分钟前
民宿/网约房数字化升级:基于智能锁的身份核验与远程授权解决方案
人工智能·智能家居
codecrafter1236 分钟前
sh:在 Python 里直接调系统命令
开发语言·python·其他
知识浅谈8 分钟前
人工智能日报 每日AI新闻(2026年6月12日):Agent安全、AI编程与国内高考场景加速落地
人工智能·安全·ai编程
麦哲思科技任甲林11 分钟前
让AI帮我们写工作日志
人工智能·ai编程·日志
invicinble11 分钟前
对于使用qoder --ai ide相关使用心得
ide·人工智能
前端 贾公子13 分钟前
Claude Code 的 skills 源码解析 (上)
数据库·人工智能
霍格沃兹测试开发学社测试人社区15 分钟前
源码解读:我如何设计一个“可插拔”的测试Skills引擎,支持热加载与隔离执行
人工智能
-山中问答-16 分钟前
【AI智能体工程化实战03】智能体工程化开发环境
人工智能·开发环境·智能体·trae·claude code