BP算法与梯度下降实战:解决神经网络训练中的梯度消失问题

1次阅读
没有评论

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

image.webp

在 ResNet50 的训练过程中,前 3 层的梯度幅度通常会衰减到最后一层的 $10^{-4}$~$10^{-6}$ 倍。这种梯度消失现象导致浅层网络参数更新缓慢,使得深层网络的训练效率大幅降低。实测数据显示,当使用传统 SGD 时,前 3 层参数每轮的更新量仅为最后一层的 0.01%~1%。

一、梯度下降算法技术对比

  1. 传统 SGD 的梯度更新公式
    $$\theta_{t+1} = \theta_t – \eta \cdot \nabla_\theta J(\theta_t)$$
    其中 $\eta$ 为固定学习率,容易在平坦区域陷入局部最优。

  2. Momentum 的改进
    $$v_t = \gamma v_{t-1} + \eta \nabla_\theta J(\theta_t)$$
    $$\theta_{t+1} = \theta_t – v_t$$
    通过引入动量项 $\gamma$(通常取 0.9),可加速峡谷区域的收敛。

  3. Adam 的自适应特性
    $$m_t = \beta_1 m_{t-1} + (1-\beta_1)g_t$$
    $$v_t = \beta_2 v_{t-1} + (1-\beta_2)g_t^2$$
    $$\hat{m}t = m_t/(1-\beta_1^t)$$
    $$\hat{v}_t = v_t/(1-\beta_2^t)$$
    $$\theta
    +\epsilon)$$} = \theta_t – \eta \cdot \hat{m}_t/(\sqrt{\hat{v}_t

二、PyTorch 优化方案实现

# Python 3.8 + torch 1.9
class ClippedAdam(torch.optim.Optimizer):
    def __init__(self, params, lr=1e-3, clip=1.0):
        defaults = dict(lr=lr, clip=clip)
        super().__init__(params, defaults)

    def step(self):
        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None: continue
                # 梯度裁剪
                grad = torch.clamp(p.grad, 
                                 -group['clip'], 
                                 group['clip'])
                # Adam 更新逻辑
                state = self.state[p]
                if len(state) == 0:
                    state['step'] = 0
                    state['exp_avg'] = torch.zeros_like(p)
                    state['exp_avg_sq'] = torch.zeros_like(p)

                exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
                beta1, beta2 = 0.9, 0.999

                state['step'] += 1
                bias_correction1 = 1 - beta1 ** state['step']
                bias_correction2 = 1 - beta2 ** state['step']

                exp_avg.mul_(beta1).add_(grad, alpha=1-beta1)
                exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1-beta2)

                denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(1e-8)
                step_size = group['lr'] / bias_correction1

                p.data.addcdiv_(exp_avg, denom, value=-step_size)

三、学习率动态调整策略

# Cosine 退火学习率
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
    optimizer, 
    T_max=50,  # 总 epoch 数
    eta_min=1e-5  # 最小学习率
)

# 每个 epoch 结束时调用
for epoch in range(50):
    train(...)
    scheduler.step()

四、关键避坑指南

  1. 批量归一化注意事项
  2. BN 层的 scale 参数会放大学习率效果,建议初始学习率降低 10 倍
  3. 冻结 BN 层统计量时(如微调阶段),需关闭 affine 参数更新

  4. 显存不足解决方案

    # 梯度累积(batch_size=128 等效)optimizer.zero_grad()
    for micro_step in range(4):  # 实际 batch_size=32
        data = next(batch_iterator)
        loss = model(data)
        loss.backward()  # 梯度累积
    optimizer.step()

五、CIFAR-10 对比实验

测试环境:RTX 3080, CUDA 11.2

方法 最终准确率 收敛 epoch
Vanilla SGD 72.3% 45
优化方案 76.8% 32

BP 算法与梯度下降实战:解决神经网络训练中的梯度消失问题

思考题

当训练损失出现震荡时,你认为应该优先调整:
1. 学习率(可能导致欠拟合或过拟合)
2. 批量大小(影响梯度方向稳定性)
3. 优化器类型(改变参数更新方式)

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