一、RNN
1.1 从单层网络到经典的RNN结构
在学习LSTM之前,得先学习RNN,而在学习RNN之前,首先要了解一下最基本的单层网络,它的结构如下图所示:
输入是x,经过变换Wx+b和激活函数f,得到输出y。相信大家对这个已经非常熟悉了。
在实际应用中,我们还会遇到很多序列形的数据:
如:
- 自然语言处理问题。x1可以看做是第一个单词,x2可以看做是第二个单词,依次类推。
- 语音处理。此时,x1、x2、x3……是每帧的声音信号。
- 时间序列问题。例如每天的股票价格等等。
而其中,序列形的数据就不太好用原始的神经网络处理了。
为了建模序列问题,RNN引入了隐状态h(hidden state)的概念,h可以对序列形的数据提取特征,接着再转换为输出。
先从的计算开始看:
图示中记号的含义是:
a)圆圈或方块表示的是向量。
b)一个箭头就表示对该向量做一次变换。如上图中和
分别有一个箭头连接,就表示对
和
各做了一次变换。
在很多论文中也会出现类似的记号,初学的时候很容易搞乱,但只要把握住以上两点,就可以比较轻松地理解图示背后的含义。的计算和
类似。但有两点需要注意下:
- 在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点,一定要牢记;
- 而下文马上要看到的LSTM中的权值则不共享,因为它是在两个不同的向量中。而RNN的权值为何共享呢?很简单,因为RNN的权值是在同一个向量中,只是不同时刻而已。
依次计算剩下来的(使用相同的参数U、W、b):
我们这里为了方便起见,只画出序列长度为4的情况,实际上,这个计算过程可以无限地持续下去。
我们目前的RNN还没有输出,得到输出值的方法就是直接通过h进行计算:
正如之前所说,一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h1进行一次变换,得到输出y1。
剩下的输出类似进行(使用和y1同样的参数V和c):
OK!大功告成!这就是最经典的RNN结构,是x1, x2, .....xn,输出为y1, y2, ...yn,也就是说,输入和输出序列必须要是等长的。
RNN的局限:长期依赖(Long-TermDependencies)问题
RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果RNN可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。
有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测“the clouds are in the sky”最后的词,我们并不再需要其他的信息,因为很显然下一个词应该是sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN可以学会使用先前的信息。
但是同样会有一些更加复杂的场景。假设我们试着去预测“I grew up in France...I speak fluent French”最后的词。当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的France的上下文的。这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。
不幸的是,在这个间隔不断增大时,RNN会丧失学习到连接如此远的信息的能力。
在理论上,RNN绝对可以处理这样的长期依赖问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN肯定不能够成功学习到这些知识。Bengio,etal.(1994)等人对该问题进行了深入的研究,他们发现一些使训练RNN变得非常困难的相当根本的原因。
换句话说, RNN 会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步。
因此,如果你正在尝试处理一段文本进行预测,RNN 可能从一开始就会遗漏重要信息。在反向传播期间(反向传播是一个很重要的核心议题,本质是通过不断缩小误差去更新权值,从而不断去修正拟合的函数),RNN 会面临梯度消失的问题。
因为梯度是用于更新神经网络的权重值(新的权值 = 旧权值 - 学习率*梯度),梯度会随着时间的推移不断下降减少,而当梯度值变得非常小时,就不会继续学习。
换言之,在递归神经网络中,获得小梯度更新的层会停止学习—— 那些通常是较早的层。 由于这些层不学习,RNN 可以忘记它在较长序列中看到的内容,因此具有短时记忆。
而梯度爆炸则是因为计算的难度越来越复杂导致。
然而,幸运的是,有个RNN的变体——LSTM,可以在一定程度上解决梯度消失和梯度爆炸这两个问题!
简单解释梳理RNN与LSTM

用RNN处理Slot Filling的流程举例如下:
- “arrive”的vector作为x 1输入RNN,通过hidden layer生成a 1 ,再根据a 1 生成y 1 ,表示“arrive”属于每个slot的概率,其中a 1 会被存储到memory中
- “Taipei”的vector作为x 2 输入RNN,此时hidden layer同时考虑x 2 和存放在memory中的a 1 ,生成a 2 ,再根据a 2 生成y 2,表示“Taipei”属于某个slot的概率,此时再把a 2 存到memory中
- 依次类推
Elman Network & Jordan Network
RNN有不同的变形:
- Elman Network:将hidden layer的输出保存在memory里
- Jordan Network:将整个neural network的输出保存在memory里
由于hidden layer没有明确的训练目标,而整个NN具有明确的目标,因此Jordan Network的表现会更好一些
Bidirectional RNN
RNN 还可以是双向的,你可以同时训练一对正向和反向的RNN,把它们对应的hidden layer x t 拿出来,都接给一个output layer,得到最后的y t
使用Bi-RNN的好处是,NN在产生输出的时候,它能够看到的范围是比较广的,RNN在产生y t + 1 的时候,它不只看了从句首x 1 开始到x t + 1 的输入,还看了从句尾x n 一直到x t + 1 的输入,这就相当于RNN在看了整个句子之后,才决定每个词汇具体要被分配到哪一个槽中,这会比只看句子的前一半要更好。
双向RNN
LSTM
前文提到的RNN只是最简单的版本,并没有对memory的管理多加约束,可以随时进行读取,而现在常用的memory管理方式叫做长短期记忆(Long Short-term Memory),简称LSTM。
Three-gate
LSTM有三个gate:
-
当某个neuron的输出想要被写进memory cell,它就必须要先经过一道叫做input gate的闸门,如果input gate关闭,则任何内容都无法被写入,而关闭与否、什么时候关闭,都是由神经网络自己学习到的
-
output gate决定了外界是否可以从memory cell中读取值,当output gate关闭的时候,memory里面的内容同样无法被读取
-
forget gate则决定了什么时候需要把memory cell里存放的内容忘记清空,什么时候依旧保存
整个LSTM可以看做是4个input,1个output:
- 4个input=想要被存到memory cell里的值+操控input gate的信号+操控output gate的信号+操控forget gate的信号
- 1个output=想要从memory cell中被读取的值
如果从表达式的角度看LSTM,它比较像下图中的样子
- z 是想要被存到cell里的输入值
- z i 是操控input gate的信号
- z o 是操控output gate的信号
- z f 是操控forget gate的信号
- a 是综合上述4个input得到的output值
把z 、z i 、z o 、z f f通过activation function,分别得到g ( z )、f ( z i ) 、f ( z o ) 、f ( z f )
其中对z i 、z o 和z f 来说,它们通过的激活函数f ( )一般会选sigmoid function,因为它的输出在0~1之间,代表gate被打开的程度
令g ( z ) 与f ( z i ) 相乘得到g ( z ) ⋅ f ( z i ) ,然后把原先存放在cell中的c 与f ( z f )相乘得到c. f ( z f ) ,两者相加得到存在memory中的新值c ′ = g ( z ) ⋅ f ( z i ) + c f ( z f )
- 若f ( z i ) = 0,则相当于没有输入,若f ( z i ) = 1,则相当于直接输入g ( z )
- 若f ( z f ) = 1,则保存原来的值c 并加到新的值上,若f ( z f ) = 0 ,则旧的值将被遗忘清除
从中也可以看出,forget gate的逻辑与我们的直觉是相反的,控制信号打开表示记得,关闭表示遗忘
此后,c ′ ′通过激活函数得到h ( c ′ ) ,与output gate的f ( z o )相乘,得到输出a = h ( c ′ ) f ( z o )
LSTM Example
下图演示了一个LSTM的基本过程,x 1 、x 2、x 3 是输入序列,y 是输出序列,基本原则是:
- 当x 2 = 1 时,将x 1 的值写入memory
- 当x 2 = − 1 时,将memory里的值清零
- 当x 3 = 1时,将memory里的值输出
- 当neuron的输入为正时,对应gate打开,反之则关闭.
LSTM Structure
你可能会觉得上面的结构与平常所见的神经网络不太一样,实际上我们只需要把LSTM整体看做是下面的一个neuron即可
假设目前我们的hidden layer只有两个neuron,则结构如下图所示:
- 输入x 1 、x 2 会分别乘上四组不同的weight,作为neuron的输入以及三个状态门的控制信号
- 在原来的neuron里,1个input对应1个output,而在LSTM里,4个input才产生1个output,并且所有的input都是不相同的
- 从中也可以看出LSTM所需要的参数量是一般NN的4倍
LSTM for RNN
从上图中你可能看不出LSTM与RNN有什么关系,接下来我们用另外的图来表示它
假设我们现在有一整排的LSTM作为neuron,每个LSTM的cell里都存了一个scalar值,把所有的scalar连接起来就组成了一个vector c t − 1
在时间点t ,输入了一个vector x t ,它会乘上一个matrix,通过转换得到z ,而z 的每个dimension就代表了操控每个LSTM的输入值,同理经过不同的转换得到z i i、z f 和z o ,得到操控每个LSTM的门信号
下图是单个LSTM的运算情景,其中LSTM的4个input分别是z 、z i 、z f 和z o 的其中1维,每个LSTM的cell所得到的input都是各不相同的,但它们却是可以一起共同运算的,整个运算流程如下图左侧所示:
f ( z f ) 与上一个时间点的cell值c t − 1 相乘,并加到经过input gate的输入g ( z ) ⋅ f ( z i ) 上,得到这个时刻cell中的值c t ,最终再乘上output gate的信号f ( z o ) ,得到输出y t
上述的过程反复进行下去,就得到下图中各个时间点上,LSTM值的变化情况,其中与上面的描述略有不同的是,这里还需要把hidden layer的最终输出y t 以及当前cell的值c t 都连接到下一个时间点的输入上
因此在下一个时间点操控这些gate值,不只是看输入的x t + 1 x^{t+1}xt+1,还要看前一个时间点的输出h t h^tht和cell值c t c^tct,你需要把x t + 1 x^{t+1}xt+1、h t h^tht和c t c^tct这3个vector并在一起,乘上4个不同的转换矩阵,去得到LSTM的4个输入值z 、z i 、z f 、z o ,再去对LSTM进行操控
注意:下图是同一个LSTM在两个相邻时间点上的情况
上图是单个LSTM作为neuron的情况,事实上LSTM基本上都会叠多层。