NLP/图解 Attention

NLP/图解 Attention

本文翻译自 https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-seq2seq-models-with-attention/

Attention 被广泛用于序列到序列(seq2seq)模型,这是一种深度学习模型,在很多任务上都取得了成功,如:机器翻译、文本摘要、图像描述生成。谷歌翻译在 2016 年年末开始使用这种模型。有 2 篇开创性的论文(Sutskever et al., 2014, Cho et al., 2014)对这些模型进行了解释。

然而,我发现,想要充分理解模型并实现它,需要深入理解一系列概念,而这些概念是层层递进的。我认为,如果能够把这些概念进行可视化,会更加有助于理解。这就是这篇文章的目标。当然你需要先了解一些深度学习的知识,才能读懂这篇文章。我希望这篇文章,可以对你理解上面提到的 2 篇论文有帮助。

一个典型的序列到序列(seq2seq)模型,接收的输入是一个(单词、字母、图像特征)序列,输出是另外一个序列。一个训练好的模型如下图所示:


在神经机器翻译中,一个输入序列是指一连串的单词。类似地,输出也是一连串单词。


进一步理解细节

模型是由编码器(Encoder)和解码器(Decoder)组成的。其中,编码器会处理输入序列中的每个元素,把这些信息转换为一个向量(称为上下文(context))。当我们处理完整个输入序列后,编码器把上下文(context)发送给解码器,解码器开始逐项生成输出序列中的元素。


这种机制,同样适用于机器翻译。


在机器翻译任务中,上下文(context)是一个向量(基本上是由数字组成的数组)。编码器和解码器一般都是循环神经网络(你可以看看 Luis Serrano写 的 一篇关于循环神经网络 的精彩介绍)。


上下文是一个浮点数向量。在下面,我们会可视化这些向量,使用更明亮的色彩来表示更大的值。

你可以在设置模型的时候设置上下文向量的长度。这个长度就是编码器 RNN 的隐藏层神经元的数量。上图的上下文向量长度为 4,但在实际应用中,上下文向量的长度可能是 256,512 或者 1024。

根据设计,RNN 在每个时间步接受 2 个输入,包括:

  • 输入序列中的一个元素(在解码器的例子中,输入是指句子中的一个单词)
  • 一个 hidden state(隐藏层状态)

这里提到的单词都需要表示为一个向量。为了把一个词转换为一个向量,我们使用一类称为词嵌入(Word Embedding) 的方法。这类方法把单词转换到一个向量空间,这种表示形式能够捕捉大量的单词的语义信息(例如,king - man + woman = queen)。


我们在处理单词之前,需要把他们转换为向量。这个转换过程是使用 Word Embedding 算法来完成的。我们可以使用预训练好的词嵌入向量,或者在我们的数据集上训练自己的词嵌入向量。通常词嵌入向量长度是 200 或者 300,为了简单起见,我们这里的向量长度是 4

现在,我们已经介绍完了向量/张量的基础知识,让我们回顾一下 RNN 的运行机制,并可视化这些 RNN 模型:


RNN 在每个时间步,采用上一个时间步的 hidden state(隐藏层状态) 和当前时间步的输入向量,来得到输出。在下文,我们会使用类似的动画,来说明这些向量在神经机器翻译模型里的运作机制。

在下面的动画中,编码器和解码器在每个时间步处理输入,并得到输出。由于编码器和解码器都是 RNN,RNN 会根据当前时间步的输入,和前一个时间步的 hidden state(隐藏层状态),更新当前时间步的 hidden state(隐藏层状态)。

让我们看下编码器的 hidden state(隐藏层状态)。注意,最后一个 hidden state(隐藏层状态)实际上是我们传给解码器的上下文(context)。


同样地,解码器也持有 hidden state(隐藏层状态),而且也需要把 hidden state(隐藏层状态)从一个时间步传递到下一个时间步。我们现在关注的是 RNN 的主要处理过程,因此没有在上图中可视化解码器的 hidden state,因为这个过程和解码器是类似的。

现在让我们用另一种方式来可视化序列到序列(seq2seq)模型。下面的动画会让我们更加容易理解模型。这种方法称为展开视图。其中,我们不只是显示一个解码器,而是在时间上展开,每个时间步都显示一个解码器。通过这种方式,我们可以看到每个时间步的输入和输出。


Attention 讲解

虽然我们将编码器的上下文向量传给解码器。但实际上,上下文向量是这类模型的瓶颈。因为这使得模型在处理长文本时面临非常大的挑战。

Bahdanau et al., 2014Luong et al., 2015 两篇论文中,提出了一种解决方法。这 2 篇论文提出并改进了一种叫做注意力(Attention)的技术,它极大地提高了机器翻译的效果。注意力使得模型可以根据需要,关注到输入序列的相关部分。


在上图中,在第 7 个时间步,注意力机制使得解码器在产生英语翻译之前,可以将注意力集中在 "étudiant" 这个词(在法语里,是 "student" 的意思)。这种从输入序列聚焦相关部分的能力,使得注意力模型,比没有注意力的模型,产生更好的结果。

让我们继续从高层次整体来理解注意力模型。一个注意力模型和经典的序列到序列(seq2seq)模型相比,主要有 2 点不同:

首先,编码器会把更多的数据传递给解码器。编码器把所有时间步的 hidden state(隐藏层状态)传递给解码器,而不是只传递最后一个 hidden state(隐藏层状态)。


第二,注意力模型的解码器在产生输出之前,会做一些额外的处理。为了把注意力集中在与该时间步相关的那些输入部分。解码器做了如下的处理:

  1. 查看所有接收到的编码器的 hidden state(隐藏层状态)。其中,编码器中每个 hidden state(隐藏层状态)都对应到输入句子中一个单词。
  2. 给每个 hidden state(隐藏层状态)打一个分数(我们先不说明这个分数的计算过程)。
  3. 将每个 hidden state(隐藏层状态)乘以经过 softmax 归一化的对应分数,从而使得,得分高对应的 hidden state(隐藏层状态)会被放大,而得分低对应的 hidden state(隐藏层状态)会被缩小弱化。


这个加权平均的步骤,会发生在解码器的每个时间步。

现在,让我们把所有内容都融合到下面的图中,来看看注意力模型的整个过程:

  1. 注意力模型的解码器 RNN 的输入包括:一个 <END> 的词嵌入向量,和一个经过初始化的解码器 hidden state(隐藏层状态)。
  2. RNN 处理上述的 2 个输入,产生一个输出(注意这里的输出会被忽略)和一个新的 hidden state(隐藏层状态向量,图中表示为 h4)。
  3. 注意力的计算步骤:我们使用编码器的 hidden state(隐藏层状态)和 h4 向量来计算这个时间步的上下文向量(C4)。
  4. 我们把 h4 和 C4 拼接起来,得到一个向量。
  5. 我们把这个向量输入一个前馈神经网络(这个网络是和整个模型一起训练的)。
  6. 前馈神经网络的产生的输出表示这个时间步输出的单词。
  7. 在下一个时间步重复这个步骤。


下图,我们使用另一种方式来可视化注意力,看看在每个解码的时间步中关注输入句子的哪些部分:


请注意,注意力模型不是盲目地把输出的第一个单词对应到输入的第一个单词。实际上,它从训练阶段学习到了如何在两种语言中,找到对应单词的关系(在我们的例子中,是法语和英语)。下图展示了注意力机制的准确程度(图片来自于上面提到的论文):


在上图中,你可以看到模型在输出 "European Economic Area" 时,注意力分数的分布情况。对于"européenne économique zone"这个词组,法语相对于英语的词序是相反的。而其他单词在两种语言中的顺序是类似的。

如果你觉得你准备好来学习注意力机制的代码实现,可以看看基于 TensorFlow 的 神经机器翻译 (seq2seq) 指南

我希望这篇文章会对你有帮助,文中的可视化的图片,来自于 Udacity 自然语言处理纳米课程](https://www.udacity.com/course/natural-language-processing-nanodegree--nd892)。在这门课里,我们会深入讨论更多细节,包括应用方面,并且会涉及到最近的注意力新方法,如来自于 Attention Is All You Need 的 Transformer。

我还创建了一些课程,作为 Udacity 机器学习纳米学位的一部分。我创建的课程包括无监督学习,以及使用协同过滤实现电影推荐的 jupyter notebook。

你可以通过 [@JayAlammmar](https://twitter.com/JayAlammar) 联系我,期望你的反馈。


如果你觉得这篇文章对你有帮助,不妨点个赞,让我有更多动力写出好文章。

评论