第四章 循环神经网络
有些文献将“循环神经网络”翻译成“递归网络”,这是不正确的。递归网络指的是 Recursive Network,是一组比较小众的模型,大约在 2013~2016 年比较火。
4.1 循环神经网络
我们在现实中会碰到很多 建模任务,之前我们学习的是图像/张量型数据的建模任务,我们可以通过 MLP 和 CNN 来完成。下面我们学习另一种建模任务——序列建模任务 Sequence Modeling。人类的自然语言、时间序列、视频数据、机器人的动作轨迹都是序列建模任务。由于我们是生活在带有时间信息的世界中,可以说 任何信息都天然的带有时间信息,只是看你是否去记录这个信息。因此我们需要对这种数据进行建模。
而对时间序列而言,数据的时间信息显得尤为重要,自然语言中的语序,股票市场中的股价上升下降等等。其中一个关键任务就是 捕捉上下文 context 信息:也就是说,在序列建模任务中,趋势信息 往往比单点信息更重要。
语言模型
语言模型 language model,LM 是自然语言处理 Natural Language Process,NLP 中的一组基础模型,也是一组核心模型。
它建模的任务就是求这样一个 条件概率分布:当给定前
词表上的概率分布 为:某词典上共有
人类具有很有的感知上下文语言的能力——类似完形填空,语言模型任务就是要找到 词表上哪个词在当前语境下发生的概率最大。一般也将上下文称为序列知识。
MLP 实现语言模型
MLP 具有 通用表达定理,理论上可以表达任意映射/函数。因此在 MLP 中,我们要建模这样一种语言模型的条件分布,即:
- 输入是过去的
个词 - 输出是第
个词的概率
通常我们会对句子有一个起始点 Start Of Sentence,SOS 和终止点 End Of Sentence,EOS。同时我们将所有的词用一个向量来表达,这也是机器学习/深度学习处理数据的通用方式:将数据单元转为向量。词向量 一般是 1000 维向量,一般也称一个词为一个 token。
如上图所示,一共有 7 个词,那么输入是一个 7000 维向量,输出是 下一个词在词表上的概率分布。之后我们根据这个概率分布进行 采样 Sampling,注意不是 Softmax 分类的那种赢者通吃,而是按照概率进行随机采样(小概率事件也是可能发生的)。
问题:一个词用 1000 维向量来表达是否太浪费?
一般词向量技术是用 300~1000 维来做,不仅不浪费而且刚刚好。
MLP 实现 LM 的缺点:
- MLP 的输入维度太多庞大(MLP 不接受矩阵/张量输入,只接受向量输入),MLP 的隐藏层参数太多。MLP 的隐藏层单元一般是 输入单元个数的 1/2~1/10 左右,MLP 中较少单元数的隐藏层被称为 瓶颈层 Bottleneck Layer。
- 同时,在 MLP 中 输入维度是可交换的,这样就丧失了时间序列的重要信息。虽然没有完全丢失,只是丢失了 时间顺序,类似 MLP 做图像处理时会丢失空间相对位置信息。
这就涉及到一个很有意思的研究课题:
当我们数据量特别庞大时,我们认为模型可以 自动学到 空间相对位置信息/时间先后顺序信息,不需要将其作为先验知识作为输入。但是当我们数据量较少时,我们认为这样不行,需要给定先验知识——争论:到底是先验知识重要还是大规模数据重要,实验室一般都是小数据 + 先验知识,大公司一般都是大规模数据。
更好的 MLP 模型
如果我们只考虑用过去的
概率表 是个什么东西?假设一共有
- 输入:任意过去相邻的两个词,一个 2000 维向量
- 输出:用 Softmax 给出的下一个词的概率分布,一共有
个类
对这
N-gram Model 实现 LM 的问题:
- 滚动预测会带来严重的误差累积:到目前为止没有解决,因为没有一个合适的数学模型;
- 同时会带来参数爆炸,N-gram 就需要输入是
维,预测 个词的句子就需要 个不同参数的 MLP 网络,也就是参数量是 的; - 这种模型只依赖过去
个词的发生概率,我们称之为 局部依赖 Local Dependency,没有办法刻画所谓的 长程依赖 Longest Dependency,而长程依赖性能正是自然语言处理任务的关键能力。例如完形填空中,越难的空可能需要回溯越远的上下文。
局部依赖
因此,我们需要设计一个新的神经网络,类比 CNN 设计过程中的局部连接和参数共享设计思想,我们也引入新的设计思想——局部依赖。
给定一个长度为
一共
我们可以认为它在 时间上的显式感受野 是
这就是局部依赖假设:过去时刻的序列信息可以被编码到一个隐藏状态变量中。根据局部依赖假设,我们可以简化我们的条件概率分布表达式。
这是否和人的某种思考方式是类似的呢?是的,这也称为 记忆力假设:将完形填空的上下文转化为更容易理解的内容缓存在脑海中,然后看当前空的前几个词,根据上下文内容缓存来进行解答。
参数共享
平稳序列:如果对联合概率分布而言,有:
即概率分布不随时间移动发生变化。也就是说,一个句子,在 任何时间窗口内的概率分布相同,在当前窗口学到的概率模型可以泛化到未来的时间窗口——可以理解为:一个人说话的习惯是不变的。本质上就是 时间上的独立同分布假设。显然,如果某个序列不具有平稳性,理论上来说,用深度学习模型是无法用过去预测未来的。
时间序列预测中有一个最基本的 baseline——简单重复,也就是把过去某段时间的序列直接搬到现在,虽然看上去很愚蠢,但是很多模型都无法超过这个 baseline。
平稳性假设就意味着如果一个隐藏层特征在
有点像卷积神经网络中,确定了卷积核大小和个数,网络的参数量就固定下来了。
循环神经网络
循环神经网络 Recurrent Neural Network,RNN 应运而生,它由三部分组成:输入层、输出层和隐藏层。我们通常在水平方向 Horizon 表示时间轴,每一个位置表示一个时刻,每个时刻都有一个输入、输出和隐藏层变量,这个隐藏层变量就是 状态变量
可以看到:状态变量在时间上存在依赖关系,例如
RNN 可以用来处理:
- 预测任务
- 识别任务
- 序列到序列任务
不同的任务由输出变量决定。而输入变量一般都是向量:
循环层
在每一个时刻,其实都是输入通过一个位于隐藏层的 MLP 网络/前馈网络(前馈:只考虑某一时刻的输入和输出的关系)得到输出。而隐藏层
- 输入:上一时刻的状态变量
和当前时刻的输入 - 输出:当前时刻的状态变量
和当前时刻的输出 - 两输入两输出,有别于前馈网络
一般写为:
可以认为
我们认为
也可以写为:
时间轴上展开
- 局部连接:任何一个时刻
只与上一时刻 有连接,与其他时刻无连接 - 参数共享:不同时刻的特征参数都是
,输入层到隐藏层参数 ,隐藏层到输出层参数 - 转移参数
:负责将 转移到 ,即 ,因此也是共享的,位于循环连接
双向 RNN
如果我们已知序列的完整信息,那么可以构造一个双向的 RNN 网络:从前往后和从后往前分别生成一组状态变量(但一般现实问题没有这种结构):
两组特征变量进行特征聚合:Concat 或者 Sum。可以提高时间序列上依赖关系的建模能力,在 BERT 上有所使用。
Deep RNN
深度学习之前都是一层的 RNN,现在可以在纵向加层,因为可能
对应的公式为:
可以看到,参数共享仅 局限于时间轴序列上的共享,在深度层之间的参数是不共享的。最后,计算一下损失函数(交叉熵损失函数,输出是 Softmax 得到的概率分布),
表示训练时, 时刻类别为 的真实标签分布,one-hot 表示 时刻类别为 的 Softmax 输出标签分布
深度学习论文就包括 网络结构、算子的计算过程、损失函数。
RNN 实现 LM
由于它编码了所有的历史信息,因此可以做 N-gram Model,但是它的递推公式又是一个二阶依赖。因此可以画出:
优点:
- RNN 可以表达没有边界的时间上的依赖
- RNN 编码历史信息到固定大小的隐藏状态向量中
- 参数不会随着序列的长度增加而增加
缺点:
- 实际上,RNN 很难建模数据中的长程依赖(信息衰减)
适用场合:
- 数据量较少的场合
- RNN 常被用于强化学习中(强化学习基于 马尔可夫决策过程 Markov Decision Process,MDP)
单选题:关于循环神经网络 RNN 的描述中,错误的是?
- RNN 的核心设计思想包括局部依赖和参数共享。正确。
- 时序上的局部依赖基于马尔可夫假设。正确,形式上是基于马尔可夫假设。
- 参数共享同时在不同时刻和不同层之间进行。错误,是不同时刻而不是不同 MLP 层之间。
- RNN 难以捕捉数据中的长序依赖关系。正确。
基本架构
RNN 的基本架构由问题决定:
- 多对一:输入是序列,输出是单个变量
- 一对多:输入是单个变量,输出是序列
- 多对多(时间上不对齐)
- 多对多(时间上对齐)
多对一
RNN 中只有状态变量
作用:多档情感分析,输入是一个句子,输出是包含不同类别的向量。
挑战:
- 输出到底依赖哪个状态
? - 理论上来说应该依赖最后一个状态
,因为它看到了全部的历史信息; - 但实际上,由于信息衰减的问题,导致真实的情感可能会被 忽略。
- 因此我们会采用将所有的
状态 Concat 起来输入到 MLP 中:但会引入噪声 - 最终解决方法:需要引入 注意力机制。
- 理论上来说应该依赖最后一个状态
- 哪一个情感词更重要?
- 这个也是 RNN 无法解决,但是注意力机制可以解决的。
一对多
一般来说,由于是一对多问题,只能把
一对多最典型的就是图像描述:给出一个图像,输出一段描述它的文字。在 2015 年曾是最火的方向,当时 CNN 趋于成熟(ResNet 的出现),同时 RNN 也快起步了。ICML 中 RNN 方兴未艾,CNN 提取图像特征,再用 RNN 描述句子,这才导致了“图像描述”任务的出现。
2015 年,Xu 和 Kelvin 在《Show, Attend and Tell》中指出:应该将特征向量
挑战:
- 长程依赖:词和图像的区域的对应关系?
- 异构 heterogeneous 的输入和输出——多模态:如今是换一个 backbone 再研究一遍。
多对多(时间上对齐)
- 语音识别:输入是音素,输出是文字(是 平行的 parallel,但也是一个异构的输入和输出)
- 语言模型:输入和输出基本是对齐的,但是差一个(因为是通过上一个词来预测下一个词,输入是前一个词,输出是下一个词)我们将这种模型称为 自回归的 autoregressive,用上一个时刻回归下一个时刻。
序列到序列
序列到序列 seq2seq 是 2014 发表的 RNN 模型。
首先需要一个 编码器 Encoder 将输入序列编码到隐藏向量
例如机器翻译任务就是典型的序列到序列任务。机器翻译任务中有两个挑战:
- 输入和输出之间是异构的
- 实现长序列建模
- 编码器的输出端是不接损失函数的:异构导致无法监督!
- 解码器接受编码器 最后时刻的状态变量 作为输入
- 解码器滚动输入预测值:灾难性的误差累积,至今无法解决,只能缓解
- 解码器有损失函数:可以用来求解解码器的最优参数
机器翻译任务是序列建模中最为困难的任务之一。
采样方法:
- 直接取概率最大的那一个:发生高频词误触,频率越高,信息越少
- 随机采样:可能会胡言乱语
- 集束搜索 Beam Search:贪心策略
集束搜索
机器翻译任务本质上就是输出这样一种概率分布:
解码器中的每个状态变量就是刻画了右边的概率。现在考虑
单选题:以下关于循环网络常见架构的描述中,错误的是:
- 文本分类是“多对一”架构的典型例子:正确,多档情感分类,输出一个类别概率向量
- 图像描述文本生成属于“一对多”架构:正确,给定一幅图像,生成描述图像的文本序列
- 某些“多对多”架构会导致信息瓶颈:正确,机器翻译存在状态变量
这个瓶颈,齐次的音频识别没有 - 机器翻译常采用同构的“多对多”架构:错误,机器翻译是异构的
时间上的反向传播
反向传播算法是深度学习的灵魂,在没有自动求导技术之前,不会推导反向传播算法的同学就不用做深度学习了。但是因为我们大脑不做反向传播,所以深度学习是错的?!
回顾 RNN 的架构图:
反向传播就是损失函数(之和)对参数
考察
其中:
注意到,以上结果是错误的!因为
注意还需要拆分其中的
也就是下面这张图:
也就是随着时间序列长度增加,
计算得到(激活函数的导数乘以矩阵
因此,我们需要计算的是相邻时刻的状态变量的偏导数。而由于
而由递推公式,PPT 上用的是
由柯西不等式可知:
表示矩阵 的最大奇异值:矩阵的范数小于等于最大奇异值 表示对角阵 的上界 依赖于激活函数 ,例如双曲正切函数和 Sigmoid 函数分别为
因此:
机器学习看着很吓人,思想及其简单。
我们发现一件事:如果
- Bengio et al.“Learning long-term dependencies with gradient descent is difficult.” IEEETNN,1994 (Cited 8755)
- Pascanu et al, “On the difficulty of training recurrent neural networks”, ICML 2013 (Cited 5466)
分段 BPTT
最简单最直观的解决办法是分段反向传播。我们将时间上的反向传播 Back-Propagation Through Time,BPTT 分为不同段 Trunk,在不同的 Trunk 内求单独的损失函数,做反向传播,称为 Truncated BPTT。还有一种方法是 Random BPTT,但由于木桶效应,不如 Truncated BPTT 的效果好。
分段 BPTT 可以解决梯度消失/爆炸现象。
总结一下针对不同问题的解决方法:
- 梯度消失
- LSTM
- GRU
- 分段 BPTT
- 梯度爆炸
- 梯度裁剪
- 分段 BPTT
单选题:以下描述中,错误的是?
- BPTT 过程中,可能会发生梯度消失或者梯度爆炸:正确。
- RNN 不同层的参数共享是导致训练困难的原因:错误,是不同时刻的参数共享
- Truncated BPTT 降低了 RNN 的长序建模能力:正确,不同 Trunk 之间没有时序依赖
- RNN 训练困难与 CNN 训练困难的原因不尽相同:正确,后来人们发现残差网络的 核心思想 来自 RNN
长短期记忆单元 LSTM
这个单元可以说是 RNN 的灵魂(堪比 ResNet 之于 CNN),是由深度学习最著名的非图灵奖得主:J. Schmihuber 于 1995 年在《Long Short-Term Memory》中提出,迄今引用 7w+ 次。他认为 RNN 训练困难是因为 梯度在网络反向传播中受到了阻碍,即信息传播不够通畅,因此他提出:
- 对于每个时刻
,我们可以获得 和 ,此时维护一个 ,刻画当前时刻记住的 重要信息 - 但这是一个短期记忆能力 Short-term Memory,因为每个时刻
都会发生变化 - 于是 Schmihuber 开始思考人类是如何构建长期记忆的:
- 首先我们需要“遗忘”:将过去状态中没有用的一部分遗忘掉
- 然后根据输入对记忆进行选择性的更新
- 最后利用输出门控制记忆中一部分暴露出来,进行状态变量预测
根据这个思想,Schmihuber 设计出了这样一个 三输入两输出 单元:
首先输入
:Forget Gate,过去的记忆 需要擦除多少 :Input Gate,将多少输入信息写入 Memory 中(写多少进 ) :Gate Gate,把什么写到 Memory 中(写什么进 ) :Output Gate,记忆 中有多少是暴露出来给 的
如何更新 Memory 呢?记忆等于 上一时刻的保留下来的记忆 和 这一时刻写入的记忆:
我们知道
梯度流
人们发现:从
ResNet 中本质上等于
- LSTM
- Highway Networks:LSTM 思想在卷积网络上的应用
- ResNet:简化,真正发现了适用于卷积网络的直连梯度高速路,
长程依赖
记忆单元的更新,在时间上展开:
如果遗忘门处于连通状态,即
传统的 RNN 可能只能做几十个时刻,但 Karpathy et al.Visualizing and understanding recurrent networks. ICLR 2016 中利用可视化技术和 LSTM 的遗忘门技术准确学到“从句、括号、if 作用域”这些传统 RNN 难以学习到的结果,同时可以实现超过 100 个时刻的长程依赖。
窥视孔
带有 窥视孔 peephole 的门控单元,让其他门也可以看到隐藏的记忆变量
但是 性能没有提升。2015 年是 RNN 爆发的一年,谷歌利用 神经架构搜索 想要发现到底哪个门没有用。结果发现:
- 没有任何一个 LSTM 变种显著超过标准 LSTM(显然,标准 LSTM 的可解释性最好)
- 遗忘门和输出门的激活函数是最重要的
- 因此可以把输入门和遗忘门的权重合并,即
- 同时可以移除输出门,这就是下一节的门控循环单元
门控循环单元 GRU
门控循环单元 Gated Recurrent Unit,GRU。唯一的区别就是遗忘门和输入门之和为 1(解释性为:遗忘门代表的以往的数据用的多了,那么输入门代表的现在的数据就一定用得少了):
复位门 reset,更新门 update:
记忆单元:
- 区别:GRU 组合了输入门和遗忘门,用 复位门代替了输出门
- 经验:GRU 和 LSTM 准确率相似,但 GRU 参数更少,训练速度更快
单选题:以下关于 LSTM 或 GRU 的描述中,错误的是?
- LSTM 的设计初衷是通过记忆增强梯度的流动:正确。
- 遗忘门处于连通状态时,梯度可以长序流动:正确。
- 遗忘门和输出门激活函数是影响 LSTM 最大的部分:正确。
- GRU 和 LSTM 效果相近,但 GRU 速度较慢:错误,GRU 更快一些。
梯度裁剪
梯度裁剪就是用于解决梯度爆炸现象的技巧。
通过对只有一层隐藏层的 RNN 做可视化,我们发现它的梯度存在悬崖 High-curvature Walls。而地形图中固有的这些悬崖峭壁是无法通过优化算法很好地解决的,只能引入数学上不那么完美的技巧。
梯度裁剪的方法:
- 计算梯度
- 如果梯度过大,即
,裁剪为上限大小:
随机丢弃的变种
这种全连接网络显然是一个 过参数化 的结构,是很容易过拟合的,因此需要随机丢弃 Variational Dropout 技术来避免过拟合:
- Naive Dropout RNN:仅对于纵向的 MLP 网络,随机丢弃 50% 的神经元和边
- Variational RNN:丢弃要保持步调一致,否则无法参数共享,每个时刻都丢弃对应位置的神经元;同时随机丢弃转移矩阵对应的连接(recurrent connections)
层归一化
回顾我们之前在卷积神经网络中做的 Batch Normalization 处理,在每一个通道的
但是在 RNN 中不能使用 BN,
课程学习
先学容易的,再学难的。
样本顺序和样本选择对局部极值的影响。
注意力机制
42:00
注意力最早被使用到循环神经网络中,解决其中的固有问题。之后将注意力机制应用到神经网络的方方面面,进入了神经网络的高级阶段和深度学习的新时代。
2015 年前后,人类开始反思:人类是到底怎么思考的?
- 人类的学习是“无监督学习”和“监督学习”并存的
- 监督学习就是 上课,无监督学习就是 消化
- 现在还不能做到监督学习和无监督学习交互,还不具有持续学习的能力
- 人类的学习是和世界进行交互的
- 由此衍生出强化学习的概念
- 人类是通过观察世界进行学习的
- 人类的学习具有 注意力 和 记忆力 机制
- 长程依赖是非常弱的记忆力机制
- AI 缺乏和人类所拥有的 常识:太阳东升西落等等
人类的注意力
人类的注意力至少包括四种:
- 持续式注意力 Sustained Attention:持续关注某个特定的任务
- 选择式注意力 Selective Attention:从干扰因素中选择出关键因素(深度学习模拟)
- 交替式注意力 Alternating Attention:在不同任务中切换
- 分配式注意力 Divided Attention:同时处理多个任务
深度学习中的注意力
是选择式注意力的一种简单方式,由 Bengio 于 2014 年引入到深度学习中。
注意力就是将相关性的概念引入到模型中,使得模型可以动态分配注意力给输入的不同片段——输出究竟对应输入的哪个片段,哪个输入最影响输出。
- 时间注意力 Temporal Attention:文本信息(最早的注意力就是时间注意力)
- 空间注意力 Spatial Attention:图像信息
《Neural Machine Translation by Jointly Learning to Align and Translate.》被认为是注意力的第一篇文章,align 这个词也被称为注意力,“对齐”。
自回归
自回归 Autoregression,AR:用这个时刻预测下一个时刻的词。
编码器和解码器之间的联系太弱了。
将原始数据编码到上下文向量
自然语言中,不同语言的语序不同,下个时刻就不是依赖上个时刻了。
如果我能看到所有的信息
注意力
加连接。又回到了全局连接。
对于解码器的任何状态
注意力最重要的就是算
注意力是服从概率分布的,概率和为 1,因此用 Softmax 计算概率。
上式
我们认为
回到原来的公式: