LESSON 1.3 · 代码 · 120 分钟

让梯度沿计算图倒着走

反向传播是从标量损失出发,沿计算图反向计算向量—雅可比积。一个节点只有在所有下游路径的梯度贡献都到齐后,才能把完整的上游梯度继续传给父节点,所以需要先拓扑排序、再逆序执行。若同一变量被多条路径使用,总导数等于各路径贡献之和,因此梯度必须累加而不能覆盖。

DIRECT ANSWER · VERIFIED SOURCES ·

反向传播为什么要按逆拓扑顺序,并且用 += 累加梯度?

反向传播是从标量损失出发,沿计算图反向计算向量—雅可比积。一个节点只有在所有下游路径的梯度贡献都到齐后,才能把完整的上游梯度继续传给父节点,所以需要先拓扑排序、再逆序执行。若同一变量被多条路径使用,总导数等于各路径贡献之和,因此梯度必须累加而不能覆盖。

关键结论

  • 前向传播记录数值与依赖关系;反向传播只执行每个运算的局部导数规则。
  • 输出节点的梯度设为1,因为损失对自身的导数是1。
  • 共享参数、分支与重复使用都会产生多条梯度路径,必须求和。

边界与常见误解

拓扑顺序保证依赖正确,但不能保证导数实现正确;仍需用中心差分和可信框架对拍。有限差分步长过大或过小都会造成误判。

一手来源

学完你应该能够

  1. 能解释 reverse-mode 为什么从标量输出沿逆拓扑顺序传播。
  2. 能为 add、mul、tanh 写局部反向规则。
  3. 能说明共享节点的梯度为何必须累加而不能覆盖。
  4. 能用有限差分和共享节点测试验证实现。

核心概念

动态 DAG

每次运算创建一个新 Value,并记录父节点和局部反向函数。图由真实执行路径生成,因此分支和重复使用会直接改变依赖关系。

局部反向规则

add 把上游梯度原样传给两个输入;mul 把上游梯度分别乘以另一个输入的前向值;tanh 乘以 1-t²。每条规则只负责一条局部边。

拓扑依赖

节点只有在所有下游贡献都到齐后才能继续向父节点传播。先通过 DFS 建立父节点在前的拓扑序,再逆序执行 _backward。

梯度累加

同一个节点可能经多条路径影响 loss。链式法则要求把各路径贡献相加;使用赋值号会静默丢掉先到的贡献。

标量种子

对最终标量 y 求 dy/dy=1,因此从输出 grad=1 开始。若输出不是标量,需要显式提供向量—雅可比积的上游向量。

实践任务

实现可测试的 Value、运算符与 backward

  • 实现 Value(data, parents, op),并让 add、mul、tanh 返回新节点。
  • 为每个运算写只处理局部贡献的 _backward,所有父梯度使用 +=。
  • DFS 建立拓扑序,从输出 grad=1 开始逆序执行。
  • 用 y=x*x+x、中心差分和 PyTorch 标量结果做三重对拍。

进入完整互动课程