在深度神经网络(DNN)模型与前向传播算法中,我们对DNN的模型和前向传播算法做了总结,这里我们更进一步,对DNN的反向传播算法(Back Propagation,BP)做一个总结。
在了解DNN的反向传播算法前,我们先要知道DNN反向传播算法要解决的问题,也就是说,什么时候我们需要这个反向传播算法?
回到我们监督学习的一般问题,假设我们有m个训练样本: ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) {(x_1,y_1),(x_2,y_2),...,(x_m,y_m)} (x1,y1),(x2,y2),...,(xm,ym),其中 x x x为输入向量,特征维度为 n n n_ i n in in,而y为输出向量,特征维度为 n n n_ o u t out out。我们需要利用这m个样本训练出一个模型,当有一个新的测试样本( x t e s t x_{test} xtest,?)来到时, 我们可以预测 y t e s t y_{test} ytest向量的输出。
如果我们采用DNN的模型,即我们使输入层有 n n n _ i n in in个神经元,而输出层有 n n n _ o u t out out个神经元。再加上一些含有若干神经元的隐藏层。此时我们需要找到合适的所有隐藏层和输出层对应的线性系数矩阵 W W W,偏倚向量 b b b,让所有的训练样本输入计算出的输出尽可能的等于或很接近样本输出。怎么找到合适的参数呢?
如果大家对传统的机器学习的算法优化过程熟悉的话,这里就很容易联想到我们可以用一个合适的损失函数来度量训练样本的输出损失,接着对这个损失函数进行优化求最小化的极值,对应的一系列线性系数矩阵 W W W,偏倚向量 b b b即为我们的最终结果。在DNN中,损失函数优化极值求解的过程最常见的一般是通过梯度下降法来一步步迭代完成的,当然也可以是其他的迭代方法比如牛顿法与拟牛顿法。如果大家对梯度下降法不熟悉,建议先阅读我之前写的梯度下降(Gradient Descent)小结。
对DNN的损失函数用梯度下降法进行迭代优化求极小值的过程即为我们的反向传播算法。
在进行DNN反向传播算法前,我们需要选择一个损失函数,来度量训练样本计算出的输出和真实的训练样本输出之间的损失。你也许会问:训练样本计算出的输出是怎么得来的?这个输出是随机选择一系列 W W W, b b b用我们上一节的前向传播算法计算出来的。即通过一系列的计算: a l = σ ( z l ) = σ ( W l a l − 1 + b l ) a^l=σ(z^l)=σ(W^la^{l−1}+b^l) al=σ(zl)=σ(Wlal−1+bl)。计算到输出层第 L L L层对应的 a L a^L aL即为前向传播算法计算出来的输出。
回到损失函数,DNN可选择的损失函数有不少,为了专注算法,这里我们使用最常见的均方差来度量损失。即对于每个样本,我们期望最小化下式:
J ( W , b , x , y ) = 1 2 ∣ ∣ a L − y ∣ ∣ 2 2 J(W,b,x,y)=\frac12||a^L−y||^2_2 J(W,b,x,y)=21∣∣aL−y∣∣22
其中, a L a^L aL和 y y y为特征维度为 n n n_ o u t out out的向量,而 ∣ ∣ || ∣∣S ∣ ∣ 2 ||_2 ∣∣2为S的L2范数。
损失函数有了,现在我们开始用梯度下降法迭代求解每一层的 W W W, b b b。
首先是输出层第 L L L层。注意到输出层的 W W W, b b b满足下式:
a L = σ ( z L ) = σ ( W L a L − 1 + b L ) a^L=σ(z^L)=σ(W^La^{L−1}+b^L) aL=σ(zL)=σ(WLaL−1+bL)
这样对于输出层的参数,我们的损失函数变为:
J ( W , b , x , y ) = 1 2 ∣ ∣ a L − y ∣ ∣ 2 2 = 1 2 ∣ ∣ σ ( W L a L − 1 + b L ) − y ∣ ∣ 2 2 J(W,b,x,y)=\frac12||a^L−y||^2_2=\frac12||σ(W^La^{L−1}+b^L)−y||^2_2 J(W,b,x,y)=21∣∣aL−y∣∣22=21∣∣σ(WLaL−1+bL)−y∣∣22
这样求解 W W W , b b b的梯度就简单了:
∂ J ( W , b , x , y ) ∂ W L = [ ( a L − y ) ⊙ σ ′ ( z L ) ] ( a L − 1 ) T \frac{∂J(W,b,x,y)}{∂W^L}=[(a^L−y)⊙σ′(z^L)](a^{L−1})^T ∂WL∂J(W,b,x,y)=[(aL−y)⊙σ′(zL)](aL−1)T
∂ J ( W , b , x , y ) ∂ b L = ( a L − y ) ⊙ σ ′ ( z L ) \frac{∂J(W,b,x,y)}{∂b^L}=(a^L−y)⊙σ′(z^L) ∂bL∂J(W,b,x,y)=(aL−y)⊙σ′(zL)
注意上式中有一个符号⊙,它代表Hadamard积,对于两个维度相同的向量 A ( a 1 , a 2 , . . . a n ) T A(a_1,a_2,...a_n)^T A(a1,a2,...an)T和 B ( b 1 , b 2 , . . . b n ) T B(b_1,b_2,...b_n)^T B(b1,b2,...bn)T,则 A ⊙ B = ( a 1 b 1 , a 2 b 2 , . . . a n b n ) T A⊙B=(a_1b_1,a_2b_2,...a_nb_n)^T A⊙B=(a1b1,a2b2,...anbn)T。
我们注意到在求解输出层的 W W W, b b b的时候,有中间依赖部分 ∂ J ( W , b , x , y ) ∂ z L \frac{∂J(W,b,x,y)}{∂z^L} ∂zL∂J(W,b,x,y),因此我们可以把公共的部分即对 z L z^L zL先算出来,记为:
δ L = ∂ J ( W , b , x , y ) ∂ z L = ( a L − y ) ⊙ σ ′ ( z L ) δL=\frac{∂J(W,b,x,y)}{∂z^L}=(a^L−y)⊙σ′(z^L) δL=∂zL∂J(W,b,x,y)=(aL−y)⊙σ′(zL)
现在我们终于把输出层的梯度算出来了,那么如何计算上一层 L − 1 L−1 L−1层的梯度,上上层 L − 2 L−2 L−2层的梯度呢?这里我们需要一步步的递推,注意到对于第 l l l层的未激活输出 z l z^l zl,它的梯度可以表示为:
δ l = ∂ J ( W , b , x , y ) ∂ z l = ( ∂ z L ∂ z L − 1 ∂ z L − 1 ∂ z L − 2 . . . ∂ z l + 1 ∂ z l ) T ∂ J ( W , b , x , y ) ∂ z L δl=\frac{∂J(W,b,x,y)}{∂z^l}=(\frac{∂z^L}{∂z^{L−1}}\frac{∂z^{L−1}}{∂z^{L−2}}...\frac{∂z^{l+1}}{∂z^l})^T\frac{∂J(W,b,x,y)}{∂z^L} δl=∂zl∂J(W,b,x,y)=(∂zL−1∂zL∂zL−2∂zL−1...∂zl∂zl+1)T∂zL∂J(W,b,x,y)
如果我们可以依次计算出第 l l l层的 δ l δ^l δl,则该层的 W l , b l W^l,b^l Wl,bl很容易计算?为什么呢?注意到根据前向传播算法,我们有:
z l = W l a l − 1 + b l z^l=W^la^{l−1}+b^l zl=Wlal−1+bl
所以根据上式我们可以很方便的计算出第 l l l层的 W l , b l W^l,b^l Wl,bl的梯度如下:
∂ J ( W , b , x , y ) ∂ W l = = δ l ( a l − 1 ) T \frac{∂J(W,b,x,y)}{∂W^l}==δ^l(a^{l−1})^T ∂Wl∂J(W,b,x,y)==δl(al−1)T
∂ J ( W , b , x , y ) ∂ b l = δ l \frac{∂J(W,b,x,y)}{∂b^l}=δ^l ∂bl∂J(W,b,x,y)=δl
其中,第一个式子的推导可以参考机器学习中的矩阵向量求导(四) 矩阵向量求导链式法则中第三节的最后一个公式。
那么现在问题的关键就是要求出 δ l δ^l δl了。这里我们用数学归纳法,第 L L L层的 δ L δ^L δL上面我们已经求出, 假设第 l + 1 l+1 l+1层的 δ l + 1 δ^{l+1} δl+1已经求出来了,那么我们如何求出第 l l l层的 δ l δ^l δl呢?我们注意到:
δ l = ∂ J ( W , b , x , y ) ∂ z l = ( ∂ z l + 1 ∂ z l ) T ∂ J ( W , b , x , y ) ∂ z l + 1 = ( ∂ z l + 1 ∂ z l ) T δ l + 1 δ^l=\frac{∂J(W,b,x,y)}{∂z^l}=(\frac{∂z^{l+1}}{∂z^l})^T\frac{∂J(W,b,x,y)}{∂z^{l+1}}=(\frac{∂z^{l+1}}{∂z^l})^Tδ^{l+1} δl=∂zl∂J(W,b,x,y)=(∂zl∂zl+1)T∂zl+1∂J(W,b,x,y)=(∂zl∂zl+1)Tδl+1
可见,用归纳法递推 δ l + 1 δ^{l+1} δl+1和 δ l δ^l δl的关键在于求解 ∂ z l + 1 ∂ z l \frac{∂z^{l+1}}{∂z^l} ∂zl∂zl+1。
而 z l + 1 z^{l+1} zl+1和 z l z^l zl的关系其实很容易找出:
z l + 1 = W l + 1 a l + b l + 1 = W l + 1 σ ( z l ) + b l + 1 z^{l+1}=W^{l+1}a^l+b^l+1=W^{l+1}σ(z^l)+b^{l+1} zl+1=Wl+1al+bl+1=Wl+1σ(zl)+bl+1
这样很容易求出:
z l + 1 ∂ z l = W l + 1 d i a g ( σ ′ ( z l ) ) z^{l+1}∂z^l=W^{l+1}diag(σ′(z^l)) zl+1∂zl=Wl+1diag(σ′(zl))
将上式带入上面 δ l + 1 δ^{l+1} δl+1和 δ l δ^l δl关系式我们得到:
δ l = ( ∂ z l + 1 ∂ z l ) T ∂ J ( W , b , x , y ) ∂ z l + 1 = d i a g ( σ ′ ( z l ) ) ( W l + 1 ) T δ l + 1 = ( W l + 1 ) T δ l + 1 ⊙ σ ′ ( z l ) δ^l=(\frac{∂z^{l+1}}{∂z^l})^T\frac{∂J(W,b,x,y)}{∂z^{l+1}}=diag(σ′(z^l))(W^{l+1})^Tδ^{l+1}=(W^{l+1})^Tδ^{l+1}⊙σ′(z^l) δl=(∂zl∂zl+1)T∂zl+1∂J(W,b,x,y)=diag(σ′(zl))(Wl+1)Tδl+1=(Wl+1)Tδl+1⊙σ′(zl)
现在我们得到了 δ l δ^l δl的递推关系式,只要求出了某一层的 δ l δ^l δl,求解 W l , b l W^l,b^l Wl,bl的对应梯度就很简单的。
现在我们总结下DNN反向传播算法的过程。由于梯度下降法有批量(Batch),小批量(mini-Batch),随机三个变种,为了简化描述,这里我们以最基本的批量梯度下降法为例来描述反向传播算法。实际上在业界使用最多的是mini-Batch的梯度下降法。不过区别仅仅在于迭代时训练样本的选择而已。
输入: 总层数L,以及各隐藏层与输出层的神经元个数,激活函数,损失函数,迭代步长 α α α,最大迭代次数MAX与停止迭代阈值 ϵ ϵ ϵ,输入的m个训练样本 ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) {(x_1,y_1),(x_2,y_2),...,(x_m,y_m)} (x1,y1),(x2,y2),...,(xm,ym)
输出:各隐藏层与输出层的线性关系系数矩阵 W W W和偏倚向量 b b b
1)初始化各隐藏层与输出层的线性关系系数矩阵W和偏倚向量b的值为一个随机值。
2)for iter to 1 to MAX:
2-1) for i =1 to m:
a) 将DNN输入 a 1 a^1 a1设置为 x i x_i xi
b) for l=2 to L,进行前向传播算法计算 a i a^i ai, l = σ ( z i , l ) = σ ( W l a i , l − 1 + b l ) l=σ(zi,l)=σ(W^la^{i,l−1}+b^l) l=σ(zi,l)=σ(Wlai,l−1+bl)
c) 通过损失函数计算输出层的 δ i , L δ^{i,L} δi,L
d) for l= L-1 to 2, 进行反向传播算法计算 δ i , l = ( W l + 1 ) T δ i , l + 1 ⊙ σ ′ ( z i , l ) δ^{i,l}=(W^{l+1})^Tδ^{i,l+1}⊙σ′(z^{i,l}) δi,l=(Wl+1)Tδi,l+1⊙σ′(zi,l)
2-2) for l = 2 to L,更新第 l l l层的 W l , b l W^l,b^l Wl,bl:
W l = W l − α ∑ i = 1 m δ i , l ( a i , l − 1 ) T W^l=W^l−α∑_{i=1}^mδ^{i,l}(a^{i,l−1})^T Wl=Wl−αi=1∑mδi,l(ai,l−1)T
b l = b l − α ∑ i = 1 m δ i , l b^l=b^l−α∑_{i=1}^mδ^{i,l} bl=bl−αi=1∑mδi,l
2-3) 如果所有 W , b W,b W,b的变化值都小于停止迭代阈值ϵ,则跳出迭代循环到步骤3。
3) 输出各隐藏层与输出层的线性关系系数矩阵 W W W和偏倚向量 b b b。
有了DNN反向传播算法,我们就可以很方便的用DNN的模型去解决第一节里面提到了各种监督学习的分类回归问题。当然DNN的参数众多,矩阵运算量也很大,直接使用会有各种各样的问题。有哪些问题以及如何尝试解决这些问题并优化DNN模型与算法,我们在下一篇讲。