)
做计算机视觉的同学没人能绕开 ResNet。2015 年何凯明团队在论文《Deep Residual Learning for Image Recognition》提出深度残差网络一举拿下 ILSVRC2015 图像分类竞赛冠军彻底解决了困扰 CNN 多年的深度网络退化问题让网络层数从几十层突破至百层、千层奠定了后续几乎所有视觉模型ResNeXt、SENet、Transformer 视觉主干的底层设计思想。一、ResNet 诞生前深层 CNN 两大致命难题在残差连接出现前单纯堆叠卷积层会遇到两个无法根治的训练障碍1. 梯度消失 / 梯度爆炸多层链式求导过程中梯度连续相乘会无限趋近于 0 或无穷大。早期依靠正则、权重初始化能轻微缓解但网络深度超过 30 层后依然无法收敛。2. 模型退化Degradation—— 最核心痛点很多人会有一个直觉多加几层网络最差也能学出恒等映射精度不会低于浅层模型。但现实完全相反同等训练条件下56 层网络准确率远低于 20 层网络。根源在于 ReLU 激活函数会持续造成特征信息损耗。每一层经过非线性激活原始图像特征不断丢失网络很难拟合 “输入 输出” 的恒等变换层数越多信息丢失越严重模型性能反向下降。二、ResNet 核心灵魂残差块Residual Block与 Shortcut 跳跃连接1. 残差学习核心思想传统 CNN堆叠卷积层直接学习完整映射 \(H(x)\) ResNet 残差设计让网络只学习残差\(F(x) H(x)-x\)最终输出 \(H(x)F(x)x\)简单说输入 x 不走卷积直接通过 shortcut 短路加到卷积分支输出上。若不需要新增特征网络只需让卷积分支输出\(F(x)≈0\)天然实现恒等映射不会退化若需要提取新特征卷积分支学习差值补充原有信息优化难度大幅降低。2. 两种残差块实现方式1恒等映射 Shortcut输入输出尺寸、通道完全一致适用场景同一阶段内的残差块不做下采样、通道不变 结构流程 输入 X → 3×3 卷积padding1→ BN → ReLU → 3×3 卷积padding1→ BN 分支原始 X 直接跳过两层卷积与卷积输出逐元素相加最后 ReLU 激活 此时输入输出特征图 shape 完全相同无需额外卷积对齐通道。2投影 Shortcut维度不匹配下采样 / 通道翻倍时当进入下一个残差阶段卷积 stride2、通道数翻倍输入 x 和卷积输出通道、尺寸不一致无法直接相加。 解决方案shortcut 分支增加1×1 卷积同步完成下采样与通道升维 输入 X → 1×1 卷积stride2padding0→ BN将 X 转换为和主分支相同尺寸、通道的特征图再进行相加。三、深层网络训练神器Batch NormalizationBN 批归一化BN 是 2015 年 Google 提出、和 ResNet 深度绑定使用的训练技巧如今所有 CNN 标配完美配合残差结构稳定深层网络训练。1. BN 解决的底层问题内部协变量偏移网络训练时前一层参数持续更新导致后一层输入数据分布不断变化模型需要持续适应新分布收敛极慢、极易梯度消失。BN 对每一批次特征统一标准化固定每层输入分布。2. BN 完整计算四步单批次数据\(B\{x_1,x_2...x_m\}\)计算批次均值\(\mu_B \frac{1}{m}\sum_{i1}^m x_i\)计算批次方差\(\sigma_B^2 \frac{1}{m}\sum_{i1}^m (x_i-\mu_B)^2\)标准化归一化\(\hat{x_i} \frac{x_i-\mu_B}{\sqrt{\sigma_B^2\epsilon}}\)ε 极小值防止分母为 0缩放平移BN 精髓可学习参数 γ、β\(y_i\gamma\hat{x_i}\beta\)归一化后数据被约束在标准正态分布若强行固定分布会削弱网络非线性表达能力γ 缩放、β 偏移由网络自主学习恢复特征原始分布兼顾稳定与拟合能力。3. BN 三大核心作用加速模型收敛数据分布稳定可使用更大学习率训练速度提升数倍缓解梯度消失 / 爆炸特征落在激活函数梯度敏感区间梯度回传更顺畅降低权重初始化难度输入特征不受权重缩放影响不用精细调初始权重。四、ResNet 完整网络逐层结构拆解输入 224×224×3 RGB 图像以经典 ResNet 主干网络为例完整流程分为输入预处理层、4 组残差阶段、输出分类头三大部分阶段 1初始卷积 池化层7×7 卷积64 卷积核stride2padding3224×224×3 → 112×112×64后接 BN、ReLU3×3 最大池化stride2padding1112×112×64 → 56×56×64。阶段 2第一组残差块conv2_x通道 64无下采样输入 56×56×64包含多个基础残差块全部使用恒等 shortcut 两层 3×3 卷积 BNReLU输入输出通道、尺寸不变原始特征直接相加输出仍为 56×56×64。阶段 3第二组残差块conv3_x通道 128首次下采样第一个残差块 stride2通道从 64→128尺寸 56×56→28×28 shortcut 分支使用 1×1 卷积做投影映射对齐通道与尺寸后续同组残差块保持恒等映射输出 28×28×128。阶段 4第三组残差块conv4_x通道 256二次下采样首个残差块 stride2通道 128→256尺寸 28×28→14×141×1 卷积调整 shortcut 维度同组其余块恒等连接输出 14×14×256。阶段 5第四组残差块conv5_x通道 512三次下采样首个残差块 stride2通道 256→512尺寸 14×14→7×7投影 shortcut 对齐维度输出 7×7×512。末端分类输出模块全局平均池化 GAP7×7×512 → 1×1×512将空间维度压缩Flatten 展平1×1×512 转为一维 512 特征向量全连接层512 维映射至分类类别数Softmax 输出分类概率。模型代码import torch from torch import nn from torchsummary import summary class Residual(nn.Module): def __init__(self,input_channels,num_channels,use_1conv False,strides1): super(Residual,self).__init__() self.ReLU nn.ReLU() self.c1 nn.Conv2d(in_channelsinput_channels,out_channelsnum_channels,kernel_size3,padding1,stridestrides) self.c2 nn.Conv2d(in_channelsnum_channels,out_channelsnum_channels,kernel_size3,padding1) self.bn1 nn.BatchNorm2d(num_channels) self.bn2 nn.BatchNorm2d(num_channels) if use_1conv: self.c3 nn.Conv2d(in_channelsinput_channels,out_channelsnum_channels,kernel_size1,padding0,stridestrides) else: self.c3 None def forward(self,x): y self.ReLU(self.bn1(self.c1(x))) y self.bn2(self.c2(y)) if self.c3: xself.c3(x) yself.ReLU(yx) return y class ResNet18(nn.Module): def __init__(self,Residual): super(ResNet18,self).__init__() self.b1 nn.Sequential( nn.Conv2d(in_channels1,out_channels64,kernel_size7,stride2,padding3), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(kernel_size3,stride2,padding1) ) self.b2 nn.Sequential( Residual(64,64,use_1convFalse, strides1), Residual(64,64,use_1convFalse, strides1) ) self.b3 nn.Sequential( Residual(64,128,use_1convTrue, strides2), Residual(128, 128, use_1convFalse, strides1) ) self.b4 nn.Sequential( Residual(128, 256, use_1convTrue, strides2), Residual(256, 256, use_1convFalse, strides1) ) self.b5 nn.Sequential( Residual(256, 512, use_1convTrue, strides2), Residual(512, 512, use_1convFalse, strides1) ) self.b6 nn.Sequential( nn.AdaptiveAvgPool2d((1,1)), nn.Flatten(), nn.Linear(512,10) ) def forward(self, x): x self.b1(x) x self.b2(x) x self.b3(x) x self.b4(x) x self.b5(x) x self.b6(x) return x if __name__ __main__: device torch.device(cuda if torch.cuda.is_available() else cpu) model ResNet18(Residual).to(device) print(summary(model,(1,224,224)))五、ResNet 为什么能成为 CV 通用主干核心优势总结彻底解决网络退化shortcut 天然支持恒等映射深层模型性能不会低于浅层梯度高速通道反向传播时梯度可直接通过短路连接回传大幅缓解梯度消失可无限加深网络从 ResNet18/34 浅层到 ResNet50/101/152 超深层适配不同算力场景结构轻量化、易迁移3×3 卷积复用 VGG 简洁设计搭配 BN 训练稳定图像分类、检测、分割、分割、OCR 全部通用残差思想具备普适性Skip Connection 被后续几乎所有深度学习模型吸收是现代网络设计的基础范式。六、总结ResNet 的成功从来不是简单多画一条短路连接线而是残差学习的创新思路搭配BN 批归一化的训练优化双管齐下打破了卷积神经网络的深度枷锁。如今不管是工业落地的图像识别、目标检测还是前沿视觉大模型ResNet 系列依旧是最常用的基础主干。理解残差块的加法逻辑、维度匹配规则、BN 层标准化流程是学习计算机视觉进阶的必经之路。后续我们可以基于 PyTorch 从零搭建 ResNet完成图像分类实战训练。