本文由 AI 自动从英文版翻译
概述
计算多层感知机(MLP)的反向传播(BP)有几种方法。我们的目标是计算损失相对于每一层权重矩阵的梯度,这表示一个标量对矩阵的导数。我们可以使用以下方法:
直接使用矩阵对矩阵的梯度计算 ∂ L ∂ W ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} ∂ W ( l ) ∂ L 。(我们不会使用这种方法,因为矩阵对矩阵的梯度难以计算)
直接计算 ∂ L ∂ W ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} ∂ W ( l ) ∂ L ,同时避免向量对矩阵的梯度。(我们也不会使用这种方法,因为它仍然相当具有挑战性)
计算每个元素的 ∂ L ∂ W i , j ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}_{i,j}} ∂ W i , j ( l ) ∂ L ,然后将它们组装成矩阵。(这是我们将采用的方法)
对于我们选择的方法,虽然计算 ∂ L ∂ W i , j ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}_{i,j}} ∂ W i , j ( l ) ∂ L 足以确定所有梯度,你可以写一个 for 循环来完成更新,但这种方法效率不高。现代加速器(如 GPU)可以显著加速矩阵乘法运算。因此,我们仍然需要将标量结果组装成矩阵形式。
我们将首先检查单个样本的梯度计算。由于 SGD 算法需要一批样本,然后我们将结果扩展到处理批量数据。
预备知识
为了使我们的计算更加直接,我们首先介绍三个你应该理解的关键概念:分母布局、多元链式法则和矩阵组装。
分母布局 :在深度学习社区中,默认使用分母布局计算导数。这意味着标量对矩阵的梯度将产生与原始矩阵相同形状的矩阵。如果结果是原始矩阵的转置,那么我们使用的是分母布局。你可以从这篇维基百科文章 了解更多关于分母布局的信息。
多元链式法则 :我们需要理解多元链式法则。如果 x = x ( t ) x=x(t) x = x ( t ) ,y = y ( t ) y=y(t) y = y ( t ) ,且 z = f ( x , y ) z=f(x,y) z = f ( x , y ) ,那么我们有 ∂ z ∂ t = ∂ f ∂ x ∂ x ∂ t + ∂ f ∂ y ∂ y ∂ t \frac{\partial z}{\partial t}=\frac{\partial f}{\partial x}\frac{\partial x}{\partial t}+\frac{\partial f}{\partial y}\frac{\partial y}{\partial t} ∂ t ∂ z = ∂ x ∂ f ∂ t ∂ x + ∂ y ∂ f ∂ t ∂ y 。这里有一篇关于多元链式法则 的文章。由于我们以标量方式计算梯度,接受向量或矩阵的函数变成多元函数。例如,f ( x ) f(\mathbf{x}) f ( x ) 变成 f ( x 0 , x 1 , ⋯ , x n ) f(\mathbf{x}_0, \mathbf{x}_1, \cdots, \mathbf{x}_n) f ( x 0 , x 1 , ⋯ , x n ) 。因此,在以下推导中,我们将始终使用多元链式法则。
矩阵组装 :最后,我们需要理解如何组装向量或矩阵。对于矩阵 W \mathbf{W} W ,我们可以将其写为 W = [ W i , j ] i , j \mathbf{W}=[\mathbf{W}_{i,j}]_{i,j} W = [ W i , j ] i , j ,其中索引 i i i 和 j j j 表示我们需要遍历每一行和每一列。类似地,对于向量,我们有 v = [ v i ] i \mathbf{v}=[\mathbf{v}_i]_i v = [ v i ] i 。组装可以看作是矩阵乘法的逆过程。例如,两个列向量的乘法 x y ⊤ = [ x i y j ] i , j \mathbf{xy}^\top=[\mathbf{x}_i\mathbf{y}_j]_{i,j} xy ⊤ = [ x i y j ] i , j 。因此,如果我们得到标量结果,其中 W i , j = x i y j \mathbf{W}_{i,j}=\mathbf{x}_i\mathbf{y}_j W i , j = x i y j ,我们可以将其组装成矩阵 W = x y T \mathbf{W}=\mathbf{x}\mathbf{y}^T W = x y T 。
定义
符号 :
标量:x , y , ⋯ x, y, \cdots x , y , ⋯
向量:x , y , ⋯ \mathbf{x}, \mathbf{y}, \cdots x , y , ⋯
矩阵:X , Y , ⋯ \mathbf{X}, \mathbf{Y}, \cdots X , Y , ⋯
下标符号:x i \mathbf{x}_i x i 表示向量 x \mathbf{x} x 的第 i i i 个元素,这是一个标量。
指示函数:1 i j \mathbf{1}_{ij} 1 ij 如果 i = j i=j i = j 则等于 1,否则为 0。
网络架构 :
输入:x \mathbf{x} x ,形状为 [ n , 1 ] [n, 1] [ n , 1 ]
标签:y \mathbf{y} y ,形状为 [ c , 1 ] [c, 1] [ c , 1 ] (独热编码)
层数:L L L
类别数:c c c
线性变换:W x + b \mathbf{Wx+b} Wx + b
第 l l l 层的权重矩阵:W ( l ) \mathbf{W}^{(l)} W ( l )
权重矩阵形状 :
第一层:W ( 1 ) \mathbf{W}^{(1)} W ( 1 ) ,形状为 [ m ( 1 ) , m ( 0 ) = n ] [m^{(1)}, m^{(0)}=n] [ m ( 1 ) , m ( 0 ) = n ]
隐藏层(从 2 到 L − 1 L-1 L − 1 ):W ( l ) \mathbf{W}^{(l)} W ( l ) ,形状为 [ m ( l ) , m ( l − 1 ) ] [m^{(l)}, m^{(l-1)}] [ m ( l ) , m ( l − 1 ) ]
最后一层:W ( L ) \mathbf{W}^{(L)} W ( L ) ,形状为 [ c , m ( L − 1 ) ] [c, m^{(L-1)}] [ c , m ( L − 1 ) ]
激活函数和激活值 :
激活函数:f f f
第 l l l 层的激活值:a ( l ) \mathbf{a}^{(l)} a ( l )
激活值形状 :
输入:a ( 0 ) \mathbf{a}^{(0)} a ( 0 ) ,形状为 [ n , 1 ] [n, 1] [ n , 1 ]
隐藏层激活值(从 1 到 L − 1 L-1 L − 1 ):a ( l ) \mathbf{a}^{(l)} a ( l ) ,形状为 [ m ( l ) , 1 ] [m^{(l)}, 1] [ m ( l ) , 1 ]
输出 logits(最后一层):a ( L ) \mathbf{a}^{(L)} a ( L ) ,形状为 [ c , 1 ] [c, 1] [ c , 1 ]
前向传播
a ( 0 ) = x z ( 1 ) = W ( 1 ) a ( 0 ) + b ( 1 ) a ( 1 ) = f ( z ( 1 ) ) ⋮ ⋮ z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) a ( l ) = f ( z ( l − 1 ) ) ⋮ ⋮ z ( L ) = W ( L ) a ( L − 1 ) + b ( L ) a ( L ) = softmax ( z ( L − 1 ) ) L = CE ( a ( L ) , y ) \begin{align*}
\mathbf{a}^{(0)} &= \mathbf{x} \\
\mathbf{z}^{(1)} &= \mathbf{W}^{(1)}\mathbf{a}^{(0)} + \mathbf{b}^{(1)} \\
\mathbf{a}^{(1)} &= f(\mathbf{z}^{(1)}) \\
\vdots & \quad\vdots\\
\mathbf{z}^{(l)} &= \mathbf{W}^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} \\
\mathbf{a}^{(l)} &= f(\mathbf{z}^{(l-1)}) \\
\vdots & \quad\vdots\\
\mathbf{z}^{(L)} &= \mathbf{W}^{(L)}\mathbf{a}^{(L-1)} + \mathbf{b}^{(L)} \\
\mathbf{a}^{(L)} &= \text{softmax}(\mathbf{z}^{(L-1)}) \\
\mathcal{L} &=\text{CE}(\mathbf{a}^{(L)},y) \\
\end{align*} a ( 0 ) z ( 1 ) a ( 1 ) ⋮ z ( l ) a ( l ) ⋮ z ( L ) a ( L ) L = x = W ( 1 ) a ( 0 ) + b ( 1 ) = f ( z ( 1 ) ) ⋮ = W ( l ) a ( l − 1 ) + b ( l ) = f ( z ( l − 1 ) ) ⋮ = W ( L ) a ( L − 1 ) + b ( L ) = softmax ( z ( L − 1 ) ) = CE ( a ( L ) , y )
其中交叉熵损失为 L = CE ( a ( L ) , y ) = − ∑ i = 1 c y i log ( a i ( L ) ) \mathcal{L}=\text{CE}(\mathbf{a}^{(L)},y)=-\sum_{i=1}^c \mathbf{y}_i\log(\mathbf{a}_i^{(L)}) L = CE ( a ( L ) , y ) = − ∑ i = 1 c y i log ( a i ( L ) ) 。
我们的目标是计算 L \mathcal{L} L 相对于 W ( l ) \mathbf{W}^{(l)} W ( l ) 和 b ( l ) \mathbf{b}^{(l)} b ( l ) 的梯度。在这个推导中,我们将专注于计算相对于 W ( l ) \mathbf{W}^{(l)} W ( l ) 的梯度。相对于 b ( l ) \mathbf{b}^{(l)} b ( l ) 的梯度遵循类似的模式。
最后一层
由于最后一层与其他层不同(它使用 softmax 而不是常规激活函数),我们将单独计算其梯度。
在前向传播中,a i ( L ) = e z i ∑ k = 1 c e z k \mathbf{a}^{(L)}_i=\frac{e^{\mathbf{z}_i}}{\sum_{k=1}^c e^{\mathbf{z}_k}} a i ( L ) = ∑ k = 1 c e z k e z i 表示 softmax 层输出的归一化概率。softmax 梯度的直接计算得到 ∂ a i ( L ) ∂ z j ( L ) = a i ( L ) ( 1 i j − a j ( L ) ) \frac{\partial \mathbf{a}^{(L)}_i}{\partial \mathbf{z}^{(L)}_j}=\mathbf{a}^{(L)}_i(\mathbf{1}_{ij}-\mathbf{a}^{(L)}_j) ∂ z j ( L ) ∂ a i ( L ) = a i ( L ) ( 1 ij − a j ( L ) ) 。
为了计算梯度 ∂ L ∂ z j ( L ) \frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(L)}} ∂ z j ( L ) ∂ L ,我们通过引入激活值 a \mathbf{a} a 使用链式法则。这里的一个重要点是,当我们在标量形式下工作时,我们遇到的大多数函数都是多元函数,因此我们需要使用多元链式法则。例如,L \mathcal{L} L 是应用于 a 0 ( L ) , a 1 ( L ) , … , a c − 1 ( L ) \mathbf{a}_0^{(L)}, \mathbf{a}_1^{(L)}, \dots, \mathbf{a}_{c-1}^{(L)} a 0 ( L ) , a 1 ( L ) , … , a c − 1 ( L ) 的交叉熵函数的结果。根据多元链式法则,我们有 ∂ L ∂ z j ( L ) = ∑ i = 1 c ∂ L ∂ a i ( L ) ∂ a i ( L ) ∂ z j ( L ) \frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(L)}}=\sum_{i=1}^c\frac{\partial \mathcal{L}}{\partial \mathbf{a}_i^{(L)}}\frac{\partial \mathbf{a}_i^{(L)}}{\partial \mathbf{z}_j^{(L)}} ∂ z j ( L ) ∂ L = ∑ i = 1 c ∂ a i ( L ) ∂ L ∂ z j ( L ) ∂ a i ( L ) 。
因此,我们有
∂ L ∂ z j ( L ) = ∑ i = 1 c ∂ L ∂ a i ( L ) ∂ a i ( L ) ∂ z j ( L ) = ∑ i = 1 c ( y i a i ( L ) ) ∂ a i ( L ) ∂ z j ( L ) = − ∑ i = 1 c y i ( 1 i j − a j ( L ) ) = − ∑ i = 1 c y i 1 i j + a j ( L ) ∑ i = 1 c y i = a j ( L ) − y j \begin{align*}
\frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(L)}} &= \sum_{i=1}^c\frac{\partial \mathcal{L}}{\partial \mathbf{a}_i^{(L)}}\frac{\partial \mathbf{a}_i^{(L)}}{\partial \mathbf{z}_j^{(L)}} \\
& =\sum_{i=1}^c(\frac{\mathbf{y}_i}{\mathbf{a}_i^{(L)}})\frac{\partial \mathbf{a}_i^{(L)}}{\partial \mathbf{z}_j^{(L)}} \\
&=-\sum_{i=1}^c\mathbf{y}_i(\mathbf{1}_{ij}-\mathbf{a}^{(L)}_j) \\
&=-\sum_{i=1}^c\mathbf{y}_i\mathbf{1}_{ij}+\mathbf{a}_j^{(L)}\sum_{i=1}^c\mathbf{y}_i \\
&=\mathbf{a}^{(L)}_j-\mathbf{y}_j
\end{align*} ∂ z j ( L ) ∂ L = i = 1 ∑ c ∂ a i ( L ) ∂ L ∂ z j ( L ) ∂ a i ( L ) = i = 1 ∑ c ( a i ( L ) y i ) ∂ z j ( L ) ∂ a i ( L ) = − i = 1 ∑ c y i ( 1 ij − a j ( L ) ) = − i = 1 ∑ c y i 1 ij + a j ( L ) i = 1 ∑ c y i = a j ( L ) − y j
现在我们将 ∂ L ∂ z j ( L ) \frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(L)}} ∂ z j ( L ) ∂ L 组装成向量:∂ L ∂ z ( L ) = a ( L ) − y \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(L)}}=\mathbf{a}^{(L)}-\mathbf{y} ∂ z ( L ) ∂ L = a ( L ) − y 。详细的组装过程是 ∂ L ∂ z ( L ) = [ ∂ L ∂ z j ( L ) ] j = 1 c = [ a j ( L ) − y j ] j = 1 c = [ a j ( L ) ] j = 1 c − [ y j ] j = 1 c = a ( L ) − y \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(L)}}=[\frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(L)}}]_{j=1}^{c}=[\mathbf{a}^{(L)}_j-\mathbf{y}_j]_{j=1}^{c}=[\mathbf{a}^{(L)}_j]_{j=1}^{c}-[\mathbf{y}_j]_{j=1}^{c}=\mathbf{a}^{(L)}-\mathbf{y} ∂ z ( L ) ∂ L = [ ∂ z j ( L ) ∂ L ] j = 1 c = [ a j ( L ) − y j ] j = 1 c = [ a j ( L ) ] j = 1 c − [ y j ] j = 1 c = a ( L ) − y 。
权重梯度
我们可以将误差项定义为:
δ ( l ) = ∂ L ∂ z ( l ) \mathbf{\delta}^{(l)}=\frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(l)}} δ ( l ) = ∂ z ( l ) ∂ L
这是一个列向量,简化了我们的链式法则计算。对于最后一层,δ ( L ) = a ( L ) − y \mathbf{\delta}^{(L)}=\mathbf{a}^{(L)}-\mathbf{y} δ ( L ) = a ( L ) − y 。
如果我们能够计算所有层 l l l 的 δ ( l ) \mathbf{\delta}^{(l)} δ ( l ) ,那么我们可以计算所有层的 ∂ L ∂ W ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} ∂ W ( l ) ∂ L 。这个计算使用多元链式法则。由于我们想使用链式法则通过 z \mathbf{z} z 连接 L \mathcal{L} L 和 W \mathbf{W} W ,我们需要使用多元链式法则:∂ L ∂ W i , j ( l ) = ∑ k ∂ L ∂ z k ( l ) ∂ z k ( l ) ∂ W i , j ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}_{i,j}}=\sum_{k}\frac{\partial \mathcal{L}}{\partial \mathbf{z}_k^{(l)}}\frac{\partial \mathbf{z}_k^{(l)}}{\partial \mathbf{W}^{(l)}_{i,j}} ∂ W i , j ( l ) ∂ L = ∑ k ∂ z k ( l ) ∂ L ∂ W i , j ( l ) ∂ z k ( l ) 。
让我们回顾一下 z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) \mathbf{z}^{(l)}= \mathbf{W}^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) 中的矩阵乘法。我们有 z i ( l ) = ∑ j = 1 n W i , j ( l ) a j ( l − 1 ) + b i ( l ) \mathbf{z}^{(l)}_i=\sum_{j=1}^n \mathbf{W}^{(l)}_{i,j}\mathbf{a}^{(l-1)}_j+\mathbf{b}^{(l)}_i z i ( l ) = ∑ j = 1 n W i , j ( l ) a j ( l − 1 ) + b i ( l ) 。由此,我们可以看到 ∂ z i ( l ) ∂ W i , j ( l ) = a j ( l − 1 ) \frac{\partial \mathbf{z}^{(l)}_i}{\partial \mathbf{W}^{(l)}_{i,j}}=\mathbf{a}^{(l-1)}_j ∂ W i , j ( l ) ∂ z i ( l ) = a j ( l − 1 ) ,对于 i ≠ k i\neq k i = k ,∂ z k ( l ) ∂ W i , j ( l ) = 0 \frac{\partial \mathbf{z}^{(l)}_k}{\partial \mathbf{W}^{(l)}_{i,j}}=0 ∂ W i , j ( l ) ∂ z k ( l ) = 0 (即 W i , j ( l ) \mathbf{W}_{i,j}^{(l)} W i , j ( l ) 只影响 z i ( l ) \mathbf{z}_i^{(l)} z i ( l ) 的计算)。因此,我们有:
∂ L ∂ W i , j ( l ) = ∂ L ∂ z i ( l ) ∂ z i ( l ) ∂ W i , j ( l ) = δ i ( l ) a j ( l − 1 ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}_{i,j}}=\frac{\partial \mathcal{L}}{\partial \mathbf{z}_i^{(l)}}\frac{\partial \mathbf{z}_i^{(l)}}{\partial \mathbf{W}^{(l)}_{i,j}}=\mathbf{\delta}_i^{(l)}\mathbf{a}_j^{(l-1)} ∂ W i , j ( l ) ∂ L = ∂ z i ( l ) ∂ L ∂ W i , j ( l ) ∂ z i ( l ) = δ i ( l ) a j ( l − 1 )
现在让我们将结果组装成矩阵形式。我们有 ∂ L ∂ W ( l ) = [ ∂ L ∂ W i , j ( l ) ] i , j = [ δ i ( l ) a j ( l − 1 ) ] i , j = δ ( l ) a ( l − 1 ) ⊤ \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}}=[\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}_{i,j}}]_{i,j}=[\mathbf{\delta}_i^{(l)}\mathbf{a}_j^{(l-1)}]_{i,j}=\mathbf{\delta}^{(l)}\mathbf{a}^{(l-1)\top} ∂ W ( l ) ∂ L = [ ∂ W i , j ( l ) ∂ L ] i , j = [ δ i ( l ) a j ( l − 1 ) ] i , j = δ ( l ) a ( l − 1 ) ⊤ 。
特别是对于最后一层,我们有 ∂ L ∂ W i , j ( L ) = ( a i ( L ) − y i ) a j ( L − 1 ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(L)}_{i,j}}=(\mathbf{a}^{(L)}_i-\mathbf{y}_i)\mathbf{a}_j^{(L-1)} ∂ W i , j ( L ) ∂ L = ( a i ( L ) − y i ) a j ( L − 1 ) ,以及 ∂ L ∂ W ( L ) = ( a ( L ) − y ) a ( L − 1 ) ⊤ \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(L)}}=(\mathbf{a}^{(L)}-\mathbf{y})\mathbf{a}^{(L-1)\top} ∂ W ( L ) ∂ L = ( a ( L ) − y ) a ( L − 1 ) ⊤ 。
层间反向传播
现在唯一剩下的任务是计算所有层 l l l 的 δ ( l ) \mathbf{\delta}^{(l)} δ ( l ) 。我们可以使用链式法则计算所有层的 δ ( l ) \mathbf{\delta}^{(l)} δ ( l ) :
δ j ( l − 1 ) = ∂ L ∂ z j ( l − 1 ) = ∑ k ∂ L ∂ z k ( l ) ∂ z k ( l ) ∂ z j ( l − 1 ) = ∑ k ∂ L ∂ z k ( l ) ∂ z k ( l ) ∂ a k ( l − 1 ) ∂ a k ( l − 1 ) ∂ z j ( l − 1 ) \begin{align*}
\mathbf{\delta}_j^{(l-1)} &= \frac{\partial \mathcal{L}}{\partial \mathbf{z}_j^{(l-1)}} \\
&= \sum_{k}\frac{\partial \mathcal{L}}{\partial \mathbf{z}_k^{(l)}}\frac{\partial \mathbf{z}_k^{(l)}}{\partial \mathbf{z}_j^{(l-1)}} \\
&= \sum_{k}\frac{\partial \mathcal{L}}{\partial \mathbf{z}_k^{(l)}}\frac{\partial \mathbf{z}_k^{(l)}}{\partial \mathbf{a}_k^{(l-1)}}\frac{\partial \mathbf{a}_k^{(l-1)}}{\partial \mathbf{z}_j^{(l-1)}} \\
\end{align*} δ j ( l − 1 ) = ∂ z j ( l − 1 ) ∂ L = k ∑ ∂ z k ( l ) ∂ L ∂ z j ( l − 1 ) ∂ z k ( l ) = k ∑ ∂ z k ( l ) ∂ L ∂ a k ( l − 1 ) ∂ z k ( l ) ∂ z j ( l − 1 ) ∂ a k ( l − 1 )
第二步成立是因为 z j ( l − 1 ) \mathbf{z}_j^{(l-1)} z j ( l − 1 ) 通过线性变换贡献给每个 z k ( l ) \mathbf{z}_k^{(l)} z k ( l ) 。第三步是因为 z j ( l − 1 ) \mathbf{z}_j^{(l-1)} z j ( l − 1 ) 只通过非线性变换影响 a j ( l − 1 ) \mathbf{a}_j^{(l-1)} a j ( l − 1 ) 。
由于 a j ( l − 1 ) = f ( z j ( l − 1 ) ) \mathbf{a}_j^{(l-1)}=f(\mathbf{z}_j^{(l-1)}) a j ( l − 1 ) = f ( z j ( l − 1 ) ) ,我们有 ∂ a j ( l − 1 ) ∂ z j ( l − 1 ) = f ′ ( z j ( l − 1 ) ) \frac{\partial \mathbf{a}_j^{(l-1)}}{\partial \mathbf{z}_j^{(l-1)}}=f'(\mathbf{z}_j^{(l-1)}) ∂ z j ( l − 1 ) ∂ a j ( l − 1 ) = f ′ ( z j ( l − 1 ) ) 。由于 z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) \mathbf{z}^{(l)}=\mathbf{W}^{(l)}\mathbf{a}^{(l-1)}+\mathbf{b}^{(l)} z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) ,从矩阵乘法,我们有 ∂ z k ( l ) ∂ a j ( l − 1 ) = W k , j ( l ) \frac{\partial \mathbf{z}_k^{(l)}}{\partial \mathbf{a}_j^{(l-1)}}=\mathbf{W}^{(l)}_{k,j} ∂ a j ( l − 1 ) ∂ z k ( l ) = W k , j ( l ) 。因此,我们有:
δ j ( l − 1 ) = ∑ k δ k ( l ) W k , j ( l ) f ′ ( z j ( l − 1 ) ) \mathbf{\delta}_j^{(l-1)}=\sum_{k}\mathbf{\delta}_k^{(l)}\mathbf{W}^{(l)}_{k,j}f'(\mathbf{z}_j^{(l-1)}) δ j ( l − 1 ) = ∑ k δ k ( l ) W k , j ( l ) f ′ ( z j ( l − 1 ) )
现在让我们将结果组装成向量形式。我们有:
δ ( l − 1 ) = [ δ j ( l − 1 ) ] j = 1 m = [ ∑ k δ k ( l ) W k , j ( l ) f ′ ( z j ( l − 1 ) ) ] j = 1 m = [ ∑ k δ k ( l ) W k , j ( l ) ] j = 1 m ⊙ [ f ′ ( z j ( l − 1 ) ) ] j = 1 m = [ W : , j ( l ) ⊤ δ ( l ) ] j = 1 m ⊙ f ′ ( z ( l − 1 ) ) = W ( l ) ⊤ δ ( l ) ⊙ f ′ ( z ( l − 1 ) ) \begin{align*}
\mathbf{\delta}^{(l-1)} &=[\mathbf{\delta}_j^{(l-1)}]_{j=1}^m=[\sum_{k}\mathbf{\delta}_k^{(l)}\mathbf{W}^{(l)}_{k,j}f'(\mathbf{z}_j^{(l-1)})]_{j=1}^m \\
&=[\sum_{k}\mathbf{\delta}_k^{(l)}\mathbf{W}^{(l)}_{k,j}]_{j=1}^m\odot[f'(\mathbf{z}_j^{(l-1)})]_{j=1}^m \\
&=[\mathbf{W}^{(l)\top}_{:,j} \mathbf{\delta}^{(l)}]_{j=1}^m\odot f'(\mathbf{z}^{(l-1)}) \\
&=\mathbf{W}^{(l)\top}\mathbf{\delta}^{(l)}\odot f'(\mathbf{z}^{(l-1)})
\end{align*} δ ( l − 1 ) = [ δ j ( l − 1 ) ] j = 1 m = [ k ∑ δ k ( l ) W k , j ( l ) f ′ ( z j ( l − 1 ) ) ] j = 1 m = [ k ∑ δ k ( l ) W k , j ( l ) ] j = 1 m ⊙ [ f ′ ( z j ( l − 1 ) ) ] j = 1 m = [ W : , j ( l ) ⊤ δ ( l ) ] j = 1 m ⊙ f ′ ( z ( l − 1 ) ) = W ( l ) ⊤ δ ( l ) ⊙ f ′ ( z ( l − 1 ) )
批量处理
我们可以将上述计算扩展到处理批量数据。在我们之前的讨论中,每个样本被表示为列向量,但在深度学习编程中,我们通常将样本表示为矩阵中的行。我们有 X = [ x 1 ⊤ , x 2 ⊤ , ⋯ , x b ⊤ ] \mathbf{X}=[\mathbf{x}_1^\top,\mathbf{x}_2^\top,\cdots,\mathbf{x}_b^\top] X = [ x 1 ⊤ , x 2 ⊤ , ⋯ , x b ⊤ ] ,形状为 [ b , n ] [b, n] [ b , n ] ,其中 x i \mathbf{x}_i x i 是列向量。类似地,我们有 Y = [ y 1 , y 2 , ⋯ , y b ] \mathbf{Y}=[\mathbf{y}_1,\mathbf{y}_2,\cdots,\mathbf{y}_b] Y = [ y 1 , y 2 , ⋯ , y b ] ,形状为 [ b , c ] [b, c] [ b , c ] ,其中 y i \mathbf{y}_i y i 是列向量。
总损失为 L = 1 b ∑ i = 1 b L ( x i , y i ) \mathcal{L}=\frac{1}{b}\sum_{i=1}^b\mathcal{L}(\mathbf{x}_i,\mathbf{y}_i) L = b 1 ∑ i = 1 b L ( x i , y i ) 。向量 a ( l ) , z ( l ) , δ ( l ) \mathbf{a}^{(l)},\mathbf{z}^{(l)},\mathbf{\delta}^{(l)} a ( l ) , z ( l ) , δ ( l ) 分别变成矩阵 A ( l ) , Z ( l ) , Δ ( l ) \mathbf{A}^{(l)},\mathbf{Z}^{(l)},\mathbf{\Delta}^{(l)} A ( l ) , Z ( l ) , Δ ( l ) ,形状均为 [ b , m ( l ) ] [b, m^{(l)}] [ b , m ( l ) ] 。对于线性变换,我们有:
Z ( l ) = A ( l − 1 ) W ( l ) ⊤ + B ( l ) \mathbf{Z}^{(l)}=\mathbf{A}^{(l-1)}\mathbf{W}^{(l)\top}+\mathbf{B}^{(l)} Z ( l ) = A ( l − 1 ) W ( l ) ⊤ + B ( l )
其中 B ( l ) \mathbf{B}^{(l)} B ( l ) 是通过在所有样本上堆叠偏置向量 b ( l ) \mathbf{b}^{(l)} b ( l ) 形成的。对于非线性变换,我们有 A ( l ) = f ( Z ( l ) ) \mathbf{A}^{(l)}=f(\mathbf{Z}^{(l)}) A ( l ) = f ( Z ( l ) ) 。(或者,你可以将权重矩阵 W \mathbf{W} W 定义为我们原始定义的转置,以避免线性变换中的转置)。
注意,由于我们之前对单个向量的讨论是按元素进行的,对于批量数据的矩阵情况的推导遵循类似的模式。
损失和 Softmax
对于损失和 softmax,∂ L ∂ Z i , j ( L ) = 1 b ( A i , j ( L ) − Y i , j ) \frac{\partial \mathcal{L}}{\partial \mathbf{Z}^{(L)}_{i,j}}=\frac{1}{b}(\mathbf{A}^{(L)}_{i,j}-\mathbf{Y}_{i,j}) ∂ Z i , j ( L ) ∂ L = b 1 ( A i , j ( L ) − Y i , j ) 。组装过程很简单,得到 ∂ L ∂ Z ( L ) = 1 b ( A ( L ) − Y ) \frac{\partial \mathcal{L}}{\partial \mathbf{Z}^{(L)}}=\frac{1}{b}(\mathbf{A}^{(L)}-\mathbf{Y}) ∂ Z ( L ) ∂ L = b 1 ( A ( L ) − Y ) 。
权重梯度
对于权重的梯度,更新等价于 Z ( l ) ⊤ = W ( l ) A ( l − 1 ) ⊤ + B ( l ) ⊤ \mathbf{Z}^{(l)\top}=\mathbf{W}^{(l)}\mathbf{A}^{(l-1)\top}+\mathbf{B}^{(l)\top} Z ( l ) ⊤ = W ( l ) A ( l − 1 ) ⊤ + B ( l ) ⊤ ,这与向量形式更相似。由于权重参与每个样本的计算,使用多元链式法则,我们有:
∂ L ∂ W i , j ( l ) = ∑ k = 1 b ∂ L ∂ Z i , k ( l ) ∂ Z i , k ( l ) ∂ W i , j ( l ) = ∑ k = 1 b Δ i , k ( l ) A k , j ( l − 1 ) \frac{\partial\mathcal{L}}{\partial \mathbf{W}_{i,j}^{(l)}}=\sum_{k=1}^{b}\frac{\partial \mathcal{L}}{\partial \mathbf{Z}_{i,k}^{(l)}}\frac{\partial \mathbf{Z}_{i,k}^{(l)}}{\partial \mathbf{W}_{i,j}^{(l)}}=\sum_{k=1}^b\mathbf{\Delta}_{i,k}^{(l)}\mathbf{A}_{k,j}^{(l-1)} ∂ W i , j ( l ) ∂ L = ∑ k = 1 b ∂ Z i , k ( l ) ∂ L ∂ W i , j ( l ) ∂ Z i , k ( l ) = ∑ k = 1 b Δ i , k ( l ) A k , j ( l − 1 )
组装后,我们有:
∂ L ∂ W ( l ) = Δ ( l ) ⊤ A ( l − 1 ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}}=\mathbf{\Delta}^{(l)\top}\mathbf{A}^{(l-1)} ∂ W ( l ) ∂ L = Δ ( l ) ⊤ A ( l − 1 )
让我们进行快速形状检查以验证我们的结果。∂ L ∂ W ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} ∂ W ( l ) ∂ L 应该与 W ( l ) \mathbf{W}^{(l)} W ( l ) 具有相同的形状,即 [ m ( l ) , m ( l − 1 ) ] [m^{(l)}, m^{(l-1)}] [ m ( l ) , m ( l − 1 ) ] 。Δ ( l ) ⊤ \mathbf{\Delta}^{(l)\top} Δ ( l ) ⊤ 的形状为 [ m ( l ) , b ] [m^{(l)}, b] [ m ( l ) , b ] ,A ( l − 1 ) \mathbf{A}^{(l-1)} A ( l − 1 ) 的形状为 [ b , m ( l − 1 ) ] [b, m^{(l-1)}] [ b , m ( l − 1 ) ] 。因此,Δ ( l ) ⊤ A ( l − 1 ) \mathbf{\Delta}^{(l)\top}\mathbf{A}^{(l-1)} Δ ( l ) ⊤ A ( l − 1 ) 的形状为 [ m ( l ) , m ( l − 1 ) ] [m^{(l)}, m^{(l-1)}] [ m ( l ) , m ( l − 1 ) ] ,与 W ( l ) \mathbf{W}^{(l)} W ( l ) 匹配。
层间反向传播
最后,对于从 Δ ( l ) \mathbf{\Delta}^{(l)} Δ ( l ) 到 Δ ( l − 1 ) \mathbf{\Delta}^{(l-1)} Δ ( l − 1 ) 的更新,由于每个样本彼此独立,很容易看出 Δ ( l − 1 ) ⊤ = W ( l ) ⊤ Δ ( l ) ⊤ ⊙ f ′ ( Z ( l − 1 ) ⊤ ) \mathbf{\Delta}^{(l-1)\top}=\mathbf{W}^{(l)\top}\mathbf{\Delta}^{(l)\top}\odot f'(\mathbf{Z}^{(l-1)\top}) Δ ( l − 1 ) ⊤ = W ( l ) ⊤ Δ ( l ) ⊤ ⊙ f ′ ( Z ( l − 1 ) ⊤ ) ,这意味着 Δ ( l − 1 ) = Δ ( l ) W ( l ) ⊙ f ′ ( Z ( l − 1 ) ) \mathbf{\Delta}^{(l-1)}=\mathbf{\Delta}^{(l)}\mathbf{W}^{(l)}\odot f'(\mathbf{Z}^{(l-1)}) Δ ( l − 1 ) = Δ ( l ) W ( l ) ⊙ f ′ ( Z ( l − 1 ) ) 。这是从向量形式的直接扩展。
实现
现在我们有了反向传播算法的完整推导:
Δ ( L ) = 1 b ( A ( L ) − Y ) Δ ( l ) = Δ ( l + 1 ) W ( l + 1 ) ⊙ f ′ ( Z ( l ) ) ∂ L ∂ W ( l ) = Δ ( l ) ⊤ A ( l − 1 ) \begin{align*}
\mathbf{\Delta}^{(L)} & = \frac{1}{b}(\mathbf{A}^{(L)}-\mathbf{Y}) \\
\mathbf{\Delta}^{(l)} & = \mathbf{\Delta}^{(l+1)}\mathbf{W}^{(l+1)}\odot f'(\mathbf{Z}^{(l)}) \\
\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} & = \mathbf{\Delta}^{(l)\top}\mathbf{A}^{(l-1)} \\
\end{align*} Δ ( L ) Δ ( l ) ∂ W ( l ) ∂ L = b 1 ( A ( L ) − Y ) = Δ ( l + 1 ) W ( l + 1 ) ⊙ f ′ ( Z ( l ) ) = Δ ( l ) ⊤ A ( l − 1 )
现在我们可以轻松实现反向传播。没有偏置的多层感知机的伪代码如下。注意 W \mathbf{W} W 矩阵严格遵循上述定义,并与 PyTorch 的 nn.Linear 一致。
# PyTorch-style API implementation
# W is a list of weight matrices
def forward_pass (X, W):
L = len (W)
A, Z = [], []
for l in range (L - 1 ):
Z[l] = A[l - 1 ] @ W[l].T
A[l] = f(Z[l])
Z[L - 1 ] = A[L - 2 ] @ W[L - 1 ]
A[L - 1 ] = softmax(Z[L - 1 ])
L = - np.sum(Y * np.log(A[L - 1 ]))
return L, A, Z
def backward_pass (Y, A, Z, W):
L = len (W)
Delta, W_g = [], []
Delta[L - 1 ] = (A[L - 1 ] - Y) / Y.shape[ 0 ]
for l in range (L - 2 , 0 , - 1 ):
Delta[l] = Delta[l + 1 ] @ W[l + 1 ] * d_f(Z[l])
W_g[l] = Delta[l + 1 ].T @ A[l - 1 ]
return W_g
L, A, Z = forward_pass(X, W)
W_g = backward_pass(Y, A, Z, W) # Equivalent to PyTorch's L.backward()