机器学习数学基础 120 章

自动微分、符号微分与数值微分

层级:B|建议先修:03-03、03-06、03-07、03-13

计算导数有三种常被混淆的方法:符号微分操作数学表达式,数值微分用有限差分近似,自动微分则把程序拆成基本运算并精确应用链式法则。深度学习反向传播是反向模式自动微分的特例。

1. 符号微分

符号微分根据代数规则把表达式变成新的导数表达式。例如

f(x)=sin(x2)f(x)=\sin(x^2)

被转换为

f(x)=2xcos(x2).f'(x)=2x\cos(x^2).

优点是得到可读的闭式公式;缺点是可能发生表达式膨胀、难以处理循环和分支等一般程序结构。

2. 数值微分

前向差分:

f(x)f(x+h)f(x)h,f'(x)\approx\frac{f(x+h)-f(x)}{h},

截断误差为 O(h)O(h)。中心差分:

f(x)f(x+h)f(xh)2h,f'(x)\approx\frac{f(x+h)-f(x-h)}{2h},

截断误差为 O(h2)O(h^2)

hh 不能无限小:太大时 Taylor 截断误差大,太小时两个接近函数值相减导致舍入消去。最佳尺度常与机器精度和函数尺度有关。

3. 自动微分的核心

自动微分(AD)把计算写成基本运算序列,每个基本运算都有已知导数,然后机械应用链式法则。除浮点舍入外,它计算的是程序所定义函数的导数,不是有限差分近似。

例如

u=x2,quadv=sinu,quady=ev.u=x^2,quad v=\sin u,quad y=e^v.

AD 沿计算图传播 du/dxdu/dxdv/dudv/dudy/dvdy/dv 并组合。

4. 前向模式

前向模式同时传播原值和切向量。对输入方向 x˙\dot x,每个节点传播

v˙=vuu˙+cdots.\dot v=\frac{\partial v}{\partial u}\dot u+cdots.

最终得到 Jacobian–vector product(JVP):

Jf(x)v.J_f(x)v.

若输入维数很小、输出维数很大,逐输入方向做前向模式较合适。

5. 对偶数直觉

前向 AD 可用对偶数表达:

x+εx˙,qquadε2=0.x+\varepsilon\dot x,qquad\varepsilon^2=0.

代入函数并按代数规则运算:

f(x+εx˙)=f(x)+εf(x)x˙.f(x+\varepsilon\dot x) =f(x)+\varepsilon f'(x)\dot x.

ε\varepsilon 系数自动携带方向导数。

6. 反向模式

反向模式先前向计算并记录中间值,再从标量输出反向传播伴随量

vˉ=yv.\bar v=\frac{\partial y}{\partial v}.

每个节点把上游梯度乘以局部导数并累加到父节点。它高效计算 vector–Jacobian product(VJP):

vTJf(x).v^TJ_f(x).

当输出是一个标量损失、输入是数百万参数时,一次反向传播即可得到全部参数梯度,所以机器学习主要使用反向模式。

7. 计算与内存代价

反向模式的运算时间通常是前向计算的常数倍,但需要保存中间激活供反向使用,内存可能很大。

梯度检查点用额外重算换内存:前向只保留部分节点,反向需要时重新计算中间值。

8. 动态图与静态图

  • 动态图按程序实际执行路径即时构建,调试灵活;
  • 静态图先捕获或编译完整计算,再做融合、内存和并行优化。

现代框架往往同时支持即时执行和编译模式。无论实现方式,AD 对实际执行的运算路径求导。

9. 分支、循环与不可导点

程序含分支时,AD 通常对本次执行的分支求导,不会对离散分支选择本身求导。循环按实际展开的运算应用链式法则。

ReLU 在 0 不可导,框架会选定一个次梯度约定,常取 0。排序、argmax、采样离散变量等运算可能没有普通梯度,需要替代松弛、估计器或其他优化方法。

10. 停止梯度与原地修改

stop_gradient/detach 让某个值参与前向计算但不让梯度穿过。它会改变优化目标的导数语义,应有明确理由。

原地修改可能覆盖反向传播需要的中间值或破坏版本追踪,框架常直接报错。写自定义算子时要保存正确上下文。

11. 高阶导数

对梯度再次使用 AD 可计算 Hessian 或 Hessian–vector product(HVP)。完整 Hessian 需要 O(d2)O(d^2) 存储,而 HVP 可在接近若干次梯度计算的代价下得到

H(x)v,H(x)v,

适用于 Newton-CG、曲率分析和隐式求导。

12. 复数步微分

对解析函数,可用

f(x)Imf(x+ih)hf'(x)\approx\frac{\operatorname{Im}f(x+ih)}{h}

避免实数差分的相减消去,允许很小的 hh。但它要求实现支持复数且函数路径保持解析,不适用于含绝对值、分支等一般情形。

13. 梯度检查

即便使用 AD,自定义算子和损失仍可能写错。可在小模型、float64、远离不可导点的位置,用中心差分检查:

gADgnummax(1,gAD,gnum)\frac{\|g_{AD}-g_{num}\|} {\max(1,\|g_{AD}\|,\|g_{num}\|)}

是否足够小。随机抽查若干方向的方向导数比逐参数检查更省成本。

14. 三者比较

方法 结果 适用场景 主要局限
符号微分 解析表达式 公式推导、简化 表达式膨胀、程序控制流困难
数值微分 近似值 验证、小维黑盒 误差折中、维数成本高
自动微分 链式法则下的浮点导数 大规模可微程序 内存、不可导运算、自定义规则

15. 易错点

  1. 自动微分不是符号化简,也不是有限差分。
  2. 梯度为 0 可能来自模型饱和、detach 或不可导约定,不一定是 AD 出错。
  3. 对随机采样路径直接反传需要可重参数化或合适梯度估计器。
  4. 数值梯度检查在 float32、小步长或不可导点附近可能误报。

常见问答

Q1:为什么反向传播一次能求所有参数梯度?
标量损失对每个中间节点只有一个伴随量;反向按图累积它对所有父节点的贡献,共享子计算不会被为每个参数重复展开。

Q2:AD 的梯度是“精确”的吗?
它精确执行链式法则,但仍受浮点舍入影响,且只对程序实际定义和执行的函数求导。

Q3:什么时候前向模式更好?
输入方向很少、输出很多,或只需要 JVP 时;反向模式适合多输入、标量输出。

Q4:为什么梯度检查要用 float64?
有限差分需要在截断误差与舍入误差之间取小步长,更高精度扩大了可用区间。

练习

  1. y=esin(x2)y=e^{\sin(x^2)} 写出计算图及反向链式导数。
  2. 比较前向差分与中心差分的截断误差阶。
  3. 说明标量损失、大量参数时反向模式更高效的原因。
  4. 为什么 argmax 通常不能直接用于梯度训练?

答案与提示

  1. 依次令 u=x2,v=sinu,y=evu=x^2,v=\sin u,y=e^v,导数为 evcosu2xe^v\cos u\cdot2x
  2. 分别为 O(h)O(h)O(h2)O(h^2)
  3. 反向一次得到输出对所有输入的 VJP;前向模式通常需对每个输入方向传播一次。
  4. argmax 的输出在大多数区域对输入保持常数,边界处不连续,普通梯度几乎处处为 0 或不存在。