在第一部分(《反向传播算法的工作原理(1)》)已经得到了如下结论,本文将在前述基础上,做进一步的证明和解释。



transformer前向传播_反向传播

换个表述方式

在(BP1)和(BP2)中使用了Hadamard积,如果不熟悉它,可以换一种方式表述,利用矩阵乘法:(BP1)可以改写为:

其中 是一个方阵,其对角线是 ,非对角线项为零。请注意,此矩阵通过矩阵乘法作用于 。

有上面粮食,可得:

对于熟悉矩阵乘法的读者来说,这个方程可能比(BP1)和(BP2)更容易理解。我之所以关注(BP1)和(BP2),是因为这种方法在数值上的实现速度更快。

四个基本方程的证明

现在我们将证明(BP1)—(BP4)四个基本方程,它们都是多元微积分链式法则的结果。

让我们从方程(BP1)开始,它给出了输出误差的表达式 。为了证明这个方程,根据定义回想一下

应用链式法则,我们可以根据输出激活的偏导数来重新表示上面的偏导数,

其中, 是对输出层中所有 个神经元的求和。当 时, 神经元的激活函数输 只依赖于 神经元的加权输入 。因此,当 时, 将会消失。所以,我们可以将(37)简化为

(38)的右边第二项 可以写成 ,然后方程变成

这是(BP1)的分量形式。

接下来,我们将证明(BP2),它给出了 的误差方程式,这个误差与下一层的误差 相关。为此,我们需要重写 , 而这个方程式与 相关。我们可以采用链式法则:

在最后一行中,交换了右侧的两项的位置,并用了 替换 。要计算 ,请注意

通过求微分,我们得到

代入(42),得:

这就得到了(BP2)的分量形式。

其它两个方程(BP3)和(BP4),也可以遵循链式法则进行证明。此处从略。

反向传播算法

根据前述方程,下面以算法的形式,显示地写出反向传播算法:

  1. 输入 :为输入层设置相应的激活 。
  2. 前向传播:对于每个 ,计算 和 。
  3. 输出误差:  :计算向量 .
  4. 反向传播误差: 对于每个 ,计算 .
  5. 输出: 代价函数梯度的是: 和 .

研究一下这个算法,你就会明白为什么它被称为反向传播。我们从最后一层开始,反向计算误差向量 。在网络中反向操作似乎很奇怪,但是如果你考虑反向传播的证据,反向传播源于这样一个事实:代价函数是网络输出的函数。为了了解代价是如何随先前的权重和偏差而变化的,我们需要反复应用链式规则,在各个层中反向操作以获得可用的表达式。

为什么说反向传播是一种快速算法?

为什么说反向传播是一种快速算法?为了回答这个问题,我们思考一下计算梯度的另一种方法。想象一下神经网络研究的早期。也许是上世纪五六十年代,你是世界上第一个想到用梯度下降来学习的人!但要使这个想法奏效,你需要一种计算代价函数梯度的方法。回想一下你的微积分知识,决定看看是否可以用链式法则来计算梯度。但是在尝试了一段时间后,所用的代数知识看起来很复杂,你变得灰心丧气。所以你试着找到另一种方法。你决定把代价仅仅看作是权重 的函数(稍后我们会回到偏差问题)。你给权重 编号,并希望计算某个特定的权重 的 。一个很明显的方法就是使用近似法:

其中,是一个小正数, 是在 方向上的单位矢量。换句话说,我们可以对 的两个稍微不同的值的代价 进行计算,然后应用方程(46),以此来估算 ——求极限。我们可以用同样的思路来计算与偏差相关的偏导数 。

这种方法看起来非常具有可行性。它在概念上很简单,而且非常容易实现,只需要几行代码。当然,它看起来比用链式法则计算梯度的想法更可取!

遗憾的是,虽然这种方法看起来很可取,但是当你实现代码时,运行过程却是非常缓慢的。为了理解原因,假设我们的网络中有一百万个权重。对于每个不同的权重 ,我们需要计算 ,以便对 进行计算。这意味着:要计算梯度,我们需要对代价函数进行一百万次不同的计算,需要通过网络(每个训练示例)进行一百万次的正向传递。我们还需要计算 ,所以总共需要通过网络进行一百万零一次的传递。

而反向传播则不同,它使我们能够同时计算所有的偏导数 ,只需通过网络正向传递一次,然后通过网络反向传递。粗略地说,反向传递的计算代价与正向传递的计算代价大致相同。这种说法貌似合理,但需要进行一些分析才能做出仔细的陈述。之所以“貌似合理”,是因为在正向传递的过程中,主要的计算代价是乘以权重矩阵;而在反向过程中,主要的计算代价是乘以权重矩阵的转置。这些操作的计算代价显然是相似的。因此,反向传播的总代价与仅仅通过网络进行两次正向传播的代价大致相同。把这个代价与我们在(46)的方法中所需的一百万零一次的正向传递的代价进行比较,两种方法的高下不言而喻!因此,尽管反向传播看起来比基于(46)的方法更复杂,但实际上它的速度要快得多。

这种提速在1986年首次得到充分的重视,它大大扩展了神经网络能够解决的问题的范围。这进而引起了人们使用神经网络的热潮。当然,反向传播不是灵丹妙药。即使在20世纪80年代后期,人们也遇到了一些限制,尤其是当人们试图用反向传播来训练深层神经网络(即具有许多隐藏层的网络)的时候。

总括反向传播

正如我所解释的,反向传播有两个谜团。首先,算法到底在做什么?我们已经绘制出了从输出反向传播的误差图片。但是当我们做这些矩阵和矢量乘法的时候,我们能不能更深入一点,建立起更多的直觉来理解到底发生了什么?第二个谜团是,一开始人们是怎样发现反向传播的?遵循算法中的步骤,甚至理解对于算法有效性的证明是一回事。但这并不意味着:你对问题的理解深度足以让你在第一时间发现算法。有没有一条合理的推理思路可以让你发现反向传播算法?在本节中,我将解决这两个谜团。

为了提高对算法的直觉,让我们想象一下,用 更新网络中的某个权重 :



transformer前向传播_反向传播_02

权重的变化会导致相应神经元的输出激活发生变化:



transformer前向传播_反向传播算法_03

这又会导致下一层的所有激活发生变化:



transformer前向传播_transformer前向传播_04

这些变化又会导致下一层的变化,再下一层的变化,以此类推,直到最后一层发生变化,然后是代价函数的变化:



transformer前向传播_反向传播算法_05

代价中的变化 与权重中的变化 有关,如下所示

这表明,计算 的一个可能的方法是:仔细研究 中的一个小变化是如何传播的,从而导致 的微小变化。如果我们能做到这一点,小心地用易于计算的量来表示所有的东西,那么我们应该能够计算 。

试一下。更改 会在 层的 神经元激活中引起一个小的变化。带来这一更改的是:

在激活 中的更改将导致下一层中所有激活的改变,即 层的改变。我们将专注于其中某一个激活(比如 )受到影响的方式。



transformer前向传播_反向传播_06

事实上,它会导致以下变化:

代入式(48)中的表达式,我们得到:

当然, 将导致下一层的激活发生变化。事实上,我们可以想象一条从 到 的整个网络路径。每次激活的变化都会导致下一次激活的变化,最后是输出代价的变化。如果路径经过激活 ,则得到的表达式是:

也就是说,我们为所经过的每一个额外的神经元选择了一个 类型项,以及末尾的 项。这表示 的变化,而变化的原因是:网络的这条特定路径上的激活发生了变化。当然,中的更改可以通过多种路径传播,从而影响代价,但我们只考虑一条路径。为了计算 的总变化,一种貌似合理的做法是:将权重和最终代价之间的所有可能的路径相加,即:

总结了路径上的中间神经元的所有可能的选择,与(47)相比,我们看到

现在,方程(53)看起来很复杂。然而,它有一个很好的直观解释,(53)是 对网络中某个权重的变化率。这个方程告诉我们的是:网络中两个神经元之间的每一条边都与一个变化因子有关;这个变化因子只是一个神经元的激活相对于另一个神经元的激活的偏导数。从第一个权重到第一个神经元的边有一个变化因子 。一条路径的变化因子就是路径上所有变化因子的乘积。总变化率 是从初始权重到最终代价的所有路径的速率因子之和。对于单个路径,此过程如下所示:



transformer前向传播_反向传播算法_07

到目前为止,我所提供的是一个启发性的论证,这也是一种思维方式,考虑网络中的权重受到干扰时,会发生什么情况。我只是概述了一个思路,你可以沿着这个思路进一步进行论证。首先,你可以导出方程(53)中所有单个偏导数的显式表达式。这一点用微积分很容易做到。做完这些之后,你可以试着把所有的指数之和写成矩阵乘法的形式。结果证明这是乏味的,需要一些毅力,但不需要非凡的洞察力。写完之后还要尽可能地简化,你会发现:你最终得到的正是反向传播算法!所以你可以把反向传播算法看作是:一种计算所有这些路径的变化因子之和的方法。或者,换个角度说,反向传播算法是一种巧妙的方法,它可以追踪权重(和偏差)的小扰动,这些扰动通过网络传播,到达输出,然后影响代价。