循环神经网络长期依赖问题及优化

什么是长期依赖问题?我们知道循环神经网络具有记忆能力,凭着它的记忆能力,能够较好地解决一般的序列问题。这些序列问题的数据内部基本上都存在着一定的依赖性,例如,前面提到的词性标注的问题,以及在演示项目中人工构造的二进制数据。

在有些现实问题中,数据间的依赖都是局部的、较短时间间隔的依赖。还是以词性标注为例,判断一个词是动词还是名词,或者是形容词,我们往往只需要看一下这个词前后的两个或多个词就可以做出判断,这种依赖关系在时间上的跨度很小。

如图 1 所示,t4 时刻网络的输出(h0 到 h5 是隐藏层的输出)除了与当前时刻的输入 x4 相关之外,还受到 t1 和 t2 时刻网络状态的影响。
时间跨度较小的依赖关系示意图
图 1:时间跨度较小的依赖关系示意图

像这种依赖关系在时间上的跨度较小的情况,RNN 基本可以较好地解决,但如果出现了像图 2 所示的依赖情况,就会出现长期依赖问题:梯度消失和梯度爆炸。
时间跨度较大的依赖关系示意图
图 2:时间跨度较大的依赖关系示意图

什么是梯度消失和梯度爆炸?图 3 是一个较为形象的描述,在深层的神经网络中,由于多个权重矩阵的相乘,会出现很多如图 3 所示的陡峭区域,当然也有可能会出现很多非常平坦的区域。
致梯度爆炸的情况
图 3:导致梯度爆炸的情况

在这些陡峭的地方,损失函数的倒数非常大,导致最终的梯度也很大,对参数进行更新后可能会导致参数的取值超出有效的取值范围,这种情况称为梯度爆炸。而在那些非常平坦的地方,损失函数的变化很小时,梯度的值也会很小(可能趋近于 0),导致参数的更新非常缓慢,甚至更新的方向都不明确,这种情况称为梯度消失。

长期依赖问题会导致循环神经网络没有办法学习到时间跨度较大的依赖关系。正如上面所说,长期依赖问题普遍存在于层数较深的神经网络之中,不仅存在于循环神经网络中,而且也存在深层的前馈神经网络中。循环神经网络中循环结构的存在,使这一问题尤为突出,而在一般的前馈神经网络中,这一问题其实并不严重。

值得注意的是,前面已经提到过梯度消失的问题,这是由于 Sigmoid 函数在其函数图像两端的倒数趋近于 0,使得在使用 BP 算法进行参数更新的时候会出现梯度趋近于 0 的情况。针对这种情况导致的梯度消失的问题,一种有效的解决方法是使用 ReLU 激活函数。但是由于本节教程所介绍的梯度消失的问题并不是由激活函数引起的,因此使用 ReLU 激活函数也无法解决问题。下面来看一个简单的例子。

如图 4 所示,我们定义一个简化的循环神经网络,该网络中的所有激活函数均为线性的,除在每个时间步上共享的参数 W 以外,其他权重矩阵均设为 1,偏置项均设为 0。
参数W在循环神经网络中随时间传递
图 4:参数 W 在循环神经网络中随时间传递

假设输入的序列中除 x0 的值为 1 外,其他输入的值均为 0,则根据公式 h1=f(Uht-1+WXt+b1) 和公式 y1=g(Vh1+b2),可以得到:

h0=1 h1=w h2=w2...hn=wn

最终可以得到 yn= Wn,神经网络的输出是关于权重矩阵 W 的指数函数。当 W 的值大于 1 时,随着 n 值的增加,神经网络最终输出的值也会呈指数级增长,而当 W 的值小于 1 时,随着 n 值的增加,神经网络最终输出的值则会非常小。这两种结果分别是导致梯度爆炸和梯度消失的根本原因。

从上面的例子可以看到,循环神经网络中梯度消失和梯度爆炸问题产生的根本原因是参数共享。

2.长期依赖问题的优化

对于梯度爆炸的问题,一般来说比较容易解决,可以用一个比较简单的叫“梯度截断”的方法。“梯度截断”的思路是设定一个阈值,当求得的梯度大于这个阈值的时候,就使用某种方法来进行干涉,从而减小梯度的变化。还有一种方法是给相关的参数添加正则化项,使得梯度处在一个较小的变化范围内。

梯度消失是循环神经网络面临的最大问题,相较于梯度爆炸问题要更难解决。目前最有效的方法就是在模型上做一些改变,这就是下节将要介绍的门控循环神经网络。