返回介绍

6.1 循环神经网络简介

发布于 2024-02-05 23:12:36 字数 12010 浏览 0 评论 0 收藏 0

6.1.1 时序的世界

许多真实问题本质上都是序列化的(sequential)。自然语言处理(NLP)中几乎所有的问题都是序列化的。例如,段落是由句子构成的序列,而单词是字符构成的序列。与之密切相关的是音视频片段,它们都是随时间变化的帧序列,甚至股票价格也仅在沿时间轴(如果有的话)进行分析时才有意义。

在所有这些应用中,观测的顺序非常重要。例如,句子“I had cleaned my car”可以修改为“I had my car cleaned”,含义就从原来的“我已经洗完车了”变为“我已安排其他人洗完车”。在口语中,这种时序的依赖关系更为突出,因为一些单词含义相去甚远但发音却可能非常相近,如“wreck a nice beach”的发音与“recognize speech”非常相似,单词必须从语境中进行重构。

如果从这个视角审视前馈神经网络(包括卷积神经网络),便会发现它们的局限性非常大。那些网络都是在单次前馈中对到来的数据进行处理,且假定所有的输入都是独立的,因此会将数据中蕴涵的许多模式丢失。虽然可以对输入进行长度填充,然后将整个序列送入网络,但这种做法并未很好地捕捉到序列的本质。

循环神经网络(recurrent neural network,RNN)是一类对时间显式建模的神经网络。用于构建RNN的神经元同样也接收来自其他神经元的加权输入。然而,在RNN中,神经元既允许与更高的层建立连接,也允许与更低的层建立连接。RNN网络的这些隐含活性值会在同一序列的相邻输入之间被记忆。

前馈神经网络与循环神经网络

自20世纪80年代以来,出现了RNN的各种变体,但均未获得广泛的应用,原因是那时的计算资源匮乏且训练中存在诸多难点,然而近年来情况已有改观。随着一些重要架构的出现,如2006年提出的LSTM,RNN已经拥有了非常强大的应用。它们能够很好地完成许多领域的序列任务,如语音识别、语音合成、手写连体字识别、时间序列预测、图像标题生成以及端到端的机器翻译等。

接下来,将首先深入探讨RNN及其优化方法,同时介绍必要的数学背景知识。之后,会介绍有助于克服标准RNN某些局限性的一些变种。当掌握了这些工具,我们将深入介绍四种自然语言处理任务,并运用RNN解决其中的问题。我们将逐一介绍所有的步骤,包括用TensorFlow完成数据处理、模型设计、实现及训练。

6.1.2 近似任意程序

下面开始介绍RNN,并试着培养一些直觉。之前介绍过的前馈神经网络只能在固定长度的向量上工作。例如,它们可将28×28像素的图像映射为10个可能类别上的概率分布,且计算在固定的步数(即层的数目)内完成。相比之下,无论输入还是输出为可变长向量,或输入输出均为可变长向量,循环神经网络都可以应对。

RNN基本上是一个由神经元和连接权值构成的任意有向图。输入神经元(input neuron)拥有“到来”连接,因为它们的活性值是由输入数据设置的。输出神经元(output neuron)也只是数据流图中的一组可从中读取预测结果的神经元。数据流图中的所有其他神经元都被称为隐含神经元(hidden neuron)。

RNN所执行的计算与普通的神经网络非常类似。在每个时间步,都会通过设置输入神经元而为网络提供输入序列的下一帧。相比于前馈网络,我们无法将隐含活性值丢弃,因为它们还将作为下一个时间步的附加输入。RNN的当前隐含活性值被称为状态。在每个序列的最开始,我们通常会设置一个值为0的空状态。

FFNN和RNN的简化表示

RNN的状态依赖于当前输入和上一个状态,而后者又依赖于更上一步的输入和状态。因此,状态与序列之前提供的所有输入都间接相关,从而可理解为工作记忆(working memory)。

可将神经网络与计算机程序做一类比。例如,假设希望从一幅包含手写文本的图像中识别字母,我们准备尝试通过编写一个使用变量、循环、分支语句的Python程序来解决该问题。不妨大胆尝试,但笔者认为要想让程序稳健地工作是极为困难的。

一个好消息是可选择另一种方式——依据样本数据训练一个RNN模型。正像我们通常会将中间信息保存在变量中一样,RNN也会学习将其中间信息保存在自身的状态中。类似地,RNN的权值矩阵定义了它所执行的程序,决定了在隐含活性值中保存什么输入,以及如何将不同活性值整合为新的活性值和输出。

实际上,带有sigmoid激活函数的RNN已由Sch?fer和Zimmermann于2006年证明是图灵完备的(Turing-complete)。这意味着,当给定正确的权值时,RNN可完成与任意计算程序相同的计算。然而这只是一个理论性质,因为当给定一项任务时,不存在找到完美权值的方法。尽管如此,利用梯度下降法仍然能够得到相当好的结果,相关内容将在下一节中进行介绍。

在开始探讨RNN的优化方法时,读者可能会疑惑,既然能够编写Python程序,为什么还需要RNN呢?可以这样理解,可能的权值矩阵构成的空间相比可能的C程序构成的空间要更加容易研究。

6.1.3 随时间反向传播

既然对于何为RNN以及为何其架构很酷已有了一些基本了解,下面来探讨如何找到一个“好”的权值矩阵,或如何对权值进行优化。对于前馈网络,最流行的优化方法是基于梯度下降法。然而,如何在RNN这种动态系统中将误差反向传播并非那么显而易见。

沿时间轴将循环神经网络展开

优化RNN可采取这样一种技巧,即沿时间轴将其展开,之后就可使用与优化前馈网络相同的方式对RNN进行优化。例如,假设希望对一个长度为10的序列进行处理。可将隐含神经元复制10次,并将它们的连接从一个副本跨连到相邻的另一个副本。这样便可将那些循环连接移除,而不更改计算的语义。经过上述处理,便形成了一个前馈网络,且相邻时间步之间的权值都拥有相同的强度。按时间展开RNN不会使计算发生改变,只是切换为了另一个视图。

沿时间将RNN展开的简化表示

这样,为计算误差相对于各权值的梯度,便可对这个展开的RNN网络运用标准的反向传播算法。这种算法被称为随时间反向传播(Back-Propagation Through Time,BPTT)。该算法将返回与时间相关的误差对每个权值(也包括那些联结[1]相邻副本的权值)的偏导。为保持联结权值相同,可采用普通的联结权值处理方法,即将它们的梯度相加。请注意,这种方式与卷积神经网络中处理卷积滤波器的方式等价。

6.1.4 序列的编码和解码

前面RNN的展开视图不但对优化十分有用,它还为RNN及其输入和输出数据的可视化提供了一种直观的方式。在开始具体实现之前,先快速了解一下RNN实现的到底是何种映射。序列任务往往有多种形式:有时,输入为一个序列,而输出为一个向量;或者反过来。对于这样的例子以及更复杂的情形,RNN都能够处理。

循环神经网络的常见映射

序列标注(sequential labelling)其实在之前的小节中我们已经接触过了。在这种任务中,将一个序列作为输入,并训练网络为每帧数据产生正确的输出。因此,基本上可以说序列标注完成的是一个序列到另一个序列的等长映射。

在序列分类(sequential classification)设置下,每个序列输入都对应一个类别标签。在这种设置下,可仅选择上一帧的输出训练RNN。在优化期间,更新权值时,误差将流经所有的时间步以收集和集成每个时间步中的有用信息。

序列生成(sequential generation)与序列分类恰好相反,它所定义的问题是,给定一个类别标签,如何生成一些序列。为了生成序列,可将输出反馈给网络作为下一步输入。这是合理的,因为实际输出通常都与这种神经网络的输出不同。例如,网络的输出可能是一个在所有类别上的概率分布,但我们仅会选择最可能的那个类别。

在序列分类和序列生成任务中,可将单个向量视为信息的稠密表示。在前者中,为了对类别做出预测,需要将序列编码为一个稠密向量;在后者中,将稠密向量解码为一个序列。

对于序列翻译(sequential translation)任务,可将这些方法进行整合。首先对一个域(如英语)中的序列进行编码,然后将最后的隐含活性值解码为另一个域(如法语)中的一个序列。对于单个RNN模型,这是完全可行的,但当输入和输出在概念层次存在差异时,使用两个不同的RNN,并用第一个模型中最后的活性值初始化第二个模型则是有意义的。使用单个网络时,需要在序列之后传入一个特殊符号作为输入,以通知网络何时停止编码,并开始解码。

带有输出投影的循环神经网络

最常见的情况下,会使用一种称为带有输出投影的RNN网络结构。这种RNN具有全连接的隐含单元,以及一些映射为这些隐含单元的输入和从这些隐含单元映射得到的输出。看待这种模型的另一种方式是:这种RNN模型的所有隐含单元都为输出,而其上堆叠了另外一个前馈层。稍后将了解到,这正是我们用TensorFlow实现RNN的方式,因为它既方便,又允许为隐含单元和输出单元指定不同的激活函数。

6.1.5 实现第一个循环神经网络

下面具体实现到目前为止所学习到的RNN知识点。TensorFlow支持RNN的各种变体,可从tf.nn.rnn_cell模块中找到这些变体的实现。借助tensorflow.models.rnn中的tf.nn.dynamic_rnn()运算,TensorFlow还为我们实现了RNN动力学。

该函数还有一个版本,可向数据流图添加展开运算,而不使用环。然而,该版本会消耗更多的内存,而且没有实际的益处。因此,我们推荐使用较新的dynamic_rnn()运算。

关于参数,dynamic_rnn()接收一个循环网络的定义以及若干输入序列构成的批数据。就目前而言,所有的序列都是等长的。该函数会向数据流图创建RNN所需的计算,并返回保存了每个时间步的输出和隐含状态的两个张量。

这样便完成了RNN的定义,并将它沿时间轴展开,我们只需加载一些数据,并选择一种TensorFlow提供的优化器,如tf.train.RMSPropOptimizer或tf.train.AdamOptimizer训练网络即可。在本章后续小节中,我们还将看到更多利用RNN解决实际问题的例子。

6.1.6 梯度消失与梯度爆炸

在上一节中,我们定义了RNN,并将其沿时间轴展开,以对误差进行反向传播和运用梯度下降法。然而,这种模型的表现目前并不尽如人意,尤其是它无法捕捉输入帧之间的长时依赖关系,而这种关系正是NLP任务所需要的。

下面给出一个示例任务,该任务涉及长时依赖性,要求RNN能够判别给定输入序列是否为给定语法的一部分。为完成该任务,网络必须记住其中含有许多后续不相关的帧的序列的第一帧。对于到目前为止我们所接触的传统RNN模型,为什么这会是一个问题?

一种包含长时依赖关系的语法

RNN之所以难于学习这种长时依赖关系,原因在于优化期间误差在网络中的传播方式。前文提到过,为了计算梯度,要将误差在展开后的RNN中传播。对长序列而言,这种展开的网络的深度将会非常大,层数非常多。在每一层中,反向传播算法都会将来自网络上一层的误差乘以局部偏导。

如果大多数局部偏导都远小于1,则梯度每经过一层都会变小,且呈指数级衰减,从而最终消失。类似地,如果许多偏导都大于1,则会使梯度值急剧增大。

深度网络中不稳定的梯度值

下面计算上图所示网络的梯度值。该网络的每层仅设置了一个隐节点,目的是帮助你更好地理解这个问题。将各个层的局部偏导相乘,可得:

求解上述偏导,可得:

从上式可以看出,误差项中包含了作为相乘项的权值矩阵的转置。在这个示例网络中,权值矩阵仅有一个分量,因此可以比较容易地看出当大多数权值都小于(或大于)1时,这个梯度值便会接近于0(或无穷大)。在一个权值矩阵为实数类型的较大规模的网络中,若权值矩阵的特征值小于(或大于)1时,也会出现同样的问题。

实际上,在任何深度网络中,该问题都是存在的,而非只有循环神经网络中才有这样的问题。在RNN中,相邻时间步是联结在一起的,因此,这样的权值的局部偏导要么都小于1,要么都大于1,原始(或展开的)RNN中每个权值都会向着相同的方向被缩放。因此,相比于前馈神经网络,梯度消失或梯度爆炸这个问题在RNN中更为突出。

在许多问题中,都伴有很小或很大的梯度值。当梯度的各分量接近于0或无穷大时,训练分别会出现停滞或发散。此外,由于我们做的是数值优化,因此,浮点精度也会对梯度值产生影响。该问题也被称为深度学习中的基本问题,在近年来已受到许多研究者的关注。目前最流行的解决方案是一种称为长短时记忆网络(long-short term memory,LSTM)的RNN架构。下一节将对该架构进行探讨。

6.1.7 长短时记忆网络

LSTM是一种特殊形式的RNN,由Hochreiter和Schmidhuber于1997年提出,它是专为解决梯度消失和梯度爆炸问题而设计的。在学习长时依赖关系时它有着卓越的表现,并成为RNN事实上的标准。自从该模型被提出后,人们相继提出了LSTM的若干变种,这些变种的实现已包含在TensorFlow中,相关内容将在本节稍后加以强调。

为解决梯度消失和梯度爆炸问题,LSTM架构将RNN中的普通神经元替换为其内部拥有少量记忆的LSTM单元(LSTM Cell)。如同普通RNN,这些单元也被联结在一起,但它们还拥有有助于记忆许多时间步中的误差的内部状态。

LSTM的窍门在于这种内部状态拥有一个固定权值为1的自连接,以及一个线性激活函数,因此其局部偏导始终为1。在反向传播阶段,这个所谓的常量误差传输子(constant error carousel)能够在许多时间步中携带误差而不会发生梯度消失或梯度爆炸。

尽管内部状态的目的是随许多时间步传递误差,LSTM架构中负责学习的实际上是环绕门(surrounding gates),这些门都拥有一个非线性的激活函数(通常为sigmoid)。在原始的LSTM单元中,有两种门:一种负责学习如何对到来的活性值进行缩放,而另一种负责学习如何对输出的活性值进行缩放。因此,这种单元可学习何时包含或忽略新的输入,以及何时将它表示的特征传递给其他单元。一个单元的输入会送入使用不同权值的所有门中。

也可将循环神经网络视为一些“层”,因为它可以用作规模更大的网络架构的组成部分。例如,我们可首先将时间步送入若干卷积和池化层,然后用一个LSTM层处理这些输出,并在位于最后的时间步LSTM活性值上添加一个softmax层。

TensorFlow为这样的LSTM网络提供了LSTMCell类,它可直接替换BasicRNNCell类,同时该类还提供了一些额外的开关。尽管该类名称从字面上看只有LSTM单元,但实际上表示了一个完整的LSTM层。在后面的小节中,我们将学习如何将LSTM层与其他网络进行连接,以形成更大规模的网络。

长短时记忆(LSTM)

6.1.8 RNN结构的变种

LSTM的一种比较流行的变种是添加一个对内部循环连接进行比例缩放的遗忘门(forget gate),以允许网络学会遗忘[2]。这样,内部循环连接的局部偏导就变成了遗忘门的活性值,从而可取为非1的值。当记忆单元上下文非常重要时,这种网络也可以学会将遗忘门保持关闭状态。

将遗忘门的初始值设为1是非常重要的,因为这样可使LSTM单元从一个记忆状态开始工作。如今,在几乎所有的实现中,遗忘门都是默认存在的。在TensorFlow中,可通过指定LSTM层的forget_bias参数对遗忘门的偏置进行初始化,默认初值为1,也建议不要修改这个默认值。

带有遗忘门和门限循环单元(GRU)的LSTM

另一种扩展是添加窥视孔连接(peephole connection),以使一些门能够看到单元的状态[3]。提出该变种的作者声称当任务中涉及精确的时间选择和间隔时,使用窥视孔连接是有益的。TensorFlow的LSTM层支持窥视孔连接。可通过为LSTM层传入use_peepholes=True标记将窥视孔连接激活。

基于LSTM的基本思想,Chung Junyoung等于2014年提出了门限循环单元(Gated Recurrent Unit,GRU)[4]。与LSTM相比,GRU的架构更简单,而且只需更少的计算量就可得到与LSTM非常相近的结果。GRU没有输出门,它将输入和遗忘门整合为一个单独的更新门(update gate)。

更新门决定了内部状态与候选活性值的融合比例。候选活性值是依据由重置门(reset gate)和新的输入确定的部分隐含状态计算得到的。TensorFlow的GRU层对应GRUCell类,除了该层中的单元数目,它不含任何其他参数。如果希望进一步了解GRU,笔者推荐参阅Jozefowicz等发表的ICML’015文章[5],这篇文章对循环单元架构进行了经验性的探索。

循环神经网络中的多个层

到目前为止,我们研究了带有全连接隐含单元的RNN。这是最一般的架构,因为这种网络能够学会在训练期间将不需要的权值置为0。不过,最常见的做法是将两层或多层全连接的RNN相互堆叠。这仍可视为一个其连接拥有某种结构的RNN网络。由于信息只能在两层之间向上流动,与规模较大的全连接RNN相比,多层RNN拥有的权值数目更少,而且有助于学习到更多的抽象特征。

[1] 为与普通的连接权值区分,本书将这类权值译“联结权值”(tied weight)。——译者注

[2] Gers, Felix A., Jürgen Schmidhuber, and Fred Cummins. “ Learning to forget: Continual prediction with LSTM.” Neural computation 12.10 (2000): 2451-2471.

[3] Gers, Felix A., Nicol N. Schraudolph, and Jürgen Schmidhuber. “ Learning precise timing with LSTM recurrent networks.” The Journal of Machine Learning Research 3 (2003): 115-143.

[4] Chung, Junyoung, et al. “ Empirical evaluation of gated recurrent neural networks on sequence modeling. ”arXiv preprint arXiv:1412.3555 (2014).

[5] Jozefowicz, Rafal, Wojciech Zaremba, and Ilya Sutskever. “ An empirical exploration of recurrent network architectures.” Proceedings of the 32nd International Conference on Machine Learning (ICML-15). 2015.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文