基于时间的反向传播算法和梯度消失 -part3

来源:互联网 发布:日系和韩系护肤品知乎 编辑:程序博客网 时间:2024/05/04 14:26

本文翻译自
前文从零开始实现了RNN,但是没有详细介绍Backpropagation Through Time (BPTT) 算法如何实现梯度计算。这篇文章将详细介绍BPTT。之后会分析梯度消失问题,它导致了LSTM和GRU的发展,这是两个在NLP领域最为流行和有效的模型。
梯度消失问题在1991年被发现,但在近来受到关注,因为深度框架的广泛应用;
为了充分理解这个教程,我建议要熟悉部分分化和基本反向传播算法的工作机制相关教程part1part2part3

BACKPROPAGATION THROUGH TIME (BPTT)
快速重述RNN中的基本公式
这里写图片描述
yt是在step t正确的单词输出,yt^指预测值。
我们还是传统的认为一个完整的序列(句子)是一个训练样本,所以总的误差为每一步的误差之和;
这里写图片描述
我们的目标是计算有关U,V,W的损失函数的梯度,使用随机梯度下降算法学习得到更好的参数值。就像我们误差相加,我们也把每一个样本的每一步中的梯度值相加
这里写图片描述
为了计算梯度,我们使用了分化链规则。
这里写图片描述
上述,这里写图片描述
这里写图片描述是两个向量的外积;
这个公式想要说明的问题是,这里写图片描述仅仅依赖于当前步的值这里写图片描述。如果知道了这些,计算V的梯度就是一个简单的矩阵相乘。
但是这里写图片描述(U的情况也是如此)的情况不同,为了分析原因,下面写出链式规则:
这里写图片描述
注意:这里写图片描述依赖于S2,而S2依赖于W和S1。如果我们求与W相关的导数,不能认为S2为常数。需要进一步运用链式规则。
这里写图片描述
我们把每一步对梯度的贡献相加。换句话说,W在每一步中都被使用,最终得到输出。我们需要从t=3通过网络反向传播梯度到t=0;
这里写图片描述
注意,这和在前馈神经网络中使用的标准的反向传播算法一样。关键的不同在于我们把每一步骤中W的梯度相加。在传统的NN,没有在层间共享参数,所以不用相加。BPTT像是一个有趣的标准反向传播算法运用在展开的RNN上面;
一个BPTT的代码实现如下:

def bptt(self, x, y):    T = len(y)    # Perform forward propagation    o, s = self.forward_propagation(x)    # We accumulate the gradients in these variables    dLdU = np.zeros(self.U.shape)    dLdV = np.zeros(self.V.shape)    dLdW = np.zeros(self.W.shape)    delta_o = o    delta_o[np.arange(len(y)), y] -= 1.    # For each output backwards...    for t in np.arange(T)[::-1]:        dLdV += np.outer(delta_o[t], s[t].T)        # Initial delta calculation: dL/dz        delta_t = self.V.T.dot(delta_o[t]) * (1 - (s[t] ** 2))        # Backpropagation through time (for at most self.bptt_truncate steps)        for bptt_step in np.arange(max(0, t-self.bptt_truncate), t+1)[::-1]:            # print "Backpropagation step t=%d bptt step=%d " % (t, bptt_step)            # Add to gradients at each previous step            dLdW += np.outer(delta_t, s[bptt_step-1])                          dLdU[:,x[bptt_step]] += delta_t            # Update delta for next step dL/dz at t-1            delta_t = self.W.T.dot(delta_t) * (1 - s[bptt_step-1] ** 2)    return [dLdU, dLdV, dLdW]

这就说明了标准的RNNs难以训练的原因,句子序列可能相当的长,可能多于20个单词,因此需要反向传播通过很多层,在实际中许多人限制反向传播在某几步;
梯度消失问题(THE VANISHING GRADIENT PROBLEM)
在之前的教程中,提到RNNs在学习长期的依赖时遇到困难。单词之间的相互作用仅仅在几步之间。这是有问题的,因为英语语句的语义是由相距较远单词共同组成的。“The man who wore a wig on his head went inside”这句话的真实语义是一个人出去,而不是假发,但是普通的RNN不能捕获到这样的信息。为了理解为什么让我们仔细看一下上面计算的梯度:
这里写图片描述
注意到,这里写图片描述就是链式规则本身;例如,这里写图片描述,同样注意我们要求与一个向量相关的向量函数的导数,结果是一个矩阵(称作雅可比矩阵),它的每个元素是逐点导数;重写上面的梯度
这里写图片描述
这证明了雅可比矩阵的2-norm(可以视为绝对值)具有上界为1,
直观的理解是因为tanh(or sigmoid)激活函数把所有值映射到-1到1之间。 它的单数同样有界,为1
这里写图片描述
你能看出tanh和sigmoid函数在端点处的导数为0.它们接近平滑的线。
当这种情况发生时,我们认为相关的神经元饱和了。它们拥有0梯度
,使之前层的梯度趋向于0 。因此,在矩阵中的小数值和倍数矩阵乘法,梯度值快速的收缩指数。最终在几步之后完全消失。从某一不开始梯度贡献为0,这就导致这些步的状态不能对学习有所贡献。最后就变成不能学习远范围的依赖关系。消失梯度不仅仅在RNNs中出现。它还出现在深度前馈神经网络。由于RNNs趋向于很深,使得问题更加
常见;
容易想象,根据我们的激活函数和神经参数,如果雅可比矩阵的值很大的话,即使不梯度消失,也会爆炸。称之为爆炸梯度问题。消失梯度相比爆炸梯度获得了更多的关注的原因是双重的。1爆炸梯度是明显的,你的梯度会变为NaN,程序会崩溃。第二,用预处理的阈值裁剪梯度是一个简单有效的方法处理爆炸梯度问题。消失梯度出现的不明显而且没有明显的方法处理它;
幸运的是:有很多方法来克服消失梯度问题。W矩阵的合适的初始化能够减少 消失梯度的影响。所以可以正则化。一个更好的方法是使用ReLU代替tanh或sigmoid激活函数。ReLU导数是0或1常数,所以不受消失梯度的影响。一个更加流行的解决方案是使用 Long Short-Term Memory (LSTM) or Gated Recurrent Unit (GRU) 框架。
LSTMs在1997年被第一次提出,可能是现在最常用的自然语言处理模型。GRUs在2014年被提出,是LSTMs的简单变体。这些RNN架构被明确的设计用来解决消失梯度的问题,能够有效的处理长距离依赖,

0 0
原创粉丝点击