在本文中,我们将深入探讨Transformer网络中注意力的概念,特别是从编码器的角度来看。我们将涵盖以下主题:
- 什么是机器翻译?
- 注意力的需求。
- 使用循环神经网络(RNN)计算注意力的方法。
- 什么是自注意力,以及如何使用Transformer的编码器计算它?
- 编码器中的多头注意力。
机器翻译
我们将以神经机器翻译(NMT)作为本文的运行示例。NMT旨在构建和训练一个单一的大型神经网络,该网络读取一个句子并输出正确的翻译。这是通过使用序列到序列模型实现的,例如循环神经网络(RNNs),其中将一个序列作为模型的输入并产生另一个序列作为输出。
图1展示了法语到英语翻译的示例。该模型由编码器和解码器两部分组成。编码器逐个单词(按自然语言处理(NLP)术语,实际上是逐个标记)处理句子。编码器产生一个上下文,然后输入解码器。解码器会逐个单词地生成输出。
进一步说明如图2所示,编码器在每个时间步生成一个隐藏状态,而最后一个隐藏状态(隐藏状态#3)作为解码器的输入。
逐个单词处理句子可能会产生不利影响,尤其是当句子变得很长时。句子的最后一个隐藏状态可能无法编码所有必要的信息,这可能导致忘记句子的开头。这是RNN所面临的众所周知的问题。已经提出了各种方法来克服这个问题,其中包括长短期记忆(LSTM)。然而,每种方法都有其优点和缺点。注意力被提出作为解决这个问题的一种方法(Bahdanau等人)。
什么是注意力?
在图3中,考虑编码器产生的所有三个隐藏状态。解码器现在不仅依赖于最后一个隐藏状态,还可以访问编码器产生的所有隐藏状态。解码器逐个单词地生成输出,因此我们需要一种机制来通知解码器在每个时间步骤上优先考虑哪个隐藏状态以生成输出。例如,为了生成第一个单词“Je”,最重要的是隐藏状态#1;对于第二个单词“suis”,最重要的是隐藏状态#2,依此类推。
基本上,每个时间步骤我们都需要不同的隐藏状态加权方案。这将告诉解码器它需要关注输入句子的哪个部分,以便产生输出。简而言之,注意力可以表示为隐藏状态的加权和。
上面的句子“Je suis etudiant”非常直观,输入和输出句子之间存在一对一的映射关系。在下面的句子(图4)中,我们观察到对于“European Economic Area”这几个词,法语到英语的词映射是颠倒的。在这种情况下,注意力机制对于学习从一种语言到另一种语言的正确词映射非常重要。
总结一下,编码器逐个单词处理句子并存储相应的隐藏状态。然后,将这些隐藏状态传递给解码器,并应用注意力机制。
注意:注意力权重会在每个时间步骤中改变。
自注意力
注意力是一种权重方案,用于学习输入和输出序列之间的映射,即当前时间步骤产生输出所需的输入的哪个部分是重要的。现在,让我们来看下面的句子。
我们需要知道“it”指的是什么。是街道还是动物?这一点非常关键,特别是在将英语翻译成有性别的语言(如法语)时。
自注意力是一种编码输入句子中单词之间关系的方法。在上面的例子中的第一个句子中,我们查看句子中的所有单词,并以一种方式对“it”进行编码,使其比其他单词更重要,要么是动物,要么是街道。
现在,让我们看一下Transformer架构,以了解如何实现自注意力。
Transformer架构
第一个Transformer是在2017年的一篇论文中介绍的。
由于它是为机器翻译而开发的,因此它具有编码器和解码器架构。编码器和解码器分别由多个编码器和解码器堆叠而成。
我们将专注于Encoder中的多头注意力块。
首先,我们使用word2vec模型为句子中的每个单词(标记)生成向量。该模型以单词作为输入,并生成表示单词含义的向量。由word2vec模型生成的向量倾向于将意义相似的单词在向量空间中更接近,即“椅子”和“桌子”之间的距离会比“椅子”和“老虎”之间的距离更近。然后,这些向量被输入到注意力模块中。
如何计算自注意力?(查询、键、值)
以前,我们将注意力计算为RNN中隐藏状态的简单加权求和。在这里,我们将以不同的方式计算自注意力,使用查询、键和值。
这基于数据库的概念,其中你有一个查找表,由键和相应的值组成。当有人查询数据库时,你将查询与键匹配,并根据匹配获取一些值。让我们考虑一个小型数据库(见下表1),其中包含三个键:Apple、banana、chair,以及与每个键关联的值(10、5、2)。
如果我们有查询 = apple,那么我们得到输出10。但是,如果我们有查询 = fruit,我们该怎么办?如果我们查看表格,关键字“fruit”不存在。我们可以抛出错误或者做一些更聪明的事情。我们能否将水果与数据集中的其他键相关联?单词“fruit”在某种程度上与apple和banana相关,与chair无关。因此,假设fruit与apple相关(0.6)次,与banana相关(0.4)次,与chair无关(0.0)次。这些本质上就是你的注意力值。因此,如果我们现在有查询 = fruit,我们得到的输出为0.6 * 10 + 0.4 * 5 + 0.0 * 2 = 8。
但是我们如何计算注意力值呢?让我们从用一个4D向量来表示查询开始。
同样地,我们也用一个矩阵来表示键(apple、banana、chair),并使用相应的向量值。
然后可以计算注意力分数:
现在让我们稍微深入一下这是如何在编码器中实现的。
让我们从一个简单的句子“Thinking machines”开始。首先,我们为每个单词生成固定长度的嵌入向量;
对于每个单词,我们需要使用以下方程式计算查询、键和值向量。
我们还可以将输入表示为矩阵,对于我们的例子来说,
对应的查询、键和值矩阵为,
现在,我们可以计算注意力值,
多头注意力
现在让我们回到我们的句子,“这个动物没有过马路,因为它太累了”。在下面的图中,我们看到有两个注意力头;第一个(橙色)将查询“它”与“动物”相关联,而第二个(绿色)将查询“它”与“累了”相关联。因此,两个头共同形成一个更好的表示,将单词“它”、“动物”和“累了”联系在一起。
因此,使用两个注意力头,我们有
这些产生2个不同的分数;
最终的分数是通过连接来自两个注意力头的分数并将其投影回原始输入的D维度来计算的。
默认情况下,变压器模型使用8个注意力头。
通过这样,我们已经完成了对变压器编码器视角中注意力的理解。解码器使用稍微不同的带有掩码的多头注意力。我将在接下来的文章中介绍这个。