第7章 循环神经网络

1.前馈神经网络总是单向的,从输入层到低级隐层,再从低级隐藏层到高级隐藏层,最后再到输出层。但不管网络有多少层,都是一层一层地前向输出。但这其实是有问题的,因为这种前馈结构需要假设数据是独立同分布,但现实中有很多复杂的数据都不满足这个条件,例如音频数据、视频数据及自然语言数据等。

2.当我们将一篇英文文本翻译成中文文本时,句子之间,段落之间是存在上下文关系的,在特定的情景中单词的意义也不一样。并且这些数据的长度还不一样,我们很难规格化这些序列数据去训练,因此前馈网络很难处理这类变长的序列数据。当然,我们也可以将整篇文章当作一条文本数据,将整段音频当作一条音频数据一次性输入前馈网络中学习,但这样的做法数据维度实在高得难以想象,并会产生可怕的维度灾难( Curse of Dimensionality)

3.另一种方式就是对序列数据进行切割,例如将机器翻译任务中的一篇文章切成段落训练,或者将段落切成句子训练,将手写识别的句子切成一个个的单词进行识别训练。但这需要很强的人为干预,数据切分越细致,数据的上下文关系就破坏得越严重,并且数据切分不合理也会引入很强的人为噪声。

4.为了有效地处理这类序列数据,我们允许神经网络进行横向连接。当前的网络输出不仅依赖于当前的输入信息,还依赖之前的数据信息,这类网络结构就是本章的主角——循环神经网络(Recurrent Neural Network, RNN)

5.这里需要注意的是,循环神经网络有时也会被翻译成递归神经网络,但这对于真正的递归网络( Recursive Neural Network)是不公平的。循环网络代表信息在时间维度从前往后的传递和积累,其展开是一个链式结构,应用在机器翻译中是假设句子后面的信息和句子之前的信息有关。而递归网络在空间维度的展开是一个树结构,应用在机器翻译中就是假设句子是一个树状结构,由几个部分(主语,谓语,宾语)组成,每个部分又可以再分成几个小部分,即某一部分的信息由它的子树组合而来。

7.1 循环神经网络

1.我们也可以理解成循环神经网络是是在前馈网络中加入了记忆单元。当神经元前向传播时,隐藏层除了向网络的前端输出信息,还会将信息保存在记忆单元中。当执行下一条数据时,会将下一条数据与当前存储在记忆单元的信息,一起输入到隐藏层中进行特征提取,然后再将处理后的信思保存进记忆单元。

2.假设有一条长度为 (T) 的序列 (X) : (I)(H) 分别表示 RNN 的输入单元与隐藏单元数量,如下式所示,为循环网络的前馈输出。

[a_h^{t} = sum_{i=1}^I w_{ih}x_i^t+sum_{h^{'}=1}^H w_{h^{'}h}b_{h^{'}}^{t-1}
]

[b_h^{t} = f(a_h^{t})
]

其中 (x_i^t) 表示第 (t) 时刻序列数据 (x) 的第 (i) 维;(a_j^{t})(b_j^{t})分别表示 (t) 时刻第 (j) 隐藏单元的输入值与激活值;(w_{ih}) 表示输入层第 (i) 单元与隐藏层 (h) 单元的连接权重; (w_{h^{'}h}) 表示隐藏层 (h^{'}) 单元与隐藏层 (h) 单元的连接权重; (b_{h^{'}}^{t-1})表 示 (t-1) 时刻隐藏层 (h^{'}) 单元的激活值。

需要注意的是,在第 (t=1)时刻,也就是序列第一条数据输入网络时,隐藏层还没有激活值,因此我们会设置 (b_{h^{'}}^{0}=0)。当然,如果我们执行类似图像说明这样的联合RNN与CNN的任务时, (b_{h^{'}}^{0}) 就为卷积网络的输出特征。

7.1.1 循环神经元展开

1.为了更好地说明循环神经网络的工作模式,如下图所示,我们将序列数据按照时间维度展开。假设我们的序列长度为 (t) ,那么该神经网络就有 (t) 层隐藏层,(t) 层输入层。其中隐层之间的连接,以及输入层到隐藏层的连接都是共享的。

如果以静态的方式看待循环神经网络,那该网络就非常的“怪异”:虽然它有 (t) 层隐藏层,是非常深的神经网络,但却层层参数共享,实际上只有三套参数,输入层到隐藏层参数 (U) ,隐藏层到隐藏层参数 (W) ,隐藏层到输出层参数 (V)

1

这样的参数共享结构有以下两个主要优点。

1,无论序列的长度多长,学习模型始终具有相同大小的输入

2,由于每个时间步的参数都相同,因此我们可以使用相同的转移函数学习。

这两个因素使得我们不需要针对每个时间片段配置特定的参数,学习单独的函数。我们只需要学习一个转移函数即可,这样既可以节约大量的参数,并且也有利于提升模型的泛化能力,可以很方便地使用网络处理任意长的序列数据。

7.1.2 循环网络训练

1.通常,我们使用时间反向传播(BackPropagation Through Time,BPTT)算法训练循环神经网络。它是BP算法应用在循环网络上的一种扩展形式,如果将循环网络展开成一个深层参数共享网络,那么将此算法看作BP算法即可。

2.根据学习输出种类的不同,我们主要将RNN分为三种:固定长度到可变长度可变长度到固定长度可变长度到可变长度的学习。接下来我们就简单地介绍这三种训练模式。

固定长度到可变长度学习

我们将固定(特征)大小的数据传入RNN中,然后将其输出位可变长度数据。比知自动图片说明任务就采用这种网络架构。在该任务中,我们首先会将图片经过卷积网络进行图片特征提取,然后将图片特征当作RNN的隐藏层初始状态进行学习,最后RNN会输出一段该图片的文字描述。

2

可变长度到固定长度的学习

如图 7-3 所示,我们将序列数据映射到一个固定大小的隐藏层,在隐藏层中进行循环处理后,将最后隐藏层的状态输出到输出层。这种方式使得我们的序列可以由变长转化为固长,例如情感分类任务便是典型地将一段文字进行分类识别的任务。

3

这种序列到固定长度学习的任务训练起来也非常的方便,只需要将时间看作是原始前馈网络的 (t) 层,然后使用BP算法训练这种按层共享参数的“深层神经网络”即可。

可变长度到可变长度的学习

如图7-4所示,序列的每一个时间片段都对应着一个输出结果,此时我们的任务就交成了学习如何映射输入序列到输出序列。

这类任务的训练方式和前面方法类似,都是将整个序列完全地输入到网络中,然后从序列的最后一个时间片段开始时间反向传播训练,只是在训练每个时间片段时,残差除了来自于上一时间片段,还来自于当前时间片段。

[delta_h^t = f^{'}(a_h^t)(sum_{k=1}^K delta_k^t w_{hk}+sum_{h^{'}=1}^K delta_{h^{'}}^t w_{hh^{'}})
]

如上式所示, (delta_h^t) 表示第 (t) 时间片段,隐藏层 (h) 的残差。而该残差来自于当前时间片段 (t)各输出层残差与隐藏层到输出层连接权重乘积的累加。需要说明的是在 (t=T),也就是训练序列最后一个时间片段时,我们没有 (t=T+1) 时的残差,一般情况下我们会将其设置为 (0) ,因此在 (t=T) 时,我们仅需要计算当前时刻的输出层残差即可。

4

7.2.1 双向循环网络结构

1.在某些任务中,我们可能也需要整合“未来”的信息来处理当前信息。如在语音识别中,由于协同发音(co- articulation)的问题,当前的语音翻译可能需要依赖于接下来的一些音素( phonemes,最小的语音单位)。甚至由于一些语言的语法依赖关系,当前的音素也可能依赖于接下来的一些单词。在这类任务中,我们需要结合之前和之后的信息去消除歧义。双向循环神经网络( Bidirectional Recurrent Neural Networks, BRNNS)是一种结合过去和未来信息处理当前信息的网络结构,目前该网络结构广泛地应用于手写识别及语音识别领域

顾名思义,双向 RNN 其实就是由两个 RNN 同时组成,其中一个 RNN 前向处理序列数据从序列的起始片段处理;另一个 RNN 反向处理序列数据,从序列的末尾片段开始,然后倒序处理。如图7-5所示, (h^{(t)}) 表示顺序处理的子 RNN 中 (t) 时刻的隐藏单元,(g^{(t)}) 表示逆序处理的子 RNN 中 (t) 时刻的隐藏单元,在计算 (t) 时刻的输出单元 (o^{(t)}) 时,网络既考虑了之前的信息 (h^{(t)}) ,又考虑了之后的信息 (g^{(t)})

5

7.2.2 编码-解码网络结构

1.编码-解码( Encoder-Decoder)序列到序列( Sequence-to- Sequence)结构便是最简单的一种映射变长序列( Variable-length Sequence)到变长序列的网络结构。需要说明的是,我们经常将 RNN 的输入称之为“上下文(context)”,而我们需要做的是生成该上下文 C 的某种表示。这种表示你可以理解成是由输入转化的一些特定概念,它可以是一个固定向量,也可以是一个序列向量。

2.如图 7-6 所示,编码-解码主要分为以下两个部分。

我们将输入序列输入到一个称为编码器( Encoder)读者( Reader)的 RNN 中,编码器将序列映射到一个向量 C 中,通常此向量为 RNN 隐藏层的最后状态。

我们将向量 C 作为输入数据,输入到一个称为解码器( Decoder)或者写者(Writer)的RNN中,从而生成一条输出序列。

6

7.2.3 深度循环网络结构

1.RNN一般可以分解成三个部分:一是输入层到隐藏层;二是前一隐藏层到下一隐藏层;三是隐藏层到输出层。这三个部分,我们可以用三组权重矩阵来表示。虽然从训练角度出发,将隐藏层单元按照时间展开可以看作是一个非常深的权重共享网络;但从模型能力的角度,其依然只能算浅层的网络。这就好像没有公主的命(不是深层结构),却得了公主的病(训练困难,梯度消失、爆炸等问题)。

2.在 RNN 中加入深层的结构,主要有三种常用的深层 RNN 结构作为参考。

如图 7-7(a) 所示,我们将RNN中隐藏层扩展为多层 RNN 的结构(h)(z) 分别表示不同的RNN隐藏层,(h) 层为低层循环网络, (z) 层为高层循环网络,其各自隐藏层的状态在各自的层中循环。(提高了隐藏层的深度)

如图 7-7(b) 所示,我们将 RNN 的三个部分分别看作是三个多层感知机:首先,输入层到循环隐藏层会经过多层前馈网络;然后,循环隐藏层也是一个多层感知机,只有在循环隐藏层的输出オ可以连接回循环隐藏层的输入。这就如同每一时间片段的序列都经过多级处理,然后再连接到前一层;最后,从隐藏层到输出层也是一个多层感知机,隐藏层的输出经过多级的前馈处理后再进行结果输出。

图7-7(b)这种网络结构虽然大大増强了网络能力,但也变得非常难训练。如图 7-7(c) 所示,为了缓解训练困难,我们也可以将循环隐藏层设计成越层循环的方式。例知 (h) 层的些神经元处理当前时间片段后,就会将结果存储起来作为下一时间片段使用,而一些神经元会经过多层的处理后再将当前时间片段存储起来。而下一时间片段,会综合单循环以及多级循环的信息。

7

7.3 门控循环神经网络

1.循环网络有一个非常严重的缺陷,那就是很难学习长期依赖(Long-Term Dependencies)

随着 RNN 的执行,当前的重要信息会不断地被“稀释”,如果关键信息之间的跨度很大,就会出现长期依赖问题。

2.在理想情况下,关键信息的位置不应该有区别,但 RNN 却像一条“快乐的小金鱼”,记忆力很差。当处理文本分类问题时,如果关键信息出现在末尾,那我们很容易就可以学习到信息。但如果这一信息出现在开头,学习起来就非常的困。该向题的根本原因是一个老生常谈的问题一一梯度消失或爆炸爆炸,由于长时间(展开为深层)的跨度,梯度不断地消耗,到达最前面时,几乎趋向为零,也就无法学习了。

3.如果“记忆力”是一间小房子,那想要记住重要信息的最简单方法其实知识给记忆之门上把“锁”而已。“选择重要的,清除不重要的,锁住重要的”这就是本小节的重点——长短期记忆网络( Long Short-Term Memory, LSTM)及其改进的基于门控(Gated)循环网络的核心思想。该类网络是目前实际应用中最高效的序列模型,井且已泛应用于语音识别手写识别机器翻译图像说明语法解析。(简单记忆就是序列数据对应的应用,序列数据包括了文字、语言、图像、视频等)

7.3.1 LSTM

1.LSTM由一组称之为记忆块( memory blocks)的循环子网构成,每个记忆块包含一个或多个自连接的记忆细胞及三个乘法控制单元一一输入门输出门遗忘门组成,提供着类似于重置的功能。如图7-9所示,是单个细胞的LSTM结构示意图。和RNN相似,其隐藏单元也是横向地连接回隐藏单元,只是将RNN的隐藏单元替换成了具有门控功能的记忆细胞。

2.输入门、输出门、遗忘门可以看作是三个 sigmoid神经元,其输出为“0”或“1”,表示某项功能的“关闭”或“开启”。和 RNN 类似,其输入为数据的当前时间片段以及前一时间片段隐藏层的输出。当然,我们也可以加入如图7-9中的虚线所示,称之为窥视孔猫眼(peephole)连接,其表示门控单元不仅需要考虑当前的输入以及之前的输出,还需考虑自身存储的信息。

输入门控制着当前信息的输入:当信息经过输入单元激活后会和输入门进行相乘,以确定是否写入当前信息

遗忘门控制着是否重置之前的记忆信息:其与细胞之前的记忆信息进行乘法运算,以确定是否保留之前的信息

输出门控制着当前记忆信息的输出:其与当前细胞记忆信息进行相乘以确定是否输出信息

细胞单元总是将当前的输入信息与之前的记忆信息进行累加

8

带peephole连接的LSTM执行过程(前向传播)

字符说明:(a_{InGate}^t) , (a_{FGate}^t) , (a_{OutGate}^t) 分别表示 (t) 时刻输入门,遗忘门,输出门的累计值;(f(x))(g(x)), (h(x)) 为激活函数; (s) 表示细胞的记忆信息;(c) 表示LSTM中的记忆细胞数量; (I) 表示输入层数量, (H) 表示隐藏层数量。

1.计算输入门:

[a_{InGate}^t = sum_{i=1}^I w_{iInGate}x_i^t + sum_{h=1}^H w_{hInGate}b_i^{t-1} + sum_{c=1}^C w_{cInGate}s_c^{t-1}
]

[b_{InGate}^t = f(a_{InGate}^t)
]

2.计算输入单元:

[b_{Input}^t = g(sum_{i=1}^I w_{iInput}x_i^t + sum_{h=1}^H w_{hInput}b_h^{t-1})
]

3.计算遗忘门:

[a_{FGate}^t = sum_{i=1}^I w_{iFGate}x_i^t + sum_{h=1}^H w_{hFGate}b_h^{t-1} + sum_{c=1}^C w_{cFGate}s_c^{t-1}
]

[b_{FGate}^t = f(a_{FGate}^t)
]

4.更新细胞记忆:

[s_c^t = b_{FGate}^t s_c^{t-1} + b_{InGate}^t b_{Input}^t
]

5.计算输出门:

[a_{OutGate}^t = sum_{i=1}^I w_{iOutGate}x_i^t + sum_{h=1}^H w_{hOutGate}b_h^{t-1} + sum_{c=1}^C w_{cOutGate}s_c^{t-1}
]

[b_{OutGate}^t = f(a_{OutGate}^t)
]

6.计算细胞输出:

[b_{c}^t = b_{OutGate}^t h(s_c^t)
]

LSTM的反向传播过程

字符说明:(a_{InGate}^t) , (a_{FGate}^t) , (a_{OutGate}^t) 分别表示 (t) 时刻输入门,遗忘门,输出门的累计值;(f(x))(g(x)), (h(x)) 为激活函数; (s) 表示细胞的记忆信息;(c) 表示LSTM中的记忆细胞数量; (I) 表示输入层数量, (H) 表示隐藏层数量。(epsilon_c^t)(epsilon_s^t) , (delta_{OutGate}^t) , (delta_{InGate}^t) , (delta_{FGate}^t) , (delta_{c}^t) 分别表示 (t) 时刻:细胞输出梯度,细胞状态梯度,输出门梯度,输入门梯度,遗忘门梯度,输入单元梯度。

LSTM 各单元梯度分别为:

细胞输出梯度:

[epsilon_c^t = sum_{k=1}^K w_{ck} delta_{k}^t + sum_{h=1}^H w_{ch} delta_{h}^(t+1)
]

输出门梯度:

[delta_{OutGate}^t = f^{'}(a_{OutGate}^t) sum_{c=1}^C h(s_c^t) epsilon_c^t
]

细胞状态梯度:

[epsilon_s^t = b_{OutGate}^t h^{'}(s_c^t) epsilon_c^t +
]

输入单元梯度:

[delta_{c}^t = b_{InGate}^t g^{'}(a_{Input}^t) epsilon_s^t
]

遗忘门梯度:

[delta_{FGate}^t =f^{'}(a_{FGate}^t) sum_{c=1}^C s_c^{t-1} epsilon_c^t
]

输入门梯度:

[delta_{InGate}^t =f^{'}(a_{InGate}^t) g(a_{Inout}^t) epsilon_s^t
]

7.3.2 门控循环单元

1.即使我们不进行 peephole 连接,LSTM 参数也相当于传统 RNN 的4倍,前馈网络的8倍。如此多的参数,虽然使得模型能力大大加强,但同时也使得该结构过于冗余。

2.LSTM的核心思想在于“门控”的概念,如果想要记住关键的信息,那我们必须学会忽略甚至遗忘一些不重要的信息,但也许我们不需要既控制信息的输入又控制信息的输出,如果当前信息很重要,那其实已经说明过去的信息可以忽略了,而这就是门控循环单元( Gated Recurrent Units,GRUs)的基本思想。

3.GRUs使用更新门( update gate)重置门( reset gate)进行信息的更新与重置。如下式所示,其更新门以及重置门和 LSTM 的门结构类似,它们的输入都是当前时间片段的输入信息以及之前隐蔵层的输出信息乘以权重后进行累加,然后再放入 Sigmoid 函数中激活。

[u_j^t = f(sum_{i=1}^I w_{ij} x_i^t + sum_{h=1}^H w_{hj} b_h^{t-1})
]

[r_j^t = f(sum_{i=1}^I w_{ij} x_i^t + sum_{h=1}^H w_{hj} b_h^{t-1})
]

GRUs 隐藏层的输入和 LSTM 有些区别,如下式所示,前一时间片段的隐藏层输出需要先与重置门相乘检验是否重置之后,才乘以相应的权重进行累加求和计算。

[a_j^t = h(sum_{i=1}^I w_{ij} x_i^t + sum_{h=1}^H w_{hj}r_h^t b_h^{t-1})
]

GRUs 隐藏层的输出就比较简单,经过更新门的选择后输出即可。如下式所示,更新门输出“0”或“1”。“0”表示当前信息不输出,前一隐藏层输出替代当前信息进行输出;“1”表示过滤之前信息,使用当前信息进行输出。

[b_j^t = (1-u_j^t)b_j^{t-1}+u_j^ta_j^t
]