# 02. 反向传播:雅可比矩阵与 VJP > 核心真相:**autograd 不显式构造完整雅可比矩阵,而是算「向量-雅可比积」(VJP)**。 --- ## 一、雅可比矩阵回顾 对函数 $\mathbf{y} = f(\mathbf{x})$,雅可比矩阵: $$J = \frac{\partial \mathbf{y}}{\partial \mathbf{x}}, \qquad J_{ij} = \frac{\partial y_i}{\partial x_j}$$ --- ## 二、反向传播的真正运算:VJP 上游传来梯度向量 $\mathbf{v} = \dfrac{\partial L}{\partial \mathbf{y}}$,链式法则: $$\frac{\partial L}{\partial \mathbf{x}} = J^\top \mathbf{v} \quad\text{(向量-雅可比积 VJP)}$$ ### 为什么不直接存 J? - 一层 1000→1000,J 是 100 万个数,太大 - 但 loss 是**标量**,上游 $\mathbf{v}$ 是向量 - $J^\top\mathbf{v}$ 只要向量结果,**不用拼出整个 J** → 这就是 backward 高效的根本原因 --- ## 三、全连接层 $y = Wx + b$ 三大梯度公式 设上游梯度 $\mathbf{g} = \dfrac{\partial L}{\partial \mathbf{y}}$: ### ① 对输入 x $$\frac{\partial \mathbf{y}}{\partial \mathbf{x}} = W \;\Rightarrow\; \boxed{\frac{\partial L}{\partial \mathbf{x}} = W^\top \mathbf{g}}$$ ### ② 对权重 W(外积) $$\boxed{\frac{\partial L}{\partial W} = \mathbf{g}\,\mathbf{x}^\top} \quad(\text{shape: out×in})$$ ### ③ 对偏置 b $$\frac{\partial \mathbf{y}}{\partial \mathbf{b}} = I \;\Rightarrow\; \boxed{\frac{\partial L}{\partial \mathbf{b}} = \mathbf{g}}$$ --- ## 四、代码验证(手推 vs autograd 完全一致) ```python W = torch.randn(3, 4, requires_grad=True) b = torch.randn(3, requires_grad=True) x = torch.randn(4, requires_grad=True) y = W @ x + b g = torch.tensor([1.0, 2.0, 3.0]) # 上游梯度 v y.backward(g) # 触发 VJP # 验证 x.grad == W.t() @ g # dL/dx = Wᵀg ✅ True W.grad == torch.outer(g, x) # dL/dW = g⊗x ✅ True b.grad == g # dL/db = g ✅ True ``` 输出三个全 `True`。完整代码:`code/jacobian_vjp_demo.py` --- ## 五、backward() 的参数细节 ```python loss.backward() # 标量,等价于 backward(tensor(1.0)) y.backward(g) # 向量必须传上游梯度 g(即 v) ``` - `loss.backward()` 隐含上游梯度 = 1(因为 loss 是标量) - 对非标量调用必须显式给 `v` --- ## 六、整条链 = 一串 VJP ``` v=1 loss ──────► fcN ──JₙᵀV──► ... ──J₂ᵀv──► fc1 ──J₁ᵀv──► x │ │ dWₙ,dbₙ dW₁,db₁ ← 每层顺手算本层参数梯度 ``` 每个节点只做:**接上游 v → 左乘自己的 Jᵀ → 传给下游**,同时算本层参数梯度。 **全链路没有一处显式构造完整雅可比。** --- by 小小叶 · OpenClaw