数学系的一线研发,关注 数据结构 | 深度学习 | 职场文章分享
前言
目录:
RNN提出的背景
- 一个问题
- 为什么不用标准神经网络
- RNN模型怎么解决这个问题
- RNN模型适用的数据特征
- RNN几种类型
RNN模型结构
- RNN block
- 简化符号表示
- stacked RNN
- 双向RNN
- 梯度消失爆炸问题
GRU模型结构
LSTM模型结构
- LSTM背后的关键思想
- Step by Step理解LSTM
本文可以解答:
-
RNN用来解决什么问题,什么样的数据特征适合用它来解决
-
RNN的缺陷是什么,LSTM,GRU是如何解决这些缺陷的
-
理解从简单RNN到LSTM的每个模型的结构
RNN提出背景/适用场景
一个问题
我们考虑一下这么一个问题,任意给定一句话,判断句子里的单词是不是人名的一部分。比如输入
x : Harry Potter and Hermione Granger invented a new spell.
单词有
['Harry', 'Potter', 'and', 'Hermione', 'Granger', 'invented', 'a', 'new', 'spell']
如果1表示单词是人名一部分,0表示不是人名的部分,那么输出的y就应该是
[1, 1, 0, 1, 1, 0, 0, 0, 0]
y的组成是和x等长,相同的index对应x中元素是不是人名。我们把x中的每个单词用one-hot向量来表征,如下所示。
为什么不用标准的神经网络?
如果我们用标准的神经网络来解决这个问题,标准神经网络的结构是这样的
标准神经网络的流程,将样本数据x一次性输入,从左边传到右边(输出层)。这样做不好的地方在于:
-
输入和输出的长度不固定,比如有的句子输入长度是10,有的句子长度就不是10。
-
如果使用标准网络,它不能共享文本不同位置学习到的东西,比如知道Harry是人名一部分,如果第i个位置又出现了Harray,它是不能自动识别的。
RNN模型怎么解决这个问题
RNN对数据的处理流程如上图所示,它不会同时输入数据。我们将首先输入X1(第一个单词Harray)得到预估值y1(Harray是不是人名一部分),然后将X1计算的结果传递到下一层与输入X2(第二个单词Potter)结合输出对X2的预测y2(Potter是不是人名一部分),这样y2的预测其实受X1的影响,以此类推。这样就解决了标准神经网络带来的问题。
RNN模型适用的数据特征
上面的句子有个特征:
-
States Dependency:当前的状态和上一步的状态是相关的;
-
数据是序列的(sequential): 当我们说一个数据是序列的,是指数据之间有时间顺序,它是在不同时间输入的,如果我们更改顺序,把句子中的单词打乱,它就变的不同失去含义了。能用标准神经网络训练的数据就是可以打乱顺序,所以我们可以在一开始就输入它们。
所以用RNN最大的动力就是它可以连接之前的数据,意味着这个模型关心之前的东西是什么,接下来将要出现什么。
RNN的几种类型
除了上面的例子,RNN根据不同的输入和输出数量,有以下几种类型:
one on one: 输入一张图片,输出图片中的活动。
one to many: 输入一张图片,输出描述图片中的内容,其中输出的内容前后是有依赖的,相关的,比如上面一张图就是‘一个人在跑步’,‘一个’和‘人’是相关的,如果‘一个’变了,那么‘人’可能也会变。
many to one: 情感分类,输入是一个句子,输出是评分,输入的前后是依赖相关的。
many to many: 翻译系统,将德语翻译成英语,输入和输出都是相关的。
many to many: 语音识别,传入一段语音,输出是转换成的文字。
RNN的模型结构
RNN block
我们的模型将会采用两个值,t时间处,X的输入值和前一个单元格 t-1 时间处的输出值A。左边是传统的神经网络模型,它在左边基础上是如何变成右边的?我们用第一个单元的运算来举例。
方程①:就像我们使用简单ANN所做的那样,它也存在权重Wax,Waa和偏差Ba。它只是增加一个输入值A0(前一个单元的输出值)。
单元有两个不同的输出: A1的输出(由公式②得到)将转到下一个单元,公式③的最终输出Y1。
所有权重的下标意思是,第一个下标是计算某类型的量,第二个下标是你要乘以的类型。比如Wax,它乘以的是X1,要计算的是A1,所以它的下标是ax。
现在,让我们进入下一个单元。整个过程如下图所示。
简化符号表示
但是为了简化符号,后面图中的公式都是简化的符号, 令:
于是:
我们将
变成
符号变了,但是意思是没有变的,只是为了方便简写,所以后面都是用简化后的公式。
Stacked RNN
如果把RNN堆在一起,输出的y又可以作为x输入到另一个神经网络中,那么它的结构就是这样的。
双向RNN
如果我们输入以下句子:
He said, “Teddy bears are on sale!”
模型可能会判断‘Teddy’是一个人名,是因为模型没有考虑之后句子的含义,所以,这时候又要考虑前面又要考虑后面,就需要双向的RNN,它的结构是这样的:
梯度消失/爆炸问题
我们看这样一个例子:
The cat,which already ate ...(中间省略很长很长), was(预测这个单词) full
The cats,which ate ...(中间省略很长很长), were(预测这个单词) full
当预测一个很长的句子,而预测的值依赖于很前面的信息(cat/cats),RNN就难以预测,这就是RNN不擅长处理的长依赖关系。为什么呢?
关键问题就在于向后传播。
向后传播的目的是在每一层更新权重,为了更新权重,我们将计算损失函数的梯度,并且因为链式法则,会把多个梯度相乘。试想一下,如果梯度大于1,那么多个梯度相乘将会使得数值比较大,用它来更新优化权重会比较大,这种情况我们叫做梯度爆炸(exploding gradients),这还不是大问题,因为我们可以做个梯度修剪,如果大于某个值就缩放就行了;真正的问题是,如果梯度小于1,那么连乘梯度将使得结果非常的小,这样它就不能更新权重,输出的结果就没有什么不同,这样的情况我们叫梯度消失(vanishing gradients),这就表明向后传播是不能影响很前面层的。
这就是人们说RNN的记忆不好,如果输入长度太长,我们就不能预测实际的值,造成这样结果最根本原因就是神经网络中的权重更新。
那么我们怎么解决这个问题呢?
一个思想就是,与其记住所有的过去,不如选择性的记住重要的信息而忽略其它信息。
GRU模型结构
如何做到选择性的记忆之前的信息?关键思想是提出了一个门控循环单元(Units Gated Recurrent Units),用它来控制重要信息。
注: 图中的tanh就是上面图中激活函数g的具体化函数。
GRU相对于RNN,多了一个绿色的框框,这个框框就是门控制器,我们把它叫做更新门,用来决定记住过去的信息多少的,提供模型的记忆能力。
GRU第一步也是和RNN第一步一样,用激活函数激活,但是它不会马上被使用,而是作为候选值静静等待。接着计算GRU更新门的值,由于它采用的是sigmoid函数,所以它的输出值范围是在0到1之间的。所以将第2步的值乘以第1步的值实际上就是在做"选择性记忆",如果是0,那么就是“不记忆/不更新/不使用”,如果是1,那么就是"记忆/更新/使用",就像一个打开关闭的门。GRU第三步就是用更新门的值来决定当前是用新的计算值还是用之前的值。
它比接下来讲的LSTM要简单,参数要少,更少的参数就意味着减少过拟合,减少训练时间。
LSTM的模型结构
LSTM背后的关键思想
LSTM的全称是Long Short Term Memory networks,LSTM被明确设计为避免长期依赖问题。长时间记住信息是它的默认行为,而不是它努力学习的东西。LSTM的关键是单元状态(cell state),最上面那条水平线。图中C表示,它将包含放在隐藏状态值之外的记忆值。它有点像是传送带,仅仅在一些微小的线性变换下贯穿整个链条,信息不加改变地流动非常容易。
LSTM可以通过门这种结构给cell state移除或增加信息。门能够选择性让信息通过,它由sigmoid神经网络层和逐点乘法操作符组成。sigmoid层输出0到1的数字,描述每个组件应该让它通过多少。数值0表示都不通过,1表示让它全部通过。
LSTM有三个门来保护和控制cell state的状态。整个模型结构如下图
图中的三个绿色框框从左到右依次是忘记门(forget gate),更新门(update gate),输出门(output gate)。
Step by Step理解LSTM
先整体看一下LSTM和GRU的不同:
让我们看看右边,前两行其实就是和GRU一样,第3行等式,它是计算忘记门的值,它用于更新单元状态(第四行等式),最后第5行是计算输出门,用于得到第6行A在t时刻的值。
第一步就是决定我们要从cell state中丢弃哪些信息。这个决定是由叫‘forget gate layer’的sigmoid层指定的。它看起来像输入ht-1,xt,然后为每个ct-1(t-1时刻的cell state)变量中的数据输出0到1之间的数字。1就是保留全部,0是全部不要。
下一步是确定要在cell state下存储哪些新信息。
这一步包括两个部分:
-
首先,称为“更新门”的sigmoid层决定了我们将更新哪些值。
-
接下来,tanh层创建新候选值(可以添加到cell state中的值)C̃ t向量。
下一步将会结合这两个值去对状态进行更新。
这一步是更新cell state。
我们将旧状态值和忘记门相乘,目的是忘记该忘记的信息,只记住重要的信息;然后用更新门和候选状态相乘,目的是决定到底我们需要多少新状态值的信息。
最后,我们需要决定要输出什么。
这个输出是在cell state基础上计算的,但是是它的过滤版本。首先我们用sigmoid层来确定要输出当前cell state的哪些部分;接着我们把结果送到tanh并和输出门相乘,这样我们就可以只输出我们期望的那部分结果。
因为有这三个额外的门,LSTM有着更强的记忆能力,它们控制着哪个部分需要记住,记住多少,所以它在处理序列模型的时候很受欢迎。
ps: 本来想一次性用一篇文章写完的,后来发现理论+代码这个工程真有点大,就分成两篇。
参考资料:
-
吴恩达deeplearning.ai第五门课 Module 1: Recurrent Neural Networks (RNNs)
-
The Most Intuitive and Easiest Guide for Recurrent Neural Network
-
Must-Read Tutorial to Learn Sequence Modeling (deeplearning.ai Course #5)
-
2015-08-Understanding-LSTMs
今日互动
文首的问题你想清楚了吗?