3.5 循环神经网络
循环神经网络(Recurrent Neural Network,RNN)在处理顺序或时间数据时十分有效,在特定时间或位置的这些数据与上一个时间或位置的数据强相关。
3.5.1 普通循环神经网络
RNN在处理文本数据方面非常成功,因为给定位置的单词与它前一个单词有很大的相关性。在RNN的每一个时间步长内(Time Step)都执行相同的处理操作,因此用循环对RNN命名。如图3-20所示为RNN架构图。
图3-20 RNN架构图
在每个给定的时间步长t上,根据之前在第t-1步的状态ht-1和输入xi计算得出记忆状态ht。新的状态ht用来预测第t步的输出ot。RNN的核心公式为
如果预测一个句子的下一个单词,那么函数f2通常是针对词汇表中所有单词的一个softmax函数。函数f1可以是任意的激活函数。
在RNN中,第t步的输出误差试图通过传递前几步的误差来修正前一步的预测。这有助于RNN学习距离较远单词之间的长度依赖关系。在现实中,由于梯度消失和梯度爆炸问题,很难通过RNN学习这么长的依赖关系。
我们已经知道,神经网络通过梯度下降进行学习,对于在第t个时间步长上的单词与在之前第k个时间步长上的单词之间的关系,可以通过记忆状态相对记忆状态(∀i)的梯度来学习,如式(3-1)所示。
对于连接第k步记忆状态和第k+1步记忆状态的权值,等式(3-2)成立。
在上面的公式中,是记忆状态在第k+1步的总输入,如式(3-3)所示。
到此很容易看出为什么梯度消失问题会发生在一个CNN里面。从式(3-1)和式(3-2)可以得出
对于RNN而言,函数通常是Sigmoid函数或tanh函数,这两个函数的输入在超过一定范围后会有很低的梯度,即出现饱和问题。现在,由于的导数相乘,如果激活函数的输入在饱和区,即便t-k的值不大,那么梯度也可能会变成零。即使函数不在饱和区,函数对Sigmoid函数的梯度也会总是小于1,因此很难学习到一个序列中单词之间的远程距离依赖关系。相似地,可能会引起梯度爆炸问题。假设第t步和第k步之间的距离大约是10,而权值的值大约是2,在这种情况下,梯度会被放大,从而导致梯度爆炸问题。
【例3-8】 用RNN学习二进制加法,实现:(1)学习当前位的加法;(2)学习前一位的进位。
运行程序,输出如下:
3.5.2 长短期记忆单元
梯度消失问题在一定程度上可以通过一个改进版本的RNN解决,它叫长短期记忆(Long Short-Term Memory,LSTM)单元。长短期记忆单元的架构如图3-21所示。
图3-21 LSTM单元架构
除了在学习RNN时知道的记忆状态,LSTM单元还引入了单元状态。单元状态由3个门控制:遗忘门、更新门和输出门。遗忘门决定了从之前的单元状态保留多少信息,它的输出为
更新门的输出为
潜在的候选新单元状态可以表示为
根据之前的单元状态和当前的潜在单元状态,更新后的单元状态输出可以从式(3-8)得到。
单元状态的所有信息不会全部被遗传至下一步,而由输出门决定单元状态的多少信息可以输出到下一步。输出门的输出为
基于当前的单元状态和输出门,更新后的记忆状态被传递到下一步。
现在存在一个问题:LSTM单元如何避免梯度消失的问题。在LSTM单元中,等价于,后者可以用式(3-10)的乘积形式表达。
此时,单元状态中的循环为
从上面的公式可以得到
结果,梯度表达式变成式(3-13)的形式
可以看到,如果我们保持遗忘单元状态接近1,那么梯度将几乎没有衰减,因此LSTM单元不会导致梯度消失问题。
【例3-9】 利用Python实现单层和多层LSTM单元。
运行程序,输出如下: