RNN(循环神经网络)
该文章来自知乎转载,只是方便自己阅读
原文链接:人人都能看懂的LSTM
神经网络
在介绍循环神经网络之前,先介绍一下常见的神经网络的结构,训练神经网络,无非是通过给定的输入x,通过一层一层的黑盒子,在不断修改黑盒子的参数过程后,得到特定的输出结果y。
这一层层的黑盒子对应一个又一个的函数,共同构成我们的神经网络模型。经典的神经网络结构如下图:
那么为什么需要RNN呢?
为什么需要RNN
在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考,也就是说我们在学习新的东西的时候都是基于之前的知识。
但是传统的神经网络并不能做到这点。例如,假设你希望对电影中的每个时间点的时间类型进行分类。传统的神经网络应该很难来处理这个问题——使用电影中先前的事件推断后续的事件。
他们都只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的。
而RNN可以很好的解决这个问题。
RNN
循环神经网络(Recurrent Neural Network)是一种用于处理序列数据的神经网络。相比一般的神经网络来说,他能够处理序列变化的数据。
下面是传统的RNN的结构:(图片来自台大李宏毅教授)
x
x
x 为当前状态下数据的输入,
h
h
h表示接收到的上一个节点的输入。
y y y为当前节点状态下的输出,而 h ′ h' h′为传递到下一个节点的输出。
通过上图的公式可以看到,输出 h ′ h' h′与 x x x 和 h h h的值都相关。
而 y y y则常常使用 h ′ h' h′ 投入到一个线性层(主要是进行维度映射)然后使用softmax进行分类得到需要的数据。
对这里的 y y y如何通过 h ′ h' h′ 计算得到往往看具体模型的使用方式。
长期依赖(Long-Term Dependencies)问题
RNN 的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果 RNN 可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。
有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测 “the clouds are in the sky” 最后的词,我们并不需要任何其他的上下文 —— 因此下一个词很显然就应该是 sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN 可以学会使用先前的信息。
但是同样会有一些更加复杂的场景。假设我们试着去预测“I grew up in France… I speak fluent French”最后的词。当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的 France 的上下文的。这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。
不幸的是,在这个间隔不断增大时,RNN 会丧失学习到连接如此远的信息的能力。在理论上,RNN 绝对可以处理这样的 长期依赖 问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN 肯定不能够成功学习到这些知识。Bengio, et al. (1994)等人对该问题进行了深入的研究,他们发现一些使训练 RNN 变得非常困难的相当根本的原因。
LSTM
长短期记忆(Long short-term memory, LSTM)是一种特殊的RNN,主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。简单来说,就是相比普通的RNN,LSTM能够在更长的序列中有更好的表现。
相比RNN只有一个传递状态
h
t
h^t
ht ,LSTM有两个传输状态,一个
c
t
c^t
ct(cell state),和一个
h
t
h^t
ht (hidden state)。(Tips:RNN中的
h
t
h^t
ht对于LSTM中的
c
t
c^t
ct )
其中对于传递下去的 c t c^t ct 改变得很慢,通常输出的 c t c^t ct 是上一个状态传过来的 c t − 1 c^{t-1} ct−1 加上一些数值。
而 h t h^t ht则在不同节点下往往会有很大的区别。
深入了解LSTM
首先使用LSTM的当前输入
x
t
x^{t}
xt和上一个状态传递下来的
h
t
−
1
h^{t-1}
ht−1 拼接训练得到四个状态。
其中,
z
f
z^f
zf ,
z
i
z^i
zi ,
z
o
z^o
zo 是由拼接向量乘以权重矩阵之后,再通过一个sigmoid 激活函数转换成0到1之间的数值,来作为一种门控状态。而
z
z
z则是将结果通过一个tanh激活函数将转换成-1到1之间的值(这里使用tanh是因为这里是将其做为输入数据,而不是门控信号)。
下面开始进一步介绍这四个状态在LSTM内部的使用。
⊙
⊙
⊙ 是Hadamard Product,也就是操作矩阵中对应的元素相乘,因此要求两个相乘矩阵是同型的。
+
+
+ 则代表进行矩阵加法。
LSTM内部主要有三个阶段:
- 忘记阶段。这个阶段主要是对上一个节点传进来的输入进行选择性忘记。简单来说就是会 “忘记不重要的,记住重要的”。
具体来说是通过计算得到的 z f z^f zf(f表示forget)来作为忘记门控,来控制上一个状态的 c t − 1 c^{t-1} ct−1哪些需要留哪些需要忘。
-
选择记忆阶段。这个阶段将这个阶段的输入有选择性地进行“记忆”。主要是会对输入 x t x^t xt进行选择记忆。哪些重要则着重记录下来,哪些不重要,则少记一些。当前的输入内容由前面计算得到的 z z z表示。而选择的门控信号则是由 z i z^i zi(i代表information)来进行控制。
将上面两步得到的结果相加,即可得到传输给下一个状态的 c t c^t ct 。也就是上图中的第一个公式。
-
输出阶段。这个阶段将决定哪些将会被当成当前状态的输出。主要是通过 z o z^o zo 来进行控制的。并且还对上一阶段得到的 c o c^o co 进行了放缩(通过一个tanh激活函数进行变化)。
与普通RNN类似,输出
y
t
y^t
yt 往往最终也是通过
h
t
h^t
ht 变化得到。
总结:
以上,就是LSTM的内部结构。通过门控状态来控制传输状态,记住需要长时间记忆的,忘记不重要的信息;而不像普通的RNN那样只能够“呆萌”地仅有一种记忆叠加方式。对很多需要“长期记忆”的任务来说,尤其好用。
但也因为引入了很多内容,导致参数变多,也使得训练难度加大了很多。因此很多时候我们往往会使用效果和LSTM相当但参数更少的GRU来构建大训练量的模型。
GRU
1.什么是GRU
GRU(Gate Recurrent Unit)是循环神经网络(Recurrent Neural Network, RNN)的一种。和LSTM(Long-Short Term Memory)一样,也是为了解决长期记忆和反向传播中的梯度等问题而提出来的。
GRU和LSTM在很多情况下实际表现上相差无几,那么为什么我们要使用新人GRU(2014年提出)而不是相对经受了更多考验的LSTM(1997提出)呢。
下图1-1 MACHINE READING COMPREHENSION WITH SELF-MATCHING NETWORKS(2017)引用论文中的一段话来说明GRU的优势所在。
简单译文:我们在我们的实验中选择GRU是因为它的实验效果与LSTM相似,但是更易于计算。
简单来说就是贫穷限制了我们的计算能力…
相比LSTM,使用GRU能够达到相当的效果,并且相比之下更容易进行训练,能够很大程度上提高训练效率,因此很多时候会更倾向于使用GRU。
OK,那么为什么说GRU更容易进行训练呢,下面开始介绍一下GRU的内部结构。
2.GRU的结构
2.1GRU的输入输出结构
GRU的输入输出结构与普通的RNN是一样的。
有一个当前的输入 x t x^t xt ,和上一个节点传递下来的隐状态(hidden state) h t − 1 h^{t-1} ht−1,这个隐状态包含了之前节点的相关信息。
结合
x
t
x^t
xt 和
h
t
−
1
h^{t-1}
ht−1,GRU会得到当前隐藏节点的输出
y
t
y^t
yt和传递给下一个节点的隐状态
h
t
h^t
ht 。
2.2GRU的内部结构
首先,我们先通过上一个传输下来的状态
h
t
−
1
h^{t-1}
ht−1 和当前节点的输入
x
t
x^t
xt来获取两个门控状态。如下图2-2所示,其中
r
r
r 控制重置的门控(reset gate),
z
z
z 为控制更新的门控(update gate)。
得到门控信号之后,首先使用重置门控来得到“重置”之后的数据
h
t
−
1
′
=
h
t
−
1
⊙
r
h^{t-1'}=h^{t-1}⊙r
ht−1′=ht−1⊙r ,再将
h
t
−
1
′
h^{t-1'}
ht−1′与输入
x
t
x^t
xt进行拼接,再通过一个tanh激活函数来将数据放缩到-1~1的范围内。即得到如下图2-3所示的
h
′
h'
h′
这里的
h
′
h'
h′ 主要是包含了当前输入的
x
t
x^t
xt数据。有针对性地对
h
′
h'
h′ 添加到当前的隐藏状态,相当于”记忆了当前时刻的状态“。类似于LSTM的选择记忆阶段.
最后介绍GRU最关键的一个步骤,我们可以称之为”更新记忆“阶段。
在这个阶段,我们同时进行了遗忘了记忆两个步骤。我们使用了先前得到的更新门控
z
z
z(update gate)。更新表达式:
首先再次强调一下,门控信号(这里的
z
z
z )的范围为0~1。门控信号越接近1,代表”记忆“下来的数据越多;而越接近0则代表”遗忘“的越多。
GRU很聪明的一点就在于,我们使用了同一个门控
z
z
z就同时可以进行遗忘和选择记忆(LSTM则要使用多个门控)。
-
( 1 − z ) ⊙ h t − 1 (1-z)⊙h^{t-1} (1−z)⊙ht−1 :表示对原本隐藏状态的选择性“遗忘”。这里的 1 − z 1-z 1−z可以想象成遗忘门(forget gate),忘记 h t − 1 h^{t-1} ht−1维度中一些不重要的信息。
-
z ⊙ h ′ z⊙h' z⊙h′ : 表示对包含当前节点信息的 h ′ h' h′进行选择性”记忆“。与上面类似,这里的 1 − z 1-z 1−z同理会忘记 h ′ h' h′维度中的一些不重要的信息。或者,这里我们更应当看做是对 h ′ h' h′ 维度中的某些信息进行选择。
-
h t = ( 1 − z ) ⊙ h t − 1 + z ⊙ h ′ h^t=(1-z)⊙h^{t-1}+z⊙h' ht=(1−z)⊙ht−1+z⊙h′ :结合上述,这一步的操作就是忘记传递下来的 h t − 1 h^{t-1} ht−1中的某些维度信息,并加入当前节点输入的某些维度信息。
可以看到,这里的遗忘 z z z 和选择 1 − z 1-z 1−z 是联动的。也就是说,对于传递进来的维度信息,我们会进行选择性遗忘,则遗忘了多少权重 ( z z z),我们就会使用包含当前输入的 h ′ h' h′中所对应的权重进行弥补 ( 1 − z ) (1-z) (1−z) 。以保持一种”恒定“状态。
2.3LSTM与GRU的关系
\GRU是在2014年提出来的,而LSTM是1997年。他们的提出都是为了解决相似的问题,那么GRU难免会参考LSTM的内部结构。那么他们之间的关系大概是怎么样的呢?这里简单介绍一下。
大家看到 r r r(reset gate)实际上与他的名字有点不符。我们仅仅使用它来获得了 h ′ h' h′ 。
那么这里的 h ′ h' h′ 实际上可以看成对应于LSTM中的hidden state;上一个节点传下来的 h t − 1 h^{t-1} ht−1则对应于LSTM中的cell state。1-z对应的则是LSTM中的 z f z^{f} zf forget gate,那么 z我们似乎就可以看成是选择门 z i z^{i} zi了。大家可以结合我的两篇文章来进行观察,这是非常有趣的。
总结
GRU输入输出的结构与普通的RNN相似,其中的内部思想与LSTM相似。
与LSTM相比,GRU内部少了一个”门控“,参数比LSTM少,但是却也能够达到与LSTM相当的功能。考虑到硬件的计算能力和时间成本,因而很多时候我们也就会选择更加”实用“的GRU。