残差网络(ResNet)原理剖析:如何通过跳跃连接解决梯度消失问题

1次阅读
没有评论

共计 1882 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

背景介绍:深度神经网络的梯度消失问题

当神经网络层数加深时,反向传播过程中梯度会随着链式法则逐层相乘。假设每层的梯度平均值为 0.9,经过 50 层后梯度会衰减到 0.9^50≈0.005,导致底层参数几乎无法更新。这种现象在 Sigmoid 激活函数中尤为明显,因为其导数最大值只有 0.25。

传统解决方案如 ReLU 激活函数、批归一化 (BN) 可以缓解但无法根治。2015 年 ImageNet 竞赛中,ResNet 首次让 152 层网络的训练成为可能,核心创新就是跳跃连接(skip connection)。

技术对比:传统 CNN vs ResNet

  • 传统 CNN 的反向传播

    ∂L/∂x = ∂L/∂y * ∂y/∂x 
    y = f(Wx + b)

    梯度必须完整通过非线性变换 f()和权重矩阵 W

  • ResNet 的反向传播

    ∂L/∂x = ∂L/∂y * (∂F/∂x + 1)
    y = F(x) + x

    多出的 +1 项形成梯度高速公路,即使∂F/∂x 趋近 0 时仍有梯度流动

核心原理:数学推导与结构图解

残差函数设计

假设理想映射为 H(x),我们让网络学习残差 F(x) = H(x) – x。当 x 已经接近最优解时,F(x)只需逼近 0 比直接拟合 H(x)更容易:

F(x) = W2σ(W1x + b1) + b2
H(x) = F(x) + x

梯度保留机制

残差网络 (ResNet) 原理剖析:如何通过跳跃连接解决梯度消失问题

前向传播时,原始信号 x 与残差 F(x)相加;反向传播时,梯度可以自由选择通过残差路径或恒等路径,确保至少有一条有效传播通道。

PyTorch 实现基础残差块

import torch
import torch.nn as nn

class BasicBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        # 第一个卷积层
        self.conv1 = nn.Conv2d(in_channels, out_channels, 
                              kernel_size=3, stride=stride, 
                              padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        # 第二个卷积层
        self.conv2 = nn.Conv2d(out_channels, out_channels,
                              kernel_size=3, stride=1,
                              padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        # 跳跃连接处理维度变化
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels,
                         kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        residual = x
        out = nn.ReLU()(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(residual)  # 关键跳跃连接
        return nn.ReLU()(out)

实战建议

深度配置方案

  • ResNet18/34:使用 BasicBlock(两层 3 ×3 卷积)
  • ResNet50+:使用 Bottleneck(1×1 降维→3×3 卷积→1×1 升维)

学习率设置

  • 初始学习率 0.1,每 30 个 epoch 除以 10
  • 配合 Warmup:前 5 个 epoch 线性增加学习率

初始化技巧

  • 卷积层:He 初始化(Kaiming Normal)
  • BN 层:γ=1,β=0
  • 最后一层全连接:较小权重(std=0.01)

延伸思考

Pre-activation ResNet 改进

将 BN 和 ReLU 移到卷积之前:

y = F(x) + x → y = x + F(ReLU(BN(x)))

实验显示这种结构梯度流动更顺畅,在 1000+ 层网络中仍可训练。

小型数据集应用

  • 冻结部分残差块参数
  • 使用更小的初始通道数(如 16→64)
  • 增加 Dropout 层(p=0.2)防止过拟合

自测问题

  1. 为什么残差函数 F(x)=H(x)- x 比直接学习 H(x)更容易?
  2. 当跳跃连接两端的维度不匹配时,ResNet 如何处理?
  3. 假设某残差块的∂F/∂x=0.01,求该层的梯度放大系数是多少?

通过理解这些核心机制,我们可以更灵活地应用 ResNet 结构,在保持模型深度的同时避免梯度消失问题。这种设计思想也启发了后来的 DenseNet、ResNeXt 等网络架构的创新。

正文完
 0
评论(没有评论)