循环神经网络(Recurrent Neural Network,RNN)
为什么使用序列模型(sequence model)?标准的全连接神经网络(fully connected neural network)处理序列会有两个问题:1)全连接神经网络输入层和输出层长度固定,而不同序列的输入、输出可能有不同的长度,选择最大长度并对短序列进行填充(pad)不是一种很好的方式;2)全连接神经网络同一层的节点之间是无连接的,当需要用到序列之前时刻的信息时,全连接神经网络无法办到,一个序列的不同位置之间无法共享特征。而循环神经网络(Recurrent Neural Network,RNN)可以很好地解决问题。
1. 序列数据
在介绍循环神经网络之前,先来了解一些序列数据:
图 1:序列数据
输入 xx 和输出 yy 可能都是序列,也可能只有一个是序列;输入 xx 和输出 yy 两个序列的长度可能相等也可能不等。下文介绍循环神经网络结构时,输入 xx 和输出 yy 两个序列的长度相同。(下文出现的 hh 和最终的输出结果 yy 在本文中被认为不相同)
2. 循环神经网络的结构
图 2 展示了一个经典的循环神经网络(RNN):(这张图初次看真的让人很懵)
图 2:循环神经网络经典结构示意图
对于 RNN,一个非常重要的概念就是时刻。RNN 会对每一个时刻的输入结合当前模型的状态给出一个输出。图 2 中,tt 时刻 RNN 的主体结构 A 的输入除了来自输入层 XtXt,还有一个循环的边来提供从 t−1t−1 时刻传递来的隐藏状态。(这一段看不懂没关系,看下面)
RNN 可以被看作是同一个神经网络结构按照时间序列复制的结果。图 3 展示了一个展开的 RNN。(展开图相对比较好理解)
图 3:循环神经网络按时间展开
从 RNN 的展开结构可以很容易得出它最擅长解决的问题是与时间序列相关的。RNN 也是处理这类问题时最自然的神经网络结构。
RNN 的主体结构 A 按照时间序列复制了多次,结构 A 也被称之为循环体。如何设计循环体 A 的网络结构是 RNN 解决实际问题的关键。和卷积神经网络(CNN)过滤器中参数共享类似,在 RNN 中,循环体 A 中的参数在不同时刻也是共享的。
图 4 展示了一个最简单的使用单个全连接层作为循环体 A 的 RNN,图中黄色的 tanh 小方框表示一个使用 tanh 作为激活函数的全连接层。
图 4:使用单层全连接神经网络作为循环体的 RNN 结构图
图 5:图 4 中各种符号代表的含义
(注:Pointwise Operation在图 4 中没有出现)
tt 时刻循环体 A 的输入包括 XtXt 和从 t−1t−1 时刻传递来的隐藏状态 ht−1ht−1(依据图 5 copy 标志,t−1t−1 和 tt时刻循环体 A 之间连接的箭头即表示隐藏状态 ht−1ht−1 的传递)。循环体 A 的两部分输入如何处理呢?依据图 5 ,将 XtXt 和 ht−1ht−1 直接拼接起来,成为一个更大的矩阵/向量 [Xt,ht−1][Xt,ht−1]。假设 XtXt 和 ht−1ht−1 的形状分别为 [1, 3] 和 [1, 4],则最后循环体 A 中全连接层输入向量的形状为 [1, 7]。拼接完后按照全连接层的方式进行处理即可。
为了将当前时刻的隐含状态 htht 转化为最终的输出 ytyt,循环神经网络还需要另一个全连接层来完成这个过程。这和卷积神经网络中最后的全连接层意义是一样的。(如果不考虑 RNN 的输出还需要一个全连接层的情况,那么 htht 和 ytyt 的值是一样的)
RNN 的前向传播计算过程如下图所示:
图 6:RNN的前向传播计算过程示意图
图 6 很清晰地向我们展示了 RNN 循环体 A 中具体的计算流程,以及当前隐藏状态 htht 转化为最终输出 ytyt的过程。
3. 循环神经网络的类型
图 7:RNN 的类型
(1)one to one:其实和全连接神经网络并没有什么区别,这一类别算不得是 RNN。
(2)one to many:输入不是序列,输出是序列。
(3)many to one:输入是序列,输出不是序列。
(4)many to many:输入和输出都是序列,但两者长度可以不一样。
(5)many to many:输出和输出都是序列,两者长度一样。
4. 基础循环神经网络的局限
上述图片中展示的都是单向的 RNN,单向 RNN 有个缺点是在 tt 时刻,无法使用 t+1t+1 及之后时刻的序列信息,所以就有了双向循环神经网络(bidirectional RNN)。
“需要特别指出,理论上循环神经网络可以支持任意长度的序列,然而在实际中,如果序列过长会导致优化时出现梯度消散的问题(the vanishing gradient problem),所以实际中一般会规定一个最大长度,当序列长度超过规定长度之后会对序列进行截断。”
RNN 面临的一个技术挑战是长期依赖(long-term dependencies)问题,即当前时刻无法从序列中间隔较大的那个时刻获得需要的信息。在理论上,RNN 完全可以处理长期依赖问题,但实际处理过程中,RNN 表现得并不好。
但是 GRU 和 LSTM 可以处理梯度消散问题和长期依赖问题。
5. 门控循环单元(Gated Recurrent Unit,GRU)和 长短时记忆网络(Long Short Term Memory,LSTM)
相比于基础的RNN,GRU 和 LSTM 与之不同的地方在于循环体 A 的网络结构。
GRU 和 LSTM 都引入了一个的概念,门(gate)。GRU 有两个“门”(“更新门”和“重置门”),而 LSTM 有三个“门”(“遗忘门”、“输入门”和“输出门”)。
图 8:LSTM
图 9:GRU
GRU 和 LSTM 靠一些“门”的结构让信息有选择地影响循环神经网络中每个时刻的状态。所谓“门”结构,就是一个使用 sigmoid 的全连接层和一个按位做乘法的操作,这两个操作合起来就是一个“门”结构,如图 9所示。
图 9:“门”结构
之所以叫“门”结构,是因为使用 sigmoid 作为激活函数的全连接神经网络层会输出一个 0 到 1 之间的数值,描述当前输入有多少信息量可以通过这个结构。于是这个结构的功能就类似于一扇门,当门打开时(sigmoid 全连接层输出为 1 时),全部信息可以通过;当门关上时(sigmoid 神经网络层输出为 0 时),任何信息都无法通过。
LSTM 有三个门,分别是“遗忘门”(forget gate)、“输入门”(input gate)和“输出门”(output gate)。“遗忘门”的作用是让循环神经网络“忘记”之前没有用的信息。“输入门”决定哪些信息进入当前时刻的状态。通过“遗忘门”和“输入门”,LSTM 结构可以很有效地决定哪些信息应该被遗忘,哪些信息应该得到保留。LSTM 在得到当前时刻状态 CtCt 之后,需要产生当前时刻的输出,该过程通过“输出门”完成。
GRU 的两个门:一个是“更新门”(update gate),它将 LSTM 的“遗忘门”和“输入门”融合成了一个“门”结构;另一个是“重置门”(reset gate)。“从直观上来说,‘重置门’决定了如何将新的输入信息与前面的记忆相结合,‘更新门’定义了前面记忆保存到当前时刻的量。” “那些学习捕捉短期依赖关系的单元将趋向于激活‘重置门’,而那些捕获长期依赖关系的单元将常常激活‘更新门’。”
LSTM 和 GRU 更多内容,请参考 Understanding LSTM Networks 和 机器之心GitHub项目:从循环到卷积,探索序列建模的奥秘。
(注:文中出现的字母 hh 表示 hidden state, CC 表示 cell state。)
原始RNN结构图:
长短时记忆网络的思路比较简单。原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常敏感。那么,假如我们再增加一个状态,即c,让它来保存长期的状态,那么问题不就解决了么?如下图所示:
新增加的状态c,称为单元状态(cell state)。我们把上图按照时间维度展开:
LSTM ——是一种特殊 RNN 类型,可以学习长期依赖信息。LSTM 由Hochreiter & Schmidhuber (1997)提出,并在近期被Alex Graves进行了改良和推广。在很多应用问题,LSTM 都取得相当巨大的成功,并得到了广泛的使用。然而LSTM结构复杂,初学者难于理解,本文通过动图形象直观的理解LSTM.
短时记忆
RNN 会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步。 因此,如果你正在尝试处理一段文本进行预测,RNN 可能从一开始就会遗漏重要信息。
在反向传播期间,RNN 会面临梯度消失的问题。 梯度是用于更新神经网络的权重值,消失的梯度问题是当梯度随着时间的推移传播时梯度下降,如果梯度值变得非常小,就不会继续学习。
梯度更新规则
因此,在递归神经网络中,获得小梯度更新的层会停止学习—— 那些通常是较早的层。 由于这些层不学习,RNN 可以忘记它在较长序列中看到的内容,因此具有短时记忆。
作为解决方案的 LSTM 和 GRU
LSTM 和 GRU 是解决短时记忆问题的解决方案,它们具有称为“门”的内部机制,可以调节信息流。
这些“门”可以知道序列中哪些重要的数据是需要保留,而哪些是要删除的。 随后,它可以沿着长链序列传递相关信息以进行预测,几乎所有基于递归神经网络的技术成果都是通过这两个网络实现的。
LSTM 和 GRU 可以在语音识别、语音合成和文本生成中找到,你甚至可以用它们为视频生成字幕。对 LSTM 和 GRU 擅长处理长序列的原因,到这篇文章结束时你应该会有充分了解。
下面我将通过直观解释和插图进行阐述,并避免尽可能多的数学运算。
本质
让我们从一个有趣的小实验开始吧。当你想在网上购买生活用品时,一般都会查看一下此前已购买该商品用户的评价。
当你浏览评论时,你的大脑下意识地只会记住重要的关键词,比如“amazing”和“awsome”这样的词汇,而不太会关心“this”、“give”、“all”、“should”等字样。如果朋友第二天问你用户评价都说了什么,那你可能不会一字不漏地记住它,而是会说出但大脑里记得的主要观点,比如“下次肯定还会来买”,那其他一些无关紧要的内容自然会从记忆中逐渐消失。
而这基本上就像是 LSTM 或 GRU 所做的那样,它们可以学习只保留相关信息来进行预测,并忘记不相关的数据。
RNN 述评
为了了解 LSTM 或 GRU 如何实现这一点,让我们回顾一下递归神经网络。 RNN 的工作原理如下;第一个词被转换成了机器可读的向量,然后 RNN 逐个处理向量序列。
逐一处理矢量序列
处理时,RNN 将先前隐藏状态传递给序列的下一步。 而隐藏状态充当了神经网络记忆,它包含相关网络之前所见过的数据的信息。
将隐藏状态传递给下一个时间步
让我们看看 RNN 的一个细胞,了解一下它如何计算隐藏状态。 首先,将输入和先前隐藏状态组合成向量, 该向量包含当前输入和先前输入的信息。 向量经过激活函数 tanh之后,输出的是新的隐藏状态或网络记忆。
激活函数 Tanh
激活函数 Tanh 用于帮助调节流经网络的值。 tanh 函数将数值始终限制在 -1 和 1 之间。
当向量流经神经网络时,由于有各种数学运算的缘故,它经历了许多变换。 因此想象让一个值继续乘以 3,你可以想到一些值是如何变成天文数字的,这让其他值看起来微不足道。
没有 tanh 函数的向量转换
tanh 函数确保值保持在 -1~1 之间,从而调节了神经网络的输出。 你可以看到上面的相同值是如何保持在 tanh 函数所允许的边界之间的。
有 tanh 函数的向量转换
这是一个 RNN。 它内部的操作很少,但在适当的情形下(如短序列)运作的很好。 RNN 使用的计算资源比它的演化变体 LSTM 和 GRU 要少得多。
LSTM
LSTM 的控制流程与 RNN 相似,它们都是在前向传播的过程中处理流经细胞的数据,不同之处在于 LSTM 中细胞的结构和运算有所变化。
LSTM 的细胞结构和运算
这一系列运算操作使得 LSTM具有能选择保存信息或遗忘信息的功能。咋一看这些运算操作时可能有点复杂,但没关系下面将带你一步步了解这些运算操作。
核心概念
LSTM 的核心概念在于细胞状态以及“门”结构。细胞状态相当于信息传输的路径,让信息能在序列连中传递下去。你可以将其看作网络的“记忆”。理论上讲,细胞状态能够将序列处理过程中的相关信息一直传递下去。
因此,即使是较早时间步长的信息也能携带到较后时间步长的细胞中来,这克服了短时记忆的影响。信息的添加和移除我们通过“门”结构来实现,“门”结构在训练过程中会去学习该保存或遗忘哪些信息。
Sigmoid
门结构中包含着 sigmoid 激活函数。Sigmoid 激活函数与 tanh 函数类似,不同之处在于 sigmoid 是把值压缩到 0~1 之间而不是 -1~1 之间。这样的设置有助于更新或忘记信息,因为任何数乘以 0 都得 0,这部分信息就会剔除掉。同样的,任何数乘以 1 都得到它本身,这部分信息就会完美地保存下来。这样网络就能了解哪些数据是需要遗忘,哪些数据是需要保存。
Sigmoid 将值压缩到 0~1 之间
接下来了解一下门结构的功能。LSTM 有三种类型的门结构:遗忘门、输入门和输出门。
遗忘门
遗忘门的功能是决定应丢弃或保留哪些信息。来自前一个隐藏状态的信息和当前输入的信息同时传递到 sigmoid 函数中去,输出值介于 0 和 1 之间,越接近 0 意味着越应该丢弃,越接近 1 意味着越应该保留。
遗忘门的运算过程
输入门
输入门用于更新细胞状态。首先将前一层隐藏状态的信息和当前输入的信息传递到 sigmoid 函数中去。将值调整到 0~1 之间来决定要更新哪些信息。0 表示不重要,1 表示重要。
其次还要将前一层隐藏状态的信息和当前输入的信息传递到 tanh 函数中去,创造一个新的侯选值向量。最后将 sigmoid 的输出值与 tanh 的输出值相乘,sigmoid 的输出值将决定 tanh 的输出值中哪些信息是重要且需要保留下来的。
输入门的运算过程
细胞状态
下一步,就是计算细胞状态。首先前一层的细胞状态与遗忘向量逐点相乘。如果它乘以接近 0 的值,意味着在新的细胞状态中,这些信息是需要丢弃掉的。然后再将该值与输入门的输出值逐点相加,将神经网络发现的新信息更新到细胞状态中去。至此,就得到了更新后的细胞状态。
细胞状态的计算
输出门
输出门用来确定下一个隐藏状态的值,隐藏状态包含了先前输入的信息。首先,我们将前一个隐藏状态和当前输入传递到 sigmoid 函数中,然后将新得到的细胞状态传递给 tanh 函数。
最后将 tanh 的输出与 sigmoid 的输出相乘,以确定隐藏状态应携带的信息。再将隐藏状态作为当前细胞的输出,把新的细胞状态和新的隐藏状态传递到下一个时间步长中去。
输出门的运算过程
让我们再梳理一下。遗忘门确定前一个步长中哪些相关的信息需要被保留;输入门确定当前输入中哪些信息是重要的,需要被添加的;输出门确定下一个隐藏状态应该是什么。
代码示例
对于那些懒得看文字的人来说,代码也许更好理解,下面给出一个用 python 写的示例。
python 伪代码
过程解释:
1.首先,我们将先前的隐藏状态和当前的输入连接起来,这里将它称为 combine;
2.其次将 combine 丢到遗忘层中,用于删除不相关的数据;
3.再用 combine 创建一个候选层,候选层中包含着可能要添加到细胞状态中的值;
4.combine 同样要丢到输入层中,该层决定了候选层中哪些数据需要添加到新的细胞状态中;
5.接下来细胞状态再根据遗忘层、候选层、输入层以及先前细胞状态的向量来计算;
6.再计算当前细胞的输出;
7.最后将输出与新的细胞状态逐点相乘以得到新的隐藏状态。
总结
总而言之,RNN适用于处理序列数据和预测任务,但会受到短期记忆的影响。LSTM通过引入门结构来减弱短期记忆影响的演化变体,其中门结构可用来调节流经序列链的信息流。目前,LSTM经常被用于语音识别、语音合成和自然语言理解等多个深度学习应用中。
原文地址:https://baijiahao.baidu.com/s?id=1615277690985871241&wfr=spider&for=pc