后向传播算法是梯度下降的效率算法
设神经网络的参数: $\theta = {w_1,w_2,...,b_1,b_2...}$
神经网络的参数从$\theta_0$开始,一直迭代$\theta_0 \rightarrow \theta_1 \rightarrow \theta_2 \rightarrow ...$。
$\bigtriangledown L (\theta) = \begin{bmatrix}
\frac{\partial L(\theta)}{\partial w_1} \\
\frac{\partial L(\theta)}{\partial w_2} \\
\vdots \\
\frac{\partial L(\theta)}{\partial b_1} \\
\frac{\partial L(\theta)}{\partial b_2} \\
\vdots
\end{bmatrix}$
$\theta_1 = \theta_0-\eta \bigtriangledown L(\theta_0)$
$\theta_2 = \theta_1-\eta \bigtriangledown L(\theta_1)$
这时我们需要用后向传播算法来有效率地计算这动辄上百万个的参数。
后向传播算法其实就是梯度下降算法,只是运算的方式不同而比较有效率地运算而已。
链式法则 Chain Rule
在介绍后向传播算法之前,先了解一下链式法则(Chain Rule)
Case 1: y=g(x) z=h(y)
x可通过公式g(x)的到y,y可通过公式h(y)得到z;
那么变量x,y,x变化的关系可以写成下面形式:
$\bigtriangleup x \rightarrow \bigtriangleup y \rightarrow \bigtriangleup z$
抽象成微分公式可以写成下面形式:
$\frac{\mathrm{d} z}{\mathrm{d} x} = \frac{\mathrm{d} z}{\mathrm{d} y}\frac{\mathrm{d} y}{\mathrm{d} x}$
case 2: x=g(s) y=h(s) z=k(x,y)
s可通过公式g(s)的到x,s可通过公式h(s)得到y,z可通过两个变量x,y和公式k(x,y)得到;
直观的变量s,x,y,z变化关系可以写成下面形式:
抽象成微分公式可以写成下面形式:
$\frac{\mathrm{d} z}{\mathrm{d} s} = \frac{\partial z}{\partial x}\frac{\mathrm{d} x}{\mathrm{d} s} + \frac{\partial z}{\partial y}\frac{\mathrm{d} y}{\mathrm{d} s}$
后向传播算法
给定一组$\underset{\theta}{NN}$神经网络参数,代入$x^n$后得到$y^n$,$y^n$和目标$\hat{y}^n$之间的距离(Cost代价)$C^n$,$C^n$越小越好,即:
$x^n \rightarrow \underset{\theta}{NN} \rightarrow y^n \underset{ C^n }{\Leftrightarrow} \hat{y}^n$
损失函数可以可以写作$L(\theta)=\sum_{n=1}^NC^n(\theta)$
对式子两边偏微分:
$L(\theta)=\sum_{n=1}^NC^n(\theta) \ \ \rightarrow \ \ \frac{\partial L(\theta)}{\partial w}=\sum_{n=1}^N\frac{\partial C^n(\theta)}{\partial w}$
拆开其中一个:
怎样计算$\frac{\partial C}{\partial w}$呢? 可以通过链式法则:
$\frac{\partial C}{\partial w}=? \ \ \ \frac{\partial z}{\partial w}\frac{\partial C}{\partial z}$
计算所有参数的$\frac{\partial z}{\partial w}$,称之为前向传导(Forward pass)
计算所有激活函数的输入z的$\frac{\partial C}{\partial z}$,称之为后向传导(Backward pass)
前向传导的计算过程
先来看看前向传导的计算过程:
因为 $z=x_1w_1+x_2w_2+b$
所以有:
$\frac{\partial z}{\partial w_1} = x_1$
$\frac{\partial z}{\partial w_2} = x_2$
可以看出,它们的梯度是上一个节点的输出
上述过程如下图:
后向传导的计算过程
再看看后向传导的计算过程:
设第一个激活函数节点的输出 $a=\sigma(z)$ ( z通过$\sigma()$得到a ) .
$\frac{\partial C}{\partial z} = \frac{\partial a}{\partial z}\frac{\partial C}{\partial a}$
$\frac{\partial a}{\partial z}$ 实际上是$\sigma(z)$的微分.
那么$\frac{\partial C}{\partial a}$怎么求得呢?
根据链式法则可得:
$\frac{\partial C}{\partial a} = \frac{\partial z'}{\partial a}\frac{\partial C}{\partial z'} + \frac{\partial z''}{\partial a}\frac{\partial C}{\partial z''}$
上述过程如下图:
那么$\frac{\partial C}{\partial z'}$ 和 $\frac{\partial C}{\partial z''}$怎么计算呢?
假设这两项已知,可得:
$\frac{\partial C}{\partial z} = \sigma'(z)[w_3\frac{\partial C}{\partial z'}+w_4\frac{\partial C}{\partial z''}]$
$\sigma'(z)$ 实际上可以看作是一个常数,因为z在前向传导计算中已经被计算好了.
假设有第一种情况:
后边跟的是输出层,那么
$\frac{\partial C}{\partial z'} = \frac{\partial y_1}{\partial z'}\frac{\partial C}{\partial y_1}$
$\frac{\partial C}{\partial z''} = \frac{\partial y_2}{\partial z''}\frac{\partial C}{\partial y_2}$
这样就结束了.
上述过程如下图:
那么假设有第二种情况:
如果还需要经过后续节点, 那么如果我们知道$\frac{\partial C}{\partial z_a}$和$\frac{\partial C}{\partial z_b}$,我们就可以计算$\sigma'(z')$.
按照这样计算直到下一层为输出层.
假设现在有三个隐藏层,每个隐藏层有两个节点,那么要想计算z1
的偏微分,就要先计算z3
和z4
的偏微分,想要知道z3
的偏微分,就算先计算z5
和z6
的偏微分...
那么如果每次都从z1
,z2
开始算,它们的式子会很长,没有效率.
但是如果从z5
和z6
开始算偏微分,那么式子就变简单了.
这个过程称之谓 后向传导.
那么后向传播算的计算过程如下:
其实是先算一次前向传导,得到$\frac{\partial z}{\partial w}$
在算一次后向传导,得到$\frac{\partial C}{\partial z}$
得到的两项相乘,就能得到$\frac{\partial C}{\partial w}$