Skip to content

第四章 循环神经网络

有些文献将“循环神经网络”翻译成“递归网络”,这是不正确的。递归网络指的是 Recursive Network,是一组比较小众的模型,大约在 2013~2016 年比较火。

4.1 循环神经网络

我们在现实中会碰到很多 建模任务,之前我们学习的是图像/张量型数据的建模任务,我们可以通过 MLP 和 CNN 来完成。下面我们学习另一种建模任务——序列建模任务 Sequence Modeling。人类的自然语言、时间序列、视频数据、机器人的动作轨迹都是序列建模任务。由于我们是生活在带有时间信息的世界中,可以说 任何信息都天然的带有时间信息,只是看你是否去记录这个信息。因此我们需要对这种数据进行建模。

而对时间序列而言,数据的时间信息显得尤为重要,自然语言中的语序,股票市场中的股价上升下降等等。其中一个关键任务就是 捕捉上下文 context 信息:也就是说,在序列建模任务中,趋势信息 往往比单点信息更重要。

语言模型

语言模型 language model,LM 是自然语言处理 Natural Language Process,NLP 中的一组基础模型,也是一组核心模型。

它建模的任务就是求这样一个 条件概率分布:当给定前 i1 个词时,第 i 个词发生的概率:

P(wordi|word1,word2,,wordi1)

词表上的概率分布 为:某词典上共有 v 个词,每个词的发生概率为 P(wordi)

人类具有很有的感知上下文语言的能力——类似完形填空,语言模型任务就是要找到 词表上哪个词在当前语境下发生的概率最大。一般也将上下文称为序列知识。

MLP 实现语言模型

MLP 具有 通用表达定理,理论上可以表达任意映射/函数。因此在 MLP 中,我们要建模这样一种语言模型的条件分布,即:

  • 输入是过去的 i1 个词
  • 输出是第 i 个词的概率

MLP 实现 LM

通常我们会对句子有一个起始点 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 模型

如果我们只考虑用过去的 n 个词来预测下一个词,就称为 N-gram Model。因此我们可以考虑用 MLP 模型来实现 2-gram Model:

wi|wi1,wi2p^(wi|wi1,wi2)

概率表 是个什么东西?假设一共有 v 个词,i 个位置,那么每个位置有 v 种选择,最后整个概率表的大小为 vi,这是一个 NP 难问题!而如果是 2-gram Model,那么只用前两个词预测下一个词,一共有 3 个位置,概率表的大小为 v3,这是可以计算的了。

2-gram Model

  • 输入:任意过去相邻的两个词,一个 2000 维向量
  • 输出:用 Softmax 给出的下一个词的概率分布,一共有 v 个类

对这 v 个类进行采样,也就是 符号的含义。从多项式分布中进行采样?计算机可以生成均匀分布的随机采样,再使用雅可比公式进行映射,从而从多项式分布中进行采样。

N-gram Model 实现 LM 的问题:

  • 滚动预测会带来严重的误差累积:到目前为止没有解决,因为没有一个合适的数学模型;
  • 同时会带来参数爆炸,N-gram 就需要输入是 1000n 维,预测 m 个词的句子就需要 m 个不同参数的 MLP 网络,也就是参数量是 O(1000mn) 的;
  • 这种模型只依赖过去 n 个词的发生概率,我们称之为 局部依赖 Local Dependency,没有办法刻画所谓的 长程依赖 Longest Dependency,而长程依赖性能正是自然语言处理任务的关键能力。例如完形填空中,越难的空可能需要回溯越远的上下文。

局部依赖

因此,我们需要设计一个新的神经网络,类比 CNN 设计过程中的局部连接和参数共享设计思想,我们也引入新的设计思想——局部依赖

给定一个长度为 T 的句子,该句子出现的概率为:

P(x1,x2,,xT)=t=1TP(xt|x1,xt1)

一共 T 个条件分布,每个条件分布 P(xt|x1,xt1) 都是给定过去 t1 个词预测第 t 个词出现的概率。在这个过程中,我们引入 马尔可夫性:对于 t 时刻,首先将 t2 时刻及之前的全部编码到某个 状态向量 st2 中,然后根据这个状态向量和 t1 时刻的词向量,给出 t 时刻的概率:

P(x1,x2,,xT)=t=1Tg(st2,xt1)

我们可以认为它在 时间上的显式感受野2,也就是在 t 时刻既能看到 t1 时刻的词向量,也能看到 t2 时刻的状态变量。

这就是局部依赖假设:过去时刻的序列信息可以被编码到一个隐藏状态变量中。根据局部依赖假设,我们可以简化我们的条件概率分布表达式。

这是否和人的某种思考方式是类似的呢?是的,这也称为 记忆力假设:将完形填空的上下文转化为更容易理解的内容缓存在脑海中,然后看当前空的前几个词,根据上下文内容缓存来进行解答。

参数共享

平稳序列:如果对联合概率分布而言,有:

P(xt1,xt2,,xtn)=P(xt1+τ,xt2+τ,,xtn+τ)

即概率分布不随时间移动发生变化。也就是说,一个句子,在 任何时间窗口内的概率分布相同,在当前窗口学到的概率模型可以泛化到未来的时间窗口——可以理解为:一个人说话的习惯是不变的。本质上就是 时间上的独立同分布假设。显然,如果某个序列不具有平稳性,理论上来说,用深度学习模型是无法用过去预测未来的。

时间序列预测中有一个最基本的 baseline——简单重复,也就是把过去某段时间的序列直接搬到现在,虽然看上去很愚蠢,但是很多模型都无法超过这个 baseline。

平稳性假设就意味着如果一个隐藏层特征在 t1 时刻是有用的,那么它在任何时刻的参数都也是和 t1 时刻一样,例如某个上升沿信号是由 t1 时刻以及许多输入决定的,在 tn 时刻后又出现了一模一样的输入,那么显然还是会出现一个上升沿,即 参数在时间上的平移不变性。这个思想大大降低了参数规模(否则参数量就与时间成正比),同时效果还不错(是可以 work 的)。

有点像卷积神经网络中,确定了卷积核大小和个数,网络的参数量就固定下来了。

参数共享

循环神经网络

循环神经网络 Recurrent Neural Network,RNN 应运而生,它由三部分组成:输入层、输出层和隐藏层。我们通常在水平方向 Horizon 表示时间轴,每一个位置表示一个时刻,每个时刻都有一个输入、输出和隐藏层变量,这个隐藏层变量就是 状态变量 ht

RNN

可以看到:状态变量在时间上存在依赖关系,例如 ht+1 是由 htxt 共同生成的。由于参数相同,因此也称为 循环结构。我们称 ht 为学习到的 特征:每个时刻都有一组特征。

RNN 可以用来处理:

  • 预测任务
  • 识别任务
  • 序列到序列任务

不同的任务由输出变量决定。而输入变量一般都是向量:xtRD,如果是标量(例如温度在时间上的序列),这种时候一般会通过很小的 MLP 网络做高维嵌入后再作为输入变量,也称为 Token Embedding。

循环层

在每一个时刻,其实都是输入通过一个位于隐藏层的 MLP 网络/前馈网络(前馈:只考虑某一时刻的输入和输出的关系)得到输出。而隐藏层 ht 就是状态变量,需要传递给下一时刻的循环层:局部依赖的实现。

循环层

  • 输入:上一时刻的状态变量 ht1 和当前时刻的输入 xt
  • 输出:当前时刻的状态变量 ht 和当前时刻的输出 yt
  • 两输入两输出,有别于前馈网络

一般写为:

{ht=fW(ht1,xt)yt=Vht

可以认为 h 就是所谓的状态变量,ht 只是它在不同时刻下的接受不同输入 xt 逐渐发生的变化。这也是局部依赖的一种实现:通过 ht1 去依赖历史序列。

我们认为 ht 包含了 t 时刻之前的所有状态信息以及当前时刻的输入信息,因此是学习得到的特征。那么最后根据这个特征再做一层映射就得到输出:yt 必须要由该时刻的状态变量 ht 生成,这样才具备 特征的可解释性:即输出是特征映射的结果,而不是 xtht1 再单独开一个小 MLP 生成,否则 ht 就没有意义。RNN 中长期以来采用 fW=tanh,V=ReLU,后来人们发现采用 ReLU 可能有更好的梯度效果。

也可以写为:

ht=tanh(Wht1+Uxt)

V 甚至可以为 1,表示为回归问题。

时间轴上展开

时间轴上展开

  • 局部连接:任何一个时刻 t 只与上一时刻 t1 有连接,与其他时刻无连接
  • 参数共享:不同时刻的特征参数都是 U,V,输入层到隐藏层参数 U,隐藏层到输出层参数 V
  • 转移参数 W:负责将 ht1 转移到 ht,即 ht=tanh(Wht1+Uxt),因此也是共享的,位于循环连接

双向 RNN

如果我们已知序列的完整信息,那么可以构造一个双向的 RNN 网络:从前往后和从后往前分别生成一组状态变量(但一般现实问题没有这种结构):

双向 RNN

两组特征变量进行特征聚合:Concat 或者 Sum。可以提高时间序列上依赖关系的建模能力,在 BERT 上有所使用。

Deep RNN

深度学习之前都是一层的 RNN,现在可以在纵向加层,因为可能 xtyt 是一个复杂映射(这里的训练难点不是时间轴上的梯度爆炸/消失,而是 MLP 随着层数的增加而导致内部的梯度爆炸/消失,因此在前深度学习时代都是 Shallow RNN)

Deep RNN

对应的公式为:

y^t=softmax(Vhtl)ht1=tanh(W1ht11+U1xt)htl=tanh(Wlht1l+Ulhtl1)

可以看到,参数共享仅 局限于时间轴序列上的共享,在深度层之间的参数是不共享的。最后,计算一下损失函数(交叉熵损失函数,输出是 Softmax 得到的概率分布),T 个时刻的损失函数,C 个类:

L=1Tt=1Tc=1Cyt,clog(y^t,c)
  • yt,c 表示训练时,t 时刻类别为 c 的真实标签分布,one-hot
  • y^t,c 表示 t 时刻类别为 c 的 Softmax 输出标签分布

深度学习论文就包括 网络结构算子的计算过程损失函数

RNN 实现 LM

由于它编码了所有的历史信息,因此可以做 N-gram Model,但是它的递推公式又是一个二阶依赖。因此可以画出:

RNN 实现 N-gram Model

优点:

  • RNN 可以表达没有边界的时间上的依赖
  • RNN 编码历史信息到固定大小的隐藏状态向量中
  • 参数不会随着序列的长度增加而增加

缺点:

  • 实际上,RNN 很难建模数据中的长程依赖(信息衰减)

适用场合:

  • 数据量较少的场合
  • RNN 常被用于强化学习中(强化学习基于 马尔可夫决策过程 Markov Decision Process,MDP)

单选题:关于循环神经网络 RNN 的描述中,错误的是?

  • RNN 的核心设计思想包括局部依赖和参数共享。正确。
  • 时序上的局部依赖基于马尔可夫假设。正确,形式上是基于马尔可夫假设。
  • 参数共享同时在不同时刻和不同层之间进行。错误,是不同时刻而不是不同 MLP 层之间。
  • RNN 难以捕捉数据中的长序依赖关系。正确。

基本架构

RNN 的基本架构由问题决定:

  • 多对一:输入是序列,输出是单个变量
  • 一对多:输入是单个变量,输出是序列
  • 多对多(时间上不对齐)
  • 多对多(时间上对齐)

基本架构

多对一

多对一

RNN 中只有状态变量 ht 是在时间轴上转移,其他变量 xt,yt 都是在时间轴上相互独立。这种思想本质上是条件独立性思想:通过引入状态变量,刻画状态变量 ht 之间的变化 间接刻画 输入变量 xt 之间的变化。从形式上来看,不同 xt 之间是独立的,但实际上它们是不独立的。

作用:多档情感分析,输入是一个句子,输出是包含不同类别的向量。

挑战:

  • 输出到底依赖哪个状态 hi
    • 理论上来说应该依赖最后一个状态 ht,因为它看到了全部的历史信息;
    • 但实际上,由于信息衰减的问题,导致真实的情感可能会被 忽略
    • 因此我们会采用将所有的 hi 状态 Concat 起来输入到 MLP 中:但会引入噪声
    • 最终解决方法:需要引入 注意力机制
  • 哪一个情感词更重要?
    • 这个也是 RNN 无法解决,但是注意力机制可以解决的。

一对多

一对多

一般来说,由于是一对多问题,只能把 x1 输入到 RNN 的第一个状态。那么能否把 x1 输入到每个状态呢?也许可以吧……

一对多最典型的就是图像描述:给出一个图像,输出一段描述它的文字。在 2015 年曾是最火的方向,当时 CNN 趋于成熟(ResNet 的出现),同时 RNN 也快起步了。ICML 中 RNN 方兴未艾,CNN 提取图像特征,再用 RNN 描述句子,这才导致了“图像描述”任务的出现。

2015 年,Xu 和 Kelvin 在《Show, Attend and Tell》中指出:应该将特征向量 x1 输入到每一个状态,但是这样会导致 信息的冗余,因此他提出了注意力的机制。不同状态对应的 注意力区域 不一样。

论文图片

挑战:

  • 长程依赖:词和图像的区域的对应关系?
  • 异构 heterogeneous 的输入和输出——多模态:如今是换一个 backbone 再研究一遍。

多对多(时间上对齐)

多对多(时间上对齐)

  • 语音识别:输入是音素,输出是文字(是 平行的 parallel,但也是一个异构的输入和输出)
  • 语言模型:输入和输出基本是对齐的,但是差一个(因为是通过上一个词来预测下一个词,输入是前一个词,输出是下一个词)我们将这种模型称为 自回归的 autoregressive,用上一个时刻回归下一个时刻。
Input:{xt}|t=0T1Output:{xt+1}|t=0T1

序列到序列

序列到序列 seq2seq 是 2014 发表的 RNN 模型。

首先需要一个 编码器 Encoder 将输入序列编码到隐藏向量 ht 中,然后 解码器 Decoder 再根据隐藏向量 ht 得到输出序列。相当于是一个 多对一 + 一个 一对多 的 RNN 网络的结合,二者的转移参数 W1,W2 一般来说是不一样的:

序列到序列

例如机器翻译任务就是典型的序列到序列任务。机器翻译任务中有两个挑战:

  • 输入和输出之间是异构的
  • 实现长序列建模

seq2seq

  • 编码器的输出端是不接损失函数的:异构导致无法监督!
  • 解码器接受编码器 最后时刻的状态变量 作为输入
  • 解码器滚动输入预测值:灾难性的误差累积,至今无法解决,只能缓解
  • 解码器有损失函数:可以用来求解解码器的最优参数

机器翻译任务是序列建模中最为困难的任务之一。

采样方法:

  • 直接取概率最大的那一个:发生高频词误触,频率越高,信息越少
  • 随机采样:可能会胡言乱语
  • 集束搜索 Beam Search:贪心策略

集束搜索

机器翻译任务本质上就是输出这样一种概率分布:

P(y1,,yT|x)=P(y1|x)P(y2|x,y1)P(yT|x,y1,,yT1)

解码器中的每个状态变量就是刻画了右边的概率。现在考虑 B=3 的情况,考虑概率最大的三个词,分别将其作为条件输入到解码器中,得到三个条件概率……重复上述操作:可以得到 3n 种(爆炸!)因此我们采取 贪心策略:每一层只是从 9 个中取 3 个,下一层依靠这 3 个扩展成 9 个。搜索技术 解决了机器学习问题无法精准刻画的问题。

集束搜索

单选题:以下关于循环网络常见架构的描述中,错误的是:

  • 文本分类是“多对一”架构的典型例子:正确,多档情感分类,输出一个类别概率向量
  • 图像描述文本生成属于“一对多”架构:正确,给定一幅图像,生成描述图像的文本序列
  • 某些“多对多”架构会导致信息瓶颈:正确,机器翻译存在状态变量 ht 这个瓶颈,齐次的音频识别没有
  • 机器翻译常采用同构的“多对多”架构:错误,机器翻译是异构的

时间上的反向传播

反向传播算法是深度学习的灵魂,在没有自动求导技术之前,不会推导反向传播算法的同学就不用做深度学习了。但是因为我们大脑不做反向传播,所以深度学习是错的?!

回顾 RNN 的架构图:

RNN 架构图

反向传播就是损失函数(之和)对参数 U,V,W 的导数,以 U 为例,由于时间序列的每个时刻都有预测输出,因此损失函数需要对时间 t 求和:

LU=tLtU

考察 t=4 时刻,如上图所示:

L4U=L4y4y4h4h4U

其中:

h4=tanh(Wh3+Ux4)

注意到,以上结果是错误的!因为 h4 不仅由 x4 构成,前面的 h3 也依赖 U(参数共享导致的问题!),因此一共有 五条链y4h4hsU,s=0,1,2,3,4

LtU=s=0tLtytythththshsU

注意还需要拆分其中的 hths 的部分,即:

hths=htht1ht1ht2hs+1hs

也就是下面这张图:

RNN 反向传播

也就是随着时间序列长度增加,ts 会越来越大,因此 hths 对应的偏导数乘积长度也 逐渐增加。由于递推公式为:

ht=f(Wht1+Uxt)

计算得到(激活函数的导数乘以矩阵 W):

hths=k=s+1thkhk1=k=s+1tWTdiag[f(Whk1)]

因此,我们需要计算的是相邻时刻的状态变量的偏导数。而由于 ht,ht1Rd,因此得到的是一个 雅可比矩阵,为(或许 m=n=d?):

htht1=[htht1,1htht1,n]=[ht,1ht1,1ht,1ht1,nht,mht1,nht,mht1,n]

而由递推公式,PPT 上用的是 ht=f(Wht1)+Uxt,可以得到 #TODO 为啥?:

hkhk1=WTdiag[f(Whk1)]

由柯西不等式可知:

htht1WTdiag[f(Wht1)]σmaxγ
  • σmax 表示矩阵 WT 的最大奇异值:矩阵的范数小于等于最大奇异值
  • γ 表示对角阵 diag[f(Wht1)] 的上界
  • γ 依赖于激活函数 f,例如双曲正切函数和 Sigmoid 函数分别为 |tanh(x)|1,|σ(x)|14

因此:

hths=htht1ht1ht2hs+1hsk=s+1tWTdiag[f(Whk1)](σmaxγ)ts

机器学习看着很吓人,思想及其简单。

我们发现一件事:如果 σmaxγ<1,梯度就会消失,如果 σmaxγ>1,梯度就会爆炸。所以 梯度消失/爆炸 最早就是从 RNN 在时间上做反向传播时,“因为时间上的参数共享导致导数连乘”中来的。前馈网络中的梯度消失是这一概念的推广,它可以通过改善激活函数来解决,但是循环网络想要解决这个问题就比困难。

  • 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

分段 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 训练困难是因为 梯度在网络反向传播中受到了阻碍,即信息传播不够通畅,因此他提出:

LSTM 示意图

  • 对于每个时刻 t,我们可以获得 xtht1,此时维护一个 ct,刻画当前时刻记住的 重要信息
  • 但这是一个短期记忆能力 Short-term Memory,因为每个时刻 ct 都会发生变化
  • 于是 Schmihuber 开始思考人类是如何构建长期记忆的:
    • 首先我们需要“遗忘”:将过去状态中没有用的一部分遗忘掉
    • 然后根据输入对记忆进行选择性的更新
    • 最后利用输出门控制记忆中一部分暴露出来,进行状态变量预测

根据这个思想,Schmihuber 设计出了这样一个 三输入两输出 单元:

LSTM 单元

首先输入 ht1xt 进入一个 MLP 网络,输出有四个头(输出层,也可以认为是进入了四个 MLP 网络),输出为标量:

  • f:Forget Gate,过去的记忆 ct1 需要擦除多少
  • i:Input Gate,将多少输入信息写入 Memory 中(写多少进 ct
  • g:Gate Gate,把什么写到 Memory 中(写什么进 ct
  • o:Output Gate,记忆 ct 中有多少是暴露出来给 ht
(ifog)=(σσσtanh)[WiWfWoWg](ht1xt)

如何更新 Memory 呢?记忆等于 上一时刻的保留下来的记忆这一时刻写入的记忆

{ct=fct1+ight=otanh(ct)

就是逐元素位相乘,用于 遗忘/保留 记忆向量和输入向量中每个维度的信息。也就是说,门控单元就是就是判断向量 ct1 中的哪些维度需要被擦除(蒙版 Mask 的思想)。

我们知道 ht 表示 特征,可以用来做预测,那么所谓的 Memory ct 到底在干什么事?它提供了一种新的机制,用于保存状态变量之外的信息,它对预测任务帮助不大,但是可以用来了解状态变量 ht 之间的转移关系。类似人脑中的 Memory,当我们完成具体的任务时,一定不会用到所有的 Memory,而是从中取出一部分用以完成任务。

图灵奖遗忘者 :smile:

梯度流

梯度流

人们发现:从 ctct1 之间进行关于 ct 的梯度传播只通过逐元素的计算而不是矩阵运算,因此是一条 梯度高速公路 Gradient Flow Highway

ResNet 中本质上等于 f=1。此时,ct=ct1+igct 的传播没有任何阻碍。就算存在 f,而这只不过是利用 ht1xt 计算出来的一个 Sigmoid 结果,是 [0,1] 之间的一个 ,因此也没有太多的阻碍。

三个重要工作

  • LSTM
  • Highway Networks:LSTM 思想在卷积网络上的应用
  • ResNet:简化,真正发现了适用于卷积网络的直连梯度高速路,f=1

长程依赖

记忆单元的更新,在时间上展开:

ct=ftct1+itgt=ftft1ct2+ftit1gt1+itgt=τ=0t(ftfτ+1)iτgτ

如果遗忘门处于连通状态,即 f=1,那么梯度可以通过 gt 长程传播,要知道 gt=tanh(Wg[ht1,xt]T),本质上就等价于传统 RNN 中的状态变量 ht。就算写入信息量发生了饱和 gτ=1,也不会阻止梯度的流动,要知道传统的 tanh 函数一旦为 1 就说明梯度进入了饱和区,求导会变为 0

传统的 RNN 可能只能做几十个时刻,但 Karpathy et al.Visualizing and understanding recurrent networks. ICLR 2016 中利用可视化技术和 LSTM 的遗忘门技术准确学到“从句、括号、if 作用域”这些传统 RNN 难以学习到的结果,同时可以实现超过 100 个时刻的长程依赖。

窥视孔

窥视孔

带有 窥视孔 peephole 的门控单元,让其他门也可以看到隐藏的记忆变量 ct

it=σ(Wxixt+Whtht1+Wcict1+bt)ft=σ(Wxfxt+Whfht1+Wcfct1+bf)ot=σ(Wxoxt+Whoht1+Wcoct+bo)

但是 性能没有提升。2015 年是 RNN 爆发的一年,谷歌利用 神经架构搜索 想要发现到底哪个门没有用。结果发现:

  • 没有任何一个 LSTM 变种显著超过标准 LSTM(显然,标准 LSTM 的可解释性最好)
  • 遗忘门和输出门的激活函数是最重要的
  • 因此可以把输入门和遗忘门的权重合并,即 ft=1it
  • 同时可以移除输出门,这就是下一节的门控循环单元

门控循环单元 GRU

门控循环单元 Gated Recurrent Unit,GRU。唯一的区别就是遗忘门和输入门之和为 1(解释性为:遗忘门代表的以往的数据用的多了,那么输入门代表的现在的数据就一定用得少了):

门控循环单元 GRU

复位门 reset,更新门 update:

rt=σ(Wxrxt+Whrht1+br)zt=σ(Wxzxt+Whzht1+bz)

记忆单元:

c~t=tanh(Wxcxt+Whc(rtht1)+bc)ct=(1zt)ct1+ztc~t

LSTM 和 GRU 的对比

  • 区别:GRU 组合了输入门和遗忘门,用 复位门代替了输出门
  • 经验:GRU 和 LSTM 准确率相似,但 GRU 参数更少,训练速度更快

单选题:以下关于 LSTM 或 GRU 的描述中,错误的是?

  • LSTM 的设计初衷是通过记忆增强梯度的流动:正确。
  • 遗忘门处于连通状态时,梯度可以长序流动:正确。
  • 遗忘门和输出门激活函数是影响 LSTM 最大的部分:正确。
  • GRU 和 LSTM 效果相近,但 GRU 速度较慢:错误,GRU 更快一些。

梯度裁剪

梯度裁剪就是用于解决梯度爆炸现象的技巧。

梯度悬崖

通过对只有一层隐藏层的 RNN 做可视化,我们发现它的梯度存在悬崖 High-curvature Walls。而地形图中固有的这些悬崖峭壁是无法通过优化算法很好地解决的,只能引入数学上不那么完美的技巧。

梯度裁剪的方法:

  • 计算梯度 g^=εθ
  • 如果梯度过大,即 g^>threshold,裁剪为上限大小:
g^g^g^threshold

随机丢弃的变种

这种全连接网络显然是一个 过参数化 的结构,是很容易过拟合的,因此需要随机丢弃 Variational Dropout 技术来避免过拟合:

Dropout 对比

  • Naive Dropout RNN:仅对于纵向的 MLP 网络,随机丢弃 50% 的神经元和边
  • Variational RNN:丢弃要保持步调一致,否则无法参数共享,每个时刻都丢弃对应位置的神经元;同时随机丢弃转移矩阵对应的连接(recurrent connections)

层归一化

回顾我们之前在卷积神经网络中做的 Batch Normalization 处理,在每一个通道的 N 条样本上求均值和方差后做归一化处理(批量归一化可以使得地形图更加平坦):

a¯il=gilσil(ailμil)μil=ExP(x)[ail]σil=ExP(x)[(ailμil)2]

但是在 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:用这个时刻预测下一个时刻的词。

202304230800332

p(y1,...,yT)=t=1Tp(yt|y1,...,yt2,yt1)=t=1Tg(st2,yt1)

编码器和解码器之间的联系太弱了。

p(y1,...,yT|x1,...,xT)=t=1Tp(yt|c,y1,,yt1)=t=1Tg(c,st2,yt1)

将原始数据编码到上下文向量 c 中,

自然语言中,不同语言的语序不同,下个时刻就不是依赖上个时刻了。

如果我能看到所有的信息

注意力

加连接。又回到了全局连接。

对于解码器的任何状态 si,我都要看到编码器的所有词 x1,x2,,xT

注意力最重要的就是算 si1xj 之间的关系:

eij=a(si1,xj)

a 可以是一个小的神经网络,用来计算 相似度,网络的输出就是相似度。

eij=vaTtanh(Wasi1+Uahj)

注意力是服从概率分布的,概率和为 1,因此用 Softmax 计算概率。

αij=exp(eij)j=1Texp(eij)

上式 αijj 求和是 1,表示在状态 i 下,注意力在每个 j 的分配情况。

202304230821514

我们认为 ci 是输入的注意力加权和,指的是在第 i 个时刻,输入的上下文向量。

回到原来的公式:

si=f(si1,yi1,ci)

Released under the MIT License.