【深度学习】后向传播(BP)算法

一、神经网络学习算法的本质

  • 当我们搭建好一个神经网络后,无论在什么应用场合,我们的目标都是:将网络的权值和偏置都变成一个最好的值,这个值可以让我们的输入得到理想的输出。
  • 可能大家会觉的神经网络架构很非常神秘和复杂,其实任何一个神经网络架构都是一个多层复合的复合函数,我们可以将它们表示为:f(x,w,b)f(x,w,b),其中x是输入,ww是权值,bb为偏置。
  • 我们的目标就变成了尝试不同的wbw,b值,使得最后的f(x,w,b)f(x,w,b)最接近我们的标签yy
  • 现在我们需要一个指标来描述接近这个概念,于是产生了损失函数,这里我们将损失函数令为:(f(x,w,b)y)2=C(w,b)(f(x,w,b)-y)^2=C(w,b),现在问题变成:将最小C(w,b)C(w,b)化。
  • 将最小C(w,b)C(w,b)化这个问题,我们能够马上想到使用梯度下降算法。即:
    • 第一步:求解梯度 [C(w,b)w1,C(w,b)(b1),C(w,b)w2,C(w,b)b2,C(w,b)(wn),C(w,b)bn]=C(w,b)[W,b][\frac{∂C(w,b)}{∂w_1 },\frac{∂C(w,b)}{(∂b_1 )},\frac{∂C(w,b)}{∂w_2 },\frac{∂C(w,b)}{∂b_2 }…,\frac{∂C(w,b)}{(∂w_n )},\frac{∂C(w,b)}{∂b_n }]=\frac{∂C(w,b)}{∂[W,b]}
    • 第二步:更新参数:[W,b]=[W,b]ηC(w,b)[W,b][W,b]=[W,b]-η\frac{∂C(w,b)}{∂[W,b]}其中ηη为步长。
    • 不断迭代上面两步直到函数C(w,b)C(w,b)不能再减少

  • 这里解释以下梯度的含义:
    • 对于任意一个多元函数f(x1,x2,,xn)f(x_1,x_2,…,x_n),它的梯度为: f=[fx1,fx2,,fxn]∇f=[\frac{∂f}{∂x_1 },\frac{∂f}{∂x_2},…,\frac{∂f}{∂x_n }]
    • 梯度的维度与函数变量维度是一样的,它表示如果自变量沿着梯度方向运动,函数ff的函数值将增长最快。当然反过来,如果沿着梯度的反方向函数值将减少最快,这也是为啥上面用减号。
    • 其中fxi\frac{∂f}{∂x_i }为函数ff对变量xix_i的偏导数,它的含义是其他变量不变的情况下xix_i增加1函数值的改变量,即函数相对xix_i的的变化率。
  • 上面推导的过程的总结:网络权值和偏置更新问题f(xwb)\Rightarrow f(x,w,b)的结果逼近yC(wb)=(f(xwb)y)2y\Rightarrow C(w,b)=(f(x,w,b)-y)^2取极小值问题 C(wb)\Rightarrow C(w,b)按梯度下降问题\Rightarrow取到极小值,网络达到最优

  • 一切都是那么顺利,但是我们忘记了上面推导都是基于一个前提:我们已经提前知道损失函数在当前点的梯度。然而事实并非如此
  • 这个问题困扰了NN研究者多年,1969年M.Minsky和S.Papert所著的《感知机》一书出版,它对单层神经网络进行了深入分析,并且从数学上证明了这种网络功能有限,甚至不能解决象"异或"这样的简单逻辑运算问题。同时,他们还发现有许多模式是不能用单层网络训练的,而对于多层网络则没有行之有效的低复杂度算法,最后他们甚至认为神经元网络无法处理非线性问题。然而于1974年,Paul Werbos首次给出了如何训练一般网络的学习算法—back propagation(BP)算法。这个算法可以高效的计算每一次迭代过程中的梯度,让以上我们的推导得以实现。不巧的是,在当时整个人工神经网络社群中无人知晓Paul所提出的学习算法。直到80年代中期,BP算法才重新被David Rumelhart、Geoffrey Hinton及Ronald Williams、David Parker和Yann LeCun独立发现,并获得了广泛的注意,引起了神经网络领域研究的第二次热潮。

二、梯度求解的链式法则

  • 上面我们已经已经知道了梯度求解的公式: f=[fx1,fx2,,fxn]∇f=[\frac{∂f}{∂x_1 },\frac{∂f}{∂x_2},…,\frac{∂f}{∂x_n }],并且我们也知道了中间的每个元素是函数对相应变量的偏导数fxi\frac{∂f}{∂x_i }。我们也知道了偏导数的含义就是:函数相对xix_i的的变化率。
  • 那么我们只要求出函数ff对每个变量的变化率,我们就可以求出函数ff的梯度,我们使用下面一个例子来说明这个问题。
  • 假设有这样一个运算图:
    【深度学习】后向传播(BP)算法
    可以将其理解为一个简单的神经网络,ee为输出层,a,ba,b为输入层,c,dc,d为隐藏层。该网络可以表示为函数:e=f(a,b)=(a+b)(b+1)e=f(a,b)=(a+b)*(b+1)
  • 假设输入a=2b=1a=2,b=1,在这种情况下,如果只考虑相邻层的偏导关系,可以得到下图:
    【深度学习】后向传播(BP)算法
  • 根据偏导的含义和上图可知:
    • 如果如果其他变量不变,aa改变1,那么cc就会改变1,而cc改变1,ee就会改变2,所有最后得到:aa改变1,ee就会改变2,即ee相对aa的变化率为2,即ea=2\frac{∂e}{∂a}=2
    • 如果如果其他变量不变,bb改变1,那么cc就会改变1并且dd也会改变1,而cc改变1,ee就会改变2,dd改变1,ee就会改变3,所有最后得到:bb改变1,ee就会改变5,即ee相对aa的变化率为5,eb=5\frac{∂e}{∂b}=5
  • 上面这个过程就是链式法则,最后总结的公式为: ea=ecca\frac{∂e}{∂a}=\frac{∂e}{∂c}*\frac{∂c}{∂a}eb=eccb+dda\frac{∂e}{∂b}=\frac{∂e}{∂c}*\frac{∂c}{∂b}+\frac{∂}{∂d}*\frac{∂d}{∂a}由此可知ea\frac{∂e}{∂a}的值等于从aaee的路径上的偏导值的乘积,而eb\frac{e}{∂b}的值等于从bbee的路径1(bce)(b-c-e)上的偏导值的乘积加上路径2(bde)(b-d-e)上的偏导值的乘积。
  • 为啥要这样分析呢?最主要的原因是一般的神经网络的深度是较深的,我们直接去求去求某个损失函数对某个参数的偏导是比较困难的,但是求临近层变量之间的偏导数是非常容易的,通过链式法则,我们可以用临近层的偏导数来求解出损失函数对所有参数的偏导数。

三、前向神经网络中链式法则求解梯度

  • 图是一个典型的前向神经网络或者叫全链接神经网络:
    【深度学习】后向传播(BP)算法
    Layer L1是输入层,Layer L2是隐含层,Layer L3是隐含层
  • 这一部分使用一个简单例子,来说明一下神经网络中梯度的求解过程,使用下面简单的网络:
    【深度学习】后向传播(BP)算法
  • 输入层包含两个神经元i1,i2i_1,i_2,和截距项b1b_1;隐含层包含两个神经元h1,h2h_1,h_2和截距项b2b_2,输出层包含两个神经元o1,o2o_1,o_2,每的权重我为wiw_i,**函为sigmoid函数:sigmoid(x)=11+exsigmoid(x)=\frac{1}{1+e^{-x} }导数为:sigmoid(x)=sigmoid(x)(1sigmoid(x))sigmoid(x)=sigmoid(x)(1-sigmoid(x))
  • 每个节点的运算关系用o1o_1做为一个例子进行说明一下:
    • o1o_1输入(neto1net_{o1})为:neto1=outh1w5+outh2w6+b21net_{o1}=out_{h1}*w_5+out_{h2}*w_6+b_{21}
    • o1o_1输出(outo1out_{o1})为:outo1=sigmoid(neto1)out_{o1}=sigmoid(net_{o1})
  • 定义损失函数: Etotal=12(targetoutput)2E_{total}=∑\frac{1}{2} (target-output)^2
  • 我们需要更新的是权重ww和偏置项bb来最小化损失函数EtotalE_{total},所有我们需要求出的是损失函数对wwbb的梯度,即损失函数对每个权重wiw_i和每个偏置项bib_i的变化率(偏导数)。
  • 求损失函数对于隐藏层到输出层的权重和偏置项的偏导数,我们用w5w_5b21b_{21}来作为一个例子:
    • 求损失函数EtotalE_{total}相对w5w_5的变化率的思路为:EtotalE_{total}相对o1o_1输出(outo1out_{o1})的变化率o1\Rightarrow o_1输出相对o1o_1输人(neto1net_{o1})的变化率o1\Rightarrow o_1输人相对w5w_5的变化率。
    • 得到链式法则:Etotalw5=Etotalouto1outo1neto1neto1w5\frac{∂E_{total}}{∂w_5} =\frac{∂E_{total}}{∂out_{o1} }*\frac{∂out_{o1}}{∂net_{o1} }*\frac{∂net_o1}{∂w_5 }
    • 过程图如下:
      【深度学习】后向传播(BP)算法
    • 具体求导(根据每个量的表达式):Etotalouto1=(targeto1outo1)\frac{∂E_{total}}{∂out_{o1}}=(target_o1-out_o1)outo1neto1=sigmoid(neto1)(1sigmoid(neto1))=outo1(1outo1)\frac{∂out_{o1}}{∂net_{o1}}=sigmoid(net_{o1} )(1-sigmoid(net_{o1} ))=out_{o1} (1-out_{o1})neto1w5=outh1\frac{∂net_{o1}}{∂w_{5 }}=out_{h1}
    • 整理公式:Etotal)w5=(targeto1outo1)outo1(1outo1)outh1\frac{∂E_{total)}}{∂w_5 }=(target_{o1}-out_{o1} )*out_{o1} (1-out_{o1} )*out_{h1}将前两项记为:δo1=Etotalouto1outo1neto1=Etotalneto1=(targeto1outo1)outo1(1outo1)δ_{o1}=\frac{∂E_{total}}{∂out_{o1}}*\frac{∂out_{o1}}{∂net_{o1}}=\frac{∂E_{total}}{∂net_{o1 }}=(target_{o1}-out_{o1} )*out_{o1} (1-out_{o1} )δo1δ_{o1}表示的是:误差EtotalE_{total}相对于输入层o1(neto1)o_1(net_{o1})的变化率
    • 最后公式变为:Etotalw5=δo1outh1\frac{∂E_{total}}{∂w_5 }=δ_{o1}*out_{h1}
    • 我们用同样的方法来分析b21b_{21}: b_2的改变会影响o1o_1的输入,即neto1net_{o1},同时neto1net_{o1}的变化必将影响outo1out_{o1},进一步outo1out_{o1}的变化会影响损失函数EtotalE_{total},于是可以得到链式法则:Etotalb21=Etotalouto1outo1neto1neto1b21\frac{∂E_{total}}{∂b_{21}}=\frac{∂E_{total}}{∂out_{o1}} *\frac{∂out_{o1}}{∂net_{o1}}*\frac{∂net_{o1}}{∂b_{21}}
    • 并且:neto1b21=1\frac{∂net_{o1}}{∂b_{21}}=1
    • 所以有:Etotalb21=Etotalouto1outo1neto1neto1b21=δo1\frac{∂E_{total}}{∂b_{21}}=\frac{∂E_{total}}{∂out_{o1}} *\frac{∂out_{o1}}{∂net_{o1}}*\frac{∂net_{o1}}{∂b_{21}}=δ_{o1}

  • 求损失函数对于输出层到隐藏层的权重和偏置项的偏导数,我们用w1w_1b11b_{11}来作为一个例子:
    • 方法其实与上面说的差不多,但是有个地方需要变一下,在上文计算损失函数对w5w_5的偏导时,导数传递的路径是:Etotalouto1neto1w5E_{total}→out_{o1}→net_{o1}→w_5
    • 但是计算损失函数对w1w_1的偏导时,导数传递的路径有多条:Etotalouto1neto1outh1neth1w1E_{total}→out_{o1}→net_{o1}→out_{h1}→net_{h1}→w_1Etotalouto2neto2outh1neth1w1E_{total}→out_{o2}→net_{o2}→out_{h1}→net_{h1}→w_1
    • 如下图所示:
      【深度学习】后向传播(BP)算法
    • 由上面的推导可知:Etotalouto1neto1=δo1E_{total}→out_{o1}→net_{o1}=δ_{o1}Etotalouto2neto2=δo2E_{total}→out_{o2}→net_{o2}=δ_{o2}
    • 所以链式法则为:Etotalw1=(δo1neto1outh1+δo2neto2outh1)outh1neth1neth1w1\frac{∂E_{total}}{∂w_1}=(δ_{o1}*\frac{∂net_{o1}}{∂out_{h1}} +δ_{o2}*\frac{∂net_{o2}}{∂out_{h1}})*\frac{∂out_{h1}}{∂net_{h1}}*\frac{∂net_{h1}}{∂w_1 }
    • 现在一项一项求:neto1outh1=w5\frac{∂net_{o1}}{∂out_h1 }=w_5neto1outh1=w7\frac{∂net_{o1}}{∂out_{h1}}=w_7outh1neth1=sigmoid(neth1)(1sigmoid(neth1))=outh1(1outh1)\frac{∂out_{h1}}{∂net_{h1 }}=sigmoid(net_{h1} )(1-sigmoid(net_{h1} ))=out_{h1} (1-out_{h1})neth1)w1=i1\frac{∂net_{h1)}}{∂w_1 }=i_1
    • 简化公式:δh1=Etotalneth1=(δo1neto1outh1+δo2neto2outh1)outh1neth1=(δo1w5+δo2w7)outh1(1outh1)δ_h1=\frac{∂E_{total}}{∂net_{h1}} =(δ_{o1}*\frac{∂net_{o1}}{∂out_{h1} }+δ_{o2}*\frac{∂net_{o2}}{∂out_{h1}})*\frac{∂out_{h1}}{∂net_{h1 }}=(δ_{o1}*w_5+δ_{o2}*w_7 )*out_{h1} (1-out_{h1})
    • 所以最后公式为:Etotalw1=δh1i1\frac{∂E_{total}}{∂w_1 }=δ_{h1} i_1
    • 我们用同样的方法来分析b11b_{11}得到链式法则:Etotalb1=δh1\frac{∂E_{total}}{∂b_1 }=δ_{h1}
  • 根据上面的分析我们能求出神经网络上所有权重和偏置项的偏导数,即求出损失函数对整个网络参数的梯度。然后使用梯度下降法就能最小化损失函数,求出最佳网络。这就是神经网络的后向传播算法

四、后向传播算法

  • 这一步主要是讲BP算法推广到一般的前向网络(全链接神经网络)当中去,并将公式进行了进一步简化。
  • 前向神经网络的一般结构如下:
    【深度学习】后向传播(BP)算法
    )
  • 一些变量:
    • 权重wjklw_{jk}^l:表示第l1l-1层的第kk个神经元连接到第ll层的第jj个神经元的权重;
    • 偏置项bjlb_j^l:表示第层ll的第个jj神经元的偏置;
    • 输入zjlz_j^l:表示第ll层的第jj个神经元的输入:zjl=kwjklakl1+bjlz_j^l=∑_kw_{jk}^l a_k^{l-1}+b_j^l
    • 输出ajla_j^l:表示第ll层的第jj个神经元的输出:ajl=σ(zjl)a_j^l=σ(z_j^l)其中σσ为**函数。
    • 损失函数:C(w,b)C(w,b)
    • 损失函数对对某一层某个神经元输入的偏导δjlδ_j^l:δjl=Czjlδ_j^l=\frac{∂C}{∂z_j^l }
    • 神经网络的最大层数:LL
  • 后向传播算法的公式:
    • δjL=CzjL=Cajlσ(zjL)δ_j^L=\frac{∂C}{∂z_j^L }=\frac{∂C}{∂a_j^l }*σ'(z_j^L )其中Cajl\frac{∂C}{∂a_j^l }是由损失函数的具体形式给出的。
    • δjl=(iwijl+1δil+1)σ(zjl)δ_j^l=(∑_iw_{ij}^{l+1} δ_i^{l+1} )*σ'(z_j^l )
    • Cwjkl=δjlajl1\frac{∂C}{∂w_{jk}^l }=δ_j^l*a_j^{l-1}
    • Cbjl=δjl\frac{∂C}{∂b_j^l }=δ_j^l
  • 最后使用梯度下降算法更新权重和偏置项,直到损失函数不能下降,得到最优网络。