RNN 与 LSTM

RNN 的简短复习

我们只在一开始输入了一次图片,使用这个模型,你的神经网络就可以回看输入的图片,然后根据图片的一步登生成描述的词汇。

每当你产生一个新词时,你使你的神经网络可以回看图片,然后找到它想要形容不同特征的下一个词,你可以通过一个可训练的方式实现这些,所以 RNN 不仅产生了形容词,还决定了下一步看向图片的哪里。所以这个模型它所做到的不仅仅是产生了 RNN 的输出结果,还要找到按顺序排列的下一个形容词的概率分布。

但是在这个例子里,这个网络给出了一个值,我们运行神经网路,得到了一个 14 14 512 大小的激活容量,而且每一次我们得到的不仅仅是这个分布,还有一个 512 维的向量,它大概就像是一个用来查询的关键词,找出你下一步想看图片哪里,但是实际上这不是这片论文主要做的事情。这个 512维 的向量是由 RNN 生成的,用一些权值来预测,然后这个向量可以和这片区域里的每一个 14 * 14 做点乘,这样我们做完了全部点乘,得到了一个 14 * 14 的 compatibility map,然后我们放一个 softmax 在上面,基本上我们对他进行正则化,得到 attention over the image,所以这是一个 14 * 14 的映射。

基于像在图中的 RNN 最有意思的一点是,我们使用这个概率分布,奇招具有这些特征的权重和。这个 RNN 可以自己产生这些向量来得出最近最吸引自己的一点是什么,然后它回到那里,最后你做了一个关于不同特征加权和,找出这一个时间点上 RNN 最感兴趣的是什么。

举个例子,RNN 正在生成一些东西,现在我决定要查找一些东西,他生成一个有 512 个数字的向量关于对象之类的东西,然后和卷积神经网络的激活函数交互,然后可能对神经网络的一部分或是激活公式,最后你就集中你的注意力在那图片的那部分。所以基本上,通过这个交互,你可以查找图片,这就是一些和 soft attention 有关的东西了

RNN 的输入可以有选择的 attention 作为对输入的处理。

RNN 的复杂化与 LSTM 的引出

如果你想把 RNN 变得更复杂,其中一种方法就是增加层数。一般情况下,层数越多工作效果越好。堆叠层数的方法有很多,其中一个就是堆叠 RNN 的层数,这部分也有很多方法。

上图中是一种人们经常使用的方法,你可以看到,可以直接向RNN里加入新的层。下一个 RNN 的输入是前一个 RNN 的隐藏状态向量。在这副图里,横轴是时间,向上看使我们的不同的 RNN 。这在张图里,我们有三个 RNN,每个都有属于自己的权重集,而这些 RNN 就是从一个流向另一个,这些 RNN 都是一起训练的(即没有依次训练的说法),全部只有一个过程,只有一个向后传播。

这个 RNN 公式,是对之前的公式一个重写,但是本质却没有变化,任然和之前做一样的处理。我们使用了一个向量形式的深度和一个向量形式的时间,我们把它们和W转移矩阵做处理,再把他们放到 tanh 的运算里。所以当你增加层数或者输入一个W转移矩阵时,这个公式可以写成一个新的公式,这就是我们可以怎样增加 RNN 的层数。这种方法使网络变得复杂,不仅仅局限于增加层数,就是使用一个更好的 RNN 公式。

Long Short Time Memory

我们现在已经了解到了这个非常简单的 RNN 公式,而在实际试验中,你基本上用不了这么简单的公式,基本上之前展示的那个网络很少使用,取而代之的是 LSTM(long short time memory),基本现在所有的论文都用这个。所以如果你要使用 RNN,这里就是你要使用的公式。在这里我要强调的是,他的一切都和之前的 Vanilla 网络是一样的,唯一不同的是RNN公式变得复杂了。我们还是从下一层接受 hidden vector,就像你的输入一样,在较早的时间点对应的是前一个状态。

一般情况下,RNN 每一步只有一个向量 h,而LSTM每次有两个向量,hidden cector h 和 cell state vector c。所以每一次我们有 h 和 c 两个并行,然后向量 c 用黄色表示(下面那一层),所以这个空间里每个点有两个向量。

我们把它串联起来,然后放入一个W转移矩阵,之后通过一个更复杂的方式得到新的 hidden state。我们现在只是找到了一个更复杂的办法来处理来自下层和过去的输入来得到一个 hidden state 的更新,接下来我们讲讲这个公式的细节。

上面展示的 LSTM 的方程我们先讲上面这部分,我们从下面和之前一个状态接受这两个向量,h 是前一个状态,x 是输入,我们用 W 映射到 x 和 h,x 和 h的大小都是 n,他们都由 n 个数字组成,最后我们会产生 4n 个数字,通过 W 矩阵产生了 4n * 2n。这里有 4 个向量,i, f, o, g,分别是输入 input,遗忘 forget,输出 output,然后i, f, o经过 sigmoid gate,g经过 tanh gate。

所以这个网络的工作方式就是,对 c 进行操作,根据你之前的状态和你下层传上来的值,使用 i, f, g, o 实现了对于 c 的操作。这里来解释一下过程,要把 i, f 和 o 想象成二进制的,不是 0 就是 1,我们想用他们来表示门(gate),之后我们对他们进行 sigmoid,因为我们想要使他们可以微分,然后队可以对所有点进行 BP。基于环境就把所有的都当做是二进制的就行了。

所以这个公式的作用是:根据这些门和 g,我们最终将完成对 c 值得更新,f 被称作忘记门,通常用来把一些细胞(cells)状态置 0,这些细胞可以理解为计数器,他们可以通过 f 运算重新置 0。这里发生的是数组运算乘法,如果 f 为 0,你会发现 c 通过与 f 相乘输出为 0,这样我们就实现了计数器重置

我们也可以将其与 i 和 g 的乘积相加,由于 i 取值在 0 到 1 之间,g 取值 -1 到 1 之间,因此我们对每一个细胞加上一个介于 -1 到 1 的数值,在每一个时间步内都进行这些运算,包括通过忘记门把它重置为 0 或者加上一个 -1 到 1 之间的数值,这就是我们细胞状态的更新

隐藏层函数 h 更新是以挤压细胞的形式进行的,tanh(c),挤压程度由输出门进行调整,所以经过 o 参数调整后只有一部分细胞进入隐含状态,我们通过学习选择一部分细胞状态进入隐藏状态。

这有一些东西需要强调一下,我们用 -1 到 1 之间的数值 i 来乘 g,但是如果用 g 替换,g 取值已经是 -1 到 1 之间,那为什么还要乘上 i 呢?在我们只想要为 c 增加 -1 到 1 情况下,这一步有什么意思吗?这是 LSTM 中 cell 参数的一部分,我认为一个原因是如果你认为 g 是前文的线性函数 tanh 得到的,如果我们直接加上 g 而不是加上 i * g,那将会得到一个非常简单的函数,通过加上 i 乘法操作,我们使函数更加复杂,这样我们更能表达我们加到细胞状态(cell state)里的东西;另一种思考方式就是把这两个概念分开来看,g 表示我们要在细胞状态里增加多少,i 表示我们是否要进行增加这个状态,所以 i 就像表示是否要进行这个操作,g 表示我们要增加的东西,通过分离这两个参数 LSTM 的训练效果会更好。

好的接下来来看下面部分的公式,我们把它看做细胞流过,第一个操作是 f 乘以 c,f 是 sigmoid 函数的输出,若 f 为 0,你将会关闭细胞,然后对计数器重置。如下图所示

这部分是 g,他基本是加到细胞状态(cell state)里的,然后细胞状态一部分进入隐藏状态(hidden state),但是他要先经过 tanh 函数,然后经过 o 参数调整,由此可见,o 向量可以决定细胞状态那一部分进入隐藏状态,你会发现隐藏状态不仅进入了 LSTM 的下一次迭代运算,还会上浮到更高层,因为这个隐藏状态向量要接通上面更远的 LSTM 网络或者进行一次预测。

所以当你展开的时候他是这个样子,从下面得到输入,从前面得到隐藏状态,x 和 h 决定了你的运算门 f, i, g和o,他们都是 n 维的向量,最后对细胞状态进行操作,你可以对它进行一次重置操作,一次加上一个 -1 到 1 之间的值,一部分细胞状态会以学习的方式释放,他可以向上去进行预测,也可以向前进入 LSTM 的下一次迭代。

下面展示的就是 LSTM 正常的工作流程

LSTM 为什么效果比 RNN 好?

RNN 也有一些状态向量,你要对他进行操作,通过递归公式进行转换,最后随着时间不断更新你的隐藏状态向量,你会发现在 LSTM 中,这些细胞状态进入网络,并且部分细胞进入隐藏状态。

我们根据隐藏状态决定如何对细胞进行操作。如果忽略忘记门,那我们只是对细胞进行加法迭代,所以这有一些操作可以看做是细胞状态的函数,但不管他们是什么,我们最后都是我们都是间接的改变细胞状态,而不是直接对他进行变换,同时这意味着这是一种依赖于加法的不断的计算。

事实上他和 ResNet 有些相似,一般来说用卷积神经网络进行转换词义,ResNet 加入了这些跳跃的链接,你会发现残差有加法操作,即 x,我们要基于 x 进行这些计算,然后再加上 x,这些是ResNet 中的基本模块,事实上这也是 LSTM 中的运算方式,我们有这些加法操作,其中的 x 取决于你的细胞,我们进行了一些函数运算,然后选择加到你的细胞状态中但是 LSTM 与 ResNet 不同,还有这些忘记门可以选择关闭一部分信号。

看起来拥有加法操作会使网络变得更好,这能使反向传播更加高效。

为何 RNN 存在梯度消失的问题?

来考虑一下 RNN 和 LSTM 中反向传播的动态特性,尤其是在 LSTM 中。如果在某些时间中诸如一些梯度信号将会变得非常清楚。如果我在上张图片的最后注入梯度函数,然后这些加法操作就像是梯度的高速公路,这些梯度会经过这些加法门。因为加法相等的分配梯度,所以如果我在这接入梯度,他会沿着网络传递。当然梯度也会通过这些 f,这些有助于梯度回到梯度流,这样使你永远不会遇到RNN中梯度消失问题,因为在 RNN 里这些梯度会在反向传播中死亡变成 0。在 LSTM 中,由于加法高速公路的存在,在任何时间内,我们从上面注入 LSTM 的梯度,都会流过细胞,不会消失

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
H = 5	# dimensionality of hidden state
T = 50 # number of time steps
Whh = np.random.randn(H, H)

# forward pass of an RNN (ignoring inputs x)
hs = {}
ss = {}
hs[-1] = np.random.randn(H)
for t in xrange(T):
ss[t] = np.dot(Whh, hs[t - 1])
hs[t] = np.maxinum(0, ss[t])

# backward pass of the RNN
dhs = {}
dss = {}
dhs[T-1] = np.random.randn(H) # start off the chain with random gradient
for t in reversed(xrange(T)):
dss[T] = (hs[t] > 0) * dhs[t] # back through the nonlinearity dhs[t-1] = np.dot(Whh.T, dss[t]) # backprop into previous hidden state

这是一个关于循环神经网络的例子,在这个循环神经网络中我们不管这些输入,只看隐藏状态的更新部分。所以要最小化权值 Whh,这是一个隐藏状态。现在要对这个 vanilla RNN 进行向前传播,这里我们的时间长度 T 设为 50,我们要用 Whh 乘前一时间长度,然后再用 ReLU 函数来计算。这就是忽略所有输入向量的前向传播,即用 Whh 乘以 h 然后和 0 比大小,再乘以 h 再比大小,循环往复。

接着要进行反向传播,在最后一步时在这里插入了一个随机的梯度,也就是说在第 50 时间长度插入一个随机的梯度,然后再做反向传播。在进行反向传播时要用到 ReLU 函数,先用 Whh 进行乘法,然后通过 ReLU 函数,再进行乘法再通过 ReLU。有件事需要注意一下,这里我再做反向传播的时候用到了 ReLU,对所有输入值去阈值,丢掉所有小于 0 的数,这里我对 Whh 乘以 h 运算符进行反向传播,事实上我们就是在进行非线性变换之前乘上 Whh 矩阵。

我们在运行过程中,一遍又一遍的乘以 Whh 这个矩阵,因为在向前传播中我们每一次循环中都乘了 Whh,并且对所有的隐藏状态进行反向传播,最后一公式 Whh 乘以 hs 结束,得到的结果就是你的梯度和 Whh 矩阵相乘,然后对他们使用 ReLU,再乘以 Whh 再运行 RelU,这样我们就乘了这个矩阵 50 次。

那么他可能会产生两个问题,首先如果你使用的是标量,而不是矩阵,例如使用了一个随机数,然后我得到了第二个数,我不停的用第二个数去乘以第一个数,那么这个序列会变成什么样?有两种情况,我不停的用同一个数去乘他,不管他是 0 还是无限,当然如果第二个数是 1,这是唯一一种不会出现爆炸的情况,否则不管是消失还是爆炸,都将是很糟糕的情况。

虽然这里是矩阵而不是数,但是实际上他的泛化也会发生这样的事情。如果 Whh 矩阵的谱半径,也就是这个矩阵的最大特征值要比1大很多,那么就会爆炸;如果小于 1,那么就会消失。因为 RNN 的这个循环的计算,使得它出现了这些很糟糕的问题,它非常不稳定有时候甚至会导致消失或者爆炸。

所以在实践中,我们可以采取一点小技巧来控制梯度爆炸,在实践中,他就像一个不完整的解决方案,比如你懂得梯度是大于 5 的,那就将元素全部裁剪为 5,这个方法叫做梯度裁剪,它可以用来解决梯度爆炸问题,这样你的循环就不会在发生爆炸了。

但是在循环神经网络中仍然可能会出现梯度消失的问题,而 LSTM 能很好的抑制梯度消失,因为这些高速公路的细胞状态只改变了加法运算,他的梯度会被一直传下去,不会消失。

这大致解释了为什么他的工作效果会更好。通常我们会使用 LSTM,也会使用梯度裁剪,因为 LSTM 也可能会出现梯度爆炸,但它一般不会出现梯度消失。

Summary

首先 RNN 是很不错的,但是只用 RNN 事实上工作的不是很好,所以通常使用 LSTM 来代替,他们最好的地方是他们的加法计算,可以使梯度传播工作的更好,并且也不会出现梯度消失的问题.