梯度下降需要损失对每个参数的导数(第 5 篇)。第 5 篇手工算出了只有一个隐藏单元的网络的导数,而第 7 篇引入的深层网络动辄有几十万个参数。本文完整推导一次算出全部导数的算法——反向传播 。我们先用具体数字在一条标量函数链上运行它,再给出适用于任何计算的一般规则,然后推导深层网络的矩阵形式并做一个算例。接着我们计算它的运算量和内存开销,说明反向计算如何从损失开始,把它放到自动微分的框架中,最后用反向传播的方程解释:为什么初始权重的尺度决定了深层网络能否训练。
1. 为什么需要一个算法
估计导数最简单的办法,是把参数挪动一点,看损失变化多少。中心差分
∂ ℓ ∂ ϕ ≈ ℓ ( ϕ + ϵ ) − ℓ ( ϕ − ϵ ) 2 ϵ (8.1) \frac{\partial \ell}{\partial \phi} \approx \frac{\ell(\phi + \epsilon) - \ell(\phi - \epsilon)}{2\epsilon}
\tag{8.1} ∂ ϕ ∂ ℓ ≈ 2 ϵ ℓ ( ϕ + ϵ ) − ℓ ( ϕ − ϵ ) ( 8.1 )
对每个参数需要计算两次损失,每次都是一遍完整的前向计算。第 7 篇中 ( 784 , 256 , 256 , 10 ) (784, 256, 256, 10) ( 784 , 256 , 256 , 10 ) 的分类器有 269,322 个参数,算一次梯度就要做 538,644 次前向计算。而梯度下降要走成千上万步,这完全不可行。
两个观察指向更好的方法。第一,权重乘的是激活值,所以它对损失的影响与那个激活值成正比:激活值为零时,改变权重毫无作用;激活值很大时,权重的微小变化影响很大。第二,前面某层的参数只能通过它之后的所有层影响损失。所有前面的参数共享同一条下游路径,所以这条路径上的导数可以只算一次、反复使用。反向传播同时利用了这两点:它从损失出发、反向计算损失对各中间值的导数,然后用每个参数所作用的中间值和它所乘的激活值,直接读出该参数的导数。
2. 一条函数链
我们从一个由标量运算串成的模型开始,这是包含反向传播全部步骤的最小情形。网络版本留到第 4 节。模型只有一个输入 x x x ,它计算:
f 0 = β 0 + ω 0 x , h 1 = sin ( f 0 ) , f 1 = β 1 + ω 1 h 1 , h 2 = exp ( f 1 ) , f 2 = β 2 + ω 2 h 2 , h 3 = cos ( f 2 ) , f 3 = β 3 + ω 3 h 3 , ℓ = ( f 3 − y ) 2 (8.2) \begin{aligned}
f_0 &= \beta_0 + \omega_0 x, & h_1 &= \sin(f_0), \\
f_1 &= \beta_1 + \omega_1 h_1, & h_2 &= \exp(f_1), \\
f_2 &= \beta_2 + \omega_2 h_2, & h_3 &= \cos(f_2), \\
f_3 &= \beta_3 + \omega_3 h_3, & \ell &= (f_3 - y)^2
\end{aligned}
\tag{8.2} f 0 f 1 f 2 f 3 = β 0 + ω 0 x , = β 1 + ω 1 h 1 , = β 2 + ω 2 h 2 , = β 3 + ω 3 h 3 , h 1 h 2 h 3 ℓ = sin ( f 0 ) , = exp ( f 1 ) , = cos ( f 2 ) , = ( f 3 − y ) 2 ( 8.2 )
它的结构就是每层只有一个单元的深层网络。每个 f k f_k f k 是带偏置 β k \beta_k β k 和权重 ω k \omega_k ω k (读作“omega”)的仿射函数,每个 h k + 1 h_{k+1} h k + 1 对它施加一个非线性函数。这里用三个不同的非线性函数代替 ReLU,是为了让推导的每一步看起来都不一样。最后一行是单个训练样本 ( x , y ) (x, y) ( x , y ) 的平方误差。模型共有八个参数。
取 x = 0.6 x = 0.6 x = 0.6 、y = 0.4 y = 0.4 y = 0.4 ,参数值为 β 0 = 0.1 \beta_0 = 0.1 β 0 = 0.1 、ω 0 = 1.2 \omega_0 = 1.2 ω 0 = 1.2 、β 1 = − 0.3 \beta_1 = -0.3 β 1 = − 0.3 、ω 1 = 0.8 \omega_1 = 0.8 ω 1 = 0.8 、β 2 = 0.2 \beta_2 = 0.2 β 2 = 0.2 、ω 2 = 0.5 \omega_2 = 0.5 ω 2 = 0.5 、β 3 = 0.1 \beta_3 = 0.1 β 3 = 0.1 、ω 3 = 1.5 \omega_3 = 1.5 ω 3 = 1.5 。
前向计算 从上到下计算式 8.2,并保存每个中间值 :f 0 = 0.1 + 1.2 × 0.6 = 0.82 f_0 = 0.1 + 1.2 \times 0.6 = 0.82 f 0 = 0.1 + 1.2 × 0.6 = 0.82 ,h 1 = sin 0.82 = 0.7311 h_1 = \sin 0.82 = 0.7311 h 1 = sin 0.82 = 0.7311 ,f 1 = − 0.3 + 0.8 × 0.7311 = 0.2849 f_1 = -0.3 + 0.8 \times 0.7311 = 0.2849 f 1 = − 0.3 + 0.8 × 0.7311 = 0.2849 ,h 2 = e 0.2849 = 1.3297 h_2 = e^{0.2849} = 1.3297 h 2 = e 0.2849 = 1.3297 ,f 2 = 0.8648 f_2 = 0.8648 f 2 = 0.8648 ,h 3 = cos 0.8648 = 0.6488 h_3 = \cos 0.8648 = 0.6488 h 3 = cos 0.8648 = 0.6488 ,f 3 = 1.0732 f_3 = 1.0732 f 3 = 1.0732 ,最后 ℓ = ( 1.0732 − 0.4 ) 2 = 0.4531 \ell = (1.0732 - 0.4)^2 = 0.4531 ℓ = ( 1.0732 − 0.4 ) 2 = 0.4531 。
反向计算 按相反的顺序,算出损失对每个中间值的导数。我们从末端开始:
∂ ℓ ∂ f 3 = 2 ( f 3 − y ) = 1.3463 \frac{\partial \ell}{\partial f_3} = 2(f_3 - y) = 1.3463 ∂ f 3 ∂ ℓ = 2 ( f 3 − y ) = 1.3463
h 3 h_3 h 3 只通过 f 3 = β 3 + ω 3 h 3 f_3 = \beta_3 + \omega_3 h_3 f 3 = β 3 + ω 3 h 3 影响损失,h 3 h_3 h 3 每增加一个单位,f 3 f_3 f 3 增加 ω 3 \omega_3 ω 3 。由链式法则:
∂ ℓ ∂ h 3 = ∂ f 3 ∂ h 3 ∂ ℓ ∂ f 3 = ω 3 × 1.3463 = 2.0195 \frac{\partial \ell}{\partial h_3} = \frac{\partial f_3}{\partial h_3}\,\frac{\partial \ell}{\partial f_3} = \omega_3 \times 1.3463 = 2.0195 ∂ h 3 ∂ ℓ = ∂ h 3 ∂ f 3 ∂ f 3 ∂ ℓ = ω 3 × 1.3463 = 2.0195
接着,f 2 f_2 f 2 只通过 h 3 = cos f 2 h_3 = \cos f_2 h 3 = cos f 2 影响损失,后者的导数是 − sin f 2 -\sin f_2 − sin f 2 :
∂ ℓ ∂ f 2 = ∂ h 3 ∂ f 2 ∂ ℓ ∂ h 3 = − sin ( 0.8648 ) × 2.0195 = − 1.5368 \frac{\partial \ell}{\partial f_2} = \frac{\partial h_3}{\partial f_2}\,\frac{\partial \ell}{\partial h_3} = -\sin(0.8648) \times 2.0195 = -1.5368 ∂ f 2 ∂ ℓ = ∂ f 2 ∂ h 3 ∂ h 3 ∂ ℓ = − sin ( 0.8648 ) × 2.0195 = − 1.5368
沿着链往回,规律不断重复。每一步都把上一步算出的导数乘以一个局部 导数,即单个运算对其输入的导数:
∂ ℓ ∂ h k = ω k ∂ ℓ ∂ f k , ∂ ℓ ∂ f k − 1 = a k ′ ( f k − 1 ) ∂ ℓ ∂ h k (8.3) \frac{\partial \ell}{\partial h_k} = \omega_k \frac{\partial \ell}{\partial f_k},
\qquad
\frac{\partial \ell}{\partial f_{k-1}} = a_k'(f_{k-1})\,\frac{\partial \ell}{\partial h_k}
\tag{8.3} ∂ h k ∂ ℓ = ω k ∂ f k ∂ ℓ , ∂ f k − 1 ∂ ℓ = a k ′ ( f k − 1 ) ∂ h k ∂ ℓ ( 8.3 )
其中 a k a_k a k 是第 k k k 步的非线性函数(这里是 sin、exp 或 cos)。用文字说:反向越过一个仿射步骤时,乘以它的权重;反向越过一个非线性步骤时,乘以该函数在前向计算中所收到的那个值处的斜率。第二条规则正是前向计算要保存 f k f_k f k 的原因。继续下去得到 ∂ ℓ / ∂ h 2 = − 0.7684 \partial\ell/\partial h_2 = -0.7684 ∂ ℓ / ∂ h 2 = − 0.7684 ,∂ ℓ / ∂ f 1 = e 0.2849 × ( − 0.7684 ) = − 1.0217 \partial\ell/\partial f_1 = e^{0.2849} \times (-0.7684) = -1.0217 ∂ ℓ / ∂ f 1 = e 0.2849 × ( − 0.7684 ) = − 1.0217 ,∂ ℓ / ∂ h 1 = − 0.8174 \partial\ell/\partial h_1 = -0.8174 ∂ ℓ / ∂ h 1 = − 0.8174 ,∂ ℓ / ∂ f 0 = cos ( 0.82 ) × ( − 0.8174 ) = − 0.5576 \partial\ell/\partial f_0 = \cos(0.82) \times (-0.8174) = -0.5576 ∂ ℓ / ∂ f 0 = cos ( 0.82 ) × ( − 0.8174 ) = − 0.5576 。图 8.1 列出了全部数值。
图 8.1 函数链上的前向与反向计算。 式 8.2 的模型,x = 0.6 x = 0.6 x = 0.6 ,y = 0.4 y = 0.4 y = 0.4 。前向计算(深色箭头)依次执行箭头上方的运算,并保存结果(深色数字)。反向计算(橙色箭头)从 ∂ ℓ / ∂ ℓ = 1 \partial\ell/\partial\ell = 1 ∂ ℓ / ∂ ℓ = 1 开始,每一步乘以一个局部导数(式 8.3),得到损失对每个中间值的导数(橙色数字)。
参数的导数 几乎是顺手得到的。偏置 β k \beta_k β k 只通过 f k f_k f k 起作用,局部导数为 1;权重 ω k \omega_k ω k 通过 f k f_k f k 起作用,并乘以 h k h_k h k (约定 h 0 = x h_0 = x h 0 = x ):
∂ ℓ ∂ β k = ∂ ℓ ∂ f k , ∂ ℓ ∂ ω k = h k ∂ ℓ ∂ f k (8.4) \frac{\partial \ell}{\partial \beta_k} = \frac{\partial \ell}{\partial f_k},
\qquad
\frac{\partial \ell}{\partial \omega_k} = h_k \frac{\partial \ell}{\partial f_k}
\tag{8.4} ∂ β k ∂ ℓ = ∂ f k ∂ ℓ , ∂ ω k ∂ ℓ = h k ∂ f k ∂ ℓ ( 8.4 )
这正是第 1 节的两个观察写成的式子:权重的导数,等于它作用处的导数乘以它所乘的激活值。代入我们的数值:
k k k
∂ ℓ / ∂ β k \partial\ell/\partial\beta_k ∂ ℓ / ∂ β k
∂ ℓ / ∂ ω k \partial\ell/\partial\omega_k ∂ ℓ / ∂ ω k
0
− 0.5576 -0.5576 − 0.5576
0.6 × ( − 0.5576 ) = − 0.3346 0.6 \times (-0.5576) = -0.3346 0.6 × ( − 0.5576 ) = − 0.3346
1
− 1.0217 -1.0217 − 1.0217
0.7311 × ( − 1.0217 ) = − 0.7470 0.7311 \times (-1.0217) = -0.7470 0.7311 × ( − 1.0217 ) = − 0.7470
2
− 1.5368 -1.5368 − 1.5368
1.3297 × ( − 1.5368 ) = − 2.0434 1.3297 \times (-1.5368) = -2.0434 1.3297 × ( − 1.5368 ) = − 2.0434
3
1.3463 1.3463 1.3463
0.6488 × 1.3463 = 0.8735 0.6488 \times 1.3463 = 0.8735 0.6488 × 1.3463 = 0.8735
这八个值与 ϵ = 10 − 6 \epsilon = 10^{-6} ϵ = 1 0 − 6 的中心差分(式 8.1)至少在小数点后六位一致。反向计算只用了十来次乘法,大致相当于一次前向计算的工作量;而中心差分需要十六次前向计算。
3. 一般规则:计算图
在函数链中,每个值到损失只有一条路径。真实网络是有分叉的:一个隐藏单元会送往下一层的每个单元,一个值也可能被使用多次。一般规则可以处理这种情况。
任何计算都可以画成一张计算图 :每个节点是一个值,从节点 u u u 指向节点 v v v 的箭头表示 v v v 是直接由 u u u (以及其他输入)算出的。如果 u u u 被几个节点 v 1 , … , v m v_1, \ldots, v_m v 1 , … , v m 使用,u u u 的微小变化会改变所有这些节点,它们对损失的影响相加。多元链式法则给出:
∂ ℓ ∂ u = ∑ j = 1 m ∂ v j ∂ u ∂ ℓ ∂ v j (8.5) \frac{\partial \ell}{\partial u} = \sum_{j=1}^{m} \frac{\partial v_j}{\partial u}\,\frac{\partial \ell}{\partial v_j}
\tag{8.5} ∂ u ∂ ℓ = j = 1 ∑ m ∂ u ∂ v j ∂ v j ∂ ℓ ( 8.5 )
这个式子可以分成两部分。导数 ∂ ℓ / ∂ v j \partial\ell/\partial v_j ∂ ℓ / ∂ v j 属于图中更靠后的节点,只要我们从损失开始反向处理节点,它们就已经算好了。因子 ∂ v j / ∂ u \partial v_j / \partial u ∂ v j / ∂ u 是局部的:每个只取决于计算 v j v_j v j 的那一个运算,在保存的前向值处求值。用文字说:损失对一个值的导数,等于所有使用这个值的节点的导数,乘以各自的局部导数,再求和。
一个很小的例子可以说明这个求和。设 u = 3 u = 3 u = 3 被两个运算使用,v 1 = 2 u = 6 v_1 = 2u = 6 v 1 = 2 u = 6 ,v 2 = u 2 = 9 v_2 = u^2 = 9 v 2 = u 2 = 9 ,并令 ℓ = v 1 + v 2 = 15 \ell = v_1 + v_2 = 15 ℓ = v 1 + v 2 = 15 。于是 ∂ ℓ / ∂ v 1 = ∂ ℓ / ∂ v 2 = 1 \partial\ell/\partial v_1 = \partial\ell/\partial v_2 = 1 ∂ ℓ / ∂ v 1 = ∂ ℓ / ∂ v 2 = 1 ,由式 8.5 得 ∂ ℓ / ∂ u = 2 × 1 + 2 u × 1 = 8 \partial\ell/\partial u = 2 \times 1 + 2u \times 1 = 8 ∂ ℓ / ∂ u = 2 × 1 + 2 u × 1 = 8 。直接把损失写成 ℓ = 2 u + u 2 \ell = 2u + u^2 ℓ = 2 u + u 2 ,导数 2 + 2 u = 8 2 + 2u = 8 2 + 2 u = 8 ,结果相同。手写梯度时常见的错误是漏掉其中一条路径,那样会得到 2 或 6。
于是算法就是:做前向计算,保存每个节点的值;令 ∂ ℓ / ∂ ℓ = 1 \partial\ell/\partial\ell = 1 ∂ ℓ / ∂ ℓ = 1 ;按计算顺序的逆序访问各节点,对每个节点使用式 8.5。图中每条箭头在两个方向上各用一次,所以无论有多少参数 ,反向计算的开销都只是前向计算的一个小常数倍。这就是反向传播的全部。本文其余部分把它具体用于深层网络,并考察它带来的结论。
4. 矩阵形式的反向传播
现在把一般规则用于第 7 篇的深层网络(式 7.4)。为方便起见,我们给预激活命名。令 h 0 = x \mathbf{h}_0 = \mathbf{x} h 0 = x ,对 k = 0 , … , K k = 0, \ldots, K k = 0 , … , K ,前向计算为:
f k = β k + Ω k h k , h k + 1 = a ( f k ) ( k < K ) (8.6) \mathbf{f}_k = \boldsymbol{\beta}_k + \boldsymbol{\Omega}_k \mathbf{h}_k,
\qquad
\mathbf{h}_{k+1} = a(\mathbf{f}_k) \ \ (k < K)
\tag{8.6} f k = β k + Ω k h k , h k + 1 = a ( f k ) ( k < K ) ( 8.6 )
输出是 f K \mathbf{f}_K f K 。我们把第 k k k 层的误差信号 定义为损失对该层预激活的导数 δ k = ∂ ℓ / ∂ f k \boldsymbol{\delta}_k = \partial\ell/\partial\mathbf{f}_k δ k = ∂ ℓ / ∂ f k ,它是有 D k + 1 D_{k+1} D k + 1 个元素的向量。
参数。 f k \mathbf{f}_k f k 的第 d d d 个元素是 f k , d = β k , d + ∑ j Ω k , d j h k , j f_{k,d} = \beta_{k,d} + \sum_j \Omega_{k,dj} h_{k,j} f k , d = β k , d + ∑ j Ω k , d j h k , j 。权重 Ω k , d j \Omega_{k,dj} Ω k , d j 只出现在这个元素中,并乘以 h k , j h_{k,j} h k , j ,所以 ∂ ℓ / ∂ Ω k , d j = δ k , d h k , j \partial\ell/\partial\Omega_{k,dj} = \delta_{k,d}\,h_{k,j} ∂ ℓ / ∂ Ω k , d j = δ k , d h k , j ,与式 8.4 完全一样。把所有 d d d 和 j j j 合在一起:
∂ ℓ ∂ β k = δ k , ∂ ℓ ∂ Ω k = δ k h k ⊤ (8.7) \frac{\partial \ell}{\partial \boldsymbol{\beta}_k} = \boldsymbol{\delta}_k,
\qquad
\frac{\partial \ell}{\partial \boldsymbol{\Omega}_k} = \boldsymbol{\delta}_k \mathbf{h}_k^\top
\tag{8.7} ∂ β k ∂ ℓ = δ k , ∂ Ω k ∂ ℓ = δ k h k ⊤ ( 8.7 )
权重梯度是一个外积 :有 D k + 1 D_{k+1} D k + 1 个元素的列向量乘以有 D k D_k D k 个元素的行向量,得到 D k + 1 × D k D_{k+1} \times D_k D k + 1 × D k 矩阵,与 Ω k \boldsymbol{\Omega}_k Ω k 形状相同。用文字说:从单元 j j j 到单元 d d d 的权重,其梯度等于单元 d d d 的误差信号乘以单元 j j j 的激活值。
反向越过权重矩阵。 h k \mathbf{h}_k h k 的单元 j j j 被 f k \mathbf{f}_k f k 的每个元素使用,局部导数为 ∂ f k , d / ∂ h k , j = Ω k , d j \partial f_{k,d}/\partial h_{k,j} = \Omega_{k,dj} ∂ f k , d / ∂ h k , j = Ω k , d j 。式 8.5 对这些使用求和:∂ ℓ / ∂ h k , j = ∑ d Ω k , d j δ k , d \partial\ell/\partial h_{k,j} = \sum_d \Omega_{k,dj}\,\delta_{k,d} ∂ ℓ / ∂ h k , j = ∑ d Ω k , d j δ k , d 。这个和沿着 Ω k \boldsymbol{\Omega}_k Ω k 的第 j j j 列进行,所以写成矩阵形式就是乘以转置:
∂ ℓ ∂ h k = Ω k ⊤ δ k (8.8) \frac{\partial \ell}{\partial \mathbf{h}_k} = \boldsymbol{\Omega}_k^\top \boldsymbol{\delta}_k
\tag{8.8} ∂ h k ∂ ℓ = Ω k ⊤ δ k ( 8.8 )
形状正确:( D k × D k + 1 ) (D_k \times D_{k+1}) ( D k × D k + 1 ) 乘以 D k + 1 D_{k+1} D k + 1 个元素,得到 D k D_k D k 个元素,h k \mathbf{h}_k h k 的每个单元一个。前向计算乘以 Ω k \boldsymbol{\Omega}_k Ω k ,反向计算乘以 Ω k ⊤ \boldsymbol{\Omega}_k^\top Ω k ⊤ ,把每个误差信号沿着同样的连接送回去。
反向越过激活函数。 h k \mathbf{h}_k h k 的第 j j j 个元素只依赖 f k − 1 \mathbf{f}_{k-1} f k − 1 的第 j j j 个元素,所以局部导数就是激活函数在那里的斜率:
δ k − 1 = a ′ ( f k − 1 ) ⊙ ∂ ℓ ∂ h k (8.9) \boldsymbol{\delta}_{k-1} = a'(\mathbf{f}_{k-1}) \odot \frac{\partial \ell}{\partial \mathbf{h}_k}
\tag{8.9} δ k − 1 = a ′ ( f k − 1 ) ⊙ ∂ h k ∂ ℓ ( 8.9 )
其中 ⊙ \odot ⊙ 表示两个向量逐元素相乘。对 ReLU,预激活为正处斜率为 1,为负处斜率为 0,所以式 8.9 就是把每个未激活单元的误差信号置零。(恰好为零处导数没有定义,深度学习库取 0。)
式 8.7–8.9 合在一起,就是深层网络的反向计算:
做前向计算(式 8.6),保存每个 h k \mathbf{h}_k h k 和 f k \mathbf{f}_k f k (对 ReLU,只需保存 f k \mathbf{f}_k f k 的符号)。
由损失算出 δ K = ∂ ℓ / ∂ f K \boldsymbol{\delta}_K = \partial\ell/\partial\mathbf{f}_K δ K = ∂ ℓ / ∂ f K (第 6 节)。
对 k = K , K − 1 , … , 0 k = K, K-1, \ldots, 0 k = K , K − 1 , … , 0 :用式 8.7 算出第 k k k 层的参数梯度;若 k > 0 k > 0 k > 0 ,再用式 8.8 和 8.9 算出 δ k − 1 \boldsymbol{\delta}_{k-1} δ k − 1 。
我们来算一个各层大小为 ( 2 , 2 , 2 , 1 ) (2, 2, 2, 1) ( 2 , 2 , 2 , 1 ) 的网络,它有 15 个参数,使用 ReLU 激活和平方误差。输入为 x = ( 1 , 2 ) ⊤ \mathbf{x} = (1, 2)^\top x = ( 1 , 2 ) ⊤ ,目标为 y = 1 y = 1 y = 1 ,参数为:
Ω 0 = [ 1 − 1 0.5 1 ] , β 0 = [ 0 − 1 ] , Ω 1 = [ 1 2 − 1 − 1 ] , β 1 = [ 0.5 0 ] , Ω 2 = [ 0.5 − 1 ] , β 2 = 0.5 \boldsymbol{\Omega}_0 = \begin{bmatrix} 1 & -1 \\ 0.5 & 1 \end{bmatrix},\
\boldsymbol{\beta}_0 = \begin{bmatrix} 0 \\ -1 \end{bmatrix},\quad
\boldsymbol{\Omega}_1 = \begin{bmatrix} 1 & 2 \\ -1 & -1 \end{bmatrix},\
\boldsymbol{\beta}_1 = \begin{bmatrix} 0.5 \\ 0 \end{bmatrix},\quad
\boldsymbol{\Omega}_2 = \begin{bmatrix} 0.5 & -1 \end{bmatrix},\
\boldsymbol{\beta}_2 = 0.5 Ω 0 = [ 1 0.5 − 1 1 ] , β 0 = [ 0 − 1 ] , Ω 1 = [ 1 − 1 2 − 1 ] , β 1 = [ 0.5 0 ] , Ω 2 = [ 0.5 − 1 ] , β 2 = 0.5
前向计算得到 f 0 = ( 1 − 2 , − 1 + 0.5 + 2 ) = ( − 1 , 1.5 ) \mathbf{f}_0 = (1 - 2,\ -1 + 0.5 + 2) = (-1, 1.5) f 0 = ( 1 − 2 , − 1 + 0.5 + 2 ) = ( − 1 , 1.5 ) ,所以 h 1 = ( 0 , 1.5 ) \mathbf{h}_1 = (0, 1.5) h 1 = ( 0 , 1.5 ) :第 1 层的第一个单元未激活。接着 f 1 = ( 0.5 + 3 , − 1.5 ) = ( 3.5 , − 1.5 ) \mathbf{f}_1 = (0.5 + 3,\ -1.5) = (3.5, -1.5) f 1 = ( 0.5 + 3 , − 1.5 ) = ( 3.5 , − 1.5 ) ,h 2 = ( 3.5 , 0 ) \mathbf{h}_2 = (3.5, 0) h 2 = ( 3.5 , 0 ) :第 2 层的第二个单元未激活。输出为 f 2 = 0.5 + 0.5 × 3.5 = 2.25 f_2 = 0.5 + 0.5 \times 3.5 = 2.25 f 2 = 0.5 + 0.5 × 3.5 = 2.25 ,损失为 ( 2.25 − 1 ) 2 = 1.5625 (2.25 - 1)^2 = 1.5625 ( 2.25 − 1 ) 2 = 1.5625 。
反向计算从 δ 2 = 2 ( 2.25 − 1 ) = 2.5 \delta_2 = 2(2.25 - 1) = 2.5 δ 2 = 2 ( 2.25 − 1 ) = 2.5 开始。由式 8.7 得 ∂ ℓ / ∂ Ω 2 = 2.5 × ( 3.5 , 0 ) = ( 8.75 , 0 ) \partial\ell/\partial\boldsymbol{\Omega}_2 = 2.5 \times (3.5, 0) = (8.75, 0) ∂ ℓ / ∂ Ω 2 = 2.5 × ( 3.5 , 0 ) = ( 8.75 , 0 ) 。由式 8.8 得 ∂ ℓ / ∂ h 2 = Ω 2 ⊤ × 2.5 = ( 1.25 , − 2.5 ) \partial\ell/\partial\mathbf{h}_2 = \boldsymbol{\Omega}_2^\top \times 2.5 = (1.25, -2.5) ∂ ℓ / ∂ h 2 = Ω 2 ⊤ × 2.5 = ( 1.25 , − 2.5 ) ,式 8.9 把未激活的第二个元素置零:δ 1 = ( 1.25 , 0 ) \boldsymbol{\delta}_1 = (1.25, 0) δ 1 = ( 1.25 , 0 ) 。再往回一层:
∂ ℓ ∂ Ω 1 = [ 1.25 0 ] [ 0 1.5 ] = [ 0 1.875 0 0 ] , ∂ ℓ ∂ h 1 = Ω 1 ⊤ [ 1.25 0 ] = [ 1.25 2.5 ] \frac{\partial \ell}{\partial \boldsymbol{\Omega}_1} = \begin{bmatrix} 1.25 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1.5 \end{bmatrix} = \begin{bmatrix} 0 & 1.875 \\ 0 & 0 \end{bmatrix},
\qquad
\frac{\partial \ell}{\partial \mathbf{h}_1} = \boldsymbol{\Omega}_1^\top \begin{bmatrix} 1.25 \\ 0 \end{bmatrix} = \begin{bmatrix} 1.25 \\ 2.5 \end{bmatrix} ∂ Ω 1 ∂ ℓ = [ 1.25 0 ] [ 0 1.5 ] = [ 0 0 1.875 0 ] , ∂ h 1 ∂ ℓ = Ω 1 ⊤ [ 1.25 0 ] = [ 1.25 2.5 ]
把未激活的第一个单元置零,得到 δ 0 = ( 0 , 2.5 ) \boldsymbol{\delta}_0 = (0, 2.5) δ 0 = ( 0 , 2.5 ) ,所以 ∂ ℓ / ∂ Ω 0 = δ 0 x ⊤ \partial\ell/\partial\boldsymbol{\Omega}_0 = \boldsymbol{\delta}_0\mathbf{x}^\top ∂ ℓ / ∂ Ω 0 = δ 0 x ⊤ 的两行分别是 ( 0 , 0 ) (0, 0) ( 0 , 0 ) 和 ( 2.5 , 5 ) (2.5, 5) ( 2.5 , 5 ) 。全部 15 个导数都与中心差分一致。图 8.2 把这些值标在网络上。
图 8.2 在一个小 ReLU 网络中做反向传播。 各层大小为 ( 2 , 2 , 2 , 1 ) (2, 2, 2, 1) ( 2 , 2 , 2 , 1 ) ,输入 x = ( 1 , 2 ) \mathbf{x} = (1, 2) x = ( 1 , 2 ) ,目标 y = 1 y = 1 y = 1 ,参数见第 4 节。每个单元标出前向计算得到的预激活 f f f 和激活值 h h h (深色),以及反向计算得到的误差信号 δ = ∂ ℓ / ∂ f \delta = \partial\ell/\partial f δ = ∂ ℓ / ∂ f (橙色)。灰色单元未激活(f < 0 f < 0 f < 0 ):它们不向前传递任何东西,误差信号为零,对这个样本而言,进出它们的所有权重梯度都为零(虚线)。
这个例子显示了两个普遍成立的性质。第一,对这个样本,未激活的 ReLU 单元在两个方向上都阻断梯度:它的输入权重得到 δ = 0 \delta = 0 δ = 0 ,它的输出权重乘的是 h = 0 h = 0 h = 0 。如果一个单元对所有训练样本都不激活,它就永远不会改变,这称为死亡 ReLU 。第二,单个样本的权重梯度 δ k h k ⊤ \boldsymbol{\delta}_k\mathbf{h}_k^\top δ k h k ⊤ 的秩为一。一批样本的梯度是这些外积之和,秩通常更高,但不超过批大小。
5. 批量、运算量与内存
训练使用一批 B B B 个样本和平均损失 L = 1 B ∑ i ℓ i L = \frac{1}{B}\sum_i \ell_i L = B 1 ∑ i ℓ i (第 5 篇)。每个样本各有一次前向和反向计算,L L L 的梯度是各样本梯度的平均。像第 7 篇那样把一批的激活值作为各列,H k ∈ R D k × B \mathbf{H}_k \in \mathbb{R}^{D_k \times B} H k ∈ R D k × B ,并把误差信号作为 Δ k ∈ R D k + 1 × B \boldsymbol{\Delta}_k \in \mathbb{R}^{D_{k+1} \times B} Δ k ∈ R D k + 1 × B 的各列,那么式 8.7 中外积对样本的求和就是一次矩阵乘法:
∂ L ∂ Ω k = 1 B Δ k H k ⊤ , ∂ L ∂ β k = 1 B Δ k 1 (8.10) \frac{\partial L}{\partial \boldsymbol{\Omega}_k} = \frac{1}{B}\,\boldsymbol{\Delta}_k \mathbf{H}_k^\top,
\qquad
\frac{\partial L}{\partial \boldsymbol{\beta}_k} = \frac{1}{B}\,\boldsymbol{\Delta}_k \mathbf{1}
\tag{8.10} ∂ Ω k ∂ L = B 1 Δ k H k ⊤ , ∂ β k ∂ L = B 1 Δ k 1 ( 8.10 )
形状为 ( D k + 1 × B ) ( B × D k ) = D k + 1 × D k (D_{k+1} \times B)(B \times D_k) = D_{k+1} \times D_k ( D k + 1 × B ) ( B × D k ) = D k + 1 × D k :这个乘积对批维度求和。式 8.8 变成 Ω k ⊤ Δ k \boldsymbol{\Omega}_k^\top\boldsymbol{\Delta}_k Ω k ⊤ Δ k ,整批只需一次乘法。
运算量。 每一层的前向计算做一次矩阵乘法 Ω k H k \boldsymbol{\Omega}_k\mathbf{H}_k Ω k H k ,需要 D k + 1 D k B D_{k+1} D_k B D k + 1 D k B 次乘加。反向计算做两次同样大小的乘法:Δ k H k ⊤ \boldsymbol{\Delta}_k\mathbf{H}_k^\top Δ k H k ⊤ 求权重梯度,Ω k ⊤ Δ k \boldsymbol{\Omega}_k^\top\boldsymbol{\Delta}_k Ω k ⊤ Δ k 继续往回传。第一层可以省掉第二次乘法,因为没有谁需要 ∂ ℓ / ∂ x \partial\ell/\partial\mathbf{x} ∂ ℓ / ∂ x 。所以反向计算的开销约为前向计算的两倍,一个训练步约为三倍。对 ( 784 , 256 , 256 , 10 ) (784, 256, 256, 10) ( 784 , 256 , 256 , 10 ) 分类器,每个样本前向需要 268,800 次乘加,反向需要 336,896 次;比值只有 1.25,因为很大的第一层省掉了它的第二次乘法。
内存。 式 8.10 需要 H k \mathbf{H}_k H k ,式 8.9 需要 f k − 1 \mathbf{f}_{k-1} f k − 1 的符号,它们都来自前向计算。因此训练必须为整批样本保留每一层的激活值,直到反向计算到达该层,所需内存与 B ∑ k D k B \sum_k D_k B ∑ k D k 成正比。预测时没有反向计算,每层的激活值在下一层算完后就可以丢弃。这就是训练比预测需要多得多的内存、批大小常常受内存限制的原因。梯度检查点 (gradient checkpointing)[7] 用计算换内存:它只保存部分层的激活值,在反向计算时重新算出其余各层;对 K K K 层的链,它把激活值内存降到大约 K \sqrt{K} K 层的量,代价约为多做一次前向计算。
6. 反向计算的起点:损失
反向计算从 δ K = ∂ ℓ / ∂ f K \boldsymbol{\delta}_K = \partial\ell/\partial\mathbf{f}_K δ K = ∂ ℓ / ∂ f K 开始,它取决于损失。
平方误差。 对 ℓ = ∑ k ( f K , k − y k ) 2 \ell = \sum_k (f_{K,k} - y_k)^2 ℓ = ∑ k ( f K , k − y k ) 2 ,导数为 δ K = 2 ( f K − y ) \boldsymbol{\delta}_K = 2(\mathbf{f}_K - \mathbf{y}) δ K = 2 ( f K − y ) :就是误差本身的两倍。如果损失是 I I I 个样本的平均,每个样本的信号再除以 I I I 。
softmax 与交叉熵。 做分类时,网络输出分数 z = f K \mathbf{z} = \mathbf{f}_K z = f K ,softmax 把它们变成概率 p k = e z k / ∑ j e z j p_k = e^{z_k}/\sum_j e^{z_j} p k = e z k / ∑ j e z j ,损失是正确类别 c c c 的 ℓ = − log p c \ell = -\log p_c ℓ = − log p c (第 4 篇)。代入 softmax,并利用 log ( e z c ) = z c \log(e^{z_c}) = z_c log ( e z c ) = z c :
ℓ = − z c + log ∑ j e z j \ell = -z_c + \log \sum_{j} e^{z_j} ℓ = − z c + log j ∑ e z j
其中 j j j 遍历所有类别。第一项只依赖 z c z_c z c :对 k = c k = c k = c 导数为 − 1 -1 − 1 ,否则为 0。第二项对 z k z_k z k 的导数是 e z k / ∑ j e z j = p k e^{z_k}/\sum_j e^{z_j} = p_k e z k / ∑ j e z j = p k 。合起来:
∂ ℓ ∂ z k = p k − 1 [ k = c ] (8.11) \frac{\partial \ell}{\partial z_k} = p_k - \mathbb{1}[k = c]
\tag{8.11} ∂ z k ∂ ℓ = p k − 1 [ k = c ] ( 8.11 )
其中 1 [ k = c ] \mathbb{1}[k = c] 1 [ k = c ] 在正确类别处为 1,其他处为 0。用文字说:误差信号就是预测概率减去目标概率。对第 4 篇的分数 z = ( 2.0 , 0.5 , − 1.0 ) \mathbf{z} = (2.0, 0.5, -1.0) z = ( 2.0 , 0.5 , − 1.0 ) ,概率为 ( 0.786 , 0.175 , 0.039 ) (0.786, 0.175, 0.039) ( 0.786 , 0.175 , 0.039 ) ;如果照片是猫,误差信号为 ( − 0.214 , 0.175 , 0.039 ) (-0.214, 0.175, 0.039) ( − 0.214 , 0.175 , 0.039 ) 。梯度下降逆着它走:提高猫的分数、降低另外两个,幅度与各自概率错得多少成正比。各元素之和总是零,因为概率之和为 1。
式 8.11 比分开计算两步更简单,数值上也更稳定。先算 p c p_c p c 再算 − log p c -\log p_c − log p c ,当 p c p_c p c 在浮点数中下溢为零时就会失败;而且 − log p -\log p − log p 单独的导数 − 1 / p -1/p − 1/ p ,恰恰在 softmax 自身导数极小的地方变得极大。因此深度学习库提供融合的“由分数直接计算交叉熵”:用 m + log ∑ j e z j − m m + \log\sum_j e^{z_j - m} m + log ∑ j e z j − m (其中 m = max j z j m = \max_j z_j m = max j z j )计算 log ∑ j e z j \log\sum_j e^{z_j} log ∑ j e z j ,保证指数不会溢出,并在反向计算中使用式 8.11。
7. 自动微分
第 3–6 节的内容并不限于神经网络。任何由已知局部导数的运算(加法、乘法、矩阵乘法、指数、ReLU 等)组成的程序都定义了一张计算图,第 3 节的反向计算能算出程序输出对其全部输入的导数。这就是反向模式自动微分 ,反向传播是它在神经网络上的特例。PyTorch、JAX 等库在前向计算运行时记录运算(或预先追踪程序),并为每个基本运算提供一条规则,把对输出的导数映射为对输入的导数。用户只需写前向计算。
理解为什么反向 才是正确的方向,会很有帮助。对一条层链,链式法则把梯度写成一串雅可比矩阵的乘积:∂ ℓ / ∂ x ⊤ = ∂ ℓ / ∂ f K ⊤ J K ⋯ J 1 \partial\ell/\partial\mathbf{x}^\top = \partial\ell/\partial\mathbf{f}_K^\top\,\mathbf{J}_K \cdots \mathbf{J}_1 ∂ ℓ / ∂ x ⊤ = ∂ ℓ / ∂ f K ⊤ J K ⋯ J 1 ,其中 J k \mathbf{J}_k J k 是第 k k k 层输出对输入的导数矩阵。最左边的因子是行向量,因为损失只是一个数。从左往右乘,每个中间结果都保持为向量,每一步只是一次向量乘矩阵。反过来从右往左乘,就要在整条链上携带完整的矩阵。前向模式 自动微分就是这样一次算一列:它把对某一个输入的导数随着数值一起向前推,每个输入或参数需要一遍。对标量损失和大量参数,反向模式的开销是一次前向计算的一个小常数倍,与参数个数无关 [5] ;前向模式的开销则是参数个数的倍数。在相反的情形,即输入少、输出多时,前向模式更好。
手写或自定义的梯度应当用中心差分(式 8.1)检查。中心差分的误差与 ϵ 2 \epsilon^2 ϵ 2 成正比,单边差分则与 ϵ \epsilon ϵ 成正比。在 64 位浮点运算中,ϵ \epsilon ϵ 取 10 − 6 10^{-6} 1 0 − 6 左右、两个梯度之间的相对误差低于约 10 − 6 10^{-6} 1 0 − 6 是典型情况;32 位浮点精度不够,无法可靠地检查。距离 ReLU 拐点不到 ϵ \epsilon ϵ 的点可能出现合理的不一致,因为两次求值落在了不同的线性段上。
8. 反向计算告诉我们的深层网络训练问题
把式 8.8 和 8.9 从输出一直展开到第一层,可以看到误差信号的结构:
δ 0 = a ′ ( f 0 ) ⊙ Ω 1 ⊤ ( a ′ ( f 1 ) ⊙ Ω 2 ⊤ ( ⋯ ( a ′ ( f K − 1 ) ⊙ Ω K ⊤ δ K ) ⋯ ) ) (8.12) \boldsymbol{\delta}_0 = a'(\mathbf{f}_0) \odot \boldsymbol{\Omega}_1^\top \Big( a'(\mathbf{f}_1) \odot \boldsymbol{\Omega}_2^\top \big( \cdots \big( a'(\mathbf{f}_{K-1}) \odot \boldsymbol{\Omega}_K^\top \boldsymbol{\delta}_K \big) \cdots \big) \Big)
\tag{8.12} δ 0 = a ′ ( f 0 ) ⊙ Ω 1 ⊤ ( a ′ ( f 1 ) ⊙ Ω 2 ⊤ ( ⋯ ( a ′ ( f K − 1 ) ⊙ Ω K ⊤ δ K ) ⋯ ) ) ( 8.12 )
到达前面某层的信号,每经过一层就乘了一个权重矩阵和一个激活斜率。许多因子的乘积往往随长度呈指数增长或衰减;前向计算也一样,每层乘一个权重矩阵、施加一次激活。权重的大小决定了是哪种情况。这正是第 5 篇未加证明给出的初始化规则背后的原因,现在可以推导它了。
前向。 设第 k k k 层的权重独立抽取,均值为 0、方差为 σ 2 \sigma^2 σ 2 ,偏置为 0,且上一层的预激活关于零对称。f k \mathbf{f}_k f k 的第 d d d 个元素是 D k D_k D k 个独立项 Ω k , d j h k , j \Omega_{k,dj}h_{k,j} Ω k , d j h k , j 之和,每项均值为 0、方差为 σ 2 E [ h k , j 2 ] \sigma^2\,\mathbb{E}[h_{k,j}^2] σ 2 E [ h k , j 2 ] 。对称变量经过 ReLU 恰好保留一半的平方量,所以 E [ h k , j 2 ] = 1 2 V a r ( f k − 1 , j ) \mathbb{E}[h_{k,j}^2] = \frac{1}{2}\mathrm{Var}(f_{k-1,j}) E [ h k , j 2 ] = 2 1 Var ( f k − 1 , j ) 。因此:
V a r ( f k ) = D k σ 2 2 V a r ( f k − 1 ) (8.13) \mathrm{Var}(f_k) = \frac{D_k\,\sigma^2}{2}\,\mathrm{Var}(f_{k-1})
\tag{8.13} Var ( f k ) = 2 D k σ 2 Var ( f k − 1 ) ( 8.13 )
反向。 式 8.8 的第 j j j 个元素是 D k + 1 D_{k+1} D k + 1 项 Ω k , d j δ k , d \Omega_{k,dj}\delta_{k,d} Ω k , d j δ k , d 之和,式 8.9 大约保留其中一半的结果。把权重、误差信号和激活模式视为相互独立(这是近似,但在初始化时相当好),同样的论证给出:
V a r ( δ k − 1 ) = D k + 1 σ 2 2 V a r ( δ k ) (8.14) \mathrm{Var}(\delta_{k-1}) = \frac{D_{k+1}\,\sigma^2}{2}\,\mathrm{Var}(\delta_k)
\tag{8.14} Var ( δ k − 1 ) = 2 D k + 1 σ 2 Var ( δ k ) ( 8.14 )
用文字说:每一层把前向信号的方差乘以 D k σ 2 / 2 D_k\sigma^2/2 D k σ 2 /2 ,把反向信号的方差乘以 D k + 1 σ 2 / 2 D_{k+1}\sigma^2/2 D k + 1 σ 2 /2 。这些因子小于 1 时,两个信号都随深度指数消失;大于 1 时,两者都指数爆炸。当 σ 2 = 2 / D k \sigma^2 = 2/D_k σ 2 = 2/ D k (前向)或 σ 2 = 2 / D k + 1 \sigma^2 = 2/D_{k+1} σ 2 = 2/ D k + 1 (反向)时因子等于 1,这就是 He 初始化 [6] 。所有隐藏层宽度都是 D D D 时,两个条件都是 σ 2 = 2 / D \sigma^2 = 2/D σ 2 = 2/ D 。
图 8.3 用模拟来验证:50 个宽度为 100 的 ReLU 层,没有偏置,一批 256 个随机输入,权重方差为 c / D c/D c / D (c = 1 , 2 , 3 c = 1, 2, 3 c = 1 , 2 , 3 ),顶层给一个随机的误差信号。c = 2 c = 2 c = 2 时,预激活和误差信号在全部 50 层中大小基本不变。c = 1 c = 1 c = 1 时,每层把标准差乘以约 1 / 2 \sqrt{1/2} 1/2 :50 层后,预激活缩小到原来的 1.45 × 10 − 8 1.45 \times 10^{-8} 1.45 × 1 0 − 8 ,到达第一层的误差信号缩小到 1.58 × 10 − 8 1.58 \times 10^{-8} 1.58 × 1 0 − 8 。c = 3 c = 3 c = 3 时,两者每层增大约 3 / 2 \sqrt{3/2} 3/2 倍,分别达到 2.9 × 10 4 2.9 \times 10^{4} 2.9 × 1 0 4 和 1.6 × 10 4 1.6 \times 10^{4} 1.6 × 1 0 4 。
图 8.3 初始权重的尺度决定信号能否穿过 50 层。 一个有 50 个宽度为 100 的层的 ReLU 网络,权重方差 σ 2 = c / D \sigma^2 = c/D σ 2 = c / D ,一批 256 个随机输入,最后一层给一个随机误差信号。a)前向计算中预激活 f k \mathbf{f}_k f k 的均方根大小。b)反向计算(从第 50 层到第 1 层)中误差信号 δ k \boldsymbol{\delta}_k δ k 的大小。c = 2 c = 2 c = 2 (绿色,He 初始化)时两者都保持在 1 附近,与式 8.13 和 8.14 的预测一致。c = 1 c = 1 c = 1 (蓝色)时两者每层缩小约 2 \sqrt{2} 2 倍,c = 3 c = 3 c = 3 (橙色)时每层增大约 1.5 \sqrt{1.5} 1.5 倍。
看权重梯度本身时,会发现一个微妙之处。由式 8.7,第 k k k 层的梯度是 δ k h k ⊤ \boldsymbol{\delta}_k\mathbf{h}_k^\top δ k h k ⊤ 。c = 1 c = 1 c = 1 时,靠后的层激活值小,靠前的层误差信号小,两者的乘积在每一层 都小了大致相同的总倍数 ( c / 2 ) K / 2 ≈ 3 × 10 − 8 (c/2)^{K/2} \approx 3 \times 10^{-8} ( c /2 ) K /2 ≈ 3 × 1 0 − 8 ;模拟测得第 1、25、50 层的权重梯度范数都在 5 × 10 − 8 5 \times 10^{-8} 5 × 1 0 − 8 到 10 − 7 10^{-7} 1 0 − 7 之间。所以在宽度相同的普通 ReLU 网络中,错误的尺度并不会让前面的层比后面的层学得更慢;它让整个网络的输出和梯度都失去意义,而输出只有应有大小的 10 − 8 10^{-8} 1 0 − 8 倍,任何单一的学习率都无法补救。在别的情形下,层与层之间的失衡是真实存在的。sigmoid 或 tanh 单元的斜率几乎处处小于 1,循环网络在每个时间步都乘以同一个矩阵,这时误差信号在往回传时缩小而激活值不缩小,于是前面的层或前面的时间步只得到极小的梯度。这就是经典意义上的梯度消失问题 [8] 。
初始化让信号在训练开始时保持合适的大小,但不能保证权重变化后仍然如此,也解决不了另一个困难:深的普通网络比浅的训练得更差(第 7 篇第 5 节)。第 11 篇介绍的残差连接为每一层加上一条恒等路径,使式 8.12 不再只是一串乘积;归一化层则在每一层重新设定激活值的尺度。
小结
反向传播用一次反向计算算出损失对每个参数的导数,开销只是前向计算的一个小倍数。前向计算保存中间值。反向计算从损失的导数开始,按逆序访问各个值,每经过一个运算乘以一个局部导数,并对一个值的所有使用求和(式 8.5)。对深层网络,这给出三个方程:权重梯度是误差信号乘以它所乘的激活值,是一个外积;误差信号反向越过权重矩阵时乘以其转置;反向越过 ReLU 时把未激活单元置零。反向计算的开销约为前向的两倍,而且训练必须保留每层的激活值,所以训练比预测需要多得多的内存。对 softmax 加交叉熵,反向计算的起点是预测概率减去目标。把同一算法用于任何程序,就是反向模式自动微分。最后,反向计算是逐层因子的乘积,分析它的方差就得到 He 初始化;训练非常深的网络的其余困难留给第 11 篇。第 9 篇接下来从拟合训练数据转向在训练数据之外的泛化。
注释
历史。 Linnainmaa 在 1970 年描述了反向模式求导 [3] ,用来计算长串运算的舍入误差;Werbos 在 1974 年的博士论文中提出把它用于神经网络 [4] 。Rumelhart、Hinton 和 Williams [2] 在 1986 年表明,它能让多层网络学到有用的内部表示,这篇论文使它得到广泛使用。Baydin 等人 [9] 综述了机器学习中的自动微分。
二阶导数。 反向传播给出梯度。二阶导数即海森矩阵有 P 2 P^2 P 2 个元素,神经网络很少显式构造它。海森矩阵与一个向量的乘积,可以通过对反向计算再做前向模式求导,以几次梯度计算的代价得到;许多关于损失曲率的分析就是这样做的。
其他初始化。 对在零附近近似线性的激活函数(如 tanh),去掉式 8.13 中的因子 1 / 2 1/2 1/2 ,就得到 σ 2 = 1 / D \sigma^2 = 1/D σ 2 = 1/ D 。Glorot 和 Bengio [10] 在前向与反向两个条件之间折中,取 σ 2 = 2 / ( D k + D k + 1 ) \sigma^2 = 2/(D_k + D_{k+1}) σ 2 = 2/ ( D k + D k + 1 ) ,常称为 Xavier 初始化。这些推导假设权重相互独立;正交初始化把每个权重矩阵取为随机旋转,在线性网络中能精确保持每个向量的长度。
不可导点。 ReLU 在 0 处没有导数,取最大值和取绝对值等运算也有类似的拐点。自动微分在那里选取一个导数值(对 ReLU 取 0)。由于预激活恰好为零的概率可以忽略,这个选择在实践中很少有影响。
延伸阅读。 Understanding Deep Learning [1] 第 7 章对同一个玩具模型和深层网络推导了反向传播,并讲解了初始化。Griewank 和 Walther [5] 是自动微分的标准参考书。
练习
练习 8.1 对第 2 节的函数链,计算 ∂ ℓ / ∂ x \partial\ell/\partial x ∂ ℓ / ∂ x 。为什么训练从来不需要它?(答案:ω 0 ∂ ℓ / ∂ f 0 = 1.2 × ( − 0.5576 ) = − 0.6691 \omega_0\,\partial\ell/\partial f_0 = 1.2 \times (-0.5576) = -0.6691 ω 0 ∂ ℓ / ∂ f 0 = 1.2 × ( − 0.5576 ) = − 0.6691 。输入是数据而不是参数,梯度下降从不更新它。)
练习 8.2 在第 4 节的网络中,把目标改为 y = 3 y = 3 y = 3 ,其他不变。新的梯度是多少?(答案:前向计算不变,δ 2 = 2 ( 2.25 − 3 ) = − 1.5 \delta_2 = 2(2.25 - 3) = -1.5 δ 2 = 2 ( 2.25 − 3 ) = − 1.5 。所有梯度都与 δ 2 \delta_2 δ 2 成线性关系,所以全部乘以 − 1.5 / 2.5 = − 0.6 -1.5/2.5 = -0.6 − 1.5/2.5 = − 0.6 :例如 ∂ ℓ / ∂ Ω 2 = ( − 5.25 , 0 ) \partial\ell/\partial\boldsymbol{\Omega}_2 = (-5.25, 0) ∂ ℓ / ∂ Ω 2 = ( − 5.25 , 0 ) ,∂ ℓ / ∂ Ω 0 \partial\ell/\partial\boldsymbol{\Omega}_0 ∂ ℓ / ∂ Ω 0 的两行为 ( 0 , 0 ) (0, 0) ( 0 , 0 ) 和 ( − 1.5 , − 3 ) (-1.5, -3) ( − 1.5 , − 3 ) 。)
练习 8.3 对分数 z = ( 2.0 , 0.5 , − 1.0 ) \mathbf{z} = (2.0, 0.5, -1.0) z = ( 2.0 , 0.5 , − 1.0 ) ,当正确类别是第二类时,计算式 8.11 的误差信号。证明式 8.11 各元素之和总是零。(答案:( 0.786 , − 0.825 , 0.039 ) (0.786, -0.825, 0.039) ( 0.786 , − 0.825 , 0.039 ) 。和为 ∑ k p k − 1 = 0 \sum_k p_k - 1 = 0 ∑ k p k − 1 = 0 。)
练习 8.4 设 u = 1 u = 1 u = 1 ,v 1 = 2 u v_1 = 2u v 1 = 2 u ,v 2 = u 2 v_2 = u^2 v 2 = u 2 ,ℓ = v 1 v 2 \ell = v_1 v_2 ℓ = v 1 v 2 。用式 8.5 计算 ∂ ℓ / ∂ u \partial\ell/\partial u ∂ ℓ / ∂ u ,并把 ℓ \ell ℓ 写成 u u u 的函数加以验证。(答案:∂ ℓ / ∂ v 1 = v 2 = 1 \partial\ell/\partial v_1 = v_2 = 1 ∂ ℓ / ∂ v 1 = v 2 = 1 ,∂ ℓ / ∂ v 2 = v 1 = 2 \partial\ell/\partial v_2 = v_1 = 2 ∂ ℓ / ∂ v 2 = v 1 = 2 ,所以 ∂ ℓ / ∂ u = 2 × 1 + 2 u × 2 = 6 \partial\ell/\partial u = 2 \times 1 + 2u \times 2 = 6 ∂ ℓ / ∂ u = 2 × 1 + 2 u × 2 = 6 。直接计算,ℓ = 2 u 3 \ell = 2u^3 ℓ = 2 u 3 ,导数为 6 u 2 = 6 6u^2 = 6 6 u 2 = 6 。)
练习 8.5 对一批 256 个样本,( 784 , 256 , 256 , 10 ) (784, 256, 256, 10) ( 784 , 256 , 256 , 10 ) 分类器的一个训练步(前向加反向)需要多少次乘加?(答案:256 × ( 268,800 + 336,896 ) = 155,058,176 256 \times (268{,}800 + 336{,}896) = 155{,}058{,}176 256 × ( 268 , 800 + 336 , 896 ) = 155 , 058 , 176 ,约 1.55 亿次。)
练习 8.6 对恒等激活函数 a ( z ) = z a(z) = z a ( z ) = z 重做第 8 节的前向方差推导。什么样的权重方差能让预激活保持大小不变?(答案:没有 ReLU 时 E [ h 2 ] = V a r ( f ) \mathbb{E}[h^2] = \mathrm{Var}(f) E [ h 2 ] = Var ( f ) ,所以 V a r ( f k ) = D k σ 2 V a r ( f k − 1 ) \mathrm{Var}(f_k) = D_k\sigma^2\,\mathrm{Var}(f_{k-1}) Var ( f k ) = D k σ 2 Var ( f k − 1 ) ,σ 2 = 1 / D k \sigma^2 = 1/D_k σ 2 = 1/ D k 。)
练习 8.7 在图 8.3 中 c = 1 c = 1 c = 1 的模拟里,按式 8.13,预激活从第 1 层到第 50 层大约应缩小多少倍?与实测的 0.988 0.988 0.988 和 1.45 × 10 − 8 1.45 \times 10^{-8} 1.45 × 1 0 − 8 比较。(答案:( 1 / 2 ) 49 / 2 ≈ 4.2 × 10 − 8 (1/2)^{49/2} \approx 4.2 \times 10^{-8} ( 1/2 ) 49/2 ≈ 4.2 × 1 0 − 8 ;实测比值为 1.47 × 10 − 8 1.47 \times 10^{-8} 1.47 × 1 0 − 8 ,数量级相同。差别来自在 49 层中不断累积的随机波动。)
参考文献
1: Simon J.D. Prince (2023). Understanding Deep Learning . MIT Press.
2: David Rumelhart, Geoffrey Hinton and Ronald Williams (1986). Learning representations by back-propagating errors . Nature 323, 533–536.
3: Seppo Linnainmaa (1970). The representation of the cumulative rounding error of an algorithm as a Taylor expansion of the local rounding errors. Master's thesis, University of Helsinki.
4: Paul Werbos (1974). Beyond regression: new tools for prediction and analysis in the behavioral sciences. PhD thesis, Harvard University.
5: Andreas Griewank and Andrea Walther (2008). Evaluating Derivatives: Principles and Techniques of Algorithmic Differentiation , 2nd edition. SIAM.
6: Kaiming He, Xiangyu Zhang, Shaoqing Ren and Jian Sun (2015). Delving deep into rectifiers: surpassing human-level performance on ImageNet classification . International Conference on Computer Vision .
7: Tianqi Chen, Bing Xu, Chiyuan Zhang and Carlos Guestrin (2016). Training deep nets with sublinear memory cost . arXiv:1604.06174.
8: Yoshua Bengio, Patrice Simard and Paolo Frasconi (1994). Learning long-term dependencies with gradient descent is difficult. IEEE Transactions on Neural Networks 5(2), 157–166.
9: Atılım Güneş Baydin, Barak Pearlmutter, Alexey Radul and Jeffrey Siskind (2018). Automatic differentiation in machine learning: a survey . Journal of Machine Learning Research 18(153), 1–43.
10: Xavier Glorot and Yoshua Bengio (2010). Understanding the difficulty of training deep feedforward neural networks . International Conference on Artificial Intelligence and Statistics .