循环神经网络 RNN

生活中,我们经常会遇到或者使用一些时序信号,比如自然语言语音,自然语言文本。以自然语言文本为例,完整的一句话中各个字符之间是有时序关系的,各个字符顺序的调换有可能变成语义完全不同的两句话,就像下面这个句子:

  • 张三非常生气,冲动之下打了李四

  • 李四非常生气,冲动之下打了张三

从以上这个例子可以看出,名字的调换造成了完全不同的后果,可见自然语言的时序性非常重要。那么如何对这种带有时序关系的数据进行建模呢?本节我们将来介绍一个非常经典的循环神经网络RNN,希望这篇本文能够对读者带来一些帮助。本文内容组织如下:

  1. 循环神经网RNN是什么

  2. RNN的公式推导

  3. RNN的缺陷

  4. RNN的几种常见模式

1. 循环神经网络RNN是什么

循环神经网络(Recurrent Neural Network,RNN)是一个非常经典的面向序列的模型,可以对自然语言句子或是其他时序信号进行建模。进一步讲,它只有一个物理RNN单元,但是这个RNN单元可以按照时间步骤进行展开,在每个时间步骤接收当前时间步的输入和上一个时间步的输出,然后进行计算得出本时间步的输出。

下面我们举个例子来讨论一下,如图1所示,假设我们现在有这样一句话:”我爱人工智能”,经过分词之后变成”我,爱,人工,智能”这4个单词,RNN会根据这4个单词的时序关系进行处理,在第1个时刻处理单词”我”,第2个时刻处理单词”爱”,依次类推。

image-20210510145249688

图1 RNN网络结构图

图1上可以看出,RNN在每个时刻\(t\)均会接收两个输入,一个是当前时刻的单词\(x_t\),一个是来自上一个时刻的输出\(h_{t-1}\),经过计算后产生当前时刻的输出\(h_t\)。例如在第2个时刻,它的输入是”爱”和\(h_1\),它的输出是\(h_2\);在第3个时刻,它的输入是”人工”和\(h_2\), 输出是\(h_3\),依次类推,直到处理完最后一个单词。

总结一下,RNN会从左到右逐词阅读这个句子,并不断调用一个相同的RNN Cell来处理时序信息,每阅读一个单词,RNN首先将本时刻\(t\)的单词\(x_t\)和这个模型内部记忆的状态向量\(h_{t-1}\)融合起来,形成一个带有最新记忆的状态向量\(h_t\)

Tip:当RNN读完最后一个单词后,那RNN就已经读完了整个句子,一般可认为最后一个单词输出的状态向量能够表示整个句子的语义信息,即它是整个句子的语义向量,这是一个常用的想法。

2. RNN的公式推导

在第1节,我们从宏观上讨论了RNN的工作原理,那么RNN内部是怎么计算的呢,即在每个时刻如何根据输入计算得到输出的呢?本节我们来探讨这个话题。

前边我们谈到,在每个时刻\(t\),RNN会接收当前时刻的输入单词\(x_t\)和上一个时刻的输出状态\(h_{t-1}\),然后计算得到当前时刻的输出\(h_t\),在RNN中这个实现机制比较简单:

\[h_t = tanh(Wx_t + Vh_{t-1}+b)\]

即在时刻\(t\),RNN单元会对两个输入\(x_t\)\(h_{t-1}\)进行线性变换,然后将结果使用\(tanh\)激活函数进行处理,得到当前时刻\(t\)的输出\(h_t\)

这里需要注意一下,tanh函数是一个值域(-1,1)的函数,如图2所示,可以长期维持内部记忆在一个固定的数值范围内,防止因多次迭代更新导致的数值爆炸,同时,tanh的导数是一个平滑的函数,让神经网络的训练变得更加简单。

图2 tanh函数图像

3. RNN的缺陷

上边我们貌似提出了一个非常优秀的RNN模型建模时序数据,但在真实的任务训练过程中,存在一个明显的缺陷,那就是当阅读很长的序列时,网络内部的信息会逐渐变得越来越复杂,以至于超过网络的记忆能力,使得最终的输出信息变得混乱无用。例如下面这句话:

我觉得这家餐馆的菜品很不错,烤鸭非常正宗,包子也不错,酱牛肉很有嚼劲,但是服务员态度太恶劣了,我们在门口等了50分钟都没有能成功进去,好不容易进去了,桌子也半天没人打扫,整个环境非常吵闹,我的孩子都被吓哭了,我下次不会带朋友来。

显然这是个比较长的文本序列,当RNN读到这句话时,有可能前半句还能准确地表达这句话的语义,但是读到后半句可能就完全混乱了,不能准确地表达这句话的语义信息,即不能保持长期的信息之间的依赖。

因此,针对这个问题,后续出现了很多基于RNN的改进模型,比如LSTM,GRU等等,这些在后续的章节我们将继续讨论。

4. RNN的几种常见模式

循环神经网络可以应用到很多不同类型的任务中,根据这些任务的的特点可以分为以下几种模式:

  • 序列到类别模式

  • 同步的序列到序列模式

  • 异步的序列到序列模式

下面我们来进一步聊聊这几种模式,以便大家RNN适合应用在什么样的任务上。

4.1 序列到类别模式

第1节我们讲到,在RNN读完一个句子的最后一个单词后,该单词对应的输出便可以看做能够代表整个句子的语义向量,根据这个语义向量可以进一步计算一些任务,比如文本分类,假设通过这个语义向量能够将”我爱人工智能”这句话划分为”科技”类别,那这就是一个完整的序列到类别模式的应用。

序列到类别模式是将一串输入映射为一个向量,如图3所示。在这种模式下,模型的输入是一个序列\(=[x_1,x_2,x_3,..,x_n]\),最终使用的模型输出是一个向量\(h_n\)(图3中输出的绿色向量),可以根据这个输出向量\(h_n\)进一步做一些任务。

image-20210510161632849

图3 序列到类别模式图

除了将最后时刻的状态作为整个序列的语义向量之外,我们还可以对整个序列的所有状态进行平均,并用这个平均状态作为整个序列的语义向量,如图4所示。

image-20210510162542974

图4 序列到类别模式图

4.2 同步的序列到序列模式

同步的序列到序列模式是将一串输入\(x=[x_1,x_2,..,x_n]\)映射为一串输出向量\(h=[h_1,h_2,...,h_n]\),并且每个输入和输出是一一对应的,如图5所示。同步的序列到序列模式主要用于 序列标注(Sequence Labeling) 任务上。

image-20210510164413446

图5 同步的序列到序列模式

词性标注(Part-of-Speech Tagging) 为例,该任务期望得到一个句子中每个单词的词性,因此它的输入就是一个句子中的单词,它的输出就是每个单词对应的词性。

4.3 异步的序列到序列模式

异步的序列到序列模式也成为编码器-解码器模型(encoder-decoder),它同样是将一串输入映射\(x=[x_1,x_2,..,x_n]\)为一串输出向量\(h=[h_1,h_2,...,h_m]\),但是输入序列和输出序列不要求有严格的一一对应关系,也不需要保持相同的长度,如图6所示。例如在机器翻译-英译汉任务中,输入为中文的单词序列,输出为英文的单词序列。

image-20210510171041678

图6 异步的序列到序列模式

在这个模式下,一般先将输入序列传到一个RNN(encoder,图6橙色单元)中,然后再将encoder的输出向量作为另一个RNN(解码器,图6黄色单元)的输入,由解码器进行序列解码。在解码器的解码过程中,往往会将前一个时刻输出的单词作为当前时刻输入的单词,采用这种自回归的方式进行解码。

5. 引用

[1] 邱锡鹏. 神经网络与深度学习[M]. 北京:机械工业出版社,2021.
[2] 吴飞. 人工智能导论:模型与算法[M]. 北京:高等教育出版社,2020.