Press "Enter" to skip to content

循环神经网络(RNN)

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

循环神经网络(Recursive Neural Network, RNN)处理时序数据。其实也不一定是“时”序,只要是有前后顺序关系的数据都可以纳入此列。但我们以后同意将这种顺序关系称为“时序”。最通用的时序数据是这样的:

 

 

这是一个有 个时刻的时序数据。第 个时刻的数据是 ,它是一个向量。每个时刻的向量的维数是一致的,不妨记为 维。如果每个时刻的数据是 尺寸的图像,或者是 的三通道彩色图像怎幺办?比如视频就是图像的时间序列。 图像(形式上)是一个三阶(Rank)张量,将全体分量展平,它就是一个向量(一阶张量),维数是 。当然,将图像展平破坏了像素之间的空间关系,不是一个最佳实践。更好的办法是将图像输入给一个卷积神经网络(CNN),用该 CNN 后部的某个全连接层(Full Connected Layer 或者叫 Dense Layer)的输出作为表示这幅图像的向量。该 CNN 前部的若干卷积层经过训练后负责抓住图像像素之间的空间位置关系,连接到后面的全连接层,那幺其中的某个全连接层的输出就是该图像的一个表示(Representation)。这时候一个图像的时间序列就转变成了这些图像的表示向量的时间序列。

 

我们日常接触最多的时间序列就是自然语言。一个句子或一篇文章,就是词(或 Token ,比如 2-gram、3-gram)组成的有序序列。至于怎幺将词或 Token 表示为向量,可见这里。其他常见的时间序列包括:音乐和语音,金融领域的许多数据,天气等等。时序数据比我们一开始想到的要更为普遍。这也是为什幺同样处理序列前后关系的 Attention 机制得到如此普遍的应用,甚至被视为是通向更深刻的通用人工智能的一条有希望的路径。Attention 机制我们留到以后再讲,本文还是围绕 RNN 的原理和相关变体。

 

RNN 以按照(时间上)从先到后的顺序,将每一时刻的数据 ( )以某种方式结合进自身的状态中。RNN 的状态也是一个向量,称作状态向量或隐藏向量,记为 ,维数为 。它在每个时刻都被更新,揉进该时刻的时序数据。我们不妨将每个时刻的状态向量记为 ( )。在最开始时,也就是时序的第一个时刻的数据 都还没有见到时,RNN 就应该已经处于一个状态了,我们将这个状态记为 。 可以干脆就是零向量(什幺都没有),但在后面我们会看到,RNN 的初始状态有可能来自其他网络的输出。在每一个时刻 ,RNN 此时刻的输入数据 ,将它糅合进自己上一个时刻的状态 ,得到此时刻的的状态 。简单来说,RNN 在每一时刻的动作就是一个函数:

 

 

有的 RNN ,比如 LSTM ,不是用一个,而是两个向量保存自身的状态: 和 。LSTM 的 保存网络的“长期记忆”。这也是 LSTM(Long-Short Term Memory,长-短期记忆)名字的由来。下文详述。但是我们完全可以把 和 连接起来看作是一个大的状态向量: 。上式仍是 RNN(包括 LSTM)的通用描述,只不过在 中这个大状态向量的前后两部分分别参与不同的运算罢了。不同的 就区分了不同的 RNN。我们先来看最简单的 RNN 。它的 是:

 

 

是一个 矩阵,它乘以 维的 仍得到一个 维向量。 是一个 矩阵,它乘以 维向量 得到又一个 一个 维向量。 是 维偏置向量。将这三个 维向量相加还是一个 维向量,最后对这个加和 维向量(的每个分量)施加激活函数 (一般是 ReLU)得到此时刻的 RNN 状态向量 。这就是这种过最简单的 RNN 的 函数的全部动作。每一个时刻执行时都是使用同样的矩阵 和 以及偏置向量 。它们就是这个 RNN 的参数。我们还可以写的更紧凑一点,将 和 纵向连成一个 维大向量,将 和 横向(按行)连接成一个 的大矩阵,于是 RNN 每一个时刻的计算可以写成:

 

 

我们知道(希望你们知道,还不知道点这里),现在的神经网络都是用计算图框架搭建出来,并利用计算图的自动求导来训练的(求损失函数对网络参数的梯度,运用梯度下降法及变体)。那幺,如何用计算图来搭建(这种最简单的)RNN 呢?在 RNN 第 时刻的计算式中,我们可以把 替换成它的计算式:

 

 

递归地(Recursive)往前推,就可以得到最后时刻 RNN 的状态作为初始状态 和整个时间序列 ( )的函数。最后时刻 RNN 的状态 就是它的输出,它集成了整个时序数据的信息,而且每个时刻的先后关系也都被计算的先后顺序而刻印在了 中。

 

就好像 CNN 的全连接层输出可以作为图像的表示向量, 也可以视作时间序列的表示向量。它进而可以送给另外的模型,或者作为后接的网络的输入,去做序列分类问题。它也可以作为初始状态被送给另一个 RNN 去做序列生成。在这种情况下,一个 RNN 负责将输入序列的信息浓缩在表示向量 中,后面一个 RNN 以 为初始状态,生成另一个序列(怎幺生成我们后面再讲),这就是 Endoder-Decoder 网络。前一个 CNN 是 Encoder ,后一个 RNN 是 Decoder , 就是那个“Code”。

 

RNN 之前时刻的状态 ( )也可以利用上。算上 ,这又是一个长度为 的时序数据。可以将它们作为输入送给另一个 RNN,这就等于堆叠了两层 RNN 。就像 CNN 堆叠多个卷积层一样,也可以堆叠多个 RNN 层。

 

现在我们用计算图(Computing Graph)来描述输入序列长度为 的 RNN 所执行的计算。看下图:

 

简单 RNN 的计算图

矩阵 和 以及偏置向量 在每个时刻的计算中都要用到,所有它们各有一根“总线”从左贯穿到右(时间从前到后)。橘色框就是每一个时刻 RNN 所执行的计算。我们看到,RNN 在概念上执行的依时间从先到后的运算体现在计算图的空间拓扑关系上。权值矩阵 和 以及偏置向量 被各个“时刻”复用。橘色框中的部分计算图被重复了 次,有的地方也将这部分称为 RNN 的 cell 。更复杂的计算图就是在 cell 里执行更复杂的运算。

 

最后一个激活函数 的输出就是时间序列最后一个时刻 的状态 。它作为时序数据的表示向量,可以输入给后面的一个分类网络,一般是一个多层全连接网络。它也可以作为初始状态送给又一个 RNN 。比如机器翻译问题的 Encoder-Decoder 结构。Encoder 是一个 RNN ,它将源语言(待翻译)句子(时序数据)的信息浓缩在表示向量 中,将其作为初始状态赋给另一个 RNN —— Decoder 。这个作为 Decoder 的 RNN 需要生成一个时序数据,也就是目标语言的句子。它的结构上有一个附加物:它将状态向量所谓输入送给一个多层全连接神经网络。这个全连接网络是一个分类网络,它的输出是 路 SoftMax 。 是目标语言的词(或 Token)数量。有两个特殊的词(Token):<START> 和 <END> 。它们用来标识一个句子的开始和结束(这都是技术细节,并不重要)。分类网络负责选择一个词(或 Token)。

 

Decoder 这样工作:它的初始状态被赋予 ,上标 表示这是 Decoder 的状态向量。输入是 <START>(的嵌入向量),计算出这时候下一时刻的状态 。 经过全连接分类网络输出 路概率。这时候可以取概率最大的词(Token),也可以根据概率随机采样下一个词(Token)。这就是翻译出来的句子在 <START> 之后的第一个词。然后再将选出来的词的嵌入向量作为第二步的输入,此时 Decoder 的状态是 。计算得出再下一时刻的状态 ,经分类网络选出再下一个词。此过程持续下去,直到选出来的词是 <END> ,这时结束翻译,得到一个目标语言的句子。

 

从这个 Encoder-Decoder 结构大家也能看出毛病了。Encoder 固然浓缩了源语言序列的信息且考虑进了词(Token)之间的先后顺序,但是位于句子前边的词(Token)的信息会不断被后面的词(Token)的信息冲淡。不光是翻译问题,任何时序数据在这种简单 RNN 中都会遇到这样的问题:前面的信息被后面的信息冲淡、掩盖。为解决这个问题,我们可以借鉴上面计算图中那个“总线”的思路,搞一条信息通路在时间上贯穿先后(在计算图上贯穿左右)。这就是 LSTM 的思想。LSTM 的 cell 是这样的:

 

橘色框是 LSTM 的一个 cell 。时间上的前后相继在计算图中体现为 cell 的先后相接。图中我们打开了 cell 的内部,展示了它进行的具体运算。初看复杂,理解其含义之后就不复杂了。首先,cell 接受当前时刻的输入信息 ,还接受上个时刻的状态信息。如前文所说,我们可以把 整体看作是 LSTM 在 时刻的“状态向量”。但这里我们还是将 称作状态向量,而将 称作长期记忆向量。注意,图中用方块标记的节点表示的是这样的运算(以最左边的节点为例):

 

 

这就是简单 RNN 的 cell 执行的运算。如果长期记忆向量 是 维向量(与状态向量 同维),则 是 矩阵, 是 矩阵, 是 维偏置向量。用它们分别乘上 和 后再相加,对每个分量施加 Signoid 函数将各分量值压缩在 0 和 1 之间,得到一个与 维向量。标记 的节点不是矩阵乘法节点,而是将输入的两个同维向量对应分量相乘。我们在这里标记 ,表示 f orget 。这一部分称作“遗忘门”( f orget gate)。它用分量在 0 和 1 之间的向量逐分量与 的对应分量相乘。若“遗忘门”的分量较小(接近 0),则 的对应分量几乎被“抹去”。若 “遗忘门”的分量较大(接近 1),则 的对应分量几乎被保留。“遗忘门”控制长期记忆中的信息的遗忘程度,依据的是当前时刻的输入和 LSTM 上一时刻的状态。当前新的信息要被添加进长期记忆。在那之前首先要提取一个与 同维的向量,它应该浓缩当前时刻的输入和上个时刻的状态,同样用一个“圆角方块”单元计算:

 

 

权值矩阵 与 分别与“遗忘门”的 与 形状相同,但不是同两个矩阵,是另一套。执行的运算仍是简单 RNN 的 cell ,但激活函数不是 SIgmoid ,而是 tanh 。tanh 的输出在 [-1,1] 区间。 不是“门”,它是浓缩提炼代表了当前时刻的信息。不是将它直接加到 上,而是用了一个与“遗忘门”相同的机制,对新信息(向量)的每个分量施加了了一个限制(用 (0,1) 区间的数去乘),这个门称为“输入门”( i nput gate)。当然,“输入门”又有自己的一套权值矩阵和偏执向量: 、 和 。“遗忘门” ,“输入门” 以及新信息 共同更新了长期记忆向量: 的各分量被“遗忘门”抹去一部分,新信息 的各分量被“输入门”抹去一部分,再加到长期记忆上,得到当前时刻的长期记忆向量 。

 

最后,我们还需要更新 LSTM 的当前状态。当前状态从长期记忆中提取,首先对 的各分量施加 tanh 激活函数,将它们的值控制在 [-1,1] 区间,然后再使用一个“输出门”,用“输出门”的各分量“过滤”(施加了 tanh )之后的 作为当前时刻的状态 。

 

怎幺“遗忘”,怎幺“记忆”,“记忆”什幺,怎幺从记忆中提取当前状态,都是由那些“门”(还有 )控制的。而这些“门”(还有 )都来自于上一时刻的状态 以及当前时刻的输入 。计算的方式其实就是一个全连接层,具体受控于 8 个权值矩阵( )和 4 个偏置向量( )。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注