直觉理解
想象在浓雾中下山,看不见山脚,只能感觉脚下哪边最陡。梯度下降就是:每一步都朝着最陡的下坡方向走一小步,重复很多次,最终走到谷底附近。「最陡的方向」由梯度给出,「一小步」有多大由学习率决定。
定义与公式
基本形式
参数 沿损失梯度的反方向更新:
其中 是学习率。学习率太大会来回震荡甚至发散,太小则收敛很慢,它通常是最重要的超参数。
批量、随机与小批量
- 批量梯度下降:每步用全部数据算梯度,准确但太慢。
- 随机梯度下降(SGD):每步只用一个样本,快但噪声大。
- 小批量梯度下降:每步用一个批次(如 32、1024 个样本)估计梯度,兼顾效率和稳定性,能充分利用 GPU 的并行能力。现在说「SGD」通常指的就是小批量版本。
梯度中的随机噪声并不全是坏事,它有助于跳出不好的局部区域。
动量
普通 SGD 在狭长的山谷里容易来回震荡。动量法累积历史梯度,像滚下山的小球一样保持惯性:
通常取 0.9。
Adam 与 AdamW
Adam 为每个参数维护两个滑动平均:梯度的一阶矩 (相当于动量)和二阶矩 (梯度平方的平均),用后者给每个参数自适应地缩放步长:
其中 、 是修正了初期偏差后的估计。 按历史梯度尺度归一化更新,但实际步长还取决于一阶矩 ;不能只凭某个参数的梯度大或小判断最终步长。
AdamW 把权重衰减从梯度中解耦出来,直接对参数做衰减,是训练大模型的常见优化器。Adam 通常为每个参数保存 和 两份状态:若状态和参数精度相同,两份状态共占参数存储的两倍;若参数为 BF16、状态为 FP32,则共占四倍。梯度、可能存在的 FP32 主权重和中间激活还需另计,分片或量化优化器又会改变实际占用。
学习率调度
学习率通常不是固定值:训练初期先预热(warmup),从很小的值线性增长,避免一开始就把模型「打坏」;之后按余弦退火等方式逐渐减小,让模型在后期精细收敛。
梯度裁剪
当梯度的范数超过阈值时按比例缩小,防止偶发的超大梯度破坏训练,是训练大模型的标准做法。
代码示例
import torch
# 用梯度下降最小化 f(w) = (w - 3)^2,最优解是 w = 3
w = torch.tensor(0.0, requires_grad=True)
opt = torch.optim.SGD([w], lr=0.1)
for step in range(50):
loss = (w - 3).pow(2)
opt.zero_grad() # 清空上一步的梯度
loss.backward() # 计算梯度
opt.step() # w ← w - lr * grad
print(round(w.item(), 4)) # ≈ 3.0
# 换成 AdamW 只需改一行
w2 = torch.tensor(0.0, requires_grad=True)
opt2 = torch.optim.AdamW([w2], lr=0.1, weight_decay=0.0)
for step in range(200):
loss = (w2 - 3).pow(2)
opt2.zero_grad(); loss.backward(); opt2.step()
print(round(w2.item(), 2)) # ≈ 3.0在 AI 中的作用
- 大多数可微神经网络用梯度下降的某种变体训练,梯度由反向传播计算;这不是神经网络唯一可能的训练方法。
- 大模型训练中优化器、学习率调度与低精度的选择,见优化器、学习率调度与混合精度。
- 优化器状态占用大量显存,这是 ZeRO 等状态分片技术要解决的问题,见分布式训练。
- 训练代码中的标准写法见PyTorch模型训练。