Skip to content

深度学习中的矩阵求导基础 ​


本文根据合集·深度学习中的数学 by 齐宪标系列视频整理。

阅读本文只需要一元函数微积分(导数与链式法则)和线性代数(矩阵乘法与转置)的基础。

在本文中,用小写字母表示标量,用粗体小写字母表示向量,用粗体大写字母表示矩阵;1 表示元素全为 1 的列向量,I 表示单位矩阵,∘ 表示逐元素相乘(Hadamard 积)。

写在前面 ​

训练神经网络,本质上是在不断微调网络中的海量参数,让损失函数尽可能小。现代神经网络的参数动辄百万甚至上千亿个,而指挥这场大规模微调的指挥棒,就是梯度:它告诉每一个参数应该往哪个方向调、调多快。

因此,如何对向量和矩阵求导,就成了理解深度学习训练过程绕不开的一关。本文从读者最熟悉的一元函数导数出发,循序渐进地完成三次推广:

  1. 一元函数 → 多元函数:偏导数、梯度、方向导数;
  2. 标量函数 → 向量函数:Jacobian 矩阵、Hessian 矩阵;
  3. 一元链式法则 → 矩阵链式法则:分子布局与分母布局,进而理解神经网络的反向传播。

在此基础上,本文再备齐常见网络层(全连接、激活、卷积、归一化)的导数积木,并把 Feed Forward 网络与自注意力块组装成端到端的反向传播。至于「为什么对输入求导」「梯度如何在层间传递」这些深入的问题,后文会在合适的地方娓娓道来。

从一元导数到偏导数 ​

回顾:一元函数的导数 ​

在高等数学和数学分析的课程中,我们知道一元函数 y=f(x) 的导数定义为

dydx=limh→0f(x+h)−f(x)h

它的几何意义是函数图像在某点处切线的斜率,物理意义则是瞬时变化率。例如 f(x)=x2,有

dfdx=limh→0(x+h)2−x2h=limh→0(2x+h)=2x

偏导数:固定其他变量 ​

多元函数的输入不止一个,例如 y=f(x1,x2,…,xn)。想求它的导数,最自然的想法是:每次只让一个输入变化,把其余输入全部固定,这样多元函数就退化成了熟悉的一元函数。这样得到的导数称为偏导数,记作

∂y∂xi=limh→0f(x1,…,xi+h,…,xn)−f(x1,…,xi,…,xn)h

计算时,只需把其他自变量当作常数,按一元函数求导即可。

举一个贯穿全文的例子:

f(x1,x2)=x12+3x1x2

把 x2 当作常数,得 ∂f∂x1=2x1+3x2;把 x1 当作常数,得 ∂f∂x2=3x1。在点 (1,2) 处,两个偏导数分别为 8 和 3。

几何上,曲面 z=f(x1,x2) 被平面 x2=2 截出一条曲线,偏导数 ∂f∂x1 就是这条曲线在 x1 方向上的斜率。

梯度与方向导数 ​

给定一个多元函数

y=f(x)=f(x1,x2,…,xn)(x∈Rn)

其梯度 ∇f(x) 定义为把 y 对 x 的所有偏导数按顺序排成的一列:

∇f(x)=[∂y∂x1∂y∂x2⋮∂y∂xn]

对于多元函数而言,其梯度是一个和 x 同样维度的向量。沿用上面的例子:

∇f(x)=[2x1+3x23x1],∇f(1,2)=[83]

而方向导数是指函数在某一点处,沿某一给定方向 v 的变化率,是一个标量。它其实就是把一元导数的定义原封不动地搬到「沿 v 方向的直线」上:

Dvf(x)=limt→0f(x+tv)−f(x)t

当 f 可微时,令 φ(t)=f(x+tv),这是一元函数,用一元链式法则对 t 求导即得

Dvf(x)=∑i=1n∂y∂xivi=∇f(x)⊤⋅v

特别地,当 v 取第 i 个坐标轴方向的单位向量时,方向导数恰好就是第 i 个偏导数——偏导数只是方向导数的特例。

梯度下降:梯度的用武之地 ​

方向导数公式 Dvf=‖∇f‖‖v‖cos⁡θ(θ 是 v 与梯度的夹角)告诉我们:当 v 是单位向量时,

  • θ=0,即 v 与梯度同向时,函数上升最快;
  • θ=π,即 v 与梯度反向时,函数下降最快。

所以,梯度方向是函数值上升最快的方向,负梯度方向是下降最快的方向。想让损失函数变小,就应该沿着负梯度方向更新参数:

x←x−η∇f(x)

这就是梯度下降法,其中 η 称为学习率。深度学习的训练,自始至终都在重复这一行公式。

式中的 x 是一个泛指的「变量」:在纯数学里它是函数的自变量,而在深度学习的训练场景中,它代表的是被优化的参数整体(所有权重矩阵、偏置拼接在一起),而不是网络的输入——数据是给定的,训练中保持不变,改变的只有参数。

Jacobian 矩阵 ​

前面讨论的都是「多个输入、一个输出」的标量函数。但在神经网络中,一层往往同时输出多个数,例如 y=Wx。这类定义域和值域都是向量的函数称为向量函数:

y=f(x)=[f1(x)f2(x)⋮fm(x)](x∈Rn,y∈Rm)

向量函数的每一个分量 fi 都是多元标量函数,都有自己的梯度。把所有梯度组合成一个矩阵,称为 Jacobian 矩阵,记为 J(x)。它定义为

J(x)=[∂y1∂x1∂y1∂x2⋯∂y1∂xn∂y2∂x1∂y2∂x2⋯∂y2∂xn⋮⋮⋱⋮∂ym∂x1∂ym∂x2⋯∂ym∂xn]=[∇f1(x)∇f2(x)⋯∇fm(x)]⊤

是一个 m×n 的矩阵,其中 m 是输出的维度,n 是输入的维度。每一行恰好是 y 的一个分量对 x 的梯度。

举一个具体的例子:y=[x12+x23x1x2],则

J(x)=[2x113x23x1]

WARNING

注意:导数的「形状」取决于约定

Jacobian 矩阵的维度是 m×n,而不是 n×m。向量函数的求导运算是一种从函数到函数的映射,用形式化的语言表达就是

J:(Rn→Rm)⟶Rm×n

在文献中存在两种通行的记录方式:

  • 分子布局:∂y∂x 就是 Jacobian 矩阵(m×n);
  • 分母布局:∂y∂x 取其转置(n×m),此时标量函数的导数恰好是与自变量同形状的列向量(梯度)。

本文约定:向量函数的导数按 Jacobian 矩阵(分子布局)记录;而标量函数对向量的导数按惯例写成列向量,即梯度(分母布局)。后文将看到,深度学习习惯使用分母布局来做链式法则,此时对 Jacobian 取转置即可。

Hessian 矩阵 ​

给定一个多元函数

y=f(x)=f(x1,x2,…,xn)(x∈Rn)

其 Hessian 矩阵定义为

H(x)=∇2f(x)=∂∂x(∂y∂x)=[∂2y∂x12∂2y∂x1∂x2⋯∂2y∂x1∂xn∂2y∂x2∂x1∂2y∂x22⋯∂2y∂x2∂xn⋮⋮⋱⋮∂2y∂xn∂x1∂2y∂xn∂x2⋯∂2y∂xn2]

Hessian 矩阵收集了所有的二阶偏导数,它是「梯度的导数」:一元情形下对应 f″(x),描述的是函数的弯曲程度(凹凸性)。当二阶偏导数连续时,求导次序可以交换(Schwarz 定理),因此 H 是对称矩阵。例如对前文的 f(x1,x2)=x12+3x1x2,有

H=[2330]

在深度学习优化中,Hessian 矩阵描述了损失曲面在各点的曲率,是牛顿法等二阶优化方法的基础。不过由于它的元素个数是 n2,面对上亿参数的网络时存储和计算都不可行,实践中更多是「借用了它的思想」。

导数的链式求导法则 ​

回顾:一元链式法则 ​

对于一元复合函数,例如 f(x)=(2x+1)3,令中间变量 u=2x+1,则

dfdx=dfdu⋅dudx=3(2x+1)2⋅2

链式法则说的是:复合函数的导数等于各层函数导数的乘积。对于多层复合

f(x)=fn∘fn−1∘⋯∘f2∘f1(x)=fn(fn−1(⋯f2(f1(x))))

其链式求导法则为

df(x)dx=df(x)dfn(x)⋅dfn(x)dfn−1(x)⋯df2(x)df1(x)⋅df1(x)dx

一元情形下,乘法交换律保证了这些因子随便怎么排都行。但如果自变量是一个向量或矩阵,因子变成了矩阵乘积,而矩阵乘法不满足交换律,于是「从哪头开始乘」就有了讲究——这就引出了分子表达式和分母表达式两种形式。

分子表达式和分母表达式 ​

这里的分子和分母指的是原微商式 df(x)dx 中的分子和分母。设各层函数的维度依次为 n0→n1→⋯→nn(fk:Rnk−1→Rnk,f=fn)。顾名思义,分子/分母表达式的含义就是先写出含分子/分母的那一项:

(1)df(x)dx=df(x)dfn(x)⋅dfn(x)dfn−1(x)⋯df2(x)df1(x)⋅df1(x)dx(2)=df1(x)dx⋅df2(x)df1(x)⋯dfn(x)dfn−1(x)⋅df(x)dfn(x)

在分子布局下,dfk(x)dfk−1(x) 是 nk×nk−1 的 Jacobian 矩阵,(1) 式各因子从左到右依次为 1×nn, nn×nn−1, …,乘起来恰好是 1×n0;在分母布局下,各因子取转置,形状反过来,(2) 式才能乘得通。由此可见:

  • 分子表达式是先求外层函数,然后逐层深入求导;
  • 分母表达式是先求内层函数,然后逐层向外求导。

两者只差转置和因子排列顺序,数学上完全等价。但在深度学习中,一般采用分母表达式,原因有二:

  1. 损失函数 L 是标量,分母布局下 dLdx 与 x 同形状(列向量),各层梯度的形状与参数矩阵的形状一致,便于直观理解和按形状更新参数;
  2. 分母表达式的乘法要从右往左进行:先算 dLdfn,再逐层左乘更内层的导数——这恰好是「从损失出发、逆着数据流方向」的计算顺序,这正是「反向传播」名字的由来,也与神经网络逐层的结构天然吻合。稍后我们就会看到这种表达方式的方便之处。

多项式向量函数的导数 ​

多项式向量函数是指,每个因变量 y1,y2,⋯,ym 都是关于自变量 x1,x2,⋯,xn 和常数的多项式函数。本小节主要讨论一次和二次多项式向量函数的导数计算,只需要理解齐次式(不含常数项)的导数计算即可。这两个结论是全连接层、注意力层等很多网络层求导的基石。

一次齐次式 ​

y=Wx(x∈Rn,y∈Rm)∂y∂x=[∂y1∂x1∂y2∂x1⋯∂ym∂x1∂y1∂x2∂y2∂x2⋯∂ym∂x2⋮⋮⋱⋮∂y1∂xn∂y2∂xn⋯∂ym∂xn]=W⊤

注意到,f:Rn→Rm 是一个将向量从 n 维映射到 m 维的向量函数。所以,这里的 W∈Rm×n 恰好是其 Jacobian 矩阵,而分母布局下的导数 ∂y∂x(n×m)是它的转置。

这个结果直观看非常合理:y 的每个分量 yi=∑jwijxj 对 xj 的偏导数就是 wij,把这些数按分母布局摆放,自然得到 W⊤。

INFO

常用线性函数求导公式

∂a⊤x∂x=a∂Wx∂x=W⊤∂Wx∂W=x⊤

第一条是标量函数的情形(分母布局下结果是与 x 同形状的列向量);第三条严格的说法是:向量对矩阵的导数是一个三维张量,深度学习文献中常把每个输出分量 yi 对 W 的导数(一个 x⊤)堆叠起来,简记为 x⊤。等它进入链式法则、对具体的标量损失求导时,会具体化为后文的外积形式。

二次齐次式(二次型) ​

y=x⊤Wx(x∈Rn,y∈R)=[x1x2⋯xn][w11w12⋯w1nw21w22⋯w2n⋮⋮⋱⋮wn1wn2⋯wnn][x1x2⋮xn]=∑i=1n∑j=1nwijxixj∂y∂x=[∂∂x1∑i=1n∑j=1nwijxixj∂∂x2∑i=1n∑j=1nwijxixj⋮∂∂xn∑i=1n∑j=1nwijxixj]=[∂∂x1[w11x12+(w12x1x2+⋯+w1nx1xn)+(w21x2x1+⋯+wn1xnx1)]∂∂x2[w22x22+(w21x2x1+⋯+w2nx2xn)+(w12x1x2+⋯+wn2xnx2)]⋮∂∂xn[wnnxn2+(wn1xnx1+⋯+wn(n−1)xnxn−1)+(w1nx1xn+⋯+w(n−1)nxn−1xn)]]=[2w11x1+(w12x2+⋯+w1nxn)+(w21x2+⋯+wn1xn)2w22x2+(w21x1+⋯+w2nxn)+(w12x1+⋯+wn2xn)⋮2wnnxn+(wn1x1+⋯+wn(n−1)xn−1)+(w1nx1+⋯+w(n−1)nxn−1)]=[∑i=1nwi1xi∑i=1nwi2xi⋮∑i=1nwinxi]+[∑j=1nw1jxj∑j=1nw2jxj⋮∑j=1nwnjxj]=[w11w21⋯wn1w12w22⋯wn2⋮⋮⋱⋮w1nw2n⋯wnn][x1x2⋮xn]+[w11w12⋯w1nw21w22⋯w2n⋮⋮⋱⋮wn1wn2⋯wnn][x1x2⋮xn]=(W⊤+W)x

推导虽然冗长,但每一步都只是「固定其他变量的一元求导」:xk 出现在 xk2 中(贡献 2wkkxk),也出现在所有 xkxj 与 xixk 的交叉项中(各贡献一次)。注意交叉项 wijxixj 和 wjixjxi 是两个不同的项,所以两类贡献不能合并,结果才会同时出现 W 和 W⊤。

特别地,当 W 是对称矩阵时,结果简化为

∂(x⊤Wx)∂x=2Wx

这与一元情形 d(wx2)dx=2wx 完全呼应,可以作为记忆的锚点。

常见神经网络层的导数计算 ​

有了上面的基础工具,现在可以逐一计算深度学习中常见网络层的导数了。请特别留意一种反复出现的模式:每个复杂的层,拆开看都是「线性变换、矩阵乘法、逐元素函数」的组合,于是它们的导数都能用一次齐次式、二次型、矩阵乘法法则和对角 Jacobian 这几块积木拼出来;而 Softmax 这类「不逐元素」的函数,则需要单独推导。

另外提醒一句:本节中出现的「对输入求导」,如前所述都不是要优化输入本身,它们是反向传播让梯度「穿过」这一层的通道;每节里真正要被梯度下降更新的,是各层的可学习参数(权重矩阵、卷积核、γ,β 等)。

全连接层 ​

一层全连接层的函数表达式为

y=Wx+b(x∈Rn,y∈Rm)

它是一个典型的一次多项式向量函数(外加一个平移),其中 b 与 x 无关,求导时直接消失,因此

dydx=W⊤

于是这一层出现了两个方向的导数,用途截然不同:dydx=W⊤ 不是要拿去调整输入 x,而是链式法则中把梯度继续传给更早一层的通道;W 才是被梯度下降更新的可学习参数,它的导数才是参数更新的直接依据。逐分量来看,yi=∑jwijxj+bi,所以

∂yi∂wij=xj⟹dydW=x⊤

严格地说,y 对 W 的导数是一个三维张量(每个 yi 对应一个 x⊤);深度学习文献中习惯简记为 x⊤。当链式法则的另一端是标量损失时(下一节),它会具体化为外积 ∂L∂yx⊤,形状与 W 相同。

激活函数 ​

以 ReLU 为例,其函数表达式为

ReLU(x)=max(0,x)

其导函数为

ddxReLU(x)={1x>00x⩽0

神经网络中的激活函数是逐元素作用的,即 y=ReLU(x) 表示 yi=ReLU(xi)。每个输出分量只依赖于同下标的输入分量,所以 Jacobian 矩阵的非对角线元素全为 0:

J(x)=diag(x>0)

它是一个对角线上的元素可能为 0 或 1,而其他元素均为 0 的矩阵。

INFO

对角矩阵

diag([x1x2⋮xn])=[x10⋯00x2⋯0⋮⋮⋱⋮00⋯xn]

事实上,任何逐元素函数的 Jacobian 都是对角矩阵,对角线上就是各分量的一元导数。例如常用的 Sigmoid 函数 σ(x)=11+e−x 满足 σ′(x)=σ(x)(1−σ(x)),于是 y=σ(x) 的 Jacobian 为 diag(σ(x)∘(1−σ(x)))。这也是反向传播中出现大量「逐元素乘法」的根源。

矩阵乘法的导数 ​

矩阵乘法是神经网络里的万金油:全连接层的 y=Wx、注意力中的缩放点积 S=QK⊤dk,本质上都是它。设

Y=AB(A∈Rn×p, B∈Rp×m, Y∈Rn×m)

矩阵乘法是双变量函数,两个输入各有一条梯度通道。逐分量展开 Yij=∑kAikBkj,设上游梯度 G=∂L∂Y 已知,用一元链式法则对每个分量求导:

∂L∂Aik=∑j∂L∂YijBkj⟹∂L∂A=GB⊤,∂L∂Bkj=∑i∂L∂YijAik⟹∂L∂B=A⊤G

两条通道的形状分别与 A、B 相同,互为镜像。事实上,一次齐次式正是这里的特例:在 y=Wx 中把 W 看成常参数、x 看成变量,对 x 的通道给出 W⊤,对 W 的通道给出外积 ∂L∂yx⊤。

注意力中的缩放点积 S=QK⊤dk 只是给矩阵乘法多乘了一个标量 1dk,套用上式立得

∂L∂Q=1dk∂L∂SK,∂L∂K=1dk(∂L∂S)⊤Q

Softmax 的导数 ​

ReLU、Sigmoid 这类逐元素函数的 Jacobian 是对角阵,但 softmax 不是逐元素函数——它的分母 ∑kesk 让每一个输出都依赖所有输入,Jacobian 是一个稠密矩阵。先看向量情形:

ai=esi∑kesk

对它用一元商法则,可以算出

∂ai∂sj=ai(δij−aj)

其中 δij 是 Kronecker 记号(i=j 时取 1,否则取 0)。设上游梯度 gi=∂L∂ai,由链式法则把各分量的贡献加权求和:

∂L∂sj=∑igi∂ai∂sj=gjaj−aj∑igiai=aj(gj−∑igiai)

读作:先算上游梯度按 softmax 权重的加权平均,再从每个分量中把它扣掉。当 softmax 逐行作用于矩阵时(A=softmax(S),A,S 同形状),把上式按行堆起来即得矩阵形式——rowsum 表示把矩阵每行求和、得到一个列向量:

∂L∂S=A∘(∂L∂A−1rowsum(∂L∂A∘A)⊤)

卷积层 ​

深度学习中的卷积(实际上是互相关)也是线性运算。以一维离散卷积为例,设输入 x∈R4、卷积核 w=(w1,w2)⊤、步长为 1 且不填充,则输出为

y=[w1x1+w2x2w1x2+w2x3w1x3+w2x4]

对输入求导:既然是线性运算,就可以套用一次齐次式的结论。写成矩阵形式 y=Wx,其中

W=[w1w2000w1w2000w1w2]

是一个由卷积核平铺而成的带状矩阵,于是 ∂y∂x=W⊤。也就是说,对输入求导等价于用卷积核(翻转后)去卷上游传回来的梯度——这正是「转置卷积」操作的经典来源。

对卷积核求导:关键在于权重共享——同一个 wk 出现在每一个输出分量中,求导时这些贡献要全部累加。设标量损失为 L,由 yi=∑kwkxi+k−1 及一元链式法则:

∂L∂wk=∑i=13∂L∂yixi+k−1例如∂L∂w1=∂L∂y1x1+∂L∂y2x2+∂L∂y3x3

把这个和写完整就会发现:核的梯度等于输入与上游梯度做互相关。总结成一句话:

卷积的梯度仍然是卷积:输入的梯度用卷积核去卷上游梯度,卷积核的梯度用输入去卷上游梯度。

二维卷积只需把求和换成横纵两个方向,结论完全类似。(数学上严格的卷积要求先把核翻转 180∘,深度学习框架实现的是不翻转的互相关,二者只差一次翻转,不影响上述结论。)

归一化层 ​

不论是 BatchNorm 还是 LayerNorm,其函数形式都相同,都是将任何特征分布转化为均值为 0、方差为 1 的特征分布,区别只在于 μ,σ2 沿哪个维度统计:

y=x−μ1σ2+ε∘γ+β(x∈Rn,y∈Rn)

其中,μ=1n∑ixi 与 σ2=1n∑i(xi−μ)2 分别是 x 各分量的均值和方差,ε 是防止除零的小常数;γ,β∈Rn 是两个可学习的参数:γ 对每个维度做逐元素的缩放,β 做逐元素的平移(∘ 表示逐元素相乘,等价于 diag(γ)z+β)。

整个函数是「中心化 → 缩放 → 仿射变换」的复合,我们分三步把它翻译成线性代数的语言,再求 Jacobian。

第一步:中心化。 分子是用线性代数表示为

x−μ1=[x1x2⋮xn]−[1n∑i=1nxi1n∑i=1nxi⋮1n∑i=1nxi]=[10⋯001⋯0⋮⋮⋱⋮00⋯1][x1x2⋮xn]−1n[11⋯111⋯1⋮⋮⋱⋮11⋯1][x1x2⋮xn]=Ix−1n11⊤x=(I−1n11⊤)x

记 P=I−1n11⊤,则 x−μ1=Px。P 有两个极好用的性质:对称(P⊤=P)且幂等(P2=P,展开并用 1⊤1=n 即可验证)。

第二步:方差。 分母的被开方数用线性代数表示为

σ2+ε=1n(x−μ1)⊤(x−μ1)+ε=1n(Px)⊤(Px)+ε=1nx⊤P⊤Px+ε=1nx⊤PPx+ε=1nx⊤Px+ε

记 s=σ2+ε=1nx⊤Px+ε,其结果是一个标量。于是归一化部分可以写成

z=Pxs

第三步:求 Jacobian。 先逐分量考察 z。zi=(xi−μ)s−12,其中 μ 与 s 都通过求和依赖所有的 xj:∂μ∂xj=1n,∂s∂xj=2n(xj−μ)。把其余分量当常数,按一元函数的乘积法则与链式法则求导:

∂zi∂xj=(δij−1n)s−12+(xi−μ)⋅(−12)s−32⋅2n(xj−μ)=δij−1ns−(xi−μ)(xj−μ)ns32

第一项的 δij−1n 排成矩阵恰好就是 P;第二项的外积形式排成矩阵是 (Px)(Px)⊤(因为 Px=x−μ1)。于是

Jz(x)=1sP−1ns32(Px)(Px)⊤

最后,y=diag(γ)z+β。β 与 x 无关,梯度为零;γ 的逐元素缩放作用在输出上,所以左乘 diag(γ):

J(x)=diag(γ)[1sP−1ns32(Px)(Px)⊤]

直觉上,两项各有分工:第一项是「先减均值、再除以标准差」这条主路径的线性缩放;第二项则来自「分母 σ 本身也在随 x 变化」——当 x 沿着自己偏离均值的方向变大时,σ2 随之变大,会把整体的幅度压回去一些,所以表现为一个减去的修正项。

同样地,这里的 Jacobian 是梯度穿过归一化层的通道,而不是用来调整 x 的。本层自己的可学习参数 γ,β 的梯度反而简单得多,逐分量求导即得:

∂L∂γ=∂L∂y∘z,∂L∂β=∂L∂y

深度神经网络的导数计算 ​

到这里积木已经凑齐:线性层、矩阵乘法、Softmax、逐元素函数。这一节把它们组装成完整的深度网络:先用最简单的 Feed Forward 网络走一遍端到端的反向传播,再把整个自注意力块——它由多个运算复合而成、还带有分支,本身就是一个小型深度网络——组装出来。

先约定两个贯穿本节的比喻,把反向传播路径上的节点分成两类:

  • 终点站:被梯度下降直接更新的参数(如 W、b)。梯度抵达它们就算完成了使命,不再继续传递——优化器会拿着这个梯度对参数做一步更新。
  • 换乘站:中间状态(如 g、h 这样的激活值,或输入 x)。梯度在这里不作停留,而是借助链式法则「换乘」下一条支路,继续向更早的层传递;虽然 ∂L∂h 这类梯度也会被算出来,但它们只是过路的通道,不会被用于更新任何东西。

后文示意图中的蓝色节点就是换乘站,橙色节点就是终点站。

计算顺序上,本节沿用「分子表达式和分母表达式」一节的结论:采用分母表达式。原因有二:其一,损失 L 是标量,分母布局下 ∂L∂x 与 x 同形状,于是每个梯度的形状恰好与分母位置的参数或中间状态一一对应,可以按形状校验公式、按形状更新参数;其二,分母表达式的链式因子要从右往左相乘——先算 ∂L∂y,再逐层向内左乘各层导数——这恰好是「从损失出发、逆着数据流回传」的计算顺序,与反向传播的实现流程天然一致。套用到两类节点上:终点站收到的是形如 ∂L∂yh⊤ 的外积,换乘站收到的是形如 W⊤∂L∂y 的回传梯度,两者在分母表达式下都有整齐的形状。

前馈网络 ​

以一个两层前馈网络为例,输入 x 经过一层全连接、ReLU 激活,再经过另一层全连接输出 y。其表达式为

y=WoutReLU(Winx+bin)+bout

将其拆开成 g,h,y 三个函数复合,则整个网络可以写成

g=Winx+binh=ReLU(g)y=Wouth+bout

其中 x∈Rn,g,h∈Rd,y∈Rm。

flowchart TD
    X["$$\mathbf{x}$$(输入)"] --> G["$$\mathbf{g} = \mathbf{W}_{in}\mathbf{x} + \mathbf{b}_{in}$$"] --> H["$$\mathbf{h} = \mathrm{ReLU}(\mathbf{g})$$"] --> Y["$$\mathbf{y} = \mathbf{W}_{out}\mathbf{h} + \mathbf{b}_{out}$$"] --> L["$$L$$(损失)"]

    L -.->|"$$\dfrac{\partial L}{\partial \mathbf{y}} = \mathbf{y} - \mathbf{t}$$"| Y
    Y -.->|"$$\dfrac{\partial L}{\partial \mathbf{W}_{out}} = \dfrac{\partial L}{\partial \mathbf{y}}\mathbf{h}^{\top}$$"| WO["$$\mathbf{W}_{out}$$、$$\mathbf{b}_{out}$$(终点站)"]
    Y -.->|"$$\dfrac{\partial L}{\partial \mathbf{h}} = \mathbf{W}_{out}^{\top}\dfrac{\partial L}{\partial \mathbf{y}}$$"| H
    H -.->|"$$\dfrac{\partial L}{\partial \mathbf{g}} = \mathrm{diag}(\mathbf{g} > \mathbf{0})\dfrac{\partial L}{\partial \mathbf{h}}$$"| G
    G -.->|"$$\dfrac{\partial L}{\partial \mathbf{W}_{in}} = \dfrac{\partial L}{\partial \mathbf{g}}\mathbf{x}^{\top}$$"| WI["$$\mathbf{W}_{in}$$、$$\mathbf{b}_{in}$$(终点站)"]

    classDef terminal fill:#ffe6cc,stroke:#d79b00,color:#333;
    classDef conduit fill:#dae8fc,stroke:#6c8ebf,color:#333;
    class WO,WI terminal;
    class G,H,Y conduit;

上图中,实线是前向传播的数据流,虚线是反向传播的梯度流:蓝色节点是换乘站(中间状态),橙色节点是终点站(被梯度下降更新的参数),各条虚线上的梯度公式将在下文逐步推导。

训练时真正要求导的对象是标量损失 L。设标签为 t,取均方误差 L=12‖y−t‖2(逐分量展开对 y 求导即得),则

∂L∂y=y−t

反向传播从损失出发,按分母表达式逐层向前(从右往左)传递梯度:

① 更靠近输出的参数 Wout,bout。 由 y=Wouth+bout,仿照全连接层一节的逐分量分析:

∂L∂(Wout)ij=∂L∂yihj⟹∂L∂Wout=∂L∂yh⊤,∂L∂bout=∂L∂y

② 继续向内传给 h。 套用一次齐次式的公式 ∂(Wx)∂x=W⊤:

∂L∂h=Wout⊤∂L∂y

③ 穿过激活函数到 g。 ReLU 的 Jacobian 是对角阵,矩阵乘对角阵即逐元素乘:

∂L∂g=diag(g>0)∂L∂h=diag(g>0)Wout⊤∂L∂y

④ 更靠近输入的参数 Win,bin。 与 ① 完全同构:

∂L∂Win=∂L∂gx⊤,∂L∂bin=∂L∂g

注意,沿途算出的 ∂L∂h、∂L∂g 并不是用来更新什么的——输入 x 和中间状态都不是被优化的对象;它们只是链式法则的中转站,负责把梯度继续送往更早的参数。真正被梯度下降使用的,是 ∂L∂Wout、∂L∂bout、∂L∂Win、∂L∂bin 这四个与参数同形状的梯度。

在 DNN 训练过程中,中间状态是需要存储在显存中的。可以认为,这里的 x,g,h,y 在反向传播时都是已知的——事实上,计算 diag(g>0)、外积里的 h 与 x 都依赖前向传播留下的中间值,这正是训练比推理更耗显存的主要原因。

参数矩阵从后向前通过梯度下降算法进行更新,即先更新 Wout,bout,再更新 Win,bin。整个过程只需要一次反向扫描,就能同时得到损失对全部参数的梯度,额外计算量约为前向传播的两倍——这正是深度学习能够训练亿级参数网络的关键。

自注意力 ​

Feed Forward 网络是一条没有分叉的链,而自注意力块(Attention Block)更接近真实的深度网络:多个运算复合而成,输入还有分支。整个块的计算流程是

Q=XWQ,K=XWK,V=XWVS=QK⊤dk,A=softmax(S) (逐行),O=AV

其中 X∈Rn×d 是 n 个 token 的输入矩阵(每个 d 维),WQ,WK∈Rd×dk,WV∈Rd×dv 是三个线性层的可学习参数。

flowchart TB
    X["$$\mathbf{X}$$(输入,换乘站)"] -->|"$$\times\mathbf{W}_{Q}$$"| Q["$$\mathbf{Q}$$"]
    X -->|"$$\times\mathbf{W}_{K}$$"| K["$$\mathbf{K}$$"]
    X -->|"$$\times\mathbf{W}_{V}$$"| V["$$\mathbf{V}$$"]
    Q -->|"$$\mathbf{Q}\mathbf{K}^{\top} / \sqrt{d_k}$$"| S["$$\mathbf{S}$$"]
    K -->|"$$\mathbf{Q}\mathbf{K}^{\top} / \sqrt{d_k}$$"| S
    S -->|"$$\mathrm{softmax}$$(逐行)"| A["$$\mathbf{A}$$"]
    A -->|"$$\mathbf{A}\mathbf{V}$$"| O["$$\mathbf{O}$$"]
    V -->|"$$\mathbf{A}\mathbf{V}$$"| O
    O --> NEXT["后续层 → 损失"]

    NEXT -.->|"$$\mathbf{G} = \dfrac{\partial L}{\partial \mathbf{O}}$$"| O
    O -.->|"$$\dfrac{\partial L}{\partial \mathbf{V}} = \mathbf{A}^{\top}\mathbf{G}$$"| V
    O -.->|"$$\dfrac{\partial L}{\partial \mathbf{A}} = \mathbf{G}\mathbf{V}^{\top}$$"| A
    A -.->|"softmax 反向"| S
    S -.->|"$$\dfrac{\partial L}{\partial \mathbf{Q}} = \dfrac{\partial L}{\partial \mathbf{S}}\mathbf{K} / \sqrt{d_k}$$"| Q
    S -.->|"$$\dfrac{\partial L}{\partial \mathbf{K}} = \left(\dfrac{\partial L}{\partial \mathbf{S}}\right)^{\top}\mathbf{Q} / \sqrt{d_k}$$"| K
    Q -.->|"$$\dfrac{\partial L}{\partial \mathbf{W}_{Q}} = \mathbf{X}^{\top}\dfrac{\partial L}{\partial \mathbf{Q}}$$"| WQ["$$\mathbf{W}_{Q}$$(终点站)"]
    K -.->|"$$\dfrac{\partial L}{\partial \mathbf{W}_{K}} = \mathbf{X}^{\top}\dfrac{\partial L}{\partial \mathbf{K}}$$"| WK["$$\mathbf{W}_{K}$$(终点站)"]
    V -.->|"$$\dfrac{\partial L}{\partial \mathbf{W}_{V}} = \mathbf{X}^{\top}\dfrac{\partial L}{\partial \mathbf{V}}$$"| WV["$$\mathbf{W}_{V}$$(终点站)"]
    Q -.->|"$$\dfrac{\partial L}{\partial \mathbf{Q}}\mathbf{W}_{Q}^{\top}$$"| X
    K -.->|"$$\dfrac{\partial L}{\partial \mathbf{K}}\mathbf{W}_{K}^{\top}$$"| X
    V -.->|"$$\dfrac{\partial L}{\partial \mathbf{V}}\mathbf{W}_{V}^{\top}$$"| X

    classDef terminal fill:#ffe6cc,stroke:#d79b00,color:#333;
    classDef conduit fill:#dae8fc,stroke:#6c8ebf,color:#333;
    class WQ,WK,WV terminal;
    class X,Q,K,V,S,A,O conduit;

上图中,实线是前向传播的数据流,虚线是反向传播的梯度流:X 在前向时分支为三路、在反向时收拢三路梯度,橙色节点 WQ,WK,WV 是终点站。设后一层传回的梯度 G=∂L∂O 已知,我们从损失出发反向走一遍。

① O=AV(矩阵乘法)。 套用矩阵乘法一节的结论:

∂L∂V=A⊤G,∂L∂A=GV⊤

② A=softmax(S)(逐行 softmax)。 套用 Softmax 一节的结论:

∂L∂S=A∘(∂L∂A−1rowsum(∂L∂A∘A)⊤)

③ S=QK⊤dk(缩放点积)。 仍然是矩阵乘法:

∂L∂Q=1dk∂L∂SK,∂L∂K=1dk(∂L∂S)⊤Q

④ 三个线性层(终点站)。 套用全连接层的结论:

∂L∂WQ=X⊤∂L∂Q,∂L∂WK=X⊤∂L∂K,∂L∂WV=X⊤∂L∂V

⑤ 分支汇合(换乘站)。 X 同时流向 Q,K,V 三条支路,所以它收到的梯度是三条之和:

∂L∂X=∂L∂QWQ⊤+∂L∂KWK⊤+∂L∂VWV⊤

按「终点站与换乘站」的观点看:WQ,WK,WV 是终点站,梯度直接用于参数更新;V,A,Q,K 乃至 X 都是换乘站。在这个块里 X 是输入数据、不是参数,但只要块前面还有带参数的运算(多头拼接、残差连接、前面的 Transformer 层……),这条梯度就是继续回传的通道——与 Feed Forward 网络里的 ∂L∂h、∂L∂g 扮演完全相同的角色。遇到更复杂的结构(多头注意力、交叉注意力等),也无非是多接几条这样的支路而已。

小结 ​

深度学习中的矩阵求导,主要是利用导数的链式法则,让输出(损失)对深度神经网络中的参数矩阵进行求导。在求导过程中,主要涉及两种情况:一是直接对参数矩阵求导(得到形如 ∂L∂yh⊤ 的外积),二是对输入中间状态进行求导(得到形如 W⊤∂L∂y 的回传梯度)。再次强调:第二种求导的目的不是优化输入或中间状态——它们是数据,只有参数会被梯度下降更新——而是让梯度得以穿过各层,最终汇聚成对每个参数的梯度。采用分母表达式进行求导,各因子的形状与参数一一对应,且可以按照神经网络「从输出到输入」的顺序进行计算,较为方便和直观。

把全文的积木总结成一张速查表:

运算梯度(分母布局)关键词
y=Wx∂y∂x=W⊤一次齐次式
y=x⊤Wx(W⊤+W)x二次型
y=ϕ(x) 逐元素diag(ϕ′(x))对角 Jacobian
y=Wx+b,对 W∂L∂W=∂L∂yx⊤外积
卷积梯度仍是卷积权重共享、梯度累加
Y=AB∂L∂A=GB⊤,∂L∂B=A⊤G(G=∂L∂Y)矩阵乘法
A=softmax(S)(逐行)A∘(G−1rowsum(G∘A)⊤)(G=∂L∂A)非逐元素

延伸阅读 ​

  • The Matrix Calculus You Need For Deep Learning,Terence Parr 与 Jeremy Howard 著,从 Chain Rules 的两种布局讲起,与本文互为补充。
  • Ian Goodfellow、Yoshua Bengio 与 Aaron Courville 的《Deep Learning》第 6 章,介绍反向传播在深度学习框架中的工程实现。

本文根据合集·深度学习中的数学 by 齐宪标系列视频整理,由 AI 辅助补全与勘误。如有疏漏,欢迎指正。

最近更新