机器学习--循环神经网络(RNN)4

时间:2024-03-09 18:01:25

一、RNN的学习方式

如果要做学习,需要定义一个损失函数(loss function)来评估模型的好坏,选一个参数要让损失最小。
在这里插入图片描述

以槽填充为例,如上图所示,给定一些句子,给定一些标签,告诉机器说第一个单词它是属于 other 槽,“上海”是目的地槽,“on“属于 other 槽,“June”和“1st”属于时间槽。
“抵达”丢到循环神经网络的时候,循环神经网络会得到一个输出 y1。接下来这个 y1会看它的参考向量(reference vector)算它的交叉熵。我们会期望如果丢进去的是“抵达”,其参考向量应该对应到 other 槽的维度(即other对应的维度为1,其他为 0),这个参考向量的长度就是槽的数量

把“上海”丢进去之后,因为“上海”属于目的地槽,希望把 x2 丢进去之后,y2 它要跟参考向量距离越近越好。那 y2 的参考向量是对应到目的地槽是 1,其它为 0。注意,在丢 x2 之前,一定要丢 x1(在丢“上海”之前先把“抵达”丢进去),不然就不知道存到记忆元里面的值是多少。所以在训练的时候,不能够把这些单词序列打散来看,单词序列仍然要当做一个整体来看。把“on”丢进去,参考向量对应的 other 的维度是 1,其它是 0。
RNN 的损失函数输出和参考向量的交叉熵的和就是要最小化的对象。

有了这个损失函数以后,对于训练也是用梯度下降来做。也就是现在定义出了损失函数L,要更新这个神经网络里面的某个参数 w,就是计算对 w 的偏微分,偏微分计算出来以后,就用梯度下降的方法去更新里面的参数。循环神经网络里面,为了要计算方便,提出了反向传播的进阶版,即随时间反向传播(BackPropagation Through Time,BPTT)。BPTT 跟反向传播其实是很类似的,只是循环神经网络它是在时间序列上运作,所以 BPTT 它要考虑时间上的信息,如下图所示。
在这里插入图片描述

RNN 的训练是比较困难的,如下图所示。
在这里插入图片描述
在做训练的时候,期待学习曲线是像蓝色这条线,这边的纵轴是总损失(total loss),横轴是回合的数量,我们会希望:随着回合的数量越来越多,随着参数不断的更新,损失会慢慢的下降,最后趋向收敛。
但在训练循环神经网络的时候,有时候会看到绿色这条线。如果第一次训练循环神经网络,绿色学习曲线非常剧烈的抖动。
在这里插入图片描述

如上图所示,RNN 的误差表面是总损失的变化是非常陡峭的或崎岖的。误差表面有一些地方非常平坦,一些地方非常陡峭。纵轴是总损失,x 和 y 轴代表是两个参数。
这样会造成什么样的问题呢?假设我们从橙色的点当做初始点,用梯度下降开始调整参数,更新参数,可能会跳过一个悬崖,这时候损失会突然爆长,损失会非常上下剧烈的震荡。有时候我们可能会遇到更惨的状况,就是以正好我们一脚踩到这个悬崖上,因为在悬崖上的梯度很大,之前的梯度会很小,所以可能把学习率调的比较大。很大的梯度乘上很大的学习率结果参数就更新的跨度就会很大,整个参数就飞出去了。
裁剪(clipping)可以解决该问题,当梯度大于某一个阈值的时候,不要让它超过那个阈值,比如当梯度大于 15 时,让梯度等于 15 。因为梯度不会太大,所以就算是踩着这个悬崖上,也不飞出来,会飞到一个比较近的地方,这样还可以继续做 RNN 的训练

之前说 ReLU 激活函数的时候,梯度消失(vanishing gradient)来源于 Sigmoid 函数。但 RNN 会有很平滑的误差表面不是来自于梯度消失。把 Sigmoid 函数换成 ReLU,其实在 RNN 性能通常是比较差的,所以激活函数并不是关键点。
在这里插入图片描述

有更直观的方法来知道一个梯度的大小,可以把某一个参数做小小的变化,看它对网络输出的变化有多大,就可以测出这个参数的梯度大小,如上图所示。
举一个很简单的例子,只有一个神经元,这个神经元是线性的。输入没有偏置,输入的权重是 1,输出的权重也是 1,转移的权重是 w。也就是说从记忆元接到神经元的输入的权重是 w。如下图所示
在这里插入图片描述

假设给神经网络的输入是 [1, 0, 0, 0]T,比如神经网络在最后一个时间(1000 个输出值是 w999)。假设 w 是要学习的参数,我们想要知道它的梯度,所以是改变 w 的值时候,对神经元的输出有多大的影响。假设 w = 1,y^1000 = 1,假设 w = 1.01,y^1000 ≈ 20000,w 有一点小小的变化,会对它的输出影响是非常大的。所以 w 有很大的梯度,那把学习率设小一点就好了。但把 w 设为 0.99,那 y1000 ≈ 0。如果把 w 设为 0.01,y1000 ≈ 0。也就是说在 1 的这个地方有很大的梯度,但是在 0.99 这个地方就突然变得非常小,这个时候需要一个很大的学习率。设置学习率很麻烦,误差表面很崎岖,梯度是时大时小的,在非常小的区域内,梯度有很多的变化。
从这个例子可以看出,RNN 训练的问题其实来自它把同样的东西在转移的时候,在时间按时间的时候,反复使用。所以 w 只要一有变化,它完全由可能没有造成任何影响,一旦造成影响,影响很大,梯度会很大或很小。

所以 RNN 不好训练的原因不是来自激活函数而是来自于它有时间序列同样的权重在不同的时间点被反复的使用。

二、解决RNN梯度消失的方法

广泛被使用的技巧是 LSTM,LSTM 可以让误差表面不要那么崎岖。它会把那些平坦的地方拿掉,解决梯度消失的问题,但不会解决梯度爆炸
(gradient exploding)的问题。有些地方是非常的崎岖的,有些地方是变化非常剧烈的,但是不会有特别平坦的地方。如果做 LSTM 时,大部分地方变化的很剧烈,可以把学习率设置的小一点,保证在学习率很小的情况下进行训练。

LSTM 可以处理梯度消失的问题。RNN 跟 LSTM 在面对记忆元的时候,它处理的操作其实是不一样的。在 RNN 里面,在每一个时间点,神经元的输出都要记忆元里面去,记忆元里面的值都是会被覆盖掉。但是在 LSTM 里面不一样,它是把原来记忆元里面的值乘上一个值再把输入的值加起来放到单元里面。所以它的记忆和输入是相加的。
LSTM 和 RNN 不同的是,如果权重可以影响到记忆元里面的值,一旦发生影响会永远都存在。而 RNN 在每个时间点的值都会被格式化掉,所以只要这个影响被格式化掉它就消失了。但是在 LSTM 里面,一旦对记忆元造成影响,影响一直会被留着,除非遗忘门要把记忆元的值洗掉。不然记忆元一旦有改变,只会把新的东西加进来,不会把原来的值洗掉,所以它不会有梯度消失的问题。

遗忘门可能会把记忆元的值洗掉。 LSTM 的第一个版本其实就是为了解决梯度消失的问题,所以它是没有遗忘门,遗忘门是后来才加上去的。在训练 LSTM的时候,要给遗忘门特别大的偏置,确保遗忘门在多数的情况下都是开启的,只有少数的情况是关闭的。
有另外一个版本用门操控记忆元,叫做 GRU,LSTM 有三个门,而 GRU 有两个门,所以 GRU 需要的参数是比较少的。因为它需要的参数量比较少,所以它在训练的时候是比较鲁棒的。

如果训练 LSTM 的时候,过拟合的情况很严重,可以试下 GRU。GRU 的精神就是:旧的不去,新的不来。它会把输入门跟遗忘门联动起来,也就是说当输入门打开的时候,遗忘门会自动的关闭 (格式化存在记忆元里面的值),当遗忘门没有要格式化里面的值,输入门就会被关起来。也就是要把记忆元里面的值清掉,才能把新的值放进来。