小修前文已经详细的介绍过神经网络的相关概念以及自然语言处理过程中相关的方法,下面介绍在众多自然语言处理中取得巨大成功以及广泛应用的循环神经网络模型(Recurrent Neural Networks, RNNs).
1. 什么是RNNs
RNNs主要用途是处理和预测序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对很多问题都无能为力。例如,如果要预测句子的下一个单词是什么,一般需要用到当前单词和前面的单词,因为一个句子中前后单词并不是独立的。比如,当前单子是“很”,前面一个单词是“天空”,那么下一个单词很大的概率是“蓝”。循环神经网络的来源就是为了刻画一个序列当前的输出与之前信息的关系。从网络结构上,循环神经网络会记忆之前的信息,并利用之前的信息影响后面节点的输出。也就是说,循环神经网络的隐藏层之间的节点是有连接的,隐藏层的输入不仅包括输入层的输出,还包括上一时刻隐藏层的输出。理论上,RNNs能够对任何长度的序列数据进行处理。但是在实践中了降低复杂性往往假设当前的状态只与前面的几个状态相关。
上图展示了一个典型的循环神经网络。对于循环神经网络,一个非常重要的概念就是时刻。循环神经网络会对于每一个时刻的输入结合当前模型的状态给出一个输出。从下图中可以看到,循环神经网络的主体结构A的输入除了来自输入层Xt,还有一个循环的边来提供当前时刻的状态。在每一个时刻,循环神经网络的模块A会读取t时刻的输入Xt,并输出一个值ht。同时A的状态会从当前步传递到下一步。因此,循环神经网络理论上可以被看作是同一神经网络结构被无限复制的结果。但出于优化的考虑,目前循环神经网络无法做到真正的无限循环,所以现实中一般会将循环体展开,于是可以得到下图所展示的结构。
在下图中可以更加清楚的看到循环神经网络在每一个时刻会有一个输入Xt,然后根据循环神经网络当前的状态At提供一个输出Ht。从而神经网络当前状态At是根据上一时刻的状态At-1和当前输入Xt共同决定的。从循环神经网络的结构特征可以很容易地得出它最擅长解决的问题是与时间序列相关的。循环神经网络也是处理这类问题时最自然的神经网络结构。对于一个序列数据,可以将这个序列上不同时刻的数据依次传入循环神经网络的输入层,而输出可以是对序列中下一个时刻的预测。循环神经网络要求每一个时刻都有一个输入,但是不一定每个时刻都需要有输出。在过去几年中,循环神经网络已经被广泛地应用在语音识别、语言模型、机器翻译以及时序分析等问题上,并取得了巨大的成功。
以机器翻译为例来介绍循环神经网络是如何解决实际问题的。循环神经网络中每一个时刻的输入为需要翻译的句子中的单词。如下图所示,需要翻译的句子为ABCD,那么循环神经网络第一段每一个时刻的输入就分别是A、B、C和D,然后用“”作为待翻译句子的结束符。在第一段中,循环神经网络没有输出。从结束符“”开始,循环神经网络进入翻译阶段。该阶段中每一个时刻的输入是上一个时刻的输出,而最终得到的输出就是句子ABCD翻译的结果。从下图中可以看到句子ABCD对应的翻译结果就是XYZ,而Q是代表翻译结束的结束符。
如之前所介绍,循环神经网络可以被看做是同一神经网络结构在时间序列上被复制多次的结果,这个被复制多次的结构被称之为循环体。如何设计循环体的网络结构是循环神经网络解决实际问题的关键。和卷积神经网络过滤器中参数是共享的类似,在循环神经网络中,循环体网络结构中的参数在不同时刻也是共享的。
2. RNNs的缺点
上面小修已经介绍了循环神经网络的特点,它的好处是可以处理和预测时间序列的数据,但是由于这样的循环体的复制有一个问题就是,下一时刻只是受到了上一时刻输出的影响,而实际上下一时刻的结果在处理自然语言的时候往往需要结合上文中很多时刻值的影响,这就是RNNs最大的不足。另外RNNs虽然写程序容易,但是训练起来却是千难万阻。因为网络是根据输入而展开的,输入越长,展开的网络越深,而越深的网络训练的时候会出现“gradient explode”和“gradient vanish”.下面举一个例子来详细说明这个问题,并且推导RNN的梯度求解的过程。
如上图所示,这里使用tanh函数作为激励函数,并且参数W,U和b每一步都复用相同,而且为了方便假设输入xt和输出ht都为一维向量,如下表达式所示:
这里设参数的起始值分别为W=0.5,U=1和b=0. 根据上一文的介绍,需要先计算正向传播各数值的结果,根据参数和函数可以得到如下各输出的值:
然后根据Loss函数来进行反向传播的计算,对参数进行更新,这里设一个中间变量:
对于Loss函数,为了简单说明先对时间序列中的值进行两步展开,如下面表达式:
由于每一步都有W,因此计算的时候,实际上是要对每一步的J对W进行偏导数,如下图所示:
现在J对W进行求偏导,为简单说明,先计算两步,如下表达式:
为了说明两部分各表示什么,现将每一步的W看成不一样的,分别为W1,W2,W3等,那么损失函数变为:
这时候同样求J对W1和W2进行偏导数计算:
根据上面的表达和J对W偏导数得到的结果对比可以发现下面的表达式:
那么J对W的偏导数为J对每一步的W进行求导求和得到。根据上面推导的过程,可以对任意一步中的W进行求偏导数:
这里W为参数,多个W的连乘,依赖于W的取值,乘级的结果分为两种情况,第一种情况为:
在这种情况下就会出现梯度消失,这使得步骤越长,对后面的影响越小,第二种情况为:
在这种情况下会出现剃度爆炸,导致整个计算出问题。如果W是高维的情况下,那么它乘积的结果依赖于W的最大特征值,即:
这时根据特征值的数值大小,会同样出现上面W的两种结果。除了W的数值有问题,W前面连乘的项也会有问题。根据tanh的曲线,如下图:
因此对ai的求导,多个ai大于2的情况下,有:
这样也会出现梯度消失的困难。梯度消失的情况比较容易解决,可以不考虑长时间的效应,但是梯度爆炸会使得训练无法到达稳态值,如下图所示:
纵轴为损失函数的变化,蓝色线为训练时的变化值,由于梯度爆炸的原因,会使得当训练到梯度变化很快的地方,蓝线会变化很大,因此在跟新各参数的权重的时候,就会使得参数发散不收敛。为了解决这个问题,有些研究提出Gradient Clipping的方法,里面设定一个梯度变化的最大值,这样在更新权重的时候变化比较小。
如果梯度变化小于c,那么就以该梯度进行更新各参数的权重,而当梯度变化大于c,对它进行缩放,而且越大缩放的也就越厉害,这样就一定程度上避免了梯度爆炸。
3. RNNs的改进-LSTM语言模型
虽然梯度爆炸,通过Gradient Clipping的方法可以一定程度上解决,但是梯度消失依旧没有办法解决,这样循环神经网络对需要长期记忆信息要求的任务就无法满足。而第二种自然语言模型LSTM的能够很好的解决这一问题,因此它在自然语言处理和机器翻译等应用中取得了巨大的成功,如下图所示:
从图中可以看出除了下面各种操作之外,还有上面一条信息流,这个语言模型,小修下一文将详细介绍。
参考内容:
[1] 文献:On the difficulty of training recurrent neural networks
[2] 博文:https://ahmedhanibrahim.wordpress.com/2016/10/09/another-lstm-tutorial/
[3] 博文:深入浅出Tensorflow(五):循环神经网络简介
网友评论