PRML读书笔记:5.3 误差反向传播


本节的主要内容是误差反向传播(error propagation),这是一种计算前馈神经网络的误差函数$E(\boldsymbol{w})$的高效方法。

神经网络的训练过程本质上可以分为两个步骤:

  1. 计算误差函数关于权重的导数
    • 误差反向传播方法的用途在于高效计算这些导数
    • 在这个阶段,误差通过网络进行反向传播
    • 可以应用于不同种类的网络、误差很熟和导数
  2. 将导数用于计算权重的调整量
    • 可以使用各种最优化方法处理

5.3.1 误差函数导数的计算

下面推导适用于一般神经网络的反向传播算法:

  • 任意前馈拓扑结构
  • 任意可微非线性激活函数
  • 一大类误差函数

推导过程使用一个简单的具有sigmoid隐层单元和平方和误差函数的层次网络进行说明。

误差函数通常由训练集的数据点对应的项之和构成:

$$
E(\boldsymbol{w})=\sum_{n=1}^{N} E_{n}(\boldsymbol{w})
$$

此处考虑的是计算$\nabla E_n(\boldsymbol{w})$。

简单线性模型

考虑一个简单的线性模型,其中输出$y_k$是输入变量$x_i$的线性组合,即

$$
y_{k}=\sum_{i} w_{k i} x_{i}
$$

对于数据点$n$,误差函数的形式为

$$
E_{n}=\frac{1}{2} \sum_{k}\left(y_{n k}-t_{n k}\right)^{2}
$$

其中$y_{nk} = y_ k(\boldsymbol{x} _ n, \boldsymbol{w})$是神经网络输出,$\boldsymbol{t}$是目标值。这个误差函数关于权值$w_ {ji}$的梯度为

$$ \begin{aligned} \frac{\partial E_n}{\partial w_{ji}} &= \frac{\partial E_n}{\partial y_{nj}} \frac{\partial y_{nj}}{\partial w_{ji}} \\ &= (y_{nj} - t_{nj}) x_{ni} \end{aligned} $$

这个结果相当于与权值$w_{ji}$相关联的“误差信号”$y_{nj} - t_{nj}$与输入端相关联的变量$x_{ni}$的乘积。

一般前馈网络

考虑一个一般的前馈网络,每个单元都会计算输入的一个加权和

$$
a_{j}=\sum_{i} w_{j i} z_{i}
$$

其中$z_i$是与它前一个相关联的单元的激活(或者是输入$x_i$),$w_{ji}$是与这个激活相关联的权值。

将激活通过一个非线性激活函数$h(\cdot)$进行变换,得到单元$j$的激活(或者输出$y_j$)

$$z_j = h(a_j)$$

正向传播(forward propagation)的过程即反复应用上述两个公式计算神经网络中所有隐单元和输出单元的激活,这一过程可以被看做是网络中一个向前流动的信息流。

下面考虑计算$E_n$关于权值$w_{ji}$的导数,为简洁起见,省略神经网络变量中的下标$n$。记$\delta_j \equiv \frac{\partial E_n}{\partial a_j}$,首先应用链式法则

$$ \begin{aligned} \frac{\partial E_n}{\partial w_{ij}} &= \frac{\partial E_n}{\partial a_j} \frac{\partial a_j}{\partial w_{ji}} \\ &= \delta_j z_i \end{aligned} $$

5.2节的讨论可得,使用一般函数作为输出单元的激活函数时,有输出单元的$\delta_k = y_k - t_k$,因此输出单元的$\delta_k$是已知的。下面只需求解隐单元的$\delta_j$。使用链式法则(其中$a_k$是所有向单元$j$发送值的单元)

$$ \begin{aligned} \delta_j & \equiv \frac{\partial E_n}{\partial a_j} \\ &= \sum_k \frac{\partial E_n}{\partial a_k} \frac{\partial a_k}{\partial a_j} \\ &= \sum_k \delta_k \frac{\partial a_k}{\partial z_j} \frac{\partial z_j}{\partial a_j} \\ &= \sum_k \delta_k w_{kj} h'(a_j) \\ &= h'(a_j) \sum_k \delta_k w_{kj} \end{aligned} $$

这表明一个特定的隐单元的$\delta$值可以通过将网络中更高层单元的$\delta$反向传播来实现。

信息流的传播方向

上图说明了信息流的传递方向。正向传播阶段的信息传递如蓝色箭头所示。在反向传播阶段,$\delta$在网络中反向传播回$\delta_j$,进行计算,如红色箭头所示。

于是反向传播算法可以总结如下:

  • 对于网络的一个输入向量$\boldsymbol{x}_n$,进行正向传播,计算出所有隐单元和输出单元的激活
  • 利用公式$\delta_k = y_k - t_k$计算出所有输出单元的$\delta_k$
  • 反向传播$\delta$,获得网络中所有隐单元的$\delta_j$
  • 使用公式$\frac{\partial E_n}{\partial w_{ij}} = \delta_j z_i$计算导数

对于批处理方法,只需对每批中的所有例子分别用反向传播算法求出导数,然后求和即可:

$$
\frac{\partial E}{\partial w_{j i}}=\sum_{n} \frac{\partial E_{n}}{\partial w_{j i}}
$$

5.3.2 一个简单的例子

两层神经网络

考虑上图中的两层神经网络,令误差函数为平方和误差函数

$$
E_{n}=\frac{1}{2} \sum_{k=1}^{K}\left(y_{k}-t_{k}\right)^{2}
$$

其中对于每个输入的例子$\boldsymbol{x}_ n$,$y_ k$是输出单元$k$的激活,$t_ k$是对应的目标值。

输出单元的激活函数为线性激活函数

$$
y_k = a_k
$$

隐单元的激活函数为双曲正切函数

$$
h(a) \equiv \tanh (a)=\frac{e^{a}-e^{-a}}{e^{a}+e^{-a}}
$$

这个函数的导数可以表示为

$$
h'(a) = 1-h^2(a)
$$

下面对于训练集中的每个例子首先进行前向传播。

$$ \begin{aligned} a_j &= \sum_{i=0}^D w_{ji}^{(1)} x_i \\ z_j &= \tanh(a_j) \\ y_k &= \sum_{j=0}^M w_{kj}^{(2)} z_j \end{aligned} $$

然后计算每个输出单元的$\delta$值:

$$
\delta_k = y_k - t_k
$$

然后将$\delta$值反向传播:

$$ \begin{aligned} \delta_j &= h'(a_j) \sum_{k=1}^K \delta_k w_{kj} \\ &= (1-h^2(a_j)) \sum_{k=1}^K \delta_k w_{kj} \\ &= (1-z_j^2) \sum_{k=1}^K \delta_k w_{kj} \end{aligned} $$

最后求出$E_n$关于第一层和第二层权重的导数:

$$ \begin{aligned} \frac{\partial E_{n}}{\partial w_{j i}^{(1)}} &= \delta_{j} x_{i} \\ \frac{\partial E_{n}}{\partial w_{k j}^{(2)}} &= \delta_{k} z_{j} \end{aligned} $$

例:手写一个两层的神经网络

如本节的例子所示,我手写了一个两层的神经网络,用它来拟合正弦函数,取得了不错的效果。

实验代码

值得注意的是:

  • 使用的初始化函数是xavier
  • 每次的梯度取的是同一批样本的平均值,而不是像上文所说的那样求和

实验结果如下图所示。

实验结果

5.3.3 反向传播的效率

反向传播的计算复杂度通常是$O(W)$,因为权重的数量通常比单元的数量大得多,正向传播的计算复杂度主要取决于求和式的计算。

另一种计算误差函数导数的反向传播方法是使用有限差。对每个权值进行一个扰动,然后用下面的表达式来近似导数

$$
\frac{\partial E_{n}}{\partial w_{j i}}=\frac{E_{n}\left(w_{j i}+\epsilon\right)-E_{n}\left(w_{j i}-\epsilon\right)}{2 \epsilon}+O\left(\epsilon^{2}\right)
$$

这种算法的计算复杂度是$O(W^2)$,因为每次正向传播需要$O(W)$步,而网络中有$W$个权值,每个权值需要被单独施加扰动。

这种方法的意义是与反向传播算法计算的导数进行对比,以便检查反向传播算法的正确性。

5.3.4 Jacobian矩阵

// TODO


 评论