本文为SIGAI原创文章,仅供个人学习使用,未经允许,不能用于商业目的
欢迎搜索关注微信公众号SIGAI获取更多原创干货
导言
序列数据建模
全连接网络和卷积网络在运行时每次接收的都是独立的输入数据,没有记忆能力。在有些应用中需要神经网络具有记忆功能,典型的是时间序列预测问题,时间序列可以抽象的表示为一个向量序列:
语音识别与自然语言处理的问题是这类序列预测问题的典型代表。前者的输入是一个时间序列的语音信号;后者是文字序列。下面我们用一个实际例子来说明序列预测问题。假设神经网络要用来完成汉语填空,考虑下面这个句子:
现在已经半夜12点了,我非常困,想回家__。
最佳答案是“睡觉”或者“休息”,这个答案需要根据上下文理解得到。在这里,神经网络每次的输入为一个词,最后要填出这个空,这需要网络能够理解语义,并记住之前输入的信息即语句上下文。这里需要神经网络具有记忆功能,能够根据之前的输入词序列计出当前使用哪个词的概率最大。如何设计一个神经网络满足上面的要求?答案就是我们接下来要介绍的循环神经网络。
循环层的工作原理
循环神经网络(简称RNN)[1]会记住网络在上一个时刻的输出值,并将该值用于当前时刻输出值的生成,这由循环层实现。RNN的输入为前面介绍的向量序列,每个时刻接收一个输入,网络会产生一个输出,而这个输出是由之前的序列共同作用决定的。假设t时刻循环层的状态值为 ,它由上一时刻的状态值以及当前时刻的输入值共同决定,即:
![](https://i-blog.csdnimg.cn/blog_migrate/f8f63f0c1348e323bf67a3b53dbb8372.png)
![](https://i-blog.csdnimg.cn/blog_migrate/18de35fdae8757d01182adfdeba8beb5.png)
其中W为权重矩阵,b为偏置向量。和全连接神经网络相比,这里只是多了一个项:
![](https://i-blog.csdnimg.cn/blog_migrate/54be8629a8164b20f99446131d7d47ce.png)
它意味着在实现循环神经网络的时候需要用变量记住隐含层上次的输出值。使用激活函数的原因在SIGAI之前公众号的文章中介绍过,是为了确保非线性。下面我们用示意图来表示一个隐含层的变换:
![](https://i-blog.csdnimg.cn/blog_migrate/487d4785a7f0d2e9f9be4ea28f648818.png)
在上图中 和
共同决定
,
体现了记忆功能,而它的值又是由
和
决定。因此
的值实际上是由
决定的,它记住了之前完整的序列信息。需要强调的是,权重矩阵
并不会随着时间变化,而是固定的,即在每个时刻进行计算时使用的是同一个矩阵。这样做的好处一方面是减少了模型参数,另一方面也记住了之前的信息。
如果把每个时刻的输入和输出值按照时间线展开,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/f63e82f69a7e58350563da84a4730965.png)
网络结构
最简单的循环神经网络由一个输入层,一个循环层,一个输出层组成。输出层接收循环层的输出值作为输入并产生输出,它不具有记忆功能。输出层实现的变换为:
![](https://i-blog.csdnimg.cn/blog_migrate/130b71e3bc68ad9017b104d53561a6e4.png)
函数g的类型根据任务而定,对于分类任务一般选用softmax函数,输出各个类的概率。结合循环层和输出层,循环神经网络完成的变换为:
![](https://i-blog.csdnimg.cn/blog_migrate/105deb9b140fbf8153664888f7f68a96.png)
在这里只使用了一个循环层和一个输出层,实际使用时可以有多个循环层,即深度循环神经网络,在下一节中会详细介绍。
深层网络
上面我们介绍的循环神经网络只有一个输入层,一个循环层和一个输出层,这是一个浅层网络。和全连接网络以及卷积网络一样,我们可以把它推广到任意多个隐含层的情况,得到深度循环神经网络[11]。
这里有3种方案,第一种方案为Deep Input-to-Hidden Function,在循环层之前加入多个普通的前馈层,将输入向量进行多层映射之后再送入循环层进行处理。
第二种方案是Deep Hidden-to-Hidden Transition,它使用多个循环层,这和前馈型神经网络类似,唯一不同的是计算隐含层输出的时候需要利用本隐含层在上一个时刻的输出值。
第三种方案是Deep Hidden-to-Output Function,它在循环层到输出层之间加入多前馈层,这和第一种情况类似。
由于循环层一般用tanh作为激活函数,层次过多之后会导致梯度消失问题,和残差网络类似,可以采用跨层连接的方案。在语音识别、自然语言处理问题上,我们会看到深层循环神经网络的应用,实验结果证明深层网络比浅层网络有更好的精度。
训练算法
前面我们介绍了循环神经网络的结构,接下来要解决的问题是网络的参数如何通过训练确定。由于循环神经网络的输入是时间序列,因此每个训练样本是一个时间序列,包含多个相同维度的向量。解决循环神经网络训练问题的算法是Back Propagation Through Time算法,简称BPTT[2-4],原理和标准的反向传播算法类似,都是建立误差项的递推公式,根据误差项计算出损失函数对权重矩阵、偏置向量的梯度值。不同的是,全连接神经网络中递推是在层之间建立的,而这里是沿着时间轴建立的。限于篇幅,在这里我们不详细介绍和推导BPTT的原理,如果有机会,SIGAI会在后续的公众号文章中给出。
挑战与改进措施
循环神经网络与其他类型的神经网络共同要面对的是梯度消失问题,对此出现了一些解决方案,如LSTM等。相比卷积神经网络,循环神经网络在结构上的改进相对要少一些。
梯度消失问题
和前馈型神经网络一样,循环神经网络在进行梯度反向传播时也面临着梯度消失和梯度爆炸问题,只不过这种消逝问题表现在时间轴上,即如果输入序列的长度很长,我们很难进行有效的梯度更新。对这一问题的解释和理论分析,SIGAI会在以后的文章中给出。文献[5]对循环神经网络难以训练的问题进行了分析。进一步的,文献[6]对这一问题作出了更深的解释,并给出了一种解决方案。
LSTM
长短期记忆模型(long short time memory,简称LSTM)由Schmidhuber等人在1997年提出[7],与高速公路网络(highway networks)有异曲同工之妙。它对循环层进行改造,具体方法是使用输入门、遗忘门、输出门3个元件,通过另外一种方式由 计算
。LSTM的基本单元称为记忆单元,它记住了上一个时刻的状态值。记忆单元在t时刻维持一个记忆值 ,循环层状态的输出值计算公式为:
![](https://i-blog.csdnimg.cn/blog_migrate/c40663a163e510a8c4199e2897a5d61e.png)
即输出门与状态值的乘积,在这里是向量对应元素相乘。其中 为输出门,是一个向量,按照如下公式计算:
![](https://i-blog.csdnimg.cn/blog_migrate/1fd2b4a103fe0a7f958bb52be3015a04.png)
其中 为sigmoid函数,后面公式中含义相同。输出门控制着记忆单元中存储的记忆值有多大比例可以被输出。使用sigmoid函数这是因为它的值域是(0,1),这样
的所有分量的取值范围都在0和1之间,它们分别与另外一个向量的分量相乘,可以控制另外一个向量的输出比例。分别为输出门的权重矩阵和偏置向量。
是输出门的权重矩阵和偏置项,这里参数通过训练得到。
记忆值 是循环层神经元记住的上一个时刻的状态值,随着时间进行加权更新,它的计算公式为:
![](https://i-blog.csdnimg.cn/blog_migrate/6b1edc926ac373e3d1060d2280428018.png)
其中 是遗忘门,
是记忆单元在上一时刻的值,遗忘门决定了记忆单元上一时刻的值有多少会被传到当前时刻。上式表明,记忆单元当前值是上时刻值与当前输入值的加权和,记忆值只是个中间值。遗忘门的计算公式为:
这里也使用了sigmoid函数,
分别为遗忘门的权重矩阵和偏置向量。
是输入门,控制着当前时刻的输入有多少可以进入记忆单元,其计算公式为:
![](https://i-blog.csdnimg.cn/blog_migrate/4ce662facf0c54235644eaceec678ca5.png)
其中 分别为输入门的权重矩阵和偏置向量。这3个门的计算公式都是一样的,分别使用了自己的权重矩阵和偏置向量,这3个值的计算都用到了
和
,它们起到了信息的流量控制作用。
隐含层的状态值由遗忘门,记忆单元上一时刻的值,以及输入门,输出门共同决定。除掉3个门之外,真正决定 的只有
和
。总结起来,LSTM的计算思路为:
输入门作用于输入信息,遗忘门作用于之前的记忆信息,二者加权和,得到汇总信息;最后通过输出门决定输出信息。
所有的权重矩阵,偏置向量都通过训练得到,这和普通的循环神经网络没有区别,根据BPTT算法,我们可以得到这些参数的梯度值,在这里不详细介绍。
GRU
门控循环单元[8](Gated Recurrent Units,简称GRU)是解决循环神经网络梯度消失的另外一种方法,它也是通过门来控制信息的流动。和LSTM不同的是,它只使用了两个门,把LSTM的输入门和遗忘门合并成更新门。在这里我们不详细介绍计算公式,感兴趣的读者可以阅读参考文献。
双向网络
前面介绍的循环神经网络是单向的,每一个时刻的输出依赖于比它早的时刻的输入值,这没有利用未来时刻的信息,对于有些问题,当前时刻的输出不仅与过去时刻的数据有关,还与将来时刻的数据有关,为此Schuster等人设计了双向循环神经网络[9],它用两个不同的循环层分别从正向和反向对数据进行扫描。正向传播时的流程为:
1.循环,对t=1,...T
用正向循环层进行正向传播,记住每一个时刻的输出值
结束循环
2.循环,对对t=T,...1
用反向循环层进行正向传播,记住每一个时刻的输出值
结束循环
3.循环,对所有的t,可以按照任意顺序进行计算
用正向和反向循环层的输出值作为输出层的输入,计算最终的输出值
结束循环
下面用一个简单的例子来说明,假设双向循环神经网络的输入序列为 。首先用第一个循环层进行正向迭代,得到隐含层的正向输出序列:
在这里
由x1决定,
由x1,x2决定,
由x1 , . . . , x3决定,
由x1 , . . . , x4 决定。即每个时刻的状态值由到当前时刻为止的所有输入值序列决定,这里利用的是序列的过去信息。然后用第二个循环层进行反向迭代,输入顺序是x4 , ..., x1,得到隐含层的反向输出序列:
在这里,由x4决定,