本文我将讨论解码器部分以及如何训练一个基本的Transformer模型。
解码器部分
到目前为止,我们已经理解了编码器部分的工作原理,即输入是如何被转换成中间表示的。现在,让我们来看看解码器部分。Transformer中的这个部分负责将中间的高维表示转换成输出标记的预测。从视觉上看,它的结构如下。解码器部分由几个单独的组件组成:
现在,让我们更详细地看看解码器的每个单独组件。
输出嵌入
与编码器类似,解码器部分的输入也是首先进行嵌入。当然,这是针对输出的,即训练原始Transformer时使用的句子对中的目标短语。这里也使用了学习到的嵌入,并且Vaswani等人(2017)共享了两个嵌入层以及上面可视化的预Softmax线性层的权重矩阵。
位置编码
解码器部分执行与编码器部分完全相同的基于正弦和余弦的位置编码。
N次解码器部分
解码器部分的前两个元素在功能上与编码器部分的前两个元素相同。现在我们将看看(一些)不同之处,因为我们将要深入解码器部分——这个部分也被复制了N次(在Vaswani等人的工作中,N=6)。
解码器部分由三个子部分组成:
最后,还有一个小附录——一个线性层和一个Softmax激活函数。这些将接收解码器部分的输出,并将其转换为logits输出(即词汇表中每个标记的基于值的输出)和伪概率输出,该输出根据logit值为每个可能的标记输出分配概率。通过简单地从这些输出中取argmax值,我们可以确定这里最有可能的预测词。
现在,我们将更详细地看看所有这些方面。
掩码多头注意力
位置编码后的嵌入输入被馈送到的第一个子部分被称为掩码多头注意力部分。它是一个相当不规则但又很规则的注意力部分:
从某种程度上说,它是规则的,因为在这里我们也有查询(queries)、键(keys)和值(values)。查询和键进行矩阵乘法运算,得到一个分数矩阵,然后将这个分数矩阵与值矩阵结合,以便对目标值应用自注意力,即确定哪些输出值是最重要的。
换句话说,其流程与编码器中的多头注意力部分的流程非常相似。
除了一个关键区别之外,这个部分与编码器中的多头注意力部分相似,那就是这个部分是解码器的一部分,负责预测下一个必须输出的目标。
当我作为一个人类构造一个短语以产生下一个单词时,我不能依赖所有未来的单词来做到这一点。相反,我只能依赖我之前已经产生的单词。这就是为什么经典的多头注意力块在解码器部分不起作用的原因,因为同样的事情也适用:在预测一个标记时,解码器不应该能够知道未来的输出(尤其是它们的注意力值),因为否则它在预测现在时就能窥见未来。
因此,上述流程将不起作用,必须进行调整。Vaswani等人(2017)通过向多头注意力层的流程中添加一个掩码来做到这一点,使其成为一个掩码多头注意力层。
但这个掩码是什么呢?
回忆一下,查询(queries)和键(keys)之间的矩阵乘法(MatMul)会产生一个分数矩阵,这个矩阵经过缩放后会被送入一个SoftMax层。当这个过程发生时,我们会得到每个标记/单词的(条件)伪概率,这些伪概率告诉我们给定另一个单词(或标记)时该单词的重要性。但是,正如你所看到的,如果我们不想看到未来的信息,这就会成为问题:如果我们正在预测<I>之后的下一个标记,它应该是<am>,我们就不想知道<doing>在它之后出现;因为当人们在即兴产生单词时,他们根本就不知道这一点。
这就是为什么在通过Softmax生成伪概率之前,要对缩放后的分数矩阵应用掩码的原因。也就是说,如果这是我们的分数矩阵……
……我们会应用一个所谓的“前瞻掩码”(look-ahead mask)。这是一个简单的矩阵加法操作:我们在分数矩阵上加上另一个矩阵,其中的值要么是零,要么是负无穷大。如你所见,对于一个标记来说所有可见的值(即所有之前的值)都被设置为零,所以它们保持不变。其他的值(Vaswani等人(2017)称之为非法连接)与负无穷大相结合,因此得出的值也是负无穷大。
如果我们随后应用Softmax函数,就可以看到所有未来值的重要性都被设置为零。它们不再重要。当应用掩码后,模型在预测现在时会学习只关注过去的值。这是一个非常重要的特性,它使Transformer能够更好地泛化到未见过的数据上。
添加残差和层归一化
在Transformer架构中很常见的是,掩码多头注意力段也使用了残差和层归一化。换句话说,添加了一个残差,将输入嵌入连接到加法层,将掩码多头注意力段的输出与原始的位置编码输出嵌入相结合。这允许梯度更自由地流动,有利于训练过程。层归一化进一步稳定了训练过程,从而获得了更好的结果。
与编码器输出相结合的常规多头注意力
解码器段中的第二个子段是多头注意力段。这是一个常规的多头注意力段,它计算查询和键之间的非掩码分数矩阵,然后将其应用于值,得出基于注意力的结果。
与对输入计算自注意力的编码器段不同,此段的操作略有不同。查询和键,以及因此产生的分数矩阵,都是基于编码器段的输出。换句话说,短语中某些单词的注意力得分是由之前编码的输入决定的。
这是非常有道理的,因为我们将看到,原始的Transformer是在不同语言的对上训练的(Vaswani等,2017)。例如,如果目标是将“I am doing okay”翻译成德语,那么语言之间的注意力在某种程度上是相似的,因此从编码输入生成的注意力可以用于生成解码器预测,实际上为Transformer模型赋予了序列到序列的能力。
从下图也可以看出这一点确实发生了,因为共同形成分数矩阵的查询和键与值矩阵进行了矩阵乘法运算,而值矩阵是由掩码多头注意力段和之前结合的残差生成的。换句话说,这个段将编码器输出与目标输出相结合,从而产生了从源语言“溢出”到目标语言(或更一般地,从源文本到目标文本)的能力。
添加残差和层归一化
在这里,我们同样会添加残差,并在继续之前执行层归一化。
前馈层
与编码器类似,这里也应用了一个由两个线性层和一个ReLU激活函数组成的前馈网络,且该网络是按位置应用的。
添加残差和层归一化
此网络的结果会与另一个残差相加,随后执行最终的层归一化操作。
生成标记预测
在添加了残差并对层进行归一化(在图中显示为“Add & Norm”)之后,我们可以开始着手进行实际的标记(即单词)预测。这通过一个线性层和一个Softmax激活函数来实现。在这个线性层中,它与嵌入层共享权重矩阵,会生成逻辑回归值(logits)——即给定编码输入和解码输出时,每个标记的重要性。通过Softmax函数,我们可以为词汇表中的所有标记生成输出(伪)概率。
选择标记预测非常简单。通过取最大值(argmax)对应的值,我们可以选择在给定的输入和输出下,模型应该预测的下一个标记。
训练Transformer
原始Transformer是所谓的序列到序列模型,能将输入序列转换为目标序列。这意味着,如果任务是机器翻译,那么它们应该在双语数据集上进行训练。
例如,Vaswani等人(2017)在WMT 2014的英语到德语翻译数据集上训练了原始Transformer,即为了翻译任务而进行的训练。
这个数据集的训练集包含450万对短语。
所有短语都有对应的德语短语,或者至少是类似德语的文本。
总结
Transformer正在自然语言处理领域掀起一场风暴。但其架构相对复杂,需要相当长的时间才能充分理解。因此,在本文中,我们探讨了Vaswani等人在2017年论文中提出的原始Transformer架构。