梯度下降与优化算法#

沿着最陡方向下山#

想象你在山上迷路了,想要下到山谷:

  • 当前位置:参数当前值 \(\theta_t\)

  • 最陡下降方向:负梯度方向(你脚下最陡的下坡方向)

  • 步长:学习率 \(\eta\)(你每一步迈多大)

  • 目标:到达山谷最低点(损失 \(L\) 最小)

神经网络训练转化为优化问题后,我们需要找到损失函数的最小值点。梯度下降(Gradient Descent)就是解决这个问题的迭代算法。

核心思想:在当前位置,沿着梯度下降方向(损失减小最快的方向)迈出一小步。

\[ \theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta) \]

其中 \(\eta\)学习率(步长),\(\nabla_\theta J(\theta)\) 是损失函数对参数的梯度。

Figure made with TikZ

梯度下降:从初始点逐步逼近最小值

学习率的重要性#

学习率的选择至关重要:

学习率

效果

类比

太小

收敛极慢

婴儿学步

合适

快速收敛

正常行走

太大

震荡/发散

大跳,可能跳过山谷

Figure made with TikZ

学习率过大导致震荡


梯度下降的变体#

梯度下降与优化算法中,我们根据反向传播算法计算的梯度更新参数。根据使用多少数据计算梯度,有三种变体:

批量梯度下降(Batch GD)#

使用全部数据计算梯度:

\[ \theta_{t+1} = \theta_t - \eta \cdot \frac{1}{N} \sum_{i=1}^N \nabla_\theta J(\theta; x_i, y_i) \]

特点

  • 梯度估计准确,收敛稳定

  • 每次迭代计算量大,不适合大数据集

随机梯度下降(SGD)#

每次使用一个样本计算梯度:

\[ \theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta; x_i, y_i) \]

特点

  • 计算快,适合在线学习

  • 梯度有噪声,收敛不稳定

  • 噪声可能帮助跳出局部最优

小批量梯度下降(Mini-batch GD)#

使用一小批样本(通常32-256个)计算梯度:

\[ \theta_{t+1} = \theta_t - \eta \cdot \frac{1}{m} \sum_{i=1}^m \nabla_\theta J(\theta; x_i, y_i) \]

特点

  • 平衡计算效率和稳定性

  • 可以利用GPU并行计算

  • 深度学习中最常用


高级优化算法#

Loss Landscape:损失函数塑造的优化地形#

Loss Landscape(损失景观)描述了损失函数在参数空间中的形状。想象一个高维的山地地形:山谷代表损失低的区域,山峰代表损失高的区域。

在深度学习中,这个景观通常是非凸的,包含:

  • 局部最优:局部山谷(浅坑)

  • 全局最优:最深的山谷(最优解)

  • 鞍点:马鞍形状的点(高维空间中非常常见)

Figure made with TikZ

Loss Landscape:局部最优 vs 全局最优

关键观察

  • 全局最优比局部最优明显更深(红色点远低于橙色点)

  • 两个最优之间由鞍点(紫色)分隔

  • 优化算法需要"翻过"鞍点才能从局部最优到达全局最优

鞍点的详细展示

鞍点像一个马鞍——沿一个方向是极小值,沿另一个方向是极大值:

Figure made with TikZ

鞍点:马鞍形状

深度学习的特殊情况

  • 高维空间中,鞍点比局部最优更常见

  • 随机梯度下降的噪声有助于跳出局部最优和鞍点

  • 现代网络通常能找到足够好的解,即使不是全局最优

动量法:积累速度翻越障碍#

直觉:想象滚雪球下山,球会积累动量,越滚越快,同时可以滚过小的坑洼。在 loss landscape 中,动量帮助我们冲过鞍点和平坦区域。

原理:引入速度变量,累积历史梯度:

\[\begin{split} \begin{aligned} v_{t+1} &= \gamma v_t + \nabla_\theta J(\theta_t) \\ \theta_{t+1} &= \theta_t - \eta \cdot v_{t+1} \end{aligned} \end{split}\]

逐步理解动量法

第一步:理解速度变量 \(v_t\)

在基础 SGD 中,我们直接用梯度更新参数: $\(\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta_t)\)$

这相当于每走一步都完全重新决定方向,没有记忆之前走过的路径。

动量法引入速度变量 \(v_t\),相当于给优化过程添加了"记忆"——记住之前累积的运动趋势。

第二步:解读速度更新公式

\[v_{t+1} = \gamma v_t + \nabla_\theta J(\theta_t)\]

这行公式的含义是:新速度 = 保留的旧速度 + 当前梯度

公式详解

符号

含义

维度

直觉理解

\(v_{t+1}\)

\(t+1\) 步的速度

与参数相同

下一步要迈的"步伐向量"

\(\gamma\)

动量系数

标量 (0~1)

惯性大小,通常 0.9(保留 90% 旧速度)

\(v_t\)

\(t\) 步的速度

与参数相同

之前累积的运动趋势

\(\nabla_\theta J(\theta_t)\)

当前梯度

与参数相同

当前位置最陡的下降方向

核心洞察

  • \(\gamma = 0\)(无动量):\(v_{t+1} = \nabla_\theta J\),退化成基础 SGD

  • \(\gamma = 0.9\)(有动量):新速度 = 90% 旧速度 + 10% 新梯度

第三步:解读参数更新公式

\[\theta_{t+1} = \theta_t - \eta \cdot v_{t+1}\]

与 SGD 的区别:

  • SGD\(\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J\)(只用当前梯度)

  • Momentum\(\theta_{t+1} = \theta_t - \eta \cdot v_{t+1}\)(用累积的速度)

为什么这样能减少震荡?

想象你在峡谷中行走:

  • SGD:每次只看脚下,向左跨一步,发现右边更低,又向右跨一步,在峡谷两侧来回震荡

  • Momentum:你有了惯性,向左走时积累了向左的速度,即使右边略低,惯性也会带你继续向左,直到左边明显比右边高,才会慢慢转向

展开公式看本质

如果我们展开 \(v_t\) 的递归定义:

\[\begin{split} \begin{aligned} v_t &= \gamma v_{t-1} + g_t \\ &= \gamma (\gamma v_{t-2} + g_{t-1}) + g_t \\ &= \gamma^2 v_{t-2} + \gamma g_{t-1} + g_t \\ &= \ldots \\ &= \sum_{i=0}^{t} \gamma^{t-i} g_i \end{aligned} \end{split}\]

其中 \(g_i = \nabla_\theta J(\theta_i)\) 是第 \(i\) 步的梯度。

关键发现:当前速度是历史梯度的加权平均,越早的梯度权重越小(按 \(\gamma^k\) 指数衰减)。

类比

  • 基础 SGD:每走一步都重新决定方向,容易在峡谷两侧来回震荡

  • Momentum:像滚下山坡的球,有惯性,同方向会加速,反方向会减速,自然减少震荡

效果

  • 加速收敛(尤其在梯度方向一致时)

  • 减少震荡(梯度方向变化时动量抵消)

  • 帮助跳出局部最优

Adam(自适应矩估计)#

Adam [KB15] 是目前最常用的优化算法之一,结合了 Momentum 和 RMSprop 的优点。

直觉:为每个参数单独调整学习率。频繁更新的参数用较小学习率,稀疏更新的用较大学习率。

原理:结合一阶矩估计(动量)和二阶矩估计(自适应学习率):

\[\begin{split} \begin{aligned} 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 &= \frac{m_t}{1-\beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1-\beta_2^t} \\ \theta_{t+1} &= \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} \end{aligned} \end{split}\]

逐步理解 Adam

第一步:一阶矩估计( momentum 部分)

\[m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t\]

这与动量法类似,计算梯度的指数移动平均:

符号

含义

维度

直觉理解

\(m_t\)

\(t\) 步的一阶矩(均值)

与参数相同

梯度的"平均趋势",平滑后的梯度方向

\(\beta_1\)

一阶矩衰减率

标量 (0~1)

通常是 0.9,保留 90% 历史信息

\(m_{t-1}\)

上一步的一阶矩

与参数相同

之前累积的梯度趋势

\(g_t\)

当前梯度

与参数相同

当前计算出的梯度值

\((1-\beta_1)\)

新梯度权重

标量

新梯度占 10% 的权重

与动量法的区别

  • 动量法\(v_{t+1} = \gamma v_t + \nabla_\theta J\)(新梯度权重为 1)

  • Adam\(m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t\)(新梯度权重为 \(1-\beta_1\)

Adam 的形式使 \(m_t\) 更接近真正的梯度均值(数学期望)。

展开看本质

\[m_t = (1-\beta_1) \sum_{i=1}^{t} \beta_1^{t-i} g_i\]

这是历史梯度的指数加权平均,越近的梯度权重越大。

第二步:二阶矩估计(自适应学习率部分)

\[v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2\]

注意这里的 \(g_t^2\)逐元素平方(element-wise square):

\[g_t^2 = (g_t^{(1)})^2, (g_t^{(2)})^2, \ldots, (g_t^{(d)})^2\]

符号

含义

维度

直觉理解

\(v_t\)

\(t\) 步的二阶矩(未中心化的方差)

与参数相同

梯度"变化幅度"的历史平均

\(\beta_2\)

二阶矩衰减率

标量 (0~1)

通常是 0.999,比 \(\beta_1\) 更大

\(g_t^2\)

梯度逐元素平方

与参数相同

梯度的幅度信息

核心洞察\(v_t\) 记录了每个参数梯度的"活跃程度":

  • 梯度经常很大 → \(v_t\) 很大

  • 梯度经常很小 → \(v_t\) 很小

为什么用平方?

平方消除了符号(正负),只保留幅度信息。我们想知道的是梯度"有多大",而不是"朝哪个方向"。

第三步:偏差修正(Bias Correction)

\[ \hat{m}_t = \frac{m_t}{1-\beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1-\beta_2^t} \]

为什么需要修正?

问题:初始时 \(m_0 = 0, v_0 = 0\)

前几步的偏差

  • 第 1 步:\(m_1 = (1-\beta_1) g_1\),比真实均值小了约 \((1-\beta_1)\)

  • 第 2 步:\(m_2 = \beta_1(1-\beta_1)g_1 + (1-\beta_1)g_2\),仍有偏差

修正原理

  • 如果 \(m_t\) 估计的是 \(E[g]\),那 \(E[m_t] = (1-\beta_1^t) E[g]\)

  • 除以 \((1-\beta_1^t)\) 就得到了无偏估计

符号

含义

计算方式

\(\hat{m}_t\)

修正后的一阶矩

\(m_t / (1-\beta_1^t)\)

\(\hat{v}_t\)

修正后的二阶矩

\(v_t / (1-\beta_2^t)\)

\(t\)

当前步数

训练迭代次数

修正效果

  • \(t\) 很小(初期):\(1-\beta_1^t\) 很小,除数很小,放大效果明显

  • \(t\) 很大(后期):\(\beta_1^t \approx 0\)\(1-\beta_1^t \approx 1\),几乎不修正

举例\(\beta_1 = 0.9\)):

  • \(t=1\):修正系数 \(1/(1-0.9) = 10\),放大 10 倍

  • \(t=10\):修正系数 \(1/(1-0.9^{10}) \approx 1.5\),放大 1.5 倍

  • \(t=100\):修正系数 \(\approx 1.0\),几乎不修正

第四步:参数更新(自适应学习率的精髓)

\[\theta_{t+1} = \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}\]

逐元素解读

对于第 \(i\) 个参数 \(\theta^{(i)}\)

\[\theta_{t+1}^{(i)} = \theta_t^{(i)} - \eta \cdot \frac{\hat{m}_t^{(i)}}{\sqrt{\hat{v}_t^{(i)}} + \epsilon}\]

符号

含义

作用

\(\hat{m}_t^{(i)}\)

\(i\) 个参数的平滑梯度

决定更新方向

\(\sqrt{\hat{v}_t^{(i)}}\)

\(i\) 个参数的梯度 RMS

决定步长缩放

\(\epsilon\)

数值稳定性常数

防止除以 0,通常 \(10^{-8}\)

\(\eta\)

全局学习率

整体步长控制

核心洞察 - 自适应学习率机制

假设有两个参数:

参数 A(梯度波动大):

  • 历史梯度:[10, -8, 12, -10, 9](大起大落)

  • \(\hat{m}_t \approx 2.6\)(平均梯度)

  • \(\hat{v}_t \approx 100\)(平均平方梯度)

  • \(\sqrt{\hat{v}_t} \approx 10\)

  • 更新步长:\(\eta \cdot 2.6 / 10 = 0.26\eta\)(步长被缩小)

参数 B(梯度波动小):

  • 历史梯度:[0.01, -0.02, 0.015, -0.01, 0.012](小幅波动)

  • \(\hat{m}_t \approx 0.009\)(平均梯度)

  • \(\hat{v}_t \approx 0.0002\)(平均平方梯度)

  • \(\sqrt{\hat{v}_t} \approx 0.014\)

  • 更新步长:\(\eta \cdot 0.009 / 0.014 = 0.64\eta\)(步长相对较大)

结果

  • 梯度波动大的参数 → 步长自动变小(谨慎更新)

  • 梯度波动小的参数 → 步长相对较大(大胆更新)

为什么用 \(\sqrt{\hat{v}_t}\) 而不是 \(\hat{v}_t\)

  • 梯度平方 \(g_t^2\) 的量纲是原梯度的平方

  • 除以 \(\sqrt{\hat{v}_t}\)(RMS,均方根)可以抵消量纲,使更新步长与原梯度同量纲

  • 类比:标准差 \(\sigma = \sqrt{\text{方差}}\),与原始数据同量纲

超参数总结

参数

推荐值

作用

\(\beta_1\)

0.9

一阶矩衰减率,控制 momentum 强度

\(\beta_2\)

0.999

二阶矩衰减率,控制自适应灵敏度

\(\epsilon\)

\(10^{-8}\)

数值稳定性

\(\eta\)

0.001(默认)

全局学习率

特点

  • 结合动量和自适应学习率

  • 对超参数不敏感,默认首选

  • 适合大多数深度学习任务

优化算法选择建议#

算法

适用场景

特点

SGD + Momentum

图像分类、大模型

泛化性能好,需调学习率

Adam

默认首选、NLP、推荐系统

自适应、收敛快、易用

AdamW

Transformer、大模型

改进权重衰减,效果更好


学习率调度:为什么需要调整学习率?#

核心问题#

训练初期,参数远离最优,需要大学习率快速接近目标。 训练后期,参数接近最优,需要小学习率精细调整,避免在最优点附近震荡。

Figure made with TikZ

固定学习率 vs 学习率调度

常见学习率调度策略#

Figure made with TikZ

学习率调度策略对比

1. 步长衰减(Step Decay)#

策略:每N个epoch,学习率乘以衰减系数(如0.1)

适用场景

  • 传统图像分类任务(ImageNet训练标配)

  • 当验证集损失不再下降时手动降低学习率

# 每30个epoch,学习率乘以0.1
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

特点:阶梯式下降,简单有效,但需要预设衰减时机。

2. 指数衰减(Exponential Decay)#

策略:每个epoch学习率按指数衰减

\[\eta_t = \eta_0 \cdot \gamma^t\]

适用场景

  • 需要平滑连续衰减

  • 训练轮数较多时

# 每个epoch学习率乘以0.95
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)

3. 余弦退火(Cosine Annealing)#

策略:学习率按余弦函数从初始值降到接近0

\[\eta_t = \eta_{min} + \frac{1}{2}(\eta_{max} - \eta_{min})(1 + \cos(\frac{t}{T_{max}}\pi))\]

适用场景

  • 现代Transformer模型(BERT、GPT等)

  • 配合Warmup使用效果更佳

scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

4. 预热(Warmup)#

问题:训练初期参数随机初始化,梯度可能很大且不稳定,大学习率会导致震荡。

策略:从很小的学习率开始,线性增加到目标学习率。

Figure made with TikZ

Warmup + Cosine Annealing

# 组合使用:Warmup + Cosine Annealing
scheduler1 = optim.lr_scheduler.LinearLR(
    optimizer, 
    start_factor=0.01,      # 从1%的目标学习率开始
    end_factor=1.0,
    total_iters=5           # 前5个epoch预热
)
scheduler2 = optim.lr_scheduler.CosineAnnealingLR(
    optimizer, 
    T_max=95                # 剩余95个epoch余弦退火
)
scheduler = optim.lr_scheduler.SequentialLR(
    optimizer, 
    schedulers=[scheduler1, scheduler2], 
    milestones=[5]
)

选择建议#

策略

推荐场景

优点

缺点

Step Decay

图像分类、CNN

简单有效

需预设衰减时机

Exponential

长周期训练

平滑连续

衰减过快

Cosine

Transformer、NLP

效果通常最好

相对复杂

Warmup + Cosine

大模型训练

稳定+效果好

需调两个参数


PyTorch实践#

  • 基本训练循环

    import torch
    import torch.nn as nn
    import torch.optim as optim
    
    # 定义模型、损失函数、优化器
    model = MyModel()
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    
    # 训练循环
    for epoch in range(num_epochs):
        for data, target in dataloader:
            # 前向传播
            output = model(data)
            loss = criterion(output, target)
            
            # 反向传播
            optimizer.zero_grad()  # 清空梯度
            loss.backward()        # 计算梯度
            optimizer.step()       # 更新参数
    
  • 学习率调度

    # 组合调度:预热 + 余弦退火
    scheduler1 = optim.lr_scheduler.LinearLR(optimizer, start_factor=0.1, total_iters=5)
    scheduler2 = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=45)
    scheduler = optim.lr_scheduler.SequentialLR(optimizer, [scheduler1, scheduler2], milestones=[5])
    
    for epoch in range(num_epochs):
        train(...)
        scheduler.step()  # 更新学习率
    

训练技巧#

  1. 梯度裁剪

    防止梯度爆炸:

    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    optimizer.step()
    
  2. 批归一化(BatchNorm)

    稳定训练、加速收敛:

    self.bn = nn.BatchNorm1d(num_features)
    
  3. 早停(Early Stopping)

    验证集损失不再下降时停止训练,防止过拟合。


总结#

梯度下降是深度学习训练的基石:

  1. 基本原理:沿反向传播算法计算的负梯度方向迭代更新参数

  2. 学习率:最关键的超参数,过大震荡,过小收敛慢

  3. Mini-batch:平衡效率和稳定性,深度学习标配

  4. Adam:默认首选优化器,自适应、易用

  5. 学习率调度:训练过程中调整学习率能提升效果

理解损失函数和优化算法后,你就掌握了深度学习训练的全流程。接下来我们将通过总结与展望回顾本章内容,然后进入实践章节。


参考文献#

[KB15]

Diederik P Kingma and Jimmy Ba. Adam: a method for stochastic optimization. In International Conference on Learning Representations. 2015.

贡献者与修订历史

查看详细修订记录
  • b20ef3e 2026-04-28 - Heyan Zhu: docs: update pytorch practice section with detailed explanations and code examples
  • 59126f4 2026-04-26 - Heyan Zhu: docs(math-fundamentals): update content structure and add citations
  • ae2053f 2026-04-26 - Heyan Zhu: docs(math-fundamentals): add task-formulations and update related content
  • 756a793 2026-04-25 - Heyan Zhu: docs(math-fundamentals): update content structure and improve explanations