技术博客

雅可比矩阵与链式法则的矩阵形式:为什么反向传播是"转置左乘"

第 2 / 147 篇 · 2026-08-24 · ai · 矩阵求导链式法则雅可比矩阵

上一篇把布局约定钉死了:标量对矩阵求导,梯度形状等于参数形状。这篇解决下一个问题——复合函数的链式法则在矩阵世界里长什么样。逐分量写链式法则谁都背得出来,但一旦变量是矩阵、中间层是矩阵到矩阵的映射,"怎么乘、乘在哪边"就成了每次推导都要重新心算的坑。把雅可比矩阵的语言建立起来,这个坑就一次填平了。

雅可比矩阵:分子布局的自然选择

设 $f:\mathbb{R}^n\to\mathbb{R}^m$,$y=f(x)$。雅可比矩阵定义为

$$ J_f=\frac{\partial f}{\partial x}\in\mathbb{R}^{m\times n},\qquad [J_f]_{ij}=\frac{\partial y_i}{\partial x_j}. $$

注意这里行索引是输出的分量、列索引是输入的分量——这是分子布局(numerator layout)。这个选法不是随意的,它让链式法则保持"和一元情形一样的乘法顺序"。设 $z=g(y)$,$g:\mathbb{R}^m\to\mathbb{R}^k$,逐分量写:

$$ \frac{\partial z_i}{\partial x_j}=\sum_{\ell=1}^{m}\frac{\partial z_i}{\partial y_\ell}\frac{\partial y_\ell}{\partial x_j}, $$

右边恰好是矩阵乘法 $[J_g J_f]_{ij}$,于是

$$ J_{g\circ f}=J_g\,J_f. $$

复合求导 = 雅可比按复合的逆序相乘,和一元链式法则 $\frac{dz}{dx}=\frac{dz}{dy}\frac{dy}{dx}$ 形态完全一致。这就是分子布局存在的全部理由:乘法顺序不用反。

标量损失的情形:梯度是雅可比的转置左乘

深度学习里链条末端是标量损失 $\ell:\mathbb{R}^m\to\mathbb{R}$。这时 $\partial\ell/\partial y$ 是个 $1\times m$ 的行向量(分子布局),而我们约定的梯度 $\bar y=\nabla_y\ell$ 是列向量(分母布局,形状等于参数形状)。两者互为转置。把链式法则套到标量输出上:

$$ \frac{\partial\ell}{\partial x_j}=\sum_{i=1}^{m}\frac{\partial\ell}{\partial y_i}\frac{\partial y_i}{\partial x_j}=\sum_{i=1}^m \bar y_i\,[J_f]_{ij}=[J_f^\top\bar y]_j, $$

即

$$ \boxed{\;\bar x=J_f^\top\,\bar y\;} $$

上游梯度左乘雅可比的转置——这就是"向量版链式法则表现为雅可比(转置)左乘"的准确陈述。转置是从分子布局回到分母布局必须付的"过路费":雅可比本身按分子布局定义,而梯度按分母布局存放,两者相遇必然夹一次转置。很多教材在这个地方记号混乱,根源就是两种布局混用又不点明。

常见层的雅可比与它的"免实例化"

反向传播里出现的 $J^\top\bar y$ 有个专门名字:向量-雅可比积(VJP, vector-Jacobian product)。关键是没有一层的 VJP 需要先把 $J$ 物化出来。逐类看:

线性层 $y=Wx+b$:$J_f=W$,所以 $\bar x=W^\top\bar y$,$\bar W=\bar y\,x^\top$。$m\times n$ 的雅可比就是权重本身,VJP 是一次矩阵-向量乘。

逐元素激活 $y=\sigma(x)$:$J_f=\mathrm{diag}(\sigma'(x))$,是对角阵。$J^\top\bar y=\sigma'(x)\odot\bar y$,退化成一次逐元素乘。$n$ 维输入的雅可比方阵有 $n^2$ 个元素,但非零的只有 $n$ 个,物化它是纯粹的浪费。

reshape / transpose / flatten:这些是"线性但无参数"的重排,雅可比是一个巨大的 0-1 置换阵。VJP 就是逆向重排——transpose 的反向是反向 transpose,flatten 的反向是 reshape 回去。框架里这类算子的 backward 从来不会真的建置换阵。

广播 $y=x+b$($b$ 被广播到 batch 维):对 $b$ 的雅可比是"复制算子"的转置,即把 $\bar y$ 沿广播维求和归约。这就是为什么广播参数的梯度要 sum 回原始形状——它不是补丁,是雅可比转置的必然结果(大纲后面有专篇展开)。

前向:雅可比按复合逆序右乘;反向:梯度沿 J 的转置逐级左乘回传 x ∈ Rⁿ 参数/输入 y = f(x) J_f ∈ R^{m×n} z = g(y) J_g ∈ R^{k×m} loss ℓ 标量 f g ℓ J_{g∘f} = J_g · J_f (矩阵乘法,顺序与一元链式法则一致) 反向(VJP 链):x̄ = J_fᵀ ȳ, ȳ = J_gᵀ z̄ 每层只算"转置左乘一个向量",雅可比本身从不实例化 z̄ = ∇ℓ

链长之后:乘法顺序决定代价

把两层推广到 $L$ 层:$J=J_LJ_{L-1}\cdots J_1$,损失对标量的梯度是 $\bar x_0=J_1^\top J_2^\top\cdots J_L^\top\bar x_L$。这一串矩阵乘法有两种结合顺序:

  • 从左往右:$\big(J_1^\top\big(J_2^\top\big(\cdots(J_L^\top\bar x_L)\big)\big)\big)$——每一步都是"矩阵×向量",$L$ 次 VJP,总代价与各层前向同阶;
  • 从右往左:先把 $J_L^\top\cdots J_2^\top$ 乘成一个稠密方阵再作用——每步是"矩阵×矩阵",代价爆炸且毫无意义。

反向传播就是第一种结合顺序的别名。这个观察看似 trivial,但它是理解"为什么自动微分要分前向/反向两种模式"的入口:前向模式本质上是带着种子向量从左端往右乘,反向模式是从右端往左乘,当输出是标量时后者的优势是 $n$ 比 $1$ 的数量级。下一篇专门算这笔账。

在 PyTorch 里验证一次

理论说完,用 torch.autograd.functional.vjp 验一下"转置左乘"是不是真的在发生:

import torch

torch.manual_seed(0)
W = torch.randn(4, 3, dtype=torch.float64)
x = torch.randn(3, dtype=torch.float64)
f = lambda x: torch.sin(W @ x)          # R^3 -> R^4,两层复合
v = torch.randn(4, dtype=torch.float64)

# vjp 返回 (前向值, J^T v)
y, jtv = torch.autograd.functional.vjp(f, x, v)
# 显式构造雅可比对照
J = torch.autograd.functional.jacobian(f, x)   # shape (4, 3)
assert torch.allclose(jtv, J.T @ v, atol=1e-12)

跑一下会发现 vjp 的结果和显式构造 $J$ 再转置相乘完全一致,但前者全程没有物化过 $4\times3$ 的雅可比。loss.backward() 做的事情就是把 v 取成全 1 向量(标量损失时 $\bar y=1$)后沿计算图逐级 VJP。平时写的 tensor.backward(gradient=g) 里那个可选的 gradient 参数,就是给非标量输出手动指定 VJP 的种子向量——看懂链式法则的矩阵形式之后,这个 API 的设计动机就一目了然了。

JVP 与 VJP:同一雅可比的两种用法

VJP 是从右端乘 $J^\top$,它的对偶操作 JVP(Jacobian-vector product)是从左端乘 $J$:给定输入方向的扰动 $u$,算出输出方向的变化 $Ju$。$Ju$ 恰好是 $f$ 在 $x$ 处沿 $u$ 的方向导数,一次前向模式的扫描就能拿到——下一篇的主角。PyTorch 里两个原语都有:

import torch
torch.manual_seed(0)
f = lambda x: torch.sin(x[:3] * 2).sum() * x   # R^4 -> R^4
x = torch.randn(4, dtype=torch.float64)
u, v = torch.randn(4, dtype=torch.float64), torch.randn(4, dtype=torch.float64)

_, jvp = torch.autograd.functional.jvp(f, x, u)   # J u,前向模式
_, vjp = torch.autograd.functional.vjp(f, x, v)   # Jᵀ v,反向模式

# 对偶性:⟨J u, v⟩ = ⟨u, Jᵀ v⟩——无需实例化 J 即可验证
assert torch.allclose(jvp @ v, u @ vjp, atol=1e-12)

最后那行断言是伴随算子的定义式 $\langle Ju,v\rangle=\langle u,J^\top v\rangle$:JVP 与 VJP 互为伴随,数值上必然满足这个内积等式。这个等式也是检验自定义层 backward 实现是否正确的零成本工具——不用构造雅可比,随机采几对 $u,v$ 验证内积相等即可(JAX 生态里称为"转置测试",自定义 CUDA kernel 配 backward 时非常值得跑一次)。

收个尾

这篇的核心结论压缩成三句:雅可比按分子布局定义(行=输出、列=输入),链式法则因此保持 $J_{g\circ f}=J_gJ_f$ 的自然顺序;梯度按分母布局存放,回传时必然表现为 $J^\top$ 左乘上游梯度;工程上每层只需实现 VJP 这一个原语,雅可比矩阵永远停留在纸面上。后面推 softmax、LayerNorm、注意力的反向时,每一步都是"写出该层的 $J$(或其转置作用在一个向量上的效果)",这就是统一武器。