自动微分非常方便,但当梯度异常时,知道它如何计算会让调试更直接。先用一个共享节点的表达式,观察“反向”和“累加”这两个关键动作。
一张很小的计算图
令 u = a×b+a,z = u²。a 在第一项乘法和第二项加法里都出现,因此它通过两条路径影响 z。若 a=2、b=3,则 u=8、z=64。
从末端开始,dz/du=2u=16。再有 du/da=b+1=4,du/db=a=2。所以 dz/da=64,dz/db=32。对 a 的两条贡献分别是 16×b 和 16×1,需要相加;只取其中一条会漏掉依赖。
用有限差分做独立核对
中心差分用 (f(x+h)−f(x−h))/(2h) 近似导数。它没有解析推导那么精确,但可用来检查实现。步长太大带来截断误差,太小则容易受到浮点误差影响。
def f(a, b):
return (a * b + a) ** 2
a, b, h = 2.0, 3.0, 1e-5
da = (f(a + h, b) - f(a - h, b)) / (2 * h)
db = (f(a, b + h) - f(a, b - h)) / (2 * h)
print(da, db) # 接近 64 和 32
接着用 PyTorch 的 autograd 再算一次,形成手算、数值方法和自动微分三方对照。对照应使用相同输入与精度,并检查绝对误差和相对误差。
广播为什么需要反向求和
若偏置 b 被广播到一个批次的每个样本,它在前向计算中被重复使用。反向时,每个样本对偏置产生的贡献都要回到同一个 b,因此需要沿广播的维度求和。
矩阵乘法也遵循同一原则。对 Y=XW,给定上游梯度 G,有 dL/dX=G×Wᵀ、dL/dW=Xᵀ×G。先核对形状,可以避免把转置位置写错。实际实现还要考虑更高维批次与广播规则。
学会检查,不必长期手写所有梯度
这节练习不是要求以后放弃 autograd,而是建立一个独立检查工具。优先给小尺寸、少元素、无随机 dropout 的函数做梯度对照,再扩大计算。
视频里的手写反向传播会把这些规则应用到 MLP。完成后,整理“共享节点要累加、广播要归并、形状要匹配”三个检查点。下一课转向文本表示,随后这些向量和梯度会一起进入注意力层。