跳转至

第 10 章:序列建模:循环和递归网络(Sequence Modeling: Recurrent and Recursive Nets)

10.1 展开计算图(Unfolding Computational Graphs)

循环神经网络(RNN)是 Rumelhart 等人(1986a)提出的一类用于处理序列数据的神经网络家族。正如卷积网络是专门用于处理网格型数值(如图像)的神经网络,循环网络是专门用于处理数值序列 \(x^{(1)},\dots,x^{(\tau)}\) 的神经网络。卷积网络能轻松扩展到宽度和高度都很大的图像,部分还能处理可变尺寸的图像;类似地,循环网络能扩展到比无序列特化网络实际能处理的还要长得多的序列,且大多数循环网络也能处理可变长度的序列。

从多层网络过渡到循环网络,需要借助 1980 年代机器学习与统计模型中一个早期思想:在模型的不同部分之间共享参数。参数共享使得模型可以扩展并应用于不同形式(这里指不同长度)的样本,并在它们之间做泛化。如果我们对时间索引的每个值都使用独立的参数,就无法泛化到训练时未出现过的序列长度,也无法在不同序列长度和不同时刻位置之间共享统计强度。当同一段信息可能出现在序列中多个位置时,这种共享尤其重要——例如句子"I went to Nepal in 2009"和"In 2009, I went to Nepal"中的年份 2009 出现在第六个词或第二个词的位置都应当被识别出来。如果训练一个处理定长句子的前馈网络,传统的全连接前馈网络会对每个输入特征使用独立的参数,因此不得不在句子的每个位置分别学习语言的所有规则。相比之下,循环神经网络在若干时间步之间共享同一套权重。

另一个相关思想是在一维时间序列上做卷积。这种卷积方法是时延神经网络(Lang and Hinton, 1988; Waibel et al., 1989; Lang et al., 1990)的基础。卷积运算允许网络跨时间共享参数,但它是浅层的:卷积的输出是一个序列,其中每个成员只是输入中少数相邻成员的函数。参数共享的思想体现在每个时间步都施加同一个卷积核。循环网络以不同方式共享参数:每个输出成员都是它之前那些输出成员的函数,使用同一条更新规则作用到之前的输出上。这种循环式形式导致参数在一个非常深的计算图中得到共享。

为简化表述,作者约定 RNN 作用于包含向量 \(x^{(t)}\) 的序列,时间步索引 \(t\) 从 1 取到 \(\tau\)。实践中循环网络通常对若干这样的序列组成的小批量(minibatch)进行操作,每个样本有不同的序列长度 \(\tau\)。小批量索引被省略以简化记号。时间步索引也不必字面上指真实世界的时间流逝,有时只指序列中的位置。RNN 也可应用于二维空间数据如图像;即使对涉及时间的数据,网络也可能有"反向时间"的连接,只要整条序列在被送入网络之前已观测完整。

本章把计算图的思想推广到包含环的情形。环表示一个变量当前值对它自身在未来时刻值的影响。这样的计算图允许我们定义循环神经网络,随后会描述构造、训练和使用循环网络的多种方式。Graves(2012)的教材提供了本章之外的更详细介绍。

10.2 循环神经网络(Recurrent Neural Networks)

借助 10.1 节的图展开和参数共享思想,可以设计多种循环神经网络。文中列出几种重要的设计模式:在每个时间步产生一个输出、且隐藏单元之间有循环连接的网络(对应图 10.3);在每个时间步产生一个输出、但循环连接只从某一时刻的输出指向下一时刻的隐藏单元的网络(对应图 10.4);隐藏单元之间有循环连接、读入整条序列后产生单个输出的网络(对应图 10.5)。其中图 10.3 是一个有代表性的例子,是后续多数章节的重点参照对象。

图 10.3 和公式 10.8 描述的循环神经网络在"任何图灵机可计算的函数都可由一个有限大小的此类循环网络计算"这一意义下是通用的。输出可在与图灵机所用时间步数及输入长度都成线性关系的时间步数之后从 RNN 读出(Siegelmann and Sontag, 1991; Siegelmann, 1995; Siegelmann and Sontag, 1995; Hyotyniemi, 1996)。图灵机可计算的函数是离散的,因此这些结果关注的是函数的精确实现而非近似。RNN 用作图灵机时接受二进制序列作为输入,其输出必须离散化以给出二进制输出。Siegelmann and Sontag (1995) 证明可以用一个特定的有限大小 RNN(886 个单元)计算所有这类函数。同一网络可模拟任何图灵机——因为图灵机的"输入"是对待计算函数的描述。理论上用于证明的 RNN 可以用有理数(任意精度)的激活与权重来表示无界栈。

接下来作者给出图 10.3 中 RNN 的前向传播方程。图未指定隐藏单元的激活函数,此处假设为双曲正切;图也未指定输出与损失函数的形式,此处假设输出是离散的(如预测词或字符)。一种自然表示离散变量的方式是把输出 \(o\) 视为给出该离散变量各可能取值未归一化对数概率,再对输出施加 softmax 后处理得到归一化概率向量 \(\hat{y}\)。前向传播从指定初始状态 \(h^{(0)}\) 开始,然后对 \(t=1\)\(t=\tau\) 的每个时间步施加以下更新方程:

\[ a^{(t)}=b+Wh^{(t-1)}+Ux^{(t)} \]
\[ h^{(t)}=\tanh(a^{(t)}) \]
\[ o^{(t)}=c+Vh^{(t)} \]
\[ \hat{y}^{(t)}=\mathrm{softmax}(o^{(t)}) \]

其中参数包括偏置向量 \(b\)\(c\),以及权重矩阵 \(U,V,W\),分别对应输入到隐藏、隐藏到输出和隐藏到隐藏的连接。这是一个把输入序列映射到等长输出序列的循环网络示例。给定 \(x\) 值序列配对 \(y\) 值序列,总损失就是各时间步损失之和。例如若 \(L^{(t)}\) 是给定 \(x^{(1)},\dots,x^{(t)}\)\(y^{(t)}\) 的负对数似然,则

\[ L(\{x^{(1)},\dots,x^{(\tau)}\},\{y^{(1)},\dots,y^{(\tau)}\})=\sum_t L^{(t)}=-\sum_t\log p_{\text{model}}(y^{(t)}\mid x^{(1)},\dots,x^{(t)}) \]

其中 \(p_{\text{model}}(y^{(t)}\mid x^{(1)},\dots,x^{(t)})\) 由模型输出向量 \(\hat{y}^{(t)}\) 中对应 \(y^{(t)}\) 的项读出。计算该损失函数关于参数的梯度开销很大:需要先做一次从左到右的前向传播扫描展开图(图 10.3),再从右到左做反向传播扫描。运行时间为 \(O(\tau)\),且无法通过并行化缩减,因为前向传播图本质上是顺序的——每一步必须等前一步算完。前向过程中算出的状态必须保留以供反向过程复用,因此内存开销也是 \(O(\tau)\)。在展开图上以 \(O(\tau)\) 代价运行的反向传播算法叫做"沿时间反向传播"(back-propagation through time, BPTT),10.2.2 节会进一步讨论。隐藏单元之间带循环的网络虽然强大但训练昂贵,文中随即提出是否存在替代方案的问题。

10.2.1 Teacher Forcing 与带输出循环的网络(Teacher Forcing and Networks with Output Recurrence)

只在某一时刻输出指向下一时刻隐藏单元的网络(图 10.4)严格来说功能较弱,因为它缺少隐藏到隐藏的循环连接;例如它不能模拟通用图灵机。由于缺少隐藏到隐藏的循环,要求输出单元捕捉网络将用于预测未来的全部过去信息;而输出单元被显式训练以匹配训练集的目标,因此除非用户知道如何描述系统的完整状态并把它作为训练集目标的一部分提供,否则它不太可能捕捉到输入历史所必需的关于过去的信息。消除隐藏到隐藏循环的好处是:对于任何基于把时刻 \(t\) 的预测与训练目标 \(t\) 比较的损失函数,所有时间步都是解耦的;训练因此可并行化,每个时间步 \(t\) 的梯度可独立计算,不必先算出前一时间步的输出,因为训练集提供了该输出的理想值。

对带有从输出回到模型自身的循环连接的模型,可以用 teacher forcing 进行训练。Teacher forcing 是一个从最大似然准则中产生的过程:训练时模型在时刻 \(t+1\) 接收真实输出 \(y^{(t)}\) 作为输入。考察一个两步序列的条件最大似然准则即可看出:在 \(t=2\) 时刻,模型被训练去最大化给定到目前为止 \(x\) 序列和训练集前一 \(y\) 值的条件下 \(y^{(2)}\) 的条件概率。最大似然因此规定,训练时这些连接应被喂入目标值(即正确的输出应当是什么)而非模型自身输出。图 10.6 展示了这一过程。

作者最初把 teacher forcing 的动机解释为:它能让我们在缺少隐藏到隐藏连接的网络中避免 BPTT。只要网络有从某时刻输出指向下一时刻值的连接,teacher forcing 仍可应用于带隐藏到隐藏连接的网络;然而一旦隐藏单元成为更早时间步的函数,BPTT 就成为必要。一些模型因此会同时使用 teacher forcing 和 BPTT。

严格 teacher forcing 的缺点是:如果网络之后要在开环模式下使用(网络输出或其采样作为输入反馈回来),则训练时网络看到的输入与测试时会看到的输入可能差异很大。一种缓解方式是同时用 teacher-forced 输入和 free-running 输入训练,例如通过在展开的循环输出到输入路径上预测未来若干步的正确目标;这样网络可以学会考虑训练中未见过、由自身在 free-running 模式下生成的输入条件,并学会把状态映射回让网络在几步之后生成合适输出的状态。另一种方法(Bengio et al., 2015b)随机选择用生成值或真实数据值作为输入,该方法利用课程学习策略让生成值作为输入的比例逐渐增加。

10.2.2 循环神经网络中的梯度计算(Computing the Gradient in a Recurrent Neural Network)

循环网络的梯度计算可以直接进行:把 6.5.6 节给出的广义反向传播算法套用到展开的计算图上即可,不需要专门的算法。BPTT 算出的梯度可与任何通用基于梯度的技术一起用来训练 RNN。

为获得一些直观认识,作者给出按 BPTT 对公式 10.8 与 10.12 计算梯度的示例。计算图的节点包括参数 \(U,V,W,b,c\) 以及 \(t\) 索引的节点序列 \(x^{(t)},h^{(t)},o^{(t)},L^{(t)}\)。对每个节点 \(N\) 需要递归地计算 \(\nabla_N L\),基于图中后续节点的梯度。递归从最终损失的紧前节点开始:\(\partial L/\partial L^{(t)}=1\)。推导中假设输出 \(o^{(t)}\) 被送入 softmax 得到概率向量 \(\hat{y}\),损失是给定到目前为止输入的真实目标 \(y^{(t)}\) 的负对数似然。对所有 \(i,t\),输出在 \(t\) 时刻的梯度为 \((\nabla_{o^{(t)}}L)_i=\partial L/\partial o_i^{(t)}=\hat{y}_i^{(t)}-\mathbf{1}_{i,y^{(t)}}\)

从序列末端开始反推。在终末时间步 \(\tau\)\(h^{(\tau)}\) 只有 \(o^{(\tau)}\) 一个后代,因此其梯度很简单:\(\nabla_{h^{(\tau)}}L=V^\top \nabla_{o^{(\tau)}}L\)。然后反向迭代,从 \(t=\tau-1\)\(t=1\) 做 BPTT。注意 \(h^{(t)}\)(对 \(t<\tau\))的后代既有 \(o^{(t)}\) 也有 \(h^{(t+1)}\),其梯度为

\[ \nabla_{h^{(t)}}L=\left(\frac{\partial h^{(t+1)}}{\partial h^{(t)}}\right)^\top(\nabla_{h^{(t+1)}}L)+\left(\frac{\partial o^{(t)}}{\partial h^{(t)}}\right)^\top(\nabla_{o^{(t)}}L)=W^\top(\nabla_{h^{(t+1)}}L)\mathrm{diag}(1-(h^{(t+1)})^2)+V^\top(\nabla_{o^{(t)}}L) \]

其中 \(\mathrm{diag}(1-(h^{(t+1)})^2)\) 是与 \(t+1\) 时刻隐藏单元相关的双曲正切函数的 Jacobian 对角矩阵。

得到内部节点的梯度后,可继续得到参数节点的梯度。由于参数在多个时间步共享,对涉及这些变量的求导运算需要谨慎处理。文中采用 6.5.6 节的 bprop 方法来计算计算图中单条边对梯度的贡献;为了避免 \(\nabla_W f\) 算符在数学上把 \(W\) 通过所有时间步边对 \(f\) 的贡献都计入的歧义,引入虚拟变量 \(W^{(t)}\),定义其为 \(W\) 的副本但每个 \(W^{(t)}\) 只在时间步 \(t\) 被使用。这样可写 \(\nabla_{W^{(t)}}\) 表示 \(W\)\(t\) 时刻的边对梯度的贡献。参数梯度由以下各式给出:\(\nabla_c L=\sum_t\nabla_{o^{(t)}}L\)\(\nabla_b L=\sum_t\mathrm{diag}(1-(h^{(t)})^2)\nabla_{h^{(t)}}L\)\(\nabla_V L=\sum_t(\nabla_{o^{(t)}}L)h^{(t)\top}\)\(\nabla_W L=\sum_t\mathrm{diag}(1-(h^{(t)})^2)(\nabla_{h^{(t)}}L)h^{(t-1)\top}\)\(\nabla_U L=\sum_t\mathrm{diag}(1-(h^{(t)})^2)(\nabla_{h^{(t)}}L)x^{(t)\top}\)。我们不需要计算关于 \(x^{(t)}\) 的梯度,因为 \(x^{(t)}\) 在定义损失的计算图中没有作为其祖先的参数。

10.2.3 循环网络作为有向图模型(Recurrent Networks as Directed Graphical Models)

前述 RNN 示例中损失 \(L^{(t)}\) 是训练目标 \(y^{(t)}\) 与输出 \(o^{(t)}\) 之间的交叉熵。和前馈网络一样,原则上几乎任何损失都可与循环网络一起使用,损失应基于任务选取。与前馈网络一样,我们通常希望把 RNN 的输出解释为概率分布,并使用与该分布相关的交叉熵来定义损失。均方误差就是与单位高斯输出分布相关联的交叉熵损失。

当使用预测对数似然训练目标(如公式 10.12)时,RNN 被训练来估计下一序列元素 \(y^{(t)}\) 在给定过去输入时的条件分布。这可以意味着最大化 \(\log p(y^{(t)}\mid x^{(1)},\dots,x^{(t)})\);若模型包含从某时刻输出指向下一时刻的连接,则意味着 \(\log p(y^{(t)}\mid x^{(1)},\dots,x^{(t)},y^{(1)},\dots,y^{(t-1)})\)。把整个 \(y\) 序列的联合概率分解为一系列单步概率预测,是捕捉整序列联合分布的一种方式。当我们不把过去的 \(y\) 值作为下一时刻预测的条件输入时,有向图模型中不存在从任何过去 \(y^{(i)}\) 到当前 \(y^{(t)}\) 的边,此时 \(y\) 在给定 \(x\) 序列条件下条件独立。当我们把实际 \(y\) 值(不是其预测,而是真实观测或生成值)反馈回网络时,有向图模型中会有从所有过去 \(y^{(i)}\) 到当前 \(y^{(t)}\) 的边。

举个简单例子:考虑 RNN 只对一组标量随机变量 \(Y=\{y^{(1)},\dots,y^{(\tau)}\}\) 建模而没有额外输入 \(x\) 的情形。\(t\) 时刻的输入只是 \(t-1\) 时刻的输出。RNN 就在 \(y\) 变量上定义了一个有向图模型。借助条件概率链式法则(即公式 3.6),观测的联合分布参数化为

\[ P(Y)=P(y^{(1)},\dots,y^{(\tau)})=\prod_{t=1}^{\tau}P(y^{(t)}\mid y^{(t-1)},y^{(t-2)},\dots,y^{(1)}) \]

公式右边 \(t=1\) 时条件为空。因此该模型下一组值 \(\{y^{(1)},\dots,y^{(\tau)}\}\) 的负对数似然为

\[ L=\sum_t L^{(t)}\quad\text{其中}\quad L^{(t)}=-\log P(y^{(t)}=y^{(t)}\mid y^{(t-1)},y^{(t-2)},\dots,y^{(1)}) \]

图模型中的边表示哪些变量直接依赖于其它变量。许多图模型通过省略不表示强相互作用的边来实现统计和计算效率。例如常做 Markov 假设,让图模型只包含从 \(\{y^{(t-k)},\dots,y^{(t-1)}\}\)\(y^{(t)}\) 的边,而不是包含从整个过去历史来的边。然而在某些情形下,我们相信所有过去输入都应当对下一元素有影响。RNN 在我们相信 \(y^{(t)}\) 的分布可能依赖于很久以前的 \(y^{(i)}\),且这种依赖不被 \(y^{(i)}\)\(y^{(t-1)}\) 的影响所捕捉时很有用。

把 RNN 解释为图模型的一种方式是:把 RNN 视为一个结构是完全图、能够表示任意一对 \(y\) 值之间直接依赖的图模型。\(y\) 上的完全图结构图模型如图 10.7 所示。RNN 的完全图解释基于把隐藏单元 \(h^{(t)}\) 通过边缘化消去的思想。

更有趣的是把隐藏单元 \(h^{(t)}\) 视为随机变量的 RNN 图模型结构。把隐藏单元纳入图模型后,RNN 对观测联合分布提供了一种非常高效参数化。假设用表格表示一个离散值上的任意联合分布——一个数组,对每种可能的赋值存一项,给出该赋值出现的概率。若 \(y\) 可取 \(k\) 个不同值,表格表示需要 \(O(k^\tau)\) 个参数。由于参数共享,RNN 中的参数数量作为序列长度的函数是 \(O(1)\)。可通过调整 RNN 参数数量控制模型容量,但不必随序列长度扩展。公式 10.5 表明 RNN 通过在每个时间步反复施加相同的函数 \(f\) 和相同的参数 \(\theta\),高效地参数化变量之间的长程关系。图 10.8 展示了图模型解释。把 \(h^{(t)}\) 节点纳入图模型把过去与未来解耦,作为它们之间的中间量。远处过去的 \(y^{(i)}\) 可通过它对 \(h\) 的影响来影响 \(y^{(t)}\)。图的结构表明:通过对每个时间步使用相同的条件概率分布,模型可被高效参数化;且当所有变量都被观测时,所有变量联合赋值的概率也可被高效求值。

即便有如此高效的参数化,某些操作仍是计算上困难的,例如难以预测序列中段的缺失值。循环网络为换得更少的参数付出的代价是参数优化可能困难。循环网络中所用的参数共享依赖于"同样的参数可用于不同时间步"的假设,等价地即假设"给定 \(t\) 时刻变量、\(t+1\) 时刻变量的条件概率分布是平稳的"——前一时间步与后一时间步的关系不依赖于 \(t\)。原则上我们可以把 \(t\) 作为每个时间步的额外输入,让学习器在尽量多的跨时间步共享基础上发现任何时间依赖,这比"对每个 \(t\) 用不同条件概率分布"好得多,但网络要在面对新 \(t\) 值时做外推。

要把 RNN 作为图模型看待,还得描述如何从模型中采样。需要执行的主要操作只是对每个时间步的条件分布采样。但还有一个额外复杂点:RNN 必须有某种机制决定序列长度。这可以通过多种方式实现。当输出是从词表中取的符号时,可以加一个对应序列结束的特殊符号(Schmidhuber, 2012);生成到该符号时采样过程停止。训练集上把这个符号作为额外成员插在每个训练例的 \(x^{(\tau)}\) 之后。另一种方案是给模型加一个额外的 Bernoulli 输出,表示在每个时间步"继续生成"或"停止生成"的决策。该方案比"给词表加一个符号"更一般,因为它可应用于任何 RNN,而不仅限于输出符号序列的 RNN;例如它可应用于发射实数序列的 RNN。新输出单元通常是一个用交叉熵损失训练的 sigmoid 单元,在这种方法中 sigmoid 被训练以最大化对每个时间步序列是否结束或继续这一正确预测的对数概率。另一种决定序列长度 \(\tau\) 的方式是给模型加一个直接预测整数 \(\tau\) 本身的额外输出。模型可对一个 \(\tau\) 值采样、再采样 \(\tau\) 步数据。该方法需要在每个时间步给循环更新加一个额外输入,让循环更新意识到它是否接近生成序列的末尾。该额外输入可以是 \(\tau\) 的值也可以是 \(\tau-t\),即剩余的时间步数。若没有这个额外输入,RNN 可能生成突然结束的序列,例如在未完成时结束的句子。该方法基于分解 \(P(x^{(1)},\dots,x^{(\tau)})=P(\tau)P(x^{(1)},\dots,x^{(\tau)}\mid\tau)\)。直接预测 \(\tau\) 的策略如 Goodfellow et al. (2014d) 所用。

10.2.4 用 RNN 建模条件于上下文信息的序列(Modeling Sequences Conditioned on Context with RNNs)

前一节描述了 RNN 如何对应一个无输入 \(x\) 的序列随机变量 \(y^{(t)}\) 的有向图模型。实际 RNN(如公式 10.8)包含输入序列 \(x^{(1)},x^{(2)},\dots,x^{(\tau)}\)。一般而言,RNN 允许把图模型视角扩展到表示 \(y\) 在给定 \(x\) 时的条件分布。如 6.2.1.1 节针对前馈网络的讨论所示:任何表示变量 \(P(y;\theta)\) 的模型都可被重新解释为表示条件分布 \(P(y\mid\omega)\)(其中 \(\omega=\theta\))的模型。我们可以把这样的模型扩展为代表 \(P(y\mid x)\),方法是用同样的 \(P(y\mid\omega)\) 但让 \(\omega\) 成为 \(x\) 的函数。在 RNN 情形下这可通过不同方式实现,下面回顾最常见和最明显的选择。

之前我们讨论的 RNN 接受向量序列 \(x^{(t)}\)\(t=1,\dots,\tau\))作为输入。另一种方案是只接受单个向量 \(x\) 作为输入。当 \(x\) 是固定大小的向量时,我们可以把它作为生成 \(y\) 序列的 RNN 的额外输入。提供这一额外输入的常见方式有:作为每个时间步的额外输入;作为初始状态 \(h^{(0)}\);两者兼有。第一种也是最常见的方式如图 10.9 所示。输入 \(x\) 与每个隐藏单元向量 \(h^{(t)}\) 的交互由新引入的权重矩阵 \(R\) 参数化——这是只对 \(y\) 值序列建模的模型中不存在的。同一乘积 \(x^\top R\) 在每个时间步被作为隐藏单元的额外输入相加。我们可以把 \(x\) 的选择看作决定了 \(x^\top R\) 的值——它实际上成为对每个隐藏单元都使用的一个新偏置参数。权重保持与输入无关。我们可以把这个模型理解为:把非条件模型的参数 \(\theta\) 变成 \(\omega\),其中 \(\omega\) 内的偏置参数现在是输入的函数。

RNN 也可以不接受单个向量 \(x\) 而接受向量序列 \(x^{(t)}\)。公式 10.8 描述的 RNN 对应一个条件分布 \(P(y^{(1)},\dots,y^{(\tau)}\mid x^{(1)},\dots,x^{(\tau)})\),它做了一个条件独立假设,即该分布因式分解为 \(\prod_t P(y^{(t)}\mid x^{(1)},\dots,x^{(t)})\)。要消除条件独立假设,可以从 \(t\) 时刻输出到 \(t+1\) 时刻隐藏单元加连接(如图 10.10 所示),这样模型就能表示 \(y\) 序列上的任意概率分布。这种表示"给定另一序列条件下序列分布"的模型仍有一个限制:两个序列长度必须相同。10.4 节会讨论如何去掉这一限制。

10.3 双向循环网络(Bidirectional RNNs)

到目前为止我们讨论过的所有循环网络都呈"因果"结构,即 \(t\) 时刻状态只捕捉来自过去 \(x^{(1)},\dots,x^{(t-1)}\) 和当前输入 \(x^{(t)}\) 的信息。我们讨论过的一些模型在 \(y\) 值可用时也允许过去的 \(y\) 值影响当前状态。

然而在许多应用中我们希望输出对 \(y^{(t)}\) 的预测可能依赖于整个输入序列。例如在语音识别中,由于协同发音,当前声音作为音素的正确解释可能依赖接下来几个音素,甚至可能因为近邻词之间的语言学依赖而依赖接下来几个词;如果当前词有两个声学上都合理的解释,我们可能必须看向远未来(以及远过去)以消歧。 handwriting recognition 和许多其他序列到序列学习任务(下一节讨论)中也存在同样情况。

双向循环神经网络(双向 RNN)正是为满足这种需求而发明(Schuster and Paliwal, 1997),在 handwriting recognition(Graves et al., 2008; Graves and Schmidhuber, 2009)、语音识别(Graves and Schmidhuber, 2005; Graves et al., 2013)和生物信息学(Baldi et al., 1999)等需要这种能力的应用中取得了极大成功。

顾名思义,双向 RNN 把一个从序列开始向时间正向移动的 RNN 与一个从序列末尾向时间反向移动的 RNN 组合起来。图 10.11 展示了典型的双向 RNN,其中 \(h^{(t)}\) 是从序列开始向时间正向移动的子 RNN 的状态,\(g^{(t)}\) 是从序列末尾向时间反向移动的子 RNN 的状态。这允许输出单元 \(o^{(t)}\) 计算一个既依赖于过去又依赖于未来、但对 \(t\) 附近的输入值最敏感的表达,而不必指定围绕 \(t\) 的固定大小窗口(用前馈网络、卷积网络或带固定前瞻缓冲区的常规 RNN 都得这么做)。

该思想可自然推广到二维输入,例如图像,方法是让四个 RNN 分别沿四个方向(上、下、左、右)移动。在二维网格的每个点 \((i,j)\),输出 \(O_{i,j}\) 接着可计算一个主要捕获局部信息、但若 RNN 能学会携带远距输入信息也能依赖远距输入的表达。卷积网络相比,应用到图像上的 RNN 通常更贵,但允许同一特征图内的特征之间存在长程横向交互(Visin et al., 2015; Kalchbrenner et al., 2015)。实际上,这些 RNN 的前向传播方程可写成一种形式:先对每一层用一次卷积计算自底向上的输入,再在特征图上做循环传播从而加入横向交互。

10.4 编码器-解码器 / 序列到序列架构(Encoder-Decoder Sequence-to-Sequence Architectures)

前面我们已看到图 10.5 中 RNN 如何把输入序列映射为固定大小向量;图 10.9 中 RNN 如何把固定大小向量映射为序列;图 10.3、10.4、10.10 和 10.11 中 RNN 如何把输入序列映射为等长输出序列。本节讨论如何训练 RNN 把输入序列映射为不一定等长的输出序列。这在许多应用中出现,例如语音识别、机器翻译或问答,这些任务中训练集的输入和输出序列一般不等长(尽管长度可能相关)。

我们常把 RNN 的输入称为"上下文"(context)。我们希望产生该上下文 \(C\) 的一个表示。\(C\) 可以是一个向量或一组向量,用以概括输入序列 \(X=(x^{(1)},\dots,x^{(n_x)})\)

把变长序列映射为另一变长序列的最简单 RNN 架构由 Cho et al. (2014a) 首先提出,不久 Sutskever et al. (2014) 独立开发了同一架构并率先用该方法取得当时最优的翻译结果。前一个系统基于对另一机器翻译系统生成的候选进行打分;后一个使用一个独立的循环网络生成翻译。作者们分别把图 10.12 展示的这一架构称为编码器-解码器(encoder-decoder)或序列到序列(sequence-to-sequence)架构。其思想很简单:(1)一个编码器/读入器/输入 RNN 处理输入序列,编码器发射出上下文 \(C\),通常是它最终隐藏状态的简单函数;(2)一个解码器/写出器/输出 RNN 以该定长向量为条件(正如图 10.9 中那样)来生成输出序列 \(Y=(y^{(1)},\dots,y^{(n_y)})\)。此类架构相对本章之前介绍的架构的创新是:长度 \(n_x\)\(n_y\) 可以互不相同,而之前架构要求 \(n_x=n_y=\tau\)。在序列到序列架构中,两个 RNN 被联合训练以最大化 \(\log P(y^{(1)},\dots,y^{(n_y)}\mid x^{(1)},\dots,x^{(n_x)})\) 在训练集上所有 \((x,y)\) 序列对上的平均值。编码器 RNN 的最后状态 \(h_{n_x}\) 通常被用作输入序列的一种表示 \(C\),提供给解码器 RNN。

如果上下文 \(C\) 是向量,则解码器 RNN 就是一个 10.2.4 节描述的向量到序列 RNN。如前所见,向量到序列 RNN 接受输入至少有两种方式:作为 RNN 的初始状态,或连接到每个时间步的隐藏单元;这两种方式也可组合。编码器和解码器并不要求隐藏层大小相同。

此架构的一个明显局限是:编码器 RNN 输出的上下文 \(C\) 的维度可能太小,无法恰当概括长序列。Bahdanau et al. (2015) 在机器翻译中观察到这一现象。他们提出让 \(C\) 是变长序列而非定长向量,并引入一个注意力机制,让 \(C\) 的序列元素与输出序列元素建立关联;详见 12.4.5.1 节。

10.5 深度循环网络(Deep Recurrent Networks)

大多数 RNN 的计算可分解为三个参数块及其相关变换:输入到隐藏状态、前一隐藏状态到下一隐藏状态、隐藏状态到输出。在图 10.3 的 RNN 架构中,这三个块各对应一个权重矩阵。换言之,当网络展开时,每一块都对应一个浅层变换——所谓"浅层"是指该变换若用深度 MLP 表示只需单层,典型形式是学到的仿射变换后接一个固定非线性。

是否应对这些运算的每一块引入深度?实验证据(Graves et al., 2013; Pascanu et al., 2014a)强烈支持引入深度,这与"我们需要足够深度才能完成所需映射"的想法一致。Schmidhuber (1992)、El Hihi and Bengio (1996)、Jaeger (2007a) 是早期关于深度 RNN 的工作。Graves et al. (2013) 首先展示了把 RNN 状态分解为多层(如图 10.13 左)有显著收益。我们可以把图 10.13a 中层次结构里较低层看作负责把原始输入变换为对高层隐藏状态更合适的表示。Pascanu et al. (2014a) 更进一步,对上述三个块中的每一块分别用单独的 MLP(可能很深),如图 10.13b。表达能力的考虑提示应为这三步中每一步分配足够容量,但通过加深来增大容量可能因让优化变难而损害学习。一般而言,浅层架构更易优化;加入图 10.13b 的额外深度使得从 \(t\) 时刻变量到 \(t+1\) 时刻变量的最短路径变长。例如若状态到状态转移用一个带单隐藏层的 MLP,则任意两个不同时间步变量之间的最短路径长度相比图 10.3 中的普通 RNN 翻了一倍。然而 Pascanu et al. (2014a) 指出,这可通过在隐藏到隐藏路径中引入跳跃连接来缓解,如图 10.13c 所示。

10.6 递归神经网络(Recursive Neural Networks)

递归神经网络是循环网络的另一类推广,其计算图结构是深度树而非 RNN 的链式结构。典型递归网络的计算图如图 10.14 所示。递归网络由 Pollack (1990) 引入,Bottou (2011) 描述了其用于学习推理的潜在用途。递归网络已成功应用于把数据结构作为神经网络输入的处理(Frasconi et al., 1997, 1998)、自然语言处理(Socher et al., 2011a,c, 2013a)以及计算机视觉(Socher et al., 2011b)。

相比循环网络,递归网络的一个明显优势是:对长度为 \(\tau\) 的序列,深度(以非线性运算的复合次数计)可从 \(\tau\) 急剧降低到 \(O(\log\tau)\),这或许有助于处理长程依赖。一个未决问题是:如何最佳地组织树结构。一种选择是让树结构不依赖于数据,例如平衡二叉树。在某些应用领域,外部方法可建议合适的树结构。例如处理自然语言句子时,递归网络的树结构可固定为自然语言解析器所提供句子的句法树结构(Socher et al., 2011a, 2013a)。理想情况下我们希望学习器自身发现并推断对任何给定输入都合适的树结构,正如 Bottou (2011) 提出的。

递归网络思想还有许多变体。例如 Frasconi et al. (1997) 与 Frasconi et al. (1998) 把数据与一种树结构关联起来,把输入和目标与树的各个节点关联。每个节点执行的计算不必是传统人工神经元计算(对所有输入做仿射变换再接单调非线性)。例如 Socher et al. (2013a) 提出使用张量运算和双线性形式,它们在概念以连续向量(嵌入)表示时已被证明对建模概念间关系有用(Weston et al., 2010; Bordes et al., 2012)。

10.7 长程依赖的挑战(The Challenge of Long-Term Dependencies)

循环网络中学习长程依赖的数学挑战已在 8.2.5 节引入。基本问题是:跨很多阶段传播的梯度倾向于消失(大多数时候)或爆炸(很少见,但会对优化造成严重损害)。即使我们假设参数使得循环网络稳定(可存储记忆、梯度不爆炸),长程依赖的困难仍来自长程交互(涉及很多 Jacobian 的乘积)相比短程交互被赋予指数级更小的权重。许多其他来源给出了更深入的处理(Hochreiter, 1991; Doya, 1993; Bengio et al., 1994; Pascanu et al., 2013)。本节更详细地描述该问题,后面各节给出克服该问题的方法。

循环网络涉及同一函数的多次复合,每个时间步一次。这些复合会导致极强非线性行为,如图 10.15 所示。具体地,循环神经网络所用的函数复合某种程度上类似于矩阵乘法。可以把递推关系 \(h^{(t)}=W^\top h^{(t-1)}\) 视为一种极简单的、没有非线性激活函数也没有输入 \(x\) 的循环神经网络。如 8.2.5 节所述,该递推关系本质上描述了幂方法。可简化为 \(h^{(t)}=(W^t)^\top h^{(0)}\);若 \(W\) 允许形如 \(W=Q\Lambda Q^\top\) 的特征分解(\(Q\) 正交),则递推可进一步化简为 \(h^{(t)}=Q^\top\Lambda^t Q h^{(0)}\)。特征值被提升到 \(t\) 次幂,导致绝对值小于 1 的特征值衰减到零,绝对值大于 1 的特征值爆炸。任何 \(h^{(0)}\) 中不与最大特征向量对齐的分量最终都将被丢弃。

这个问题对循环网络尤为特殊。在标量情形下,想象把一个权重 \(w\) 自身相乘多次,乘积 \(w^t\) 取决于 \(w\) 的大小会消失或爆炸。但如果我们构建一个非循环网络,在每个时间步用不同的权重 \(w^{(t)}\),情形就不同了。设初始状态为 1,则 \(t\) 时刻状态为 \(\prod_i w^{(i)}\)。假设 \(w^{(t)}\) 独立同分布、零均值方差 \(v\),则乘积方差为 \(O(v^n)\)。为得到某个目标方差 \(v^*\),可让各权重方差为 \(v=\sqrt{v^*/n}\)。非常深的前馈网络通过仔细选择尺度可避免梯度消失和爆炸问题,如 Sussillo (2014) 所论。RNN 梯度消失和爆炸问题由 Hochreiter (1991)、Bengio et al. (1993, 1994) 独立发现。有人可能希望只要停留在参数空间中梯度不消失也不爆炸的区域就能避免问题。遗憾的是,为了以对小扰动鲁棒的方式存储记忆,RNN 必须进入一个梯度消失的参数空间区域(Bengio et al., 1993, 1994)。具体而言,每当模型能表示长程依赖时,长程交互梯度的幅度就比短程交互梯度小指数级。这并不意味着不可能学习,而是说学习长程依赖可能需要极长时间,因为这些依赖的信号会被短程依赖产生的小波动掩盖。实践中 Bengio et al. (1994) 的实验表明:随着需要捕获的依赖跨度增加,基于梯度的优化变得越来越困难;用 SGD 训练传统 RNN 成功的概率在序列长度仅为 10 或 20 时就迅速降到 0。

关于把循环网络视为动力系统的更深入处理,见 Doya (1993)、Bengio et al. (1994) 和 Siegelmann and Sontag (1995),并在 Pascanu et al. (2013) 中有综述。本章剩余各节讨论已被提出用于降低学习长程依赖困难度的多种方法(在某些情况下允许 RNN 学习跨数百步的依赖),但学习长程依赖仍是深度学习的主要挑战之一。

10.8 回声状态网络(Echo State Networks)

在循环网络中,从 \(h^{(t-1)}\)\(h^{(t)}\) 的循环权重和从 \(x^{(t)}\)\(h^{(t)}\) 的输入权重是最难学的参数。一种被提出(Jaeger, 2003; Maass et al., 2002; Jaeger and Haas, 2004; Jaeger, 2007b)用来避免这种困难的方法是:固定循环权重使循环隐藏单元能很好地捕捉过去输入的历史,只学习输出权重。这是回声状态网络(echo state networks, ESN)和液体状态机(liquid state machines)各自独立提出的思想。后者类似,只不过它使用脉冲神经元(具有二值输出),而 ESN 用的是连续值隐藏单元。ESN 和液体状态机被统称为"水库计算"(reservoir computing)(Lukoševičius and Jaeger, 2009),用以表示隐藏单元形成了时间特征的"水库"、可捕捉输入历史的各个方面这一事实。

水库计算循环网络的一种思考方式是:它们类似于核机器——把任意长度序列(到 \(t\) 时刻为止的输入历史)映射为定长向量(循环状态 \(h^{(t)}\)),再对其施加一个线性预测器(通常是线性回归)以求解感兴趣的问题。训练准则因此可被容易地设计为关于输出权重的凸函数。例如若输出由隐藏单元到输出目标的线性回归组成、训练准则是均方误差,则它是凸的,可用简单学习算法可靠求解(Jaeger, 2003)。

因此重要问题是:如何设置输入和循环权重,使循环神经网络状态能表示丰富的历史集?水库计算文献给出的答案是:把循环网络视为动力系统,把输入和循环权重设置为使该动力系统接近稳定性的边缘。最初的思想是让状态到状态转移函数 Jacobian 的特征值接近 1。如 8.2.5 节所述,循环网络的一个重要特征是其 Jacobian \(J^{(t)}=\partial s^{(t)}/\partial s^{(t-1)}\) 的特征值谱。谱半径(即 \(J^{(t)}\) 特征值绝对值的最大值)尤其重要。

为理解谱半径的效应,考虑 Jacobian 矩阵 \(J\) 不随 \(t\) 变化这一简单情形。当网络是纯线性时即如此。设 \(J\) 有特征向量 \(v\) 和对应特征值 \(\lambda\)。考虑沿时间反向传播梯度向量的情形:若从梯度向量 \(g\) 出发,则反向传播一步后得到 \(Jg\)\(n\) 步后得到 \(J^n g\)。再看若从 \(g\) 的扰动版本 \(g+\delta v\) 出发反向传播:一步后得到 \(J(g+\delta v)\)\(n\) 步后得到 \(J^n(g+\delta v)\)。可以看出:从 \(g\) 和从 \(g+\delta v\) 出发的反向传播在 \(n\) 步反向传播后相差 \(\delta J^n v\)。若 \(v\) 被选为 \(J\) 对应特征值 \(\lambda\) 的单位特征向量,则 Jacobian 的乘法仅在每步对差距做缩放。两次反向传播以距离 \(\delta|\lambda|^n\) 分离。当 \(v\) 对应 \(|\lambda|\) 的最大值时,该扰动达到对大小 \(\delta\) 的初始扰动最宽的分离。当 \(|\lambda|>1\) 时偏差大小 \(\delta|\lambda|^n\) 指数级增长;当 \(|\lambda|<1\) 时偏差大小指数级缩小。

当然这一例子假设 Jacobian 在每个时间步都相同,对应一个无非线性的循环网络。当存在非线性时,非线性的导数在很多时间步将接近零,有助于防止谱半径较大导致的爆炸。事实上,关于回声状态网络的最新工作主张使用远大于 1 的谱半径(Yildiz et al., 2012; Jaeger, 2012)。上面关于通过重复矩阵乘法做反向传播的讨论同样适用于无非线性网络的前向传播,此时 \(h^{(t+1)}=h^{(t)\top}W\)

当线性映射 \(W^\top\) 总是按 \(L_2\) 范数收缩 \(h\) 时,我们称该映射是收缩的。当谱半径小于 1 时,\(h^{(t)}\)\(h^{(t+1)}\) 的映射是收缩的,因此当用有限精度(如 32 位整数)存储状态向量时,小变化在每个时间步后变得更小,这必然让网络忘记关于过去的信息。Jacobian 矩阵告诉我们 \(h^{(t)}\) 的小变化如何前向传播一步,或等价地,\(h^{(t+1)}\) 上的梯度如何在反向传播中后向传播一步。注意 \(W\)\(J\) 都不必是对称的(尽管它们是方阵且实),因此可有复值特征值和特征向量,复数部分对应潜在的振荡行为(若同一 Jacobian 被迭代施加)。即使 \(h^{(t)}\) 或反向传播中感兴趣的 \(h^{(t)}\) 小变化是实值的,它们也可在该复值基下表示。重要的是当我们用矩阵乘以向量时这些可能为复值的基系数的大小(复数绝对值)会如何变化。绝对值大于 1 的特征值对应放大(迭代施加则指数增长),绝对值小于 1 的对应缩小(迭代施加则指数衰减)。

对非线性映射,Jacobian 在每步可自由变化,动力学会更复杂。然而一个小的初始变化在若干步后可能变成大的变化这一事实仍然成立。纯线性情形与非线性情形的一个区别是:使用 tanh 这类压缩非线性可使循环动力系统变得有界。注意反向传播可能在正向传播有界时仍保持无界动力,例如一组 tanh 单元都处在其线性区中段并被谱半径大于 1 的权重矩阵连接时。但所有 tanh 单元同时处在其线性激活点的情况很罕见。

回声状态网络的策略很简单:把权重固定为某个谱半径,例如 3,使信息能在时间上向前传递,但由于 tanh 等饱和非线性的稳定作用不会爆炸。更近期的工作表明,用来在 ESN 中设置权重的技术也可用于在完全可训练的循环网络(其隐藏到隐藏循环权重用 BPTT 训练)中初始化权重,从而帮助学习长程依赖(Sutskever, 2012; Sutskever et al., 2013)。在此设定下,初始谱半径 1.2 配合 8.4 节描述的稀疏初始化方案表现良好。

10.9 渗漏单元与多时间尺度的其它策略(Leaky Units and Other Strategies for Multiple Time Scales)

处理长程依赖的一种方法是设计一个在多个时间尺度上运作的模型:模型一部分在细粒度时间尺度上运作以处理小细节,另一部分在粗粒度时间尺度上运作以更高效地把信息从远处过去传递到现在。构造细和粗两种时间尺度的策略有多种,包括:跨时间增加跳跃连接、带不同时间常数整合信号的"渗漏单元"(leaky units)、以及去除一些用于建模细粒度时间尺度的连接。

10.9.1 跨时间增加跳跃连接(Adding Skip Connections through Time)

获得粗时间尺度的一种方法是从远处过去的变量向当前变量加直接连接。使用这种跳跃连接的思想可追溯到 Lin et al. (1996),源于在时间延迟神经网络中引入延迟的思想(Lang and Hinton, 1988)。在普通循环网络中,循环连接从 \(t\) 时刻单元指向 \(t+1\) 时刻单元;也可以构造有更长延迟的循环网络(Bengio, 1991)。如 8.2.5 节所见,梯度可关于时间步数指数级消失或爆炸。Lin et al. (1996) 引入时间延迟为 \(d\) 的循环连接以缓解此问题。梯度现在以 \(\tau/d\) 而非 \(\tau\) 的函数形式衰减。由于同时存在延迟连接和单步连接,梯度仍可能在 \(\tau\) 上指数级爆炸。这允许学习算法捕获更长依赖,但并非所有长程依赖都能以这种方式很好地表示。

10.9.2 渗漏单元与不同时间尺度的谱(Leaky Units and a Spectrum of Different Time Scales)

获得导数乘积接近 1 的另一条路径是让单元具有线性自连接且这些连接上的权重接近 1。当我们通过对值 \(v^{(t)}\) 施加更新 \(\mu^{(t)}\leftarrow\alpha\mu^{(t-1)}+(1-\alpha)v^{(t)}\) 累积运行平均 \(\mu^{(t)}\) 时,参数 \(\alpha\) 就是从 \(\mu^{(t-1)}\)\(\mu^{(t)}\) 的线性自连接例子。当 \(\alpha\) 接近 1 时,运行平均长时间记忆关于过去的信息;\(\alpha\) 接近 0 时,关于过去的信息迅速被丢弃。具有线性自连接的隐藏单元行为可类似于这种运行平均。这样的隐藏单元称为渗漏单元。跨 \(d\) 步的跳跃连接是保证单元总能学习到受 \(d\) 步之前的值影响的一种方式。使用权重接近 1 的线性自连接则是保证单元能访问过去值的另一种方式。线性自连接方法允许通过调节实值 \(\alpha\)(而非整数值的跳跃长度)更平滑灵活地调整该效应。这些思想由 Mozer (1992) 和 El Hihi and Bengio (1996) 提出。渗漏单元在回声状态网络语境下也被发现有用(Jaeger et al., 2007)。

设置渗漏单元使用的时间常数有两种基本策略。一种策略是手动把它们固定为常数值,例如在初始化时从某个分布中采样这些值。另一种策略是让时间常数成为自由参数并通过学习获得。在不同时间尺度上使用这些渗漏单元似乎有助于长程依赖(Mozer, 1992; Pascanu et al., 2013)。

10.9.3 去除连接(Removing Connections)

处理长程依赖的另一种方法是组织 RNN 的状态使其在多时间尺度上运作(El Hihi and Bengio, 1996),让信息在较慢的时间尺度上更容易流动经过长距离。该思想不同于前面讨论的跨时间跳跃连接,因为它涉及主动去除长度为一的连接并用更长的连接替换。以这种方式修改的单元被迫在长时间尺度上运作。跨时间跳跃连接是增加边,接收这些新连接的单元可能学会在长时间尺度上运作,但也可选择专注于其其它短程连接。

让一组循环单元被迫在不同时间尺度上运作有多种方式。一种选择是让循环单元具有渗漏性,但让不同组单元关联到不同的固定时间尺度。这是 Mozer (1992) 提出的方案,已在 Pascanu et al. (2013) 中成功使用。另一种选择是让显式离散的更新在不同时间发生,对不同组单元使用不同频率。这是 El Hihi and Bengio (1996) 和 Koutnik et al. (2014) 的方法,在多个基准数据集上效果良好。

10.10 长短期记忆和其它门控 RNN(The Long Short-Term Memory and Other Gated RNNs)

截至写作时,实践中应用最有效的序列模型称为门控 RNN(gated RNNs)。其中包括长短期记忆(long short-term memory, LSTM)和基于门控循环单元(gated recurrent unit, GRU)的网络。和渗漏单元一样,门控 RNN 基于"创建梯度既不消失也不爆炸的时间路径"的思想。渗漏单元通过连接权重(要么是手动选定的常数,要么是参数)做到这点。门控 RNN 把这点推广到每个时间步都可能改变的连接权重。

渗漏单元允许网络长时间累积信息(例如对某一特征或类别的证据)。然而一旦这些信息被使用过,神经网络可能希望忘掉旧状态。例如若序列由若干子序列组成且我们希望渗漏单元在每个子序列内累积证据,我们就需要一种通过把状态置零来忘掉旧状态的机制。我们不想手动决定何时清除状态,而是希望神经网络自己学会何时去做这件事。这正是门控 RNN 所做的。

10.10.1 LSTM(LSTM)

把自环引入以产生让梯度能长期流动的路径,是初始长短期记忆(LSTM)模型(Hochreiter and Schmidhuber, 1997)的核心贡献。一个关键的扩展是让该自环上的权重条件于上下文而非固定不变(Gers et al., 2000)。通过让此自环的权重被门控(由另一个隐藏单元控制),积分时间尺度可被动态地改变。这里所谓"动态"是指:即使对固定参数的 LSTM,积分时间尺度也能基于输入序列而变化,因为时间常数由模型自身输出。LSTM 已在许多应用中被发现极其成功,例如无约束 handwriting recognition(Graves et al., 2009)、语音识别(Graves et al., 2013; Graves and Jaitly, 2014)、handwriting generation(Graves, 2013)、机器翻译(Sutskever et al., 2014)、图像描述(Kiros et al., 2014b; Vinyals et al., 2014b; Xu et al., 2015)以及句法分析(Vinyals et al., 2014a)。

LSTM 块状图如图 10.16 所示。在浅层循环网络架构情形下,前向传播方程如下。深度架构也已成功使用(Graves et al., 2013; Pascanu et al., 2014a)。LSTM 循环网络不是用对输入和循环单元做仿射变换再施加逐元素非线性的简单单元,而是使用" LSTM 单元",具有内部循环(自环),加上 RNN 的外部循环。每个单元与普通循环网络有同样的输入输出,但有更多参数以及一套控制信息流的门控单元。最重要的组件是状态单元 \(s_i^{(t)}\),具有与上一节渗漏单元类似的线性自环。然而这里自环的权重(或相应时间常数)由遗忘门单元 \(f_i^{(t)}\)(对应时间步 \(t\) 和单元 \(i\))控制,它通过 sigmoid 单元把该权重设置为 0 到 1 之间的值:

\[ f_i^{(t)}=\sigma\left(b_i^f+\sum_j U_{i,j}^f x_j^{(t)}+\sum_j W_{i,j}^f h_j^{(t-1)}\right) \]

其中 \(x^{(t)}\) 是当前输入向量,\(h^{(t)}\) 是当前隐藏层向量,包含所有 LSTM 单元的输出,\(b^f,U^f,W^f\) 分别是遗忘门的偏置、输入权重和循环权重。LSTM 单元的内部状态由此按以下方式更新,但带条件自环权重 \(f_i^{(t)}\)

\[ s_i^{(t)}=f_i^{(t)} s_i^{(t-1)}+g_i^{(t)}\sigma\left(b_i+\sum_j U_{i,j} x_j^{(t)}+\sum_j W_{i,j} h_j^{(t-1)}\right) \]

其中 \(b,U,W\) 分别是 LSTM 单元的偏置、输入权重和循环权重。外部输入门单元 \(g_i^{(t)}\) 与遗忘门类似计算(用 sigmoid 单元获得 0 到 1 之间的门控值),但有自己的参数:

\[ g_i^{(t)}=\sigma\left(b_i^g+\sum_j U_{i,j}^g x_j^{(t)}+\sum_j W_{i,j}^g h_j^{(t-1)}\right) \]

LSTM 单元的输出 \(h_i^{(t)}\) 也可被关闭,通过输出门 \(q_i^{(t)}\),它同样用 sigmoid 单元做门控:

\[ h_i^{(t)}=\tanh(s_i^{(t)})q_i^{(t)},\quad q_i^{(t)}=\sigma\left(b_i^o+\sum_j U_{i,j}^o x_j^{(t)}+\sum_j W_{i,j}^o h_j^{(t-1)}\right) \]

它有参数 \(b^o,U^o,W^o\) 分别对应偏置、输入权重和循环权重。在这些变体中,我们可以选择把单元状态 \(s_i^{(t)}\) 作为额外输入(带其权重)送入第 \(i\) 个单元的三个门,如图 10.16 所示,这需要再加三个参数。LSTM 网络已被证明比简单循环架构更容易学得长程依赖,首先是在为测试长程依赖学习能力而设计的人工数据集上(Bengio et al., 1994; Hochreiter and Schmidhuber, 1997; Hochreiter et al., 2001),然后是在获得当时最优性能的具挑战性序列处理任务上(Graves, 2012; Graves et al., 2013; Sutskever et al., 2014)。LSTM 的变体和替代品已被研究和使用,接下来讨论。

10.10.2 其它门控 RNN(Other Gated RNNs)

LSTM 架构中哪些组件是真正必需的?还可以设计哪些能动态控制不同单元时间尺度与遗忘行为的成功架构?关于这些问题的一些回答由门控 RNN 的近期工作给出,其单元也称为门控循环单元(GRU)(Cho et al., 2014b; Chung et al., 2014, 2015a; Jozefowicz et al., 2015; Chrupala et al., 2015)。与 LSTM 的主要区别是:单个门控单元同时控制遗忘因子和状态单元更新决策。更新方程如下:

\[ h_i^{(t)}=u_i^{(t-1)} h_i^{(t-1)}+(1-u_i^{(t-1)})\sigma\left(b_i+\sum_j U_{i,j} x_j^{(t-1)}+\sum_j W_{i,j} r_j^{(t-1)} h_j^{(t-1)}\right) \]

其中 \(u\) 代表"更新"门,\(r\) 代表"重置"门。其值照常定义:

\[ u_i^{(t)}=\sigma\left(b_i^u+\sum_j U_{i,j}^u x_j^{(t)}+\sum_j W_{i,j}^u h_j^{(t)}\right),\quad r_i^{(t)}=\sigma\left(b_i^r+\sum_j U_{i,j}^r x_j^{(t)}+\sum_j W_{i,j}^r h_j^{(t)}\right) \]

重置门和更新门可单独"忽略"状态向量的某些部分。更新门像有条件的渗漏积分器:可对任何维度做线性门控,从而(在 sigmoid 一个极端处)选择复制它,或(在另一极端)通过用新的"目标状态"值替换它来完全忽略它(渗漏积分器所要收敛的目标)。重置门控制状态的哪些部分被用于计算下一目标状态,在过去状态与未来状态之间引入额外的非线性效应。

围绕这个主题还可以设计更多变体。例如重置门(或遗忘门)输出可在多个隐藏单元间共享。或者用全局门(覆盖整组单元,例如整个层)与局部门(每单元)的乘积把全局控制和局部控制结合起来。然而若干对 LSTM 和 GRU 架构变体的调查未发现在广泛任务上明显优于这两者的变体(Greff et al., 2015; Jozefowicz et al., 2015)。Greff et al. (2015) 发现一个关键组件是遗忘门;Jozefowicz et al. (2015) 发现给 LSTM 遗忘门加 1 的偏置(Gers et al. (2000) 倡导的做法)使 LSTM 与所探索的架构变体中最强者一样强。

10.11 长程依赖的优化(Optimization for Long-Term Dependencies)

8.2.5 节和 10.7 节已描述在多时间步上优化 RNN 时出现的梯度消失和爆炸问题。Martens and Sutskever (2011) 提出的一个有趣想法是:二阶导数可能与一阶导数同时消失。二阶优化算法大致可理解为用二阶导数除一阶导数(在高维情形下是用梯度的逆 Hessian 矩阵相乘)。若二阶导数以与一阶导数相似的速率缩小,则一阶和二阶导数之比可能保持相对恒定。遗憾的是二阶方法有许多缺点:包括计算开销大、需要大 minibatch、且倾向被鞍点吸引。Martens and Sutskever (2011) 用二阶方法得到了有希望的结果。后来 Sutskever et al. (2013) 发现 Nesterov 动量加仔细初始化等更简单方法能取得类似结果。详见 Sutskever (2012)。这两种方法都已在很大程度上被简单地使用 SGD(甚至不用动量)应用于 LSTM 所取代。这是机器学习中一个持续的主题:设计易于优化的模型通常比设计更强的优化算法要容易得多。

10.11.1 梯度截断(Clipping Gradients)

如 8.2.4 节所讨论,由循环网络在多时间步上计算的强非线性函数倾向于具有在幅度上可能非常大或非常小的导数。这在图 8.3 和图 10.17 中可见:目标函数(作为参数的函数)有一种"地形",可以发现"悬崖"——由平坦的较宽区域被目标函数变化迅速的小区域分隔而形成。

由此带来的困难是:当参数梯度非常大时,梯度下降的参数更新可能把参数抛到很远,使目标函数更大的区域,从而抹掉为达到当前解所做的大量工作。梯度告诉我们在当前参数的无限小邻域内对应最陡下降的方向。超出该无限小邻域之外,代价函数可能开始向上弯曲。更新必须选得足够小以避免走过太多向上弯曲。通常我们使用衰减得足够慢的学习率,使连续几步的学习率大致相同。适应于地形相对线性部分的步长在下一步进入更弯曲区域时往往不适用并引起向上的运动。

一种简单的解决方案已被实践者使用多年:截断梯度。该思想有不同实例(Mikolov, 2012; Pascanu et al., 2013)。一种做法是在参数更新前对一个 minibatch 的参数梯度做逐元素截断(Mikolov, 2012)。另一种做法是在参数更新前截断梯度 \(g\) 的范数 \(\|g\|\)(Pascanu et al., 2013):

\[ g\leftarrow\begin{cases}g & \text{若 }\|g\|\le v\\ \dfrac{gv}{\|g\|} & \text{若 }\|g\|>v\end{cases} \]

其中 \(v\) 是范数阈值,\(g\) 被用来更新参数。由于所有参数(包括不同参数组,如权重和偏置)的梯度通过一个单一缩放因子被联合重新归一化,后一种方法的优点是保证每一步仍处于梯度方向;但实验表明这两种形式表现类似。虽然参数更新方向与真实梯度方向相同,使用梯度范数截断后,参数更新向量的范数现在有界。这种有界梯度避免了在梯度爆炸时做有害步。实际上即使在梯度幅度超过阈值时简单地走一个随机步也几乎一样有效。如果爆炸严重到梯度为数值上的 Inf 或 NaN,可走一个大小为 \(v\) 的随机步,通常会移出数值不稳定配置。

对每个 minibatch 截断梯度范数不会改变单个 minibatch 的梯度方向。然而把多个 minibatch 的范数截断梯度求平均不等价于对真实梯度(用所有样本形成的梯度)的范数做截断。那些具有大梯度范数的样本以及与这些样本出现在同一 minibatch 的样本,对最终方向的贡献会被减小。这与传统的 minibatch 梯度下降不同:传统 SGD 使用梯度的无偏估计,而范数截断的梯度下降引入了经验上已知有用的启发式偏置。在逐元素截断下,更新方向不与真实梯度或 minibatch 梯度对齐,但它仍然是下降方向。Graves (2013) 还提出对反向传播的梯度(关于隐藏单元的)做截断,但没有发表过这些变体之间的比较;作者猜测所有这些方法行为类似。

10.11.2 正则化以鼓励信息流(Regularizing to Encourage Information Flow)

梯度截断有助于处理梯度爆炸,但无助处理梯度消失。为应对梯度消失并更好地捕获长程依赖,我们讨论了在展开循环架构的计算图中创建路径、使关联弧的梯度乘积接近 1 的想法。一种实现该想法的方式是 LSTM 及上述 10.10 节介绍的其它自环与门控机制。另一种想法是正则化或约束参数以鼓励"信息流"。具体而言,我们希望正在被反向传播的梯度向量 \(\nabla_{h^{(t)}}L\) 保持其大小,即使损失函数只惩罚序列末端的输出。形式化地,我们希望 \((\nabla_{h^{(t)}}L)\partial h^{(t)}/\partial h^{(t-1)}\) 尽可能与 \(\nabla_{h^{(t)}}L\) 一样大。基于该目标,Pascanu et al. (2013) 提出了以下正则化项:

\[ \Omega=\sum_t\left(\frac{\left\|(\nabla_{h^{(t)}}L)\partial h^{(t)}/\partial h^{(t-1)}\right\|}{\|\nabla_{h^{(t)}}L\|}-1\right)^2 \]

计算该正则化项的梯度看似困难,但 Pascanu et al. (2013) 提出一个近似:把反向传播的向量 \(\nabla_{h^{(t)}}L\) 视为常数(就该正则化项而言,因此无需对它们再做反向传播)。使用该正则化项的实验表明:若与处理梯度爆炸的范数截断启发式结合使用,正则化项可显著增加 RNN 能学到的依赖跨度。因为它把 RNN 动力学保持在梯度爆炸的边缘,梯度截断尤其重要。没有梯度截断,梯度爆炸会让学习无法成功。该方法的一个关键弱点是:在数据丰富的任务(如语言建模)上它不如 LSTM 有效。

10.12 显式记忆(Explicit Memory)

智能需要知识,获取知识可通过学习完成,这推动了大规模深度架构的发展。然而知识有不同种类:有些知识是隐含的、下意识的、难以言表的——例如如何行走,或狗与猫看起来有何不同;其它知识是显式的、陈述性的、相对容易用语言表达——日常常识知识如"猫是一种动物",或为完成当前目标所需的具体事实如"与销售团队在下午 3 点 141 房间开会"。神经网络擅长存储隐式知识,但它们难以记忆事实。随机梯度下降需要对同一输入做多次呈现才能把其存储到神经网络参数中,且即使存储了,输入也不会被存储得特别精确。Graves et al. (2014b) 假设这是因为神经网络缺少相当于人类工作记忆系统的机制——该系统让人能显式地保持和操纵与实现某目标相关的信息块。这种显式记忆组件不仅能让我们的系统快速而"有意地"存储和检索特定事实,还能用它们做序列化推理。神经网络能在一步步中处理信息、在每步改变输入被送入网络的方式这一需求,很早就被认识到对推理能力(而非对输入做自动直觉反应)的重要性(Hinton, 1990)。

为解决该困难,Weston et al. (2014) 引入了记忆网络(memory networks),它包含一组可通过寻址机制访问的记忆单元。记忆网络原本需要关于如何使用记忆单元的监督信号。Graves et al. (2014b) 引入了神经图灵机(neural Turing machine),它能学会读写任意内容到记忆单元而无需关于采取哪些动作的显式监督,且通过使用基于内容的软注意力机制(见 Bahdanau et al. (2015) 及 12.4.5.1 节)允许端到端训练而无需该监督信号。该软寻址机制已成为其它相关架构模拟算法机制但仍允许基于梯度的优化的标准做法(Sukhbaatar et al., 2015; Joulin and Mikolov, 2015; Kumar et al., 2015; Vinyals et al., 2015a; Grefenstette et al., 2015)。

每个记忆单元可被看作 LSTM 和 GRU 记忆单元的扩展。区别在于:网络输出一个内部状态来选择从哪个单元读或写到哪个单元,正如数字计算机中的内存访问从特定地址读或写到特定地址。优化产生精确整数地址的函数是困难的。为缓解该问题,NTM 实际上同时对多个记忆单元进行读写:读时对许多单元取加权平均,写时以不同量修改多个单元。这些操作的系数被选为集中在少数几个单元上,例如通过对它们施加 softmax 函数来产生。使用这些具有非零导数的权重让控制访问记忆的函数可被梯度下降优化。这些系数上的梯度指示每个系数应当增加还是减少,但梯度通常仅对那些获得大系数的记忆地址是大的。

这些记忆单元通常被扩展为包含一个向量而非 LSTM 或 GRU 记忆单元所存储的单个标量。增大记忆单元大小有两条理由。一是访问记忆单元的代价已增加:我们付出了为许多单元产生系数的计算代价,但希望这些系数集中在少数几个单元。通过读一个向量值而非标量值,可抵消部分代价。另一个使用向量值记忆单元的理由是:它允许基于内容的寻址——用于读或写一个单元的权重是该单元的函数。向量值单元允许我们若能产生一个匹配其部分(而非全部)元素的模式,就检索到完整的向量值记忆。这类似于人能用几个词回忆一首歌的歌词。我们可把基于内容的读指令视为"检索那首副歌是'We all live in a yellow submarine'的歌的歌词"。基于内容的寻址在把要检索的对象做得很大时更有用——若每首歌的每个字母都存在单独记忆单元中,我们就无法以这种方式找到它们。相比之下,基于位置的寻址不允许引用记忆的内容。我们可把基于位置的读指令视为"检索在 347 槽位的歌的歌词"。即使记忆单元很小,基于位置的寻址也常常是完全合理的机制。

如果记忆单元的内容在大多数时间步被复制(不遗忘),则它所含信息可在时间上向前传播,在时间上反向传播的梯度也既不消失也不爆炸。

显式记忆方法如图 10.18 所示,其中"任务神经网络"与一个记忆耦合。虽然任务神经网络可以是前馈或循环的,但整个系统是循环网络。任务网络可选择从特定记忆地址读或写。显式记忆似乎允许模型学习普通 RNN 或 LSTM RNN 不能学习的任务。这种优势的一个原因可能是信息和梯度可分别沿时间向前和向后传播非常长的时长。

作为对通过记忆单元加权平均做反向传播的替代,我们可以把记忆寻址系数解释为概率并随机地只读一个单元(Zaremba and Sutskever, 2015)。优化做离散决策的模型需要专门的优化算法,描述见 20.9.1 节。迄今为止,训练这些做离散决策的随机架构仍比训练做软决策的确定性算法更难。无论是软(允许反向传播)还是随机且硬的,选择地址的机制在形式上都与之前在机器翻译(Bahdanau et al., 2015)和 12.4.5.1 节讨论的注意力机制相同。神经网络注意力机制的思想更早由 Graves (2013) 在 handwriting generation 语境下提出,其注意力机制被约束为只沿序列向前移动。在机器翻译和记忆网络情形下,每步注意力的焦点可移到与前一步完全不同的地方。

循环神经网络为把深度学习扩展到序列数据提供了一种方式。它们是深度学习工具箱中的最后一件主要工具。接下来的讨论转向如何选择和使用这些工具,以及如何把它们应用到现实任务中。

本章个人批注

本章是全书第一次真正进入"序列建模"主题,对应读者第一次面对"循环"计算图。从阅读体验看,10.1 节的"展开"概念是整章的认知支点——把循环方程 \(h^{(t)}=f(h^{(t-1)},x^{(t)};\theta)\) 沿时间展开成 \(g^{(t)}(x^{(1)},\dots,x^{(t)})\),既是后续 BPTT、注意力、显式记忆等机制的共同基础,也是把"参数共享"这个 1980 年代统计学习思想具体化的核心动作。作者把"展开"与"参数共享"绑定起来写,比单独讲计算图或单独讲参数共享都更有说服力——读者明白"为什么需要共享"和"共享怎么实现"是同一件事的两面。

10.2 节关于 RNN 的图灵完备性那段(Siegelmann 等人的 886 单元结果)让我重新校准了对 RNN 表达能力的直觉。RNN 在理论上足以模拟任何图灵机,但这并不意味着它能学出来——这正是 10.7 节"长程依赖挑战"的伏笔。"理论上能做"和"梯度能传到能做的地方"是两件完全不同的事。

10.2.1 节关于 teacher forcing 的讨论非常细致。"训练用 ground truth、测试用自身输出"造成的 train/test 不匹配是序列生成中一个根本性的痛点,作者给出的两个缓解方案(混合 teacher-forced 与 free-running 输入;Bengio 等人的 curriculum 策略)至今仍是序列建模的标准技巧。这一节还顺便点出了"输出到隐藏循环"与"隐藏到隐藏循环"的权衡:前者并行化易但功能弱,后者功能强但训练贵——这一对比也是后续 10.10 节门控机制存在的动机。

10.2.3 节把 RNN 解释为有向图模型那段("complete graph 解释 vs 隐藏单元解耦")是本章我读起来最费力的部分,但也是概念收益最大的一段。完全图解释强调"任意远过去的影响都可能直接传递",但参数随序列长度指数增长;隐藏单元解耦则让参数数量为 \(O(1)\),代价是优化困难。这一对比让我明白:RNN 的"参数高效"不是免费的——它把"用更多参数换更易优化"这道权衡推向了极端,从而造就了 10.7 节的根本性困难。

10.7 节的"梯度消失与爆炸"分析用最简单的标量情形 \(h^{(t)}=W^\top h^{(t-1)}\) 和特征分解 \(W=Q\Lambda Q^\top\) 把现象讲得非常清楚。Bengio et al. (1994) 关于"为了让记忆鲁棒而梯度必须消失"的悖论也很有启发——它给后续 10.10 节 LSTM 提供了"必要性"的论据。

10.10 节是本章实际工程价值最高的一节。LSTM 引入"自环权重由上下文决定"这一思想(Hochreiter and Schmidhuber 1997 的核心想法 + Gers et al. 2000 的关键扩展)是深度学习史上最优雅的工程抽象之一:把"梯度路径长度"从一个需要手动设定的超参数变为由数据驱动。本节读后我对"为什么 LSTM 在很多年里是默认序列模型"有了结构性的理解——不是因为它在所有基准上最强(10.10.2 节明确说没有变体能稳定击败 LSTM 和 GRU 两者),而是因为它在"长程依赖"这一最难目标上的失败模式比简单 RNN 优雅得多。

最后,10.12 节关于显式记忆的内容(NMT、神经图灵机、记忆网络)让我意识到"注意力"机制其实不是 transformer 时代的新发明——它在 Bahdanau et al. (2015) 的机器翻译和 Graves (2014b) 的神经图灵机中已经成型,且与"软寻址 vs 硬寻址"这一离散化权衡紧密相关。RNN 这一章实质上为本书后续"注意力 / Transformer"主题埋下了完整的伏笔。

与上下章的衔接(一段话)

本章是全书三大支柱架构(卷积、循环、注意力 / 显式记忆)中的"循环"支柱,与第 9 章卷积网络一样是"针对特定数据结构特化"的深度网络——卷积针对空间网格,循环针对时序序列。本章承接第 6、7、8 章建立的前馈网络与优化基础(10.2.2 节的 BPTT 直接复用 6.5.6 节的通用 bprop,10.7 节的"梯度消失与爆炸"问题在 8.2.5 节已系统铺垫),并把"参数共享"这一 1980 年代思想在序列上完整实现。下一章(第 11 章 实践方法论)会跳出具体架构,谈论在实际问题中如何选择 / 评估 / 调试这些工具;之后第 12 章应用章节会再次落到具体任务(语音、视觉、NLP)上,本章建立的循环 / 递归 / 显式记忆是这些应用最重要的技术储备。从更长远的视角看,本章还为后续章节中"注意力机制"(12.4.5.1 节)以及"Transformer"思想做了概念铺垫——10.12 节末尾已经明确指出软寻址与注意力的形式等价。