序列模型中的注意力机制

简介

综合转载以下文章:

Seq2Seq

经典RNN结构

抛开起始符和终止符不说,输入和输出序列必须要有相同的时间长度。这个对很多序列任务来说是个问题,例如文本翻译、阅读理解等,所以就有了Seq2Seq结构。

编码器Encoder把所有的输入序列都编码成一个统一的语义向量context(其实就是最后一个时刻的隐藏状态),然后再由解码器Decoder解码。在解码器Decoder解码的过程中,第一个时刻输入起始符,之后不断地将前一个时刻 $t-1$ 的输出作为后一个时刻 $t$ 的输入,循环解码,直到输出终止符符为止。Seq2Seq结构不再要求输入和输出序列有相同的时间长度,所以它被应用于很多问题中。

Embedding

在将字词和标识符作为输入时,需要把它们转化为向量,称为“嵌入”。

  1. 最简单是one-hot编码

    1
    2
    3
    4
    '<start>' : 0  <-----> [1, 0, 0, 0, ..., 0].T
    '<stop>' : 1 <-----> [0, 1, 0, 0, ..., 0].T
    'good' : 2 <-----> [0, 0, 1, 0, ..., 0].T
    'morning' : 3 <-----> [0, 0, 0, 1, ..., 0].T

    one-hot编码过于稀疏。

  2. 随机矩阵

    首先生成一个embedding随机矩阵,之后每一行随机向量代表一个字词/标识符。在Tensorflow中上述过程通过以下函数实现:

    1
    tf.nn.embedding_lookup

    而在pytorch中通过以下接口实现:

    1
    torch.nn.Embedding

    需要注意的是:train和test阶段必须使用一样的embedding矩阵。

另外,还有word2vec/elmo/bert等方法,而且有的方法也可以在训练过程中迭代更新embedding。

Train

在seq2seq结构中将 $y_t$ 作为下一时刻输入 $x_{t+1}\Leftarrow y_t$ 进网络,那么某一时刻输出 $y_t$ 错误就会导致后面全错。在训练时由于网络尚未收敛,这种效应格外明显。为了解决这个问题,Google提出了Scheduled Sampling,即在训练中 $x_t$ 按照一定概率选择输入 $y_{t-1}$ 或 $y$ 时刻对应的真实标签,如图。

Seq2Seq with Attention

Attention

当用神经网络来处理大量的输入信息时,可以借鉴人脑的注意力机制,只选择一些关键的信息输入进行处理,来提高效率。目前总体上分为两类:

  • 聚焦式(focus)注意力:自上而下的有意识的注意力,主动注意—是指有预定目的、依赖任务的、主动有意识地聚焦于某一对象的注意力;

  • 显著性(saliency-based)注意力:自下而上的有意识的注意力,被动注意—基于显著性的注意力是由外界刺激驱动的注意,不需要主动干预,也和任务无关;可以将最大池化和门控机制来近似地看作是自下而上的基于显著性的注意力机制。

    这么描述也是不太准确的,显著性注意力主要应用于弱监督的目标检测和语义分割,它们所指的显著性并不是被动的,而是先有了分类这个任务的需求,然后才注意到某些高显著性的区域。当然,这边指的任务无关,可能指的是当前任务。

目前,注意力机制一般就是特指聚焦式注意力。

seq2seq的Encoder把所有的输入序列都编码成一个统一的语义向量context,然后再由Decoder解码。由于context包含原始序列中的所有信息,它的长度就成了限制模型性能的瓶颈。如机器翻译问题,当要翻译的句子较长时,一个Context可能存不下那么多信息,就会造成精度的下降。除此之外,如果按照上述方式实现,只用到了编码器的最后一个隐藏层状态,信息利用率低下。所以如果要改进Seq2Seq结构,就是利用Encoder所有隐藏层状态 $h_t$ 解决context长度限制问题。

步骤

主要结构图:

左侧为Encoder+输入,右侧为Decoder+输出。中间为Attention。

  1. $h_t=\text{RNN}_{\text{enc}}(x_t,h_{t-1})$,Encoder方面接受的是每一个单词的word embedding,和上一个时间点的hidden state。输出的是这个时间点的hidden state。

  2. $s_t=\text{RNN}_{\text{dec}}(\hat y_{t-1},s_{t-1})$,Decoder方面接受的是目标句子里单词的word embedding,和上一个时间点的hidden state。

  3. $e_{ij}=\text{score}(s_i,h_j)$,通过Decoder的hidden states与Encoder的hidden states来计算一个分数,用于计算权重。

  4. $\alpha_{ij}=\frac{\exp(e_{ij})}{\sum_{k=1}^{T_x}\exp (e_{ik})}$,每一个Encoder的hidden states在当前时间步对应的权重。

  5. $c_i=\sum_{j=1}^{T_x}\alpha_{ij}h_j$,context向量是一个对于Encoder输出的hidden states的一个加权平均。

  6. 将context向量作为下一个时刻的输入和目标的单词串起来作为LSTM的输入,之后又得到一个hiddn state,以此循环。

  7. $\hat s_t=\tanh(W_c[c_t;s_t])$,将context向量和Decoder的hidden states拼接。

  8. $p(y_t|y_{<t},x)=\text{softmax}(W_s\hat s_t)$,计算最后的输出概率。

score function

Luong

dot

输入是Encoder的所有hidden states $H_t$:大小为 (h_dim, sequence_length)。Decoder在一个时刻上的hidden states $\bar h_s$:大小为 (h_dim, 1)

  1. 旋转 $H_t$ 与 $\bar h_s$ 做点乘得到一个 大小为 (sequence_length, 1) 的分数;
  2. 对分数做softmax,得到一个合为1的权重;
  3. 将 $H_t$ 与第2步得到的权重做点乘得到一个大小为 (h_dim, 1) 的context向量。

general

输入是Encoder的所有hidden states $H_t$:大小为 (h_dim1, sequence_length)。Decoder在一个时刻上的hidden states $\bar h_s$:大小为 (h_dim2, 1)。此处两个hidden state的维度并不一样。

  1. 旋转 $H_t$ 与 $W_a$ (h_dim1, h_dim2) 做点乘, 再和 $\bar h_s$ 做点乘得到一个 大小为 (sequence_length, 1) 的分数;
  2. 对分数做softmax得到一个合为1的权重;
  3. 将 $H_t$ 与第2步得到的权重做点乘得到一个大小为 (h_dim1, 1) 的context向量。

scaled dot

cosine

MLP

location-based function

这种方法的对齐分数仅从目标隐藏状态学习得到。

另外一个角度:Key、Value和Query

Attention机制的实质其实就是一个寻址(addressing)的过程,如上图所示:给定一个和任务相关的查询Query向量,通过计算与Key的相似性(注意力分布)并附加在Value上,从而计算Attention Value,这个过程实际上是Attention机制缓解神经网络模型复杂度的体现:不需要将所有的N个输入信息都输入到神经网络进行计算,只需要从X中选择一些和任务相关的信息输入给神经网络。

为什么要引入Key、Value和Query这个角度呢?因为很多Attention的模型的变种和任务的变种,都是围绕这三者的变化来做。

观察

在实际应用中当输入一组 $x$ ,除了可以获得输出 $y$,还能提取出 $x_t$ 与 $y_t$ 的权重数值 $\alpha_{i,j}$ 并画出来,这样就可以直观的看到时刻 $t$ 注意力机制到底“注意”了什么。

Multi-dimensional Attention

Multi-dimensional Attention是一种较为容易理解的Attention变种。原始的Attention中,每个Query对应一组Attention分数。然而,这样的表示在很多时候会认为是信息不足的,当Key本身包括多维度信息时,显然我们需要多组Attention分数共同表示其结果。于是,Multi-dimensional Attention使用2维Attention而不是1维Attention,如下表:

最终,对于每个Query我们可以计算出多个加权求和的结果,因此如果想用向量表示结果,就需要将多个Attention结果拼接起来。

此外,我们可能需要加入一些强约束来保证各组Attention能学到更加充分的信息,如果各组Attention的结果相同,那么将失去其意义。尽管随机初始化也可能隐式地让各组Attention能够到达自己的局部最优点,但显式的强约束将让该条件变得更加有力:

该式子中 $A$ 表示权重矩阵,$I$ 表示单位矩阵。利用上述约束,让权重矩阵A倾向于是正交的,进而保证各个权重维度都能尽量垂直。

这个 $A$ 权重矩阵是指注意力Attention还是指score计算时的 $\pmb W$ 矩阵?如果是注意力Attention的话,应该是 [$k$, $d$] 维的,那就是在 $k$ 个信道上要正交。

Soft Attention 和 Hard Attention

  1. Soft Attention,这是比较常见的Attention方式,对所有key求权重概率,每个key都有一个对应的权重,是一种全局的计算方式(也可以叫Global Attention)。这种方式比较理性,参考了所有key的内容,再进行加权。但是计算量可能会比较大一些。,每个权重取值在[0, 1]。其选择的信息是所有输入信息在注意力 分布下的期望。

  2. Hard Attention,直接精准定位到某个key,这个key的概率是1,其余key的概率全部是0。因此这种对齐方式要求很高。有两种实现方式:1. 一种是选取最高概率的输入信息;2. 另一种硬性注意力可以通过在注意力分布式上随机采样的方式实现。如下图所示,Hard Attention在Soft Attention的第二步和第三步中间加入了采样层,这里使用的是蒙特卡洛采样,可以保证整个模型的端到端特性。在采样层中,以标准化后的权重为概率值,随机抽样出有限个权重值,抽样中的结果权重设置为1,其余设置为0。

    硬性注意力的一个缺点是基于最大采样或随机采样的方式来选择信息。因此最终的损失函数与注意力分布之间的函数关系不可导,因此无法使用在反向传播算法进行训练。为了使用反向传播算法,一般使用软性注意力来代替硬性注意力。硬性注意力需要通过强化学习(或者使用gumbel softmax之类的)来进行训练。——《神经网络与深度学习》

Global Attention 和 Local Attention

原始的Attention即为Global Attention,具体地说,在计算语义向量时,会考虑编码器所有的隐藏状态。如下图:

由于Global Attention必须计算源句子序列所有隐藏状态,当句子长度过长会使得计算代价昂贵并使得注意力不集中,比如在翻译段落和文档的时候。因此可以通过限制注意力机制的范围,令注意力机制更加有效。

Local Attention中,计算语义向量时只关注每个目标词的一部分编码器隐藏状态。每个解码器的 $h_t$ 对应一个编码器位置 $p_t$,根据经验选定区间大小 $D$ ,进而只在编码器的 $[p_t-D, p_t+D]$ 位置使用注意力机制。根据不同的 $p_t$ 选择方式,存在两种常见的Local Attention分类:Local-m和Local-p。

  • Local-m:简单设置 $p_t$ 为 $h_t$ 对应位置。
  • Local-p:利用 $h_t$ 预测 $p_t$,进而使用高斯分布令Local Attention的权重以 $p_t$ 呈现出峰值形状。

​ 在高斯分布中,我们设置:

Hierarchical Attention

Hierarchical Attention也可以用来解决长文本注意力不集中的问题,与Local Attention不同的是,Local Attention强行限制了注意力机制的范围,忽略剩余位置。而Hierarchical Attention则使用分层的思想,在所有的状态上都利用了注意力机制,如下图:

在篇章级文本分类中,文本由多个句子组成,句子由多个词语组成。在这样的思路中,首先分别在各个句子中使用注意力机制,提取出每个句子的关键信息,进而对每个句子的关键信息使用注意力机制,提取出文本的关键信息,最终利用文本的关键信息进行篇章及文本分类。这种结构能够反映文档的层次结构。模型在单词和句子级别分别设计了两个不同级别的注意力机制,这样做能够在构建文档表示时区别地对待这些内容。Hierarchical attention可以相应地构建分层注意力,自下而上(即词级到句子级)或自上而下(词级到字符级),即下图:

和机器翻译类似,作者依旧采用Encoder-Decoder架构,然后用Word-Level Attention对全局语法和流畅性纠错,设计Character-Level Attention对本地拼写错误纠正。

Attention Over Attention

Attention Over Attention在阅读理解的完形填空任务中提出,基本思想是对Attention之后的结果再进行Attention,但具体步骤区别于Hierarchical Attention。

在Attention Over Attention中,第一次Attention的对象是Query中的词向量和阅读中整个文本构成的词向量。在第一次Attention中,我们可以获取到一个权重矩阵,两个维度分别是Query长度和文本长度,横轴和纵轴分别代表一方对另一方的注意力分布。在文本长度上进行取均值并归一化,则能得出和Query长度相同的注意力平均分布向量,可以用来表示引入了注意力机制的Query。在Query长度上归一化,则可以表示文本中各个词关于Query的注意力分布矩阵。

在第二次Attention中,我们用第一次Attention的两个结果再次求Attention权重,可以得到一个请求关于阅读文本的注意力分布向量,进而可以用来进行完形填空,例如求出 $P(\text{“Mary”}|D, Q)$。模型架构图如下:

Memory-based Attention

Memory Network也可以视为是一种Attention的变种,这里列出End-to-End Memory Network来解释Memory-based Attention,如下图(a):

图中是阅读理解任务,阅读理解任务中,给定一个阅读,阅读由多个语句构成,然后给定一个问题,返回一个答案。图中对阅读中的多个句子进行两套Embedding,蓝色部分表示抽离出的问题Embedding,对应于注意力机制中的Key,黄色部分表示抽离出的答案Embedding,对应于注意力机制中的Value。对于整个过程中,输入问题对应于注意力中的Query,与问题Embedding进行对齐匹配,得到权重后,与答案Embedding进行加权求和。整个过程中都可以和注意力机制中的步骤对应,如下:

preview

通过第5步迭代内存更新(也称为多跳)来模拟时间推理过程,以逐步引导注意到答案的正确位置。在每次迭代中,使用新内容更新查询,并且使用更新的查询来检索相关内容。一种简单的更新方法为相加 $q_{t+1}=q_t+c_t$。

没看懂为什么要更新。

Self-Attention

见另外一篇博客:Transformer

评价指标

Attention的评价方式主要分为两类:定量指标和定性指标。

定量指标

一般而言,Attention机制是模型的一部分,而非独立为一个模型。因此,想要定量评价Attention的效果一般都需要下游任务指标的支撑。例如,我们会使用BLEU值去评价机器翻译任务,那么可以在机器翻译模型中加入Attention机制,根据最终机器翻译的BLEU值的升降来评价Attention机制的好坏。

除此之外,也存在直接评价Attention机制的方法,但存在一定的困难。例如,某些数据存在实现标注好的对齐值,可以当做Attention的目标值,那么可以计算出实际值和目标值的差异来计算出对齐错误率(AER)。但这样的方式非常受限,标注对齐值的成本过大。

定性指标

定性指标也是Attention机制常用的一种评价标准,我们可以通过可视化的方式来定性评价Attention机制的好坏。

例如,在某个Seq2Seq任务中,我们可以用颜色表示Attention的权重值,深红色表示权重值接近于1,白色表示接近于0,那么我们可以通过颜色深浅的直观可视化表示来判断Attention机制的好坏。

此外,也存在其他类似的可视化表示:

任务

Attention机制比较适合有以下特点的任务:

  1. 长文本任务,document级别,因为长文本本身所携带的信息量比较大,可能会带来信息过载问题,很多任务可能只需要用到其中一些关键信息(比如文本分类),所以Attention机制用在这里正适合捕获这些关键信息。
  2. 涉及到两段的相关文本,可能会需要对两段内容进行对齐,找到这两段文本之间的一些相关关系。比如机器翻译,将英文翻译成中文,英文和中文明显是有对齐关系的,Attention机制可以找出,在翻译到某个中文字的时候,需要对齐到哪个英文单词。又比如阅读理解,给出问题和文章,其实问题中也可以对齐到文章相关的描述,比如“什么时候”可以对齐到文章中相关的时间部分。
  3. 任务很大部分取决于某些特征。举个例子,比如在AI+法律领域,根据初步判决文书来预测所触犯的法律条款,在文书中可能会有一些罪名判定,而这种特征对任务是非常重要的,所以用Attention来捕获到这种特征就比较有用。

下面的任务,其中机器翻译、摘要生成、图文互搜属于seq2seq任务,需要对两段内容进行对齐,文本蕴含用到前提和假设两段文本,阅读理解也用到了文章和问题两段文本,文本分类、序列标注和关系抽取属于单文本Attention的做法。

  1. 机器翻译:Encoder用于对原文建模,Decoder用于生成译文,Attention用于连接原文和译文,在每一步翻译的时候关注不同的原文信息。
  2. 摘要生成:Encoder用于对原文建模,Decoder用于生成新文本,从形式上和机器翻译都是seq2seq任务,但是从任务特点上看,机器翻译可以具体对齐到某几个词,但这里是由长文本生成短文本,Decoder可能需要捕获到Encoder更多的内容,进行总结。
  3. 图文互搜:Encoder对图片建模,Decoder生成相关文本,在Decoder生成每个词的时候,用Attention机制来关注图片的不同部分。
  4. 文本蕴含:判断前提和假设是否相关,Attention机制用来对前提和假设进行对齐。
  5. 阅读理解:可以对文本进行Self-Attention,也可以对文章和问题进行对齐。
  6. 文本分类:一般是对一段句子进行Attention,得到一个句向量去做分类。
  7. 序列标注:Deep Semantic Role Labeling with Self-Attention,这篇论文在softmax前用到了Self-Attention,学习句子结构信息,和利用到标签依赖关系的CRF进行比较。
  8. 关系抽取:也可以用到self attention
一分一毛也是心意