• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

《神经网络的梯度推导与代码验证》之FNN(DNN)的前向传播和反向梯度推导

开发技术 开发技术 3周前 (09-02) 20次浏览

《神经网络的梯度推导与代码验证》之数学基础篇:矩阵微分与求导中,我们总结了一些用于推导神经网络反向梯度求导的重要的数学技巧。此外,通过一个简单的demo,我们初步了解了使用矩阵求导来批量求神经网络参数的做法。在本篇章,我们将专门针对DNN/FNN这种网络结构进行前向传播介绍和反向梯度推导。更多相关内容请见《神经网络的梯度推导与代码验证》系列介绍

 

注意:

  • 本系列的关注点主要在反向梯度推导以及代码上的验证,涉及到的前向传播相对而言不会做太详细的介绍。
  • 反向梯度求导涉及到矩阵微分和求导的相关知识,请见《神经网络的梯度推导与代码验证》之数学基础篇:矩阵微分与求导

目录

  • 2.1 FNN(DNN)的前向传播
  • 2.2 FNN(DNN)的反向传播
  • 2.3 总结
  • 参考资料

 


 

2.1 FNN(DNN)的前向传播

下面是两张DNN的示意图:

 《神经网络的梯度推导与代码验证》之FNN(DNN)的前向传播和反向梯度推导

 

我们用$w_{24}^{3}$来表示第2层的第4个神经元与第三层第2个神经元之间的参数

《神经网络的梯度推导与代码验证》之FNN(DNN)的前向传播和反向梯度推导

 

我们用$b_{3}^{2}$表示第2层的第3个神经元的偏置。用$a_{1}^{3}$表示第3层的第1个神经元的输出(注意是经过激活函数后的)。

 

上图的从第一层到第二层的参数计算公式如下:

$a_{1}^{2} = sigmaleft( {w_{11}^{2}x_{1} + w_{12}^{2}x_{2} + w_{13}^{2}x_{3} + b_{1}^{2}} right)$

$a_{2}^{2} = sigmaleft( {w_{21}^{2}x_{1} + w_{22}^{2}x_{2} + w_{23}^{2}x_{3} + b_{2}^{2}} right)$

$a_{3}^{2} = sigmaleft( {w_{31}^{2}x_{1} + w_{32}^{2}x_{2} + w_{33}^{2}x_{3} + b_{3}^{2}} right)$

$a_{4}^{2} = sigmaleft( {w_{41}^{2}x_{1} + w_{42}^{2}x_{2} + w_{43}^{2}x_{3} + b_{4}^{2}} right)$

其中$sigmaleft( ~ right)$表示激活函数。

 

将上图写成矩阵的编排方式就是下面这样:

$leftlbrack begin{array}{l} begin{array}{l} a_{1}^{2} \ a_{2}^{2} \ end{array} \ a_{3}^{2} \ a_{4}^{2} \ end{array} rightrbrack = sigmaleft( {leftlbrack begin{array}{lll} begin{array}{l} w_{11}^{2} \ w_{21}^{2} \ end{array} & begin{array}{l} w_{12}^{2} \ w_{22}^{2} \ end{array} & begin{array}{l} w_{13}^{2} \ w_{23}^{2} \ end{array} \ w_{31}^{2} & w_{32}^{2} & w_{33}^{2} \ w_{41}^{2} & w_{42}^{2} & w_{43}^{2} \ end{array} rightrbrackleftlbrack begin{array}{l} x_{1} \ x_{2} \ x_{3} \ end{array} rightrbrack + leftlbrack begin{array}{l} begin{array}{l} b_{1}^{2} \ b_{2}^{2} \ end{array} \ b_{3}^{2} \ b_{4}^{2} \ end{array} rightrbrack} right)$

$boldsymbol{a}^{2} = sigmaleft( {boldsymbol{W}^{2}boldsymbol{x} + boldsymbol{b}^{2}} right)$

同理得到第二层到第三层的计算公式:

$boldsymbol{a}^{3} = sigmaleft( {boldsymbol{W}^{3}boldsymbol{a}^{2} + boldsymbol{b}^{3}} right)$

于是总结下来,DNN的层间关系如下:

$boldsymbol{a}^{boldsymbol{l}} = sigmaleft( {boldsymbol{W}^{l}boldsymbol{a}^{l – 1} + boldsymbol{b}^{l}} right)$

所以DNN的前向传播逻辑如下:

输入:总层数L,所有隐藏层和输出层对应的参数矩阵$boldsymbol{W}$,偏置向量$boldsymbol{b}$和输入向量$boldsymbol{x}$

输出:$boldsymbol{a}^{L}$

1) 初始化$boldsymbol{a}^{1} = boldsymbol{x}$

2) for $l = 2$ to L,计算:$boldsymbol{a}^{l} = sigmaleft( {boldsymbol{W}^{l}boldsymbol{a}^{l – 1} + boldsymbol{b}^{l}} right)$

最后的结果即为输出$boldsymbol{a}^{L}$

 


 

2.2 FNN(DNN)的反向梯度求导

在进行DNN反向传播算法前,我们需要选择一个损失函数,来度量训练样本计算出的输出和真实的训练样本输出之间的损失。这里用mse作为损失函数,则每一条样本的loss计算公式如下:

$l = frac{1}{2}left| {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right|_{2}^{2} = frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)$

其中,$boldsymbol{y}$就是样本标签$boldsymbol{y}_boldsymbol{t}boldsymbol{u}boldsymbol{r}boldsymbol{e}$,而$boldsymbol{a}^{boldsymbol{L}}$就是预测值$boldsymbol{y}_boldsymbol{p}boldsymbol{r}boldsymbol{e}boldsymbol{d}boldsymbol{i}boldsymbol{c}boldsymbol{t}$。

预测值$boldsymbol{a}^{boldsymbol{L}}$和输入$boldsymbol{x}$满足$boldsymbol{a}^{boldsymbol{L}} = boldsymbol{D}boldsymbol{N}boldsymbol{N}left( {boldsymbol{x};boldsymbol{W},boldsymbol{b}} right)$,这就是2.1中提到的DNN的前向传播过程,这么看来,所谓前向传播,不过是一个复杂的函数罢了。

 

于是写得再全一点,某条样本$boldsymbol{a}^{boldsymbol{L}} = boldsymbol{D}boldsymbol{N}boldsymbol{N}left( {boldsymbol{x};boldsymbol{W},boldsymbol{b}} right)$根据mse计算出来的loss就是下面这样:

$lleft( {boldsymbol{x},boldsymbol{y},boldsymbol{W},boldsymbol{b}} right) = frac{1}{2}left| {boldsymbol{D}boldsymbol{N}boldsymbol{N}left| {boldsymbol{x};boldsymbol{W},boldsymbol{b}} right| – boldsymbol{y}} right|_{2}^{2} = frac{1}{2}left( {boldsymbol{D}boldsymbol{N}boldsymbol{N}left( {boldsymbol{x};boldsymbol{W},boldsymbol{b}} right) – boldsymbol{y}} right)^{T}left( {boldsymbol{D}boldsymbol{N}boldsymbol{N}left( {boldsymbol{x};boldsymbol{W},boldsymbol{b}} right) – boldsymbol{y}} right)$

 

铺垫了这么多接下来正式开始求梯度。

我们先求$frac{partial l}{partialboldsymbol{a}^{boldsymbol{L}}}$,

$dl = frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T}dleft( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right) + frac{1}{2}dleft( left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T} right)left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right) = frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T}dboldsymbol{a}^{boldsymbol{L}} + frac{1}{2}dleft( left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T} right)left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)$

对$frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)dleft( left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T} right)$使用迹技巧,有:

$frac{1}{2}dleft( left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T} right)left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right) = frac{1}{2}trleft( {dleft( left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T} right)left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)} right) = frac{1}{2}trleft( {left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{boldsymbol{T}}dleft( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)} right) = frac{1}{2}trleft( {left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{boldsymbol{T}}dboldsymbol{a}^{boldsymbol{L}}} right) = frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{boldsymbol{T}}dboldsymbol{a}^{boldsymbol{L}}$

所以有:

$dl = frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{T}dboldsymbol{a}^{boldsymbol{L}} + frac{1}{2}left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{boldsymbol{T}}dboldsymbol{a}^{boldsymbol{L}} = left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right)^{boldsymbol{T}}dboldsymbol{a}^{boldsymbol{L}}$

$frac{partial l}{partialboldsymbol{a}^{boldsymbol{L}}} = boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}$

我们令$boldsymbol{z}^{L} = boldsymbol{W}^{boldsymbol{L}}boldsymbol{a}^{boldsymbol{L} – 1} + boldsymbol{b}^{boldsymbol{L}}$

可求得$frac{partial l}{partialboldsymbol{z}^{boldsymbol{L}}} = left( frac{partialboldsymbol{a}^{boldsymbol{L}}}{partialboldsymbol{a}^{boldsymbol{L}}} right)^{T}frac{partial l}{partialboldsymbol{a}^{boldsymbol{L}}}$

因为$dboldsymbol{a}^{boldsymbol{L}} = dsigmaleft( boldsymbol{z}^{L} right) = sigma^{‘}left( boldsymbol{z}^{L} right) odot dboldsymbol{z}^{L} = diagleft( {sigma^{‘}left( boldsymbol{z}^{L} right)} right)dboldsymbol{z}^{L}$

所以$frac{partial l}{partialboldsymbol{z}^{boldsymbol{L}}} = diagleft( {sigma^{‘}left( boldsymbol{z}^{L} right)} right)left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right) = left( {boldsymbol{a}^{boldsymbol{L}} – boldsymbol{y}} right) odot sigma^{‘}left( boldsymbol{z}^{L} right)$

有了$frac{partial l}{partialboldsymbol{z}^{boldsymbol{L}}}$,那么求L层的$boldsymbol{W}^{boldsymbol{L}}$和$boldsymbol{b}^{boldsymbol{L}}$的梯度就非常容易了,根据标量对线性变换的求导结论,直接得到:

$frac{partial l}{partialboldsymbol{W}^{boldsymbol{L}}} = frac{partial l}{partialboldsymbol{z}^{boldsymbol{L}}}left( boldsymbol{a}^{boldsymbol{L} – 1} right)^{T}$

$frac{partial l}{partialboldsymbol{b}^{boldsymbol{L}}} = frac{partial l}{partialboldsymbol{z}^{boldsymbol{L}}}$

这样第L层的所有参数的梯度就得到了。

为了方便起见,今后用$delta^{l}$表示$frac{partial l}{partialboldsymbol{z}^{boldsymbol{l}}}$。

有上面求L层参数的梯度的思路,可以发现,如果我们想求出第$l$层的参数的梯度,我们可以先求出$delta^{l}$然后直接套用标量对线性变换的求导结论就可以快速求得结果了。因此,这里我们用数学归纳法,第L层的$delta^{L}$我们已经求出来了,假设第$l+1$层的$delta^{l + 1}$已求出来,那我们如何求$delta^{l}$呢?

 

根据链式法则,有$frac{partial l}{partialboldsymbol{z}^{boldsymbol{l}}} = left( frac{partialboldsymbol{z}^{boldsymbol{l} + 1}}{partialboldsymbol{z}^{boldsymbol{l}}} right)^{T}frac{partial l}{partialboldsymbol{z}^{boldsymbol{l} + 1}}$

现在问题转到求$frac{partialboldsymbol{z}^{boldsymbol{l} + 1}}{partialboldsymbol{z}^{boldsymbol{l}}}$上。

我们注意到有$boldsymbol{z}^{l + 1} = boldsymbol{W}^{l + 1}sigmaleft( boldsymbol{z}^{boldsymbol{l}} right) + boldsymbol{b}^{l + 1}$成立,

所以$dboldsymbol{z}^{l + 1} = boldsymbol{W}^{l + 1}dsigmaleft( boldsymbol{z}^{boldsymbol{l}} right) = boldsymbol{W}^{l + 1}left( {sigma^{‘}left( boldsymbol{z}^{l} right) odot dboldsymbol{z}^{l}} right) = boldsymbol{W}^{l + 1}diagleft( {sigma^{‘}left( boldsymbol{z}^{boldsymbol{l}} right)} right)dboldsymbol{z}^{boldsymbol{l}}$

所以$frac{partialboldsymbol{z}^{l + 1}}{partialboldsymbol{z}^{l}} = boldsymbol{W}^{l + 1}diagleft( {sigma^{‘}left( boldsymbol{z}^{boldsymbol{l}} right)} right)$

于是通过$delta^{l + 1}$,我们可以求得:

$delta^{l} = diagleft( {sigma^{‘}left( boldsymbol{z}^{boldsymbol{l}} right)} right)left( boldsymbol{W}^{l + 1} right)^{T}delta^{l + 1} = left( boldsymbol{W}^{l + 1} right)^{T}delta^{l + 1} odot sigma^{‘}left( boldsymbol{z}^{boldsymbol{l}} right)$

同理,根据$delta^{l + 1}$可以秒求出$boldsymbol{W}^{l} = delta^{l}left( boldsymbol{a}^{boldsymbol{l} – 1} right)^{T}$,$boldsymbol{b}^{boldsymbol{l}} = delta^{l}$

 


 

2.3 总结

在求神经网络某一层的参数的梯度时,先求出$delta^{l}$是一种比较合理的策略,因为借助标量对线性变换的求导结论可以快速通过$delta^{l}$求得参数的梯度;通过推导出$delta^{l}$与$delta^{l+1}$的关系,可以将这种求参数梯度的模式推广到其他层上。

 

同时我们也可以发现,对参数梯度造成影响的因素主要有以下几个:

  • 损失函数的选取,它决定了最初的$frac{partial l}{partialboldsymbol{a}^{boldsymbol{L}}}$
  • 激活函数的选取,它决定了层间$delta^{l} = boldsymbol{W}^{boldsymbol{l} + 1}diagleft( {sigma^{‘}left( boldsymbol{z}^{boldsymbol{l}} right)} right)delta^{l + 1}$的递推计算
  • 神经网络的参数,例如每一层的神经元个数影响了$boldsymbol{W}$的尺寸;而整体深度则影响了神经网络隐藏层(尤其是靠前的隐藏层)的梯度稳定性(靠前的隐藏层可能会发生梯度消失或梯度爆炸)。
  • 神经网络的结构,因为显然它会直接影响反向梯度的推导方式(在LSTM的反向梯度推导中大家会有更深的体会)。

 


 

参考资料

(欢迎转载,转载请注明出处。欢迎留言或沟通交流: lxwalyw@gmail.com

 

 

 

 

 


喜欢 (0)