循环神经网络原理

5837 字 · 508 阅读 · 2023 年 06 月 03 日

本文已更新,你可以访问 AI By Doing 以获得更好的阅读体验。
本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

介绍

本节实验我们进入循环神经网络(Recurrent Neural Networks,简称 RNN)的学习。循环神经网络在自然语言处理,语音识别等领域取得了重大成功并且得到广泛运用,它在序列模型处理上有巨大的优势,也逐渐让深度学习能成功地在更多的领域发挥效用。

知识点

  • 序列模型介绍
  • 简单循环神经网络
  • LSTM 长短期记忆模型
  • GRU 门控循环单元

自然语言处理介绍

自然语言处理中的「自然语言」是指自然形成的语言,即平时人们日常使用的交流的语言。语言是人类区别其他动物的本质特性。在所有生物中,只有人类才具有语言处理和理解能力。人类的逻辑思维以语言为形式,人类的绝大部分知识也是以语言文字的形式记载和流传下来的。

自然语言处理不仅是计算语言学的应用领域,还是计算机科学和人工智能领域的一个重要研究方向。自然语言处理主要研究人与计算机之间用自然语言进行有效通信的各种理论和方法。因此,自然语言处理实际上是一门集语言学、计算机科学、数学于一体的学科。NLP 专家 Michael Frederikse 认为自然语言处理与机器学习、深度学习及人工智能具有以下的交集关系。


自然语言处理虽然与语言学有关,但自然语言处理又与语言学那种纯粹的研究自然语言有所区别。语言学是以自然语言为研究对象,研究语言的性质、功能、结构、运用和历史发展以及其他与语言有关的问题。自然语言处理则在于研究能有效实现人机间自然语言通信的计算机系统。

经过多年的发展与进步,从自然语言的角度出发,自然语言处理可以划分为两个部分:自然语言理解和自然语言生成。

其中,自然语言理解是个综合的系统工程,涉及了很多细分的学科。例如,系统化研究语言中发音的音系学,研究单词构成以及相互之间的关系词态学等。但总的来说,自然语言理解又可以分为三个方面,分别是:词义分析,句法分析,语义分析。

自然语言的生成则是从结构化的数据(可以通俗理解为自然语言理解分析后的数据)以读取的方式自动生成文本。主要有三个阶段:

  • 文本规划:完成结构化数据中的基础内容规划。
  • 语句规划:从结构化数据中组合语句来表达信息流。
  • 实现:产生语法通顺的语句来表达文本。

目前,自然语言处理有着十分丰富的应用。总体来说,主要有以下一些分类:

  • 信息检索:对大规模文档进行索引。
  • 语音识别:识别包含口语在内的自然语言的声学信号转换成符合预期的信号。
  • 机器翻译:将一种语言翻译成另外一种语言。
  • 智能问答:自动回答问题。
  • 对话系统:通过多回合对话,跟用户进行聊天、回答、完成某项任务。
  • 文本分类:将文本自动归类。
  • 情感分析:判断某段文本的情感倾向
  • 文本生成:根据需求自动生成文本
  • 自动文摘:归纳,总结文本的摘要。

不过,在真正接触到自然语言应用层面之前,我们需要先学习其最为常用的深度神经网络类型:循环神经网络。

序列模型介绍

在机器学习的任务中,很多任务其实都可以看作序列的模型。所谓序列,就是指这个模型中的元素不再独立,而是具有一定的相关性。一般,我们根据输入输出,把常用模型划分为如下几类:

  • 一对一模型:我们之前的模型,比如全连接神经网络,就是一对一的结构。我们给它一个输入,它能得到一个输出,而且不同输入间被视作了独立的关系,分别进行学习或者识别等任务。而现在我们要关注的,是后四幅图,也就是时序的模型。
  • 一对多模型:根据模型的一个输出,可以用来预测一个序列。比如对于一个图像,输出一串字幕来描述它。
  • 多对一模型:根据一个序列输入,从而预测一个值。比如根据用户在一家饭店的留言,判断用户对这家饭店的评价。那么,输入是一段话是一个序列输入,输出是 0 至 5 之间的一个数作为打分。
  • 多对多有延迟:根据模型的序列输入,我们根据已有的输入,有延迟地输出一个序列。常见的任务比如翻译的学习任务,英文翻译成中文。模型需要等英文输入到一定程度后,才能翻译成中文,这样输入输出都为一个序列。
  • 多对多无延迟:根据模型的序列输入,根据输入同步输出一个序列。常见的比如做天气预报,需要实时根据测得的温度、湿度等做出下雨概率的预测。但是,每一次预测其实也要考虑之前的一些序列输入,而不仅由这一时刻的输入所决定。

可以看出,序列模型的典型特点是输入输出不独立,往往输出跟前一步、甚至前几步的输入相关。考虑到这种模型的存在,也为了更好地拟合这样的一个过程,循环神经网络就孕育而生了。

简单循环神经网络

在传统的神经网络中,输入层到输出层的每层直接是全连接的,但是层内部的神经元彼此之间没有连接。这种网络结构应用到很多场合确实是有难度的,比如文本处理,语音翻译等。

循环神经网络的解决方法,是通过让隐藏层不仅只考虑上一层的输出,还包括了上一时刻该隐藏层的输出。理论上,循环神经网络能够包含前面任意多个时刻的状态。实践中为了降低模型的复杂性,我们一般只处理前面几个状态的输出。

相比于传统的前馈式神经网络,循环神经网络将不同时序的同一层前后连了起来,权值上就会通过引入另一个权值,来决定上一时刻的输出如何作为输入,并影响到下一时刻的输出。于是就有了这样一个简单的基本结构:

左边表示一个循环的图像,右边则是网络在具体搭建时的思路,二者完全等价。对于神经元 $A$ 来讲,$X_t$ 为输入,$h_t$ 为隐含层输出。其中,$h_t$ 不仅和 $X_t$ 相关,同时还受到 $h_{t-1}$ 的影响。

此时,你就需要了解循环神经网络的输入和前馈式神经网络之间的巨大区别,前馈式神经网络的输入 $x_0 \cdots x_t$ 是一次性传入的,而循环神经网络是 $x_0$ 传入后得到 $h_0$ 之后,再进行 $x_1$ 的输入。

下面是一张更加完整的循环神经网络结构图,你可以明显看出每一个时间步长输出是如何计算得到的。

上面的公式中添加了激活函数,例如我们想求得 $h_2$,实际上简单来写:

$$ h_2 = l_1(x_1) + r_1(h_1) \tag{1}$$

LSTM 长短期记忆模型

接下来的实验内容中,我们参考了 Christopher Olah 撰写的 Understanding LSTM Networks 中的配图及部分内容。

简单的循环神经网络一般只能与前面若干序列有关,若超过十步,就很容易产生 梯度消失或者梯度爆炸 等问题。其中,梯度消失是因为在链式求导过程中由于与之相关的序列深度比较深,所以导致了梯度指数式的消失。

除此之外,简单循环神经网络无法解决长依赖问题。例如,当我们预测「我住在___石景山区」时,RNN 可能很容易就能得到「北京市」的结果。这是因为所要预测的内容和相关信息之间的间隔很小,循环神经网络就能够利用过去的信息得出预测结果。

但如果预测内容和相关信息间隔很远,例如,「我是土生土长的北京人,…… [此处省略较长段落] ……,我的母语是 ____ 」。此时,循环神经网络就很难作出准确预测了。因为信息依赖距离太长,RNN 很难将其关联起来。

为了有效解决这个问题,循环神经网络也可以通过改变模块,来增强隐藏层的功能。其中,LSTM 以及 GRU 最为经典。

LSTM 全称是 长短期记忆模型,是目前非常流行的循环神经网络元结构。它与一般的 RNN 结构本质上并没有什么不同,只是使用了不同的函数去计算隐藏层的状态。

下图是我们之前的简单 RNN 的结构:

现在,我们引出 LSTM 的结构。有了这种结构 LSTM 就可以很好地解决长依赖问题,因为其本身就具备长久记忆信息的能力。

接下来,我们一步一步来剖析 LSTM 的模块连接。

像一根连接线一样,元状态 $C$ 在时序上连接各个时序的 LSTM 模块。它可能是 LSTM 里最精髓的一部分了,其中乘法操作操控着上个时序的元状态有多少可以进入下个时序,加法操作决定这个时序对元状态有何更新。

上面的那条水平的连接线是没办法实现添加或者删除信息。LSTM 通过一种叫做「门」的结构来实现。简单来讲,门结构控制着信息的流通,其主要是通过一个 $sigmoid$ 模块和逐点相乘的操作来实现。我们都知道,$sigmoid$ 输出介于 0 到 1 之间。通过逐点相乘运算,0 就可以不让任何信息通过,而 1 则可以让所有信息通过。LSTM 中有三个这样的门结构,来实现保护和控制信息。分别是:遗忘门,输入门和输出门。

首先来看「遗忘门」的结构,其主要作用在于决定让哪些信息能够继续通过这个单元。

上图中,$C$ 表示元状态,下标 $t$ 表示它在不同时序的取值。最左端那部分,通过上一时序的输出 $h_{t-1}$ 和这一时序的该层输入 $x_{t}$ 做为输入,通过 $sigmoid$ 模块以及其中相应的权值更新激活后,输出在 $[0, 1]$ 间的数值 $f_{t}$ ,它就决定了上一个时序的元状态 $C_{t-1}$ 有多少能进入这个时序。

我们再把视线放在中间部分,这部分决定了又有多少新的信息会通过这个时序的 LSTM 模块加入元状态中。这里包含两个部分,首先由 $sigmoid$ 激活后的 $i_{t}$ 决定一部分,然后与 $tanh$ 激活的 $\tilde{C}_{t}$ 与之相乘:

之后,将遗忘门结果,输入门结果一起汇入元状态就可以完成对元状态的更新了。这也就组成了完整的「输入门」结构。

完成对元状态的更新后,我们来看看 LSTM 的「输出门」结构。$tanh$ 帮助我们促使 $C_{t}$ 变到 $[-1, 1]$ 的值域上,将两个输入 $h_{t-1}$ 和 $x_{t}$ 做 $sigmoid$ 激活后相乘,就得到最终输出:

这里,我们再举一个更形象的例子重新梳理一下 LSTM 不同门结构的作用。例如需要训练一个语言模型,LSTM 单元状态中都应该包含当前主语的性别信息,这样才能正确预测并使用人称代词。但是,当语句开始使用新的主语时,遗忘门的作用就是把上文中的主语性别给忘掉避免对下文造成影响。接下来,输入门就需要把新的主语性别信息传入到单元中。

最后,输出门的作用是对最终输出进行过滤。假设模型刚刚接触了一个代词,接下来可能要输出一个动词,而这就与代词的信息相关。比如这个动词应该采用单数还是复数形式,这就需要把刚学到的和代词相关的信息都加入到元状态中来,才能得到正确的输出。

现在,我们来总结下我们在 LSTM 里要学习的参数:

$$ f_{t}=\sigma(W_{f}\cdot x_{t}+U_{f}\cdot h_{t-1}+b_{f}) \tag{2}$$
$$ i_{t}=\sigma(W_{i}\cdot x_{t}+U_{i}\cdot h_{t-1} +b_{i}) \tag{3}$$
$$ \tilde{C}_{t}=\tanh(W_{c}\cdot x_{t}+U_{c}\cdot h_{t-1}+b_{c}) \tag{4}$$
$$ o_{t}=\sigma(W_{o}\cdot x_{t}+U_{o}\cdot h_{t-1}+b_{o}) \tag{5}$$

最终记忆单元:

$$ C_{t}=f_{t}\cdot C_{t-1}+i_{t}\cdot \tilde{C}_{t} \tag{6}$$

最终输出:

$$ h_{t} = o_{t}\cdot tanh({C}_{t}) \tag{7}$$

所以,$W_{f},U_{f},b_{f}$;$W_{i},U_{i},b_{i}$;$W_{c},U_{c},b_{c}$;$W_{o},U_{o},b_{o}$ 均为待学习的变量。同样,通过反向传播的方法更新他们的数值。

GRU 门控循环单元

GRU 即 ↗ Gated Recurrent Unit。前面说到,为了克服 RNN 无法很好处理远距离依赖而提出了 LSTM,而 GRU 则是 LSTM 的一个变体。当然,LSTM 还有很多其它的变体。GRU 保持了 LSTM 的效果同时又使结构更加简单,所以它也非常流行。我们来看一下 GRU 的核心模块。

前面介绍 LSTM 时,提到了 LSTM 实现了三个门计算,即:遗忘门、输入门和输出门。做为 LSTM 的一种变体,GRU 有两个门来实现相同的操作:更新门和重置门,即上图里的 $z_{t}$ 和 $r_{t}$。我们根据上图表述 GRU 的运算过程:

$$ r_{t}=\sigma(W_{r}\cdot x_{t}+U_{r}\cdot h_{t-1}+b_{r}) \tag{8}$$
$$ z_{t}=\sigma(W_{z}\cdot x_{t}+U_{z}\cdot h_{t-1}+b_{z}) \tag{9}$$
$$ \tilde{h_{t}}=tanh(W_{h}\cdot x_{t}+U_{h}\cdot h_{t-1}\cdot r_{t}+b_{h}) \tag{10}$$
$$ h_{t}= z_{t}\cdot \tilde{h_{t}}+(1-z_{t})\cdot h_{t-1} \tag{11}$$

前两个式子 $r_{t}$ 和 $z_{t}$ 分别计算出了 GRU 模块有多少信息需要遗忘和更新。然后,$\tilde{h_{t}}$ 决定了当前的记忆内容,如果 $r_{t}$ 为 0,那么 $\tilde{h_{t}}$ 只保留当前的信息。最后,$z_{t}$ 控制需要从前一时刻 $h_{t-1}$ 遗忘多少信息,从当前时刻已有的记忆 $\tilde{h_{t}}$ 加入多少信息,最终得到这个时刻的输出 $h_{t}$。

可以看出 GRU 与 LSTM 重大的区别是 GRU 没有一个输出控制门。

LSTM 还有很多的变种,比如 Gers, et al. (2000) 介绍的广受欢迎的 LSTM 变种添加了「门镜连接」。这意味着我们可以让门观察 Cell 状态。比如 Yao, et al. (2015) 提出的 Depth Gate RNN,又比如 Koutnik, et al. (2014) 提出的 Clockwork RNN 等等。就 GRU 和 LSTM 来说,GRU 的参数更少,因而训练稍快或需要更少的数据来泛化。另一方面,如果你有足够的数据,LSTM 的强大表达能力可能会产生更好的结果。

双向 RNN

在经典的循环神经网络中,状态的传输是从前往后单向进行的。然而在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。就比如我们平常的中英文翻译,我们根据前后文才能让我们的翻译更加准确。这时就需要双向 RNN(Bidirectional Recurrent Neural Networks)来解决这类问题。

原始的双向 RNN 是由两个相对简单的 RNN 上下叠加在一起组成的。输出由这两个 RNN 的状态共同决定。

从上图可以看出,双向 RNN 的主体结构就是两个单向 RNN 的结合。在每一个时刻 $t$,输入会同时提供给这两个方向相反的 RNN,而输出则是由这两个单向 RNN 共同决定。双向 RNN 能更好地模拟双向的时序模型,它的应用也在不断延伸发展,例如将双向的思想和 LSTM、GRU 相结合,变成双向 LSTM 或双向 GRU 等。

深度 RNN

深度 RNN 则是通过叠加多个 RNN 网络构建更复杂的模型,如下图所示。


深度 RNN 可以使用普通的 RNN 结构,也可以使用 GRU 或 LSTM。除了单向 NN,也可以使用双向 RNN,更可以根据不同任务改变自己的结构,比如一对多,多对一,多对多这些我们在序列模型中提到的学习任务。

对于普通的神经网络,其层数可以非常多,比如 100 多层。而对于深度 RNN,像上图那样有三层已经是非常深了,因为每一层 RNN 还有时间维度上的深度,即便 RNN 层数不多,整体网络的规模也会非常巨大。对于深度 RNN,还有一种做法是在像上图那样叠加几层 RNN 之后,将 RNN 在每个时刻上的输出再输入到单独的普通深度网络中,由这些普通的深度网络给出每个时刻上的最终预测值,这些普通的深度网络不再像 RNN 那样在时间维度上相连接。

总之,RNN 的结构变化非常灵活,学术界及工业界对 RNN 的研究都在不断深入,相信会有更多新的结构能进一步提升我们深度学习的拟合和泛化能力。

小结

这篇文章,我们了解并学习了循环神经网络的相关知识。与卷积神经网络相似,循环神经网络目前也拥有比较清晰的应用场景和发展势头,是自然语言处理、语音识别等方面不可或缺的手段之一。循环神经网络的基础结构简单,但变种丰富,Hojjat Salehinejad 等在 Recent Advances in Recurrent Neural Networks 论文中总结了循环神经网络的重要发展节点,有兴趣可以阅读。

本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

系列文章