BP算法,在深度神经网络的发展中有着举足轻重的地位,对于反向传播算法的推导过程,各种资料介绍可谓是多不胜数。但,由于深度神经网络的复杂性,要比较深刻的理解反向传播算法还是需要自己手动的推导一遍。
本文以前篇深度学习与计算机视觉: 深度学习必知基本概念以及链式求导介绍了神经网络的反向传播中的链式求导法则为基础,以一个三层的神经网络为例 ,一步步的推导,最终归纳出反向传播算法四个基本方程。
在前文中,利用链式求导法则,得到如下结果
[egin{align*}
frac{partial e_{o1}}{partial w_{11}^2} &=(a_1^3 - y_1) cdot sigmoid(z_1^3)(1 - sigmoid(z_1^3))cdot a_1^2 \
frac{partial e_{o1}}{partial b_1^3} &= (a_1^3 - y_1) cdot sigmoid(z_1^3)(1 - sigmoid(z_1^3)) \
frac{partial e_{o1}}{partial w_{11}^1}&= (a_1^3 - y_1) cdot sigmoid(z_1^3)(1 - sigmoid(z_1^3)cdot w_{11}^3 cdot sigmoid(z_1^2)(1 - sigmoid(z_1^2))cdot a_1^1 \
frac{partial e_{o1}}{partial b_1^2} &= (a_1^3 - y_1) cdot sigmoid(z_1^3)(1 - sigmoid(z_1^3)cdot w_{11}^3 cdot sigmoid(z_1^2)(1 - sigmoid(z_1^2))
end{align*}
]
这还只是很小神经网络中,每层第一个神经元的求梯度过程,如果有成百上千的神经元利用上述单纯的链式求导法则去计算各个权值和偏置的梯度,无疑是很困难的。
不过好消息是,上面的公式中有很大一部分的重复计算过程,而神经网络也是个叠加的结构,利用上述的重复的部分,就能够很好的完成反向传播的计算过程。 这也是有名的反向传播四个基本方程所做的事情。
本文就以上述的推导为基础,在知道 反向传播四个基本方程的前提下,推导这四个基本方程的过程。
引入中间变量(delta_j^l)
反向传播的最终目的是为了求误差关于权值和神经元的权值和偏置的导数,即(frac{partial E}{partial w_{jk}^l})和(frac{partial E}{partial b_j^l})。为了能够利用上面提到的各个偏导数计算的重复元素,这里引入个中间变量(delta_j^l)。
使用(sigma)表示激活函数(sigmoid),(sigma')表示该函数的导数,来重写下上面的公式
[egin{align*}
frac{partial e_{o1}}{partial w_{11}^3} &=(a_1^3 - y_1) cdot sigma'(z_1^3)cdot a_1^2 \
frac{partial e_{o1}}{partial w_{12}^3} &=(a_1^3 - y_1) cdot sigma'(z_1^3)cdot a_2^2 \
frac{partial e_{o1}}{partial w_{21}^3} &=(a_1^3 - y_1) cdot sigma'(z_2^3)cdot a_1^2 \
frac{partial e_{o1}}{partial w_{12}^3} &=(a_1^3 - y_1) cdot sigma'(z_2^3)cdot a_2^2 \
frac{partial e_{o1}}{partial b_1^3} &= (a_1^3 - y_1) cdot sigma'(z_1^3) \
frac{partial e_{o1}}{partial b_2^3} &= (a_1^3 - y_1) cdot sigma'(z_2^3) \
end{align*}
]
这里设中间变量(delta_1^3,delta_2^3)
[delta_1^3 = (a_1^3 - y_1) cdot sigma'(z_1^3) \
delta_2^3 = (a_2^3 - y_2)cdot sigma'(z_2^3)
]
可以得到如下结果:
- 权值(W^3)的偏导数,输出层到隐藏层之间的权值。
[left[
egin{array}{cc}
frac{partial e_{o}}{partial w_{11}^3} & frac{partial e_{o}}{partial w_{12}^3} \
frac{partial e_{o}}{partial w_{21}^3} &frac{partial e_{o}}{partial w_{22}^3}
end{array}
ight] = left[ egin{array}{cc}delta_1^3cdot a_1^2 & delta_1^3cdot a_2^2 \ delta_2^3 cdot a_1^2 & delta_2^3 cdot a_2^2end{array}
ight]
]
[left[
egin{array}{c}
frac{partial e_{o}}{b_1^3} \ frac{partial e_{o}}{b_2^3}
end{array}
ight] = left[
egin{array}{c}
delta_1^3 \delta_2^3
end{array}
ight]
]
有了上面的公式,这对于输出层和隐藏层之间的权值偏导数求起来就很简单了。只需要求出(delta_1^3,delta_2^3),再乘以神经元相应的输入即可。而(delta_1^3,delta_2^3)的计算也很简单,前部分是输出代价函数的导数,后部分中的(z_j^3)为神经元的输入的加权和,在计算正向传播的过程可以缓存下来。
上面计算的是输出层和隐藏层之间权值和偏置的导数,利用中间变量(delta_j^3)不但是整个计算很清晰,而且能够利用正向传播的中间结果,加快反向传播的计算。
(delta^l)的递推公式
这里不禁就有个疑问,对于隐藏层和输入层之间的权值和偏置是不是也可以利用类似的中间变量(delta_j^2)呢。答案是肯定的。
首先来分析下 (delta_j^3)的组成,
[delta_j^3 = (a_j^3 - y_j) cdot sigma'(z_j^3)
]
很明显,前面部分是输出层的误差 (e_{oj} = frac{1}{2}(a_j^3 - y_j)^2)关于(a_j^3)的偏导数;后面部分则是神经元的输出(a_j^3 = sigma(z_j^3))关于加权后的(z_j^3)导数,应用链式求导法则
[egin{align*}
delta_j^3 &= frac{partial e_{oj}}{partial a_j^3} cdot frac{partial a_j^3}{z_j^3}\
&= frac{partial e_{oj}}{z_j^3}
end{align*}
]
到这里,就可以很容易的得到,
[egin{align*}
delta_j^2 &= frac{partial e_o}{partial z_j^2} \
&= sum_k^2frac{partial e_{ok}}{partial z_j^2} \
& = frac{partial e_{o1}}{partial z_j^2} + frac{partial e_{o2}}{partial z_j^2}
end{align*}
]
其中,(e_o)表示输出层的误差,(z_j^2)表示输入到隐藏层加权后的值。 那么上式的意义就是输出层最终的输出误差关于隐藏层加权后的偏导数。
注意上面的展开,是因为在输出层(e_{ok})只和第(k)个神经元有关,而往前传播到了隐藏层,则隐藏层的所有神经元都有关。
有了上述的表达式,而且由正向传播,可以知道(z_j^3 = w_{j1}^3 a_1^2 + w_{j2}^3 a_2^2),利用链式求导的方法,将其和(delta_j^3)关联起来。
[egin{align*}
delta_j^2 &= frac{partial e_{o1}}{partial z_j^2} + frac{partial e_{o2}}{partial z_j^2} \
&= frac{partial e_{o1}}{partial z_1^3} cdot frac{partial z_1^3}{partial a_j^2} cdot frac{partial a_j^2}{partial z_j^2} + frac{partial e_{o2}}{partial z_2^3} cdot frac{partial z_2^3}{partial a_j^2} cdot frac{partial a_j^2}{partial z_j^2} \
&= delta_1^3 cdot w_{1j}^3 cdot sigma'(z_j^2) + delta_2^3 cdot w_{2j}^3 cdot sigma'(z_j^2)
end{align*}
]
将上式按照矩阵的形式展开
[egin{align*}
left[egin{array}{c}delta_1^2 \ delta_2^2end{array}
ight]
&= left[egin{array}{c}delta_1^3 cdot w_{11}^3 cdot sigma'(z_1^2) + delta_2^3 cdot w_{21}^3 cdot sigma'(z_1^2) \
delta_1^3 cdot w_{12}^3 cdot sigma'(z_2^2) + delta_2^3 cdot w_{22}^3 cdot sigma'(z_2^2) end{array}
ight] \
&= left[egin{array}{c} w_{11}^3 & w_{21}^3 \ w_{12}^3 & w_{22}^3end{array}
ight] cdot
left[egin{array}{c}delta_1^3 \ delta_2^3end{array}
ight] odot left[egin{array}{c} sigma'(z_1^2) \ sigma'(z_2^2)end{array}
ight]
end{align*}
]
令 $delta^l =left[egin{array}{c}delta_1^l delta_2^lend{array}
ight] $ 则有
[delta^2 = (W^3)^Tdelta^3odot sigma'(z^l)
]
在(delta^3)已知的情况下上述公式的计算也很简单,((W^3)^T)是输出层和隐藏层之间的权值矩阵,(sigma'(z^l))是关于(z^l)的导数,(z^l)的值可以在正向传播计算的过程中缓存下来。
现在可以将(W^2)的偏导数使用(delta^2)的形式表达了
[left[
egin{array}{cc}
frac{partial e_{o}}{partial w_{11}^2} & frac{partial e_{o}}{partial w_{12}^2} \
frac{partial e_{o}}{partial w_{21}^2} & frac{partial e_{o}}{partial w_{22}^2}
end{array}
ight] = left[ egin{array}{cc}delta_1^2cdot a_1^1 & delta_1^2cdot a_2^1 \ delta_2^2 cdot a_1^1 & delta_2^2 cdot a_2^1end{array}
ight]
]
偏置矩阵(B^2)的偏导数
[left[
egin{array}{c}
frac{partial e_{o}}{b_1^2} \ frac{partial e_{o}}{b_2^2}
end{array}
ight] = left[
egin{array}{c}
delta_2^3 \delta_2^3
end{array}
ight]
]
反向传播的4个基本公式
在上面的推导过程中,使用的是一个3层神经网络,每个层有两个神经元。
对上面的推到过程,进行归纳。可以得到以下几个公式
[delta^L = frac{partial e}{partial a^L} odot sigma'(z^L) ag{BP1}
]
- (delta^L)和 (delta^{L-1})递推关系
[delta^{L-1} = (W^L)^Tdelta^L odot sigma'(z^{L-1}) ag{BP2}
]
[frac{partial e}{b_j^l} = delta_j^l ag{BP3}
]
[frac{partial e}{w_{jk}^l} = delta_j^l a_k^{l-1} ag{BP4}
]
这样在正向传播的过程中,换成每个神经元的加权后的值(z^l)。
- 正向传播的过程中对于每个(l = 2,3,4,cdots,L)计算出(z^l = W^la^{l-1} + b^l)以及(a^l = sigma(z^l))
- 输出层的误差,根据公式({BP1}) 计算 (delta^L)
- 反向误差传播,计算每个层的(delta^l),根据公式(BP2)
- 偏置的梯度,根据公式(BP3)
- 权值矩阵的梯度,根据公式(BP4)
上述反向传播四个基本方程的推导与证明,可以参看《神经网络与深度学习》http://neuralnetworksanddeeplearning.com/
总结
花了几天的时间,终于算是把深度学习的入门基础概念以及反向传播算法的总结完成了。
特别是在反向传播算法的计算过程,本文是在已经知道四个基本方程的基础上,以一个简单的神经网络为例,一步步的导出最后的四个基本方程,对反向传播的过程有了个更深刻的理解。 看着那么复杂的求导过程,一步步的变成4个简洁的数据公式,还是有点小兴奋的...