一步一步计算反向传播

2,144 阅读2分钟

反向传播(Backpropagation) 是训练神经网络的一种常见手段。在 神经网络基础 这一节里,我们提到,确定好每个神经节点的权重(weight)与偏差(bias)之后,就能通过前馈(feedforward)确定神经网络的输出,但这只是输出预测值。我们还需要根据预测值,来 反推 各个权重与偏差需要如何调整,以使得预测值和期望值误差更小。这就是反向传播所做的事情。

要弄明白反向传播是如何工作的,以一个具体例子计算是最简单直接的。假设现有一个 3 层的神经网络如下:

假设现有一样本,输入为 0.1、0.5,期望输出为 0.9、0.1。我们初始权重、初始偏差如图所示:

接下来我们通过这个样本的训练,来调整初始权重。

前馈

首先我们通过前馈来确定神经网络的输出,也就是预测值。回顾一下神经节点的输入输出,它的输入是上一层各个节点通过权重与偏差计算的结果,我们记为 z;它的输出是 z 经过 激活函数(activation functions)产生的输出,记为 a。通式如下:

z^{l}_j = 
\sum_k w^{l}_{jk} a^{l-1}_k + b^l_j
a^{l}_j = \sigma(z^{l}_j)

其中,激活函数定义为

\sigma(x) = \frac{1}{1 + e^{-x}}

先不用过于纠结公式的各个字符的含义,暂时只需要知道计算方法即可。

带入得:

z_{h1} = 0.1 * 0.1 + 0.5 * 0.2 + 0.1 = 0.21
a_{h1} = \sigma(z_{h1}) = 0.549833997
z_{h2} = 0.1 * 0.3 + 0.5 * 0.4 + 0.2 = 0.43
a_{h2} = \sigma(z_{h2}) = 0.605873668

同理算出输出层:

z_{o1} = 0.549833997 * 0.5 + 0.605873668 * 0.6 + 0.3 = 0.938441199
a_{o1} = \sigma(z_{o1}) = 0.718784679
z_{o2} = 0.549833997 * 0.7 + 0.605873668 * 0.8 + 0.4 = 1.269582732
a_{o2} = \sigma(z_{o2}) = 0.780671310

这样我们完成了一次前馈的计算,得到了神经网络的两个预测值(output),但这和样本的期望值(target)有一定的误差。我们使用 平方误差函数(squared error function)计算误差 C,通式为:

C = \frac{1}{2n} \sum_{x} \|target-a^L(x)\|^2

带入值为:

C_{o1} = \frac{1}{2} * (0.9 - 0.718784679)^2 = 0.016419496
C_{o2} = \frac{1}{2} * (0.1 - 0.780671310)^2 = 0.231656716
C = C_{o1} + C_{o2} = 0.016419496 + 0.231656716 = 0.248076212

也就是说,神经网络的预测值与实际值有 0.248076212 误差,我们的目标就是通过调节权重和偏差,让这个误差更小。

反向传播

反向传播实际上就是一个求导的过程。如果我们要知道如何通过改变 w_5,使得总误差能变小,这其实就是求总误差 C 对 w_5 求偏导。根据链式法则

\frac{\partial{C}}{\partial{w_{5}}} = \frac{\partial{C}}{\partial{a_{o1}}} * \frac{\partial{a_{o1}}}{\partial{z_{o1}}} * \frac{\partial{z_{o1}}}{\partial{w_{5}}}

这个公式的含义实际上就是 w_5 的改变能对 C 造成的影响,接下来分别求等式右边的三个部分。

首先因为

C = C_{o1} + C_{o2} = \frac{1}{2} * (0.9 - a_{o1})^2 + C_{o2}

其中 C_{o2}a_{o1} 无关,所以 C 对 a_{o1} 求偏导为

\frac{\partial{C}}{\partial{a_{o1}}} = -(0.9 - a_{o1}) = -0.181215321

接下来求 \frac{\partial{a_{o1}}}{\partial{z_{o1}}},而

a_{o1} = \sigma(z_{o1})

所以

\frac{\partial{a_{o1}}}{\partial{z_{o1}}} = \sigma'(z_{o1})

\sigma'(x) = \sigma(x) * (1 - \sigma(x))

所以带入可得

\frac{\partial{a_{o1}}}{\partial{z_{o1}}} = a_{o1} * (1 - a_{o1}) = 0.202133264

最后,我们再看 \frac{\partial{z_{o1}}}{\partial{w_{5}}},因为

z_{o1} = w_5 * a_{h1} + w_6 * a_{h2} + b_3

所以

\frac{\partial{z_{o1}}}{\partial{w_{5}}} = a_{h1} = 0.21

综上,

\frac{\partial{C}}{\partial{w_{5}}} = \frac{\partial{C}}{\partial{a_{o1}}} * \frac{\partial{a_{o1}}}{\partial{z_{o1}}} * \frac{\partial{z_{o1}}}{\partial{w_{5}}}
= -0.181215321 * 0.202133264 * 0.21
= -0.007692225

为了使误差更小,我们使用 w_5 减去这个数,这里引入一个较小的 学习速率(learning rate),用来控制梯度下降的速度,这里取 0.5,则:

w_5 = w_5 - \eta * \frac{\partial{C}}{\partial{w_{5}}} = 0.5 - (-0.007692225) = 0.507692225

另外,计算偏差也是一样的道理,以 b3 举例

\frac{\partial{C}}{\partial{b_{3}}} = 
\frac{\partial{C}}{\partial{a_{o1}}} * \frac{\partial{a_{o1}}}{\partial{z_{o1}}} * \frac{\partial{z_{o1}}}{\partial{b_{3}}}

z_{o1} = w_5 * a_{h1} + w_6 * a_{h2} + b_3

所以

\frac{\partial{z_{o1}}}{\partial{b_{3}}} = 1

\frac{\partial{C}}{\partial{b_{3}}} = \frac{\partial{C}}{\partial{a_{o1}}} * \frac{\partial{a_{o1}}}{\partial{z_{o1}}} * \frac{\partial{z_{o1}}}{\partial{w_{5}}}
= -0.181215321 * 0.202133264 * 1
= -0.036629644

所以

b_3 = b_3 - \eta * \frac{\partial{C}}{\partial{b_{3}}} = 0.3 - (-0.036629644) = 0.336629644

同理,我们可以计算出新的 w_6w_7w_8w_9b_4,再根据公式计算出新的 w_1w_2w_3w_4b_1b_2。这样,我们就完成了一次反向传播。