EECS498 · Deep Learning Revisited · 02
Backpropagation and Computational Graphs
一个 scalar loss 为什么能更新几百万个参数?你在 A2/A3 里已经把答案逐层写过:每个 operation 保存局部信息,反向时把 upstream gradient 乘进来。
30-second recall
30 秒回忆
你实现过 Linear.backward、ReLU.backward、整网 backward,并用 numerical gradient 检查。
-
dout表示什么? - cache 为什么来自 forward?
- 梯度在分支处如何组合?
Why Did We Need This?
Forward 只能告诉我们当前预测有多差;训练还需要回答:每个参数轻微变化时,loss 会怎样变化。直接对每个参数试探一次代价巨大。Backprop 复用计算图,按反向拓扑顺序一次求完。
What You Actually Implemented
A2/two_layer_net.py 手写整条 gradient;A3/fully_connected_networks.py 把它拆成可组合的 Linear、ReLU 以及 cache;eecs598/grad.py 提供数值梯度基线。后者是 assignment helper,不归因为用户实现。
反向传播不是“loss 把答案广播给所有参数”,而是每个局部 operation 收到 ∂L/∂output,再用自己的局部导数产出 ∂L/∂inputs。
Mechanism · vector-Jacobian product
无需显式构造巨大 Jacobian。若 Y=XW+b,upstream 是 dY,局部反向只需矩阵乘法:
dX = dY Wᵀ [N,H]·[H,D] → [N,D]
dW = Xᵀ dY [D,N]·[N,H] → [D,H]
db = Σₙ dY[n] [H]
ReLU 更局部:forward 时哪些位置 x>0,backward 才允许对应 gradient 穿过。
Your code · 2022
A3/fully_connected_networks.py · Linear.backward()
dx = dout.mm(w.t()).view(x.shape)
dw = x.view(x.shape[0], -1).t().mm(dout)
db = dout.sum(axis=0)
Why This Design?
Cache 用内存换计算:forward 保存 x,w,backward 不必重新构造它们。模块化 backward 又让任意深度网络都能由同样的 local contract 组合。
Common misconception:
gradient 不是“参数应该变成什么”,而是当前点最陡的上升方向。优化器还要决定符号、步长、动量和尺度。
From manual backward to autograd
A4 开始用 PyTorch autograd;这不是换了训练原理,只是 framework 在 forward 时动态记录 operation,并调用相同类别的局部 backward。现代 Transformer 的 attention、MLP、LayerNorm 最终仍沿这张图回传。
Concept Checks
若 Y=XW,X=[32,128],W=[128,64],dY 的 shape 是什么?dW 呢?
dY=[32,64],dW=XᵀdY=[128,64]。每个 gradient 与它所描述的变量 shape 相同。
同一个 tensor 同时流入两个后续分支,backward 时怎么办?
来自两条路径的梯度相加,因为总 loss 对该 tensor 的变化是各路径影响之和。
为什么 numerical gradient 适合检查,却不适合训练?
每个参数至少需要额外 forward 扰动;参数量为 P 时成本约放大 O(P),而 backprop 一次图遍历即可。
Mechanism Walkthrough · 一条真实 two-layer graph
X [N,D] → z1=XW1+b1 [N,H] → h=ReLU(z1) [N,H]
→ scores=hW2+b2 [N,C] → L scalar
dL/dscores [N,C]
→ dW2=hᵀ·dscores [H,C]
→ dh=dscores·W2ᵀ [N,H]
→ dz1=dh⊙1[z1>0] [N,H]
→ dW1=Xᵀ·dz1 [D,H]Scalar loss 不只产生一个 gradient。对 graph 上每个 tensor,backward 计算“若它微小变化,L 如何变化”;parameter 有多少元素,就有多少偏导。
- 从
dL/dL=1开始。 - 每个 operation 用 local derivative 变换 upstream gradient。
- activation 流向多个 branches 时,gradient contributions 相加。
- matrix multiply backward 产生对两侧 operands 的 gradients。
- optimizer 只更新 parameters,不更新中间 activations。
Backward 无法判断哪些 units 当时为正;可以重算,但必须恢复完全相同的 activation。cache 是计算便利,不是模型 memory。
若 z1=[-2,0.5,3]、upstream dh=[4,4,4],ReLU backward 得 dz1=[0,4,4]。负 unit 不是 parameter 被删除,而是本次 example 的局部 path 被 gate 掉。
Gradient 不是 parameter 应变成的值,而是 loss 的局部斜率;SGD 还需乘 learning rate 并沿负方向走。
为什么 dW2 shape 与 W2 相同?
每个 W2 元素需要自己的斜率;hᵀ@dscores 消去 batch axis,留下 [H,C]。
Residual 分成两条 paths,backward 汇合时怎么办?
两条 path 对共同 tensor 的 gradients 相加。
Transfer Question
Transformer loss 经 residual 流向 identity 与 attention branch。哪条 chain-rule 规则解释汇合处 gradient?
闭卷解释
- 从 scalar L 解释 dW1 为什么是矩阵。
- 按顺序说出 W2、hidden、ReLU、W1 的 backward。
- 区分 cache 与 parameter。
Connection
Backprop 给斜率;下一课决定沿方向走多远、怎样利用历史 gradients,并诊断训练失败。
Based on your implementation
- EECS498 · A2 · two_layer_net.py
- EECS498 · A3 · fully_connected_networks.py