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


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

     

    注意:


    目录

    提醒:

    • 后续会反复出现$oldsymbol{delta}^{l}$这个(类)符号,它的定义为$oldsymbol{delta}^{l} = frac{partial l}{partialoldsymbol{z}^{oldsymbol{l}}}$,即loss $l$对$oldsymbol{z}^{oldsymbol{l}}$的导数
    • 其中$oldsymbol{z}^{oldsymbol{l}}$表示第$l$层(DNN,CNN,RNN或其他例如max pooling层等)未经过激活函数的输出。
    • $oldsymbol{a}^{oldsymbol{l}}$则表示$oldsymbol{z}^{oldsymbol{l}}$经过激活函数后的输出。

    这些符号会贯穿整个系列,还请留意。


    2.1 FNN(DNN)的前向传播

    下面是两张DNN的示意图:

     

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

    我们用$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}} ight)$

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

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

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

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

     

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

    $leftlbrack egin{array}{l} egin{array}{l} a_{1}^{2} \ a_{2}^{2} \ end{array} \ a_{3}^{2} \ a_{4}^{2} \ end{array} ight brack = sigmaleft( {leftlbrack egin{array}{lll} egin{array}{l} w_{11}^{2} \ w_{21}^{2} \ end{array} & egin{array}{l} w_{12}^{2} \ w_{22}^{2} \ end{array} & egin{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} ight brackleftlbrack egin{array}{l} x_{1} \ x_{2} \ x_{3} \ end{array} ight brack + leftlbrack egin{array}{l} egin{array}{l} b_{1}^{2} \ b_{2}^{2} \ end{array} \ b_{3}^{2} \ b_{4}^{2} \ end{array} ight brack} ight)$

    $oldsymbol{a}^{2} = sigmaleft( {oldsymbol{W}^{2}oldsymbol{x} + oldsymbol{b}^{2}} ight)$

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

    $oldsymbol{a}^{3} = sigmaleft( {oldsymbol{W}^{3}oldsymbol{a}^{2} + oldsymbol{b}^{3}} ight)$

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

    $oldsymbol{a}^{oldsymbol{l}} = sigmaleft( {oldsymbol{W}^{l}oldsymbol{a}^{l - 1} + oldsymbol{b}^{l}} ight)$

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

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

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

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

    2) for $l = 2$ to L,计算:$oldsymbol{a}^{l} = sigmaleft( {oldsymbol{W}^{l}oldsymbol{a}^{l - 1} + oldsymbol{b}^{l}} ight)$

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


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

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

    $l = frac{1}{2}left| {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight|_{2}^{2} = frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)$

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

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

     

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

    $lleft( {oldsymbol{x},oldsymbol{y},oldsymbol{W},oldsymbol{b}} ight) = frac{1}{2}left| {oldsymbol{D}oldsymbol{N}oldsymbol{N}left| {oldsymbol{x};oldsymbol{W},oldsymbol{b}} ight| - oldsymbol{y}} ight|_{2}^{2} = frac{1}{2}left( {oldsymbol{D}oldsymbol{N}oldsymbol{N}left( {oldsymbol{x};oldsymbol{W},oldsymbol{b}} ight) - oldsymbol{y}} ight)^{T}left( {oldsymbol{D}oldsymbol{N}oldsymbol{N}left( {oldsymbol{x};oldsymbol{W},oldsymbol{b}} ight) - oldsymbol{y}} ight)$

     

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

    我们先求$frac{partial l}{partialoldsymbol{a}^{oldsymbol{L}}}$,

    $dl = frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T}dleft( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight) + frac{1}{2}dleft( left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T} ight)left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight) = frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T}doldsymbol{a}^{oldsymbol{L}} + frac{1}{2}dleft( left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T} ight)left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)$

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

    $frac{1}{2}dleft( left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T} ight)left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight) = frac{1}{2}trleft( {dleft( left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T} ight)left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)} ight) = frac{1}{2}trleft( {left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{oldsymbol{T}}dleft( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)} ight) = frac{1}{2}trleft( {left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{oldsymbol{T}}doldsymbol{a}^{oldsymbol{L}}} ight) = frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{oldsymbol{T}}doldsymbol{a}^{oldsymbol{L}}$

    所以有:

    $dl = frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{T}doldsymbol{a}^{oldsymbol{L}} + frac{1}{2}left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{oldsymbol{T}}doldsymbol{a}^{oldsymbol{L}} = left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight)^{oldsymbol{T}}doldsymbol{a}^{oldsymbol{L}}$

    $frac{partial l}{partialoldsymbol{a}^{oldsymbol{L}}} = oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}$

    我们令$oldsymbol{z}^{L} = oldsymbol{W}^{oldsymbol{L}}oldsymbol{a}^{oldsymbol{L} - 1} + oldsymbol{b}^{oldsymbol{L}}$

    可求得$frac{partial l}{partialoldsymbol{z}^{oldsymbol{L}}} = left( frac{partialoldsymbol{a}^{oldsymbol{L}}}{partialoldsymbol{a}^{oldsymbol{L}}} ight)^{T}frac{partial l}{partialoldsymbol{a}^{oldsymbol{L}}}$

    因为$doldsymbol{a}^{oldsymbol{L}} = dsigmaleft( oldsymbol{z}^{L} ight) = sigma^{'}left( oldsymbol{z}^{L} ight) odot doldsymbol{z}^{L} = diagleft( {sigma^{'}left( oldsymbol{z}^{L} ight)} ight)doldsymbol{z}^{L}$

    所以$frac{partial l}{partialoldsymbol{z}^{oldsymbol{L}}} = diagleft( {sigma^{'}left( oldsymbol{z}^{L} ight)} ight)left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight) = left( {oldsymbol{a}^{oldsymbol{L}} - oldsymbol{y}} ight) odot sigma^{'}left( oldsymbol{z}^{L} ight)$

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

    $frac{partial l}{partialoldsymbol{W}^{oldsymbol{L}}} = frac{partial l}{partialoldsymbol{z}^{oldsymbol{L}}}left( oldsymbol{a}^{oldsymbol{L} - 1} ight)^{T}$

    $frac{partial l}{partialoldsymbol{b}^{oldsymbol{L}}} = frac{partial l}{partialoldsymbol{z}^{oldsymbol{L}}}$

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

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

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

     

    根据链式法则,有$frac{partial l}{partialoldsymbol{z}^{oldsymbol{l}}} = left( frac{partialoldsymbol{z}^{oldsymbol{l} + 1}}{partialoldsymbol{z}^{oldsymbol{l}}} ight)^{T}frac{partial l}{partialoldsymbol{z}^{oldsymbol{l} + 1}}$

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

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

    所以$doldsymbol{z}^{l + 1} = oldsymbol{W}^{l + 1}dsigmaleft( oldsymbol{z}^{oldsymbol{l}} ight) = oldsymbol{W}^{l + 1}left( {sigma^{'}left( oldsymbol{z}^{l} ight) odot doldsymbol{z}^{l}} ight) = oldsymbol{W}^{l + 1}diagleft( {sigma^{'}left( oldsymbol{z}^{oldsymbol{l}} ight)} ight)doldsymbol{z}^{oldsymbol{l}}$

    所以$frac{partialoldsymbol{z}^{l + 1}}{partialoldsymbol{z}^{l}} = oldsymbol{W}^{l + 1}diagleft( {sigma^{'}left( oldsymbol{z}^{oldsymbol{l}} ight)} ight)$

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

    $delta^{l} = diagleft( {sigma^{'}left( oldsymbol{z}^{oldsymbol{l}} ight)} ight)left( oldsymbol{W}^{l + 1} ight)^{T}delta^{l + 1} = left( oldsymbol{W}^{l + 1} ight)^{T}delta^{l + 1} odot sigma^{'}left( oldsymbol{z}^{oldsymbol{l}} ight)$

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

     


     

    2.3 总结

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

     

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

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

    如果本文对您有所帮助的话,不妨点下“推荐”让它能帮到更多的人,谢谢。


    参考资料

    • https://www.cnblogs.com/pinard/p/6418668.html
    • https://www.cnblogs.com/pinard/p/6422831.html

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

  • 相关阅读:
    JVM 源码分析
    GGGGCCCC
    正则化(Regularization)、过拟合(Overfitting)
    名校课程
    数据库垂直拆分 水平拆分
    运维角度浅谈MySQL数据库优化
    表的垂直拆分和水平拆分
    Eclipse去掉对JS文件的Validation
    Linux定时任务工具crontab详解及系统时间同步
    高性能分布式哈希表FastDHT
  • 原文地址:https://www.cnblogs.com/sumwailiu/p/13600984.html
Copyright © 2020-2023  润新知