微积分基础:自动微分与梯度检查

本节目标

反向传播给出组织链式法则的方法,自动微分(automatic differentiation, AD)则让程序按实际计算图执行这些局部规则。本节区分符号求导、有限差分和自动微分,解释 JVP/VJP 的形状,并给出一份能定位梯度错误的检查流程。

三种“求导”不能混用

方法做什么主要用途主要限制
符号求导操作公式表达式推导和化简小公式表达式可能膨胀,难覆盖任意程序分支
数值差分扰动输入并比较函数值小规模独立核验有截断/舍入误差,成本随输入维度增长
自动微分对实际基本运算应用链式法则训练和可微程序依赖图、dtype、算子导数与状态语义正确

自动微分不是把 取得很小;它通常在机器精度范围内执行解析局部导数规则。有限差分适合做独立参照,不适合替代大模型反向传播。

Jacobian、JVP 与 VJP

若 ,Jacobian 的 shape 是 。完整存储它可能很昂贵,自动微分常直接计算乘积:

  • 前向模式计算 Jacobian-vector product:;
  • 反向模式计算 vector-Jacobian product:。

标量损失对应 、参数很多,对一个上游标量从输出往输入传播即可得到全部参数梯度,因此反向模式特别合适。PyTorch 官方 autograd 文档将其描述为基于计算历史的反向自动微分系统;具体 API 与稳定性状态会随版本变化。

计算图中的状态

调试框架代码时要分清:

  • 哪些 tensor 需要梯度,哪些是常量;
  • 哪些是叶子节点,梯度保存在哪里;
  • 多次 backward 是否在累积旧梯度;
  • detach、无梯度上下文或转成普通数值是否切断图;
  • 原地修改是否破坏反向需要的中间量;
  • 混合精度、随机算子和并行归约是否引入额外差异。

这些是框架语义,不是微积分公式本身。使用时以当前官方文档和最小复现为准。

中心有限差分

单变量近似:

多参数时,每次只扰动一个坐标,重建数值梯度或 Jacobian。中心差分的截断误差通常比单边差分低,但 太小会发生相减消减。比较应同时使用绝对与相对容差:

其中 是解析/AD 梯度, 是数值梯度。接近 0 时绝对容差重要,数值很大时相对容差更有意义。

可运行的 step 扫描

def function(value: float) -> float:
    return value ** 3 - 2.0 * value
 
 
x = 1.5
analytical = 3.0 * x ** 2 - 2.0
errors = []
for step in (1e-1, 1e-3, 1e-5, 1e-7, 1e-9, 1e-11):
    numerical = (function(x + step) - function(x - step)) / (2.0 * step)
    error = abs(analytical - numerical)
    errors.append(error)
    print(f"step={step:.0e} error={error:.3e}")
 
assert min(errors) < 1e-8

误差通常先随 step 缩小,再受浮点舍入影响。一次碰巧通过不等于实现全对,应检查多个输入、多个方向和边界。

不适合直接 gradcheck 的点

  • ReLU 的 0、绝对值的 0 等不可导点;框架选定的次梯度可能与对称差分不同。
  • 随机 dropout、采样或数据增强未固定随机状态。
  • batch normalization 等依赖 batch 状态的算子处于变化模式。
  • 输入内存重叠,一个坐标扰动同时改变多个逻辑元素。
  • 低精度输入使默认差分 step 与容差不合适。

PyTorch 2.13 官方 gradcheck 文档明确提示:默认参数面向 double precision;低精度、不可导点和重叠内存可能导致检查失败。失败不应被无条件“放宽容差”掩盖。

梯度检查流程

  1. 将问题缩成小 tensor 和确定性标量输出。
  2. 使用高精度,固定随机种子与模型状态。
  3. 避开已知不可导点,或明确框架采用的约定。
  4. 比较解析/AD 与多组 step 的中心差分。
  5. 同时记录绝对误差、相对误差、输入值和梯度 shape。
  6. 若失败,逐层缩小计算图,检查广播、归约因子、符号和共享路径累加。
  7. 修复后保留回归测试,不只在 notebook 手工看一次。

常见错误

  • MSE 求导时漏掉平均分母或因子 2。
  • 广播把一个梯度沿多个元素累加,却忘记对应归约。
  • 共享参数在多路径出现,却覆盖而非相加梯度。
  • 在不可导点比较两个不同但都合理的次梯度约定。
  • 只用一个 step,失败后不断调容差直到通过。
  • 用训练 loss 最终下降来“证明”每个局部梯度实现正确。

练习与自测

  1. 运行 step 扫描,找出误差最小的数量级并解释两侧变化。
  2. 对 手算梯度,再逐坐标做中心差分。
  3. 画出 broadcast -> sum 的计算图,说明反向为何要在广播轴求和。
  4. 为 ReLU 在 x=0 的 gradcheck 失败写一段不误导的排查说明。
  5. 列出一个随机模型做梯度检查前必须冻结的状态。
  • 我能区分符号求导、有限差分和自动微分。
  • 我能写出 JVP/VJP 的 shape 和适用场景。
  • 我会用多组 step、绝对/相对容差检查梯度。
  • 我知道不可导点、低精度、随机状态和重叠内存的风险。

上一节:链式法则与反向传播|下一节:优化与梯度下降。

参考资料

核验日期:2026-07-14。下列 PyTorch 页面对应本次检索到的 2.13 文档;框架接口和稳定性标记可能变化,实际使用前应复查当前官方文档。