直观易懂的 Transformer 系列:自然语言处理方向

这是我撰写的 Transformer 系列文章的第三篇。我们正采用自上而下的方式讲解它的功能原理。(top-down manner 译为自上而下)在前两篇文章中,我们已经介绍了 Transformer 是什么、它的整体架构以及工作机制。

在本文中,我们将更进一步,深入探讨多头注意力机制—— 这一堪称 Transformer 核心的关键模块。

以下是本系列往期及后续文章内容的快速概览。贯穿整个系列的核心目标,不仅是让大家弄懂各个组件的工作方式,更要理解它们为何要以这样的方式运行。

  1. 功能总览(Transformer 的应用场景、相较循环神经网络的优势、架构组成模块、以及训练与推理阶段的运行机制)
  2. 工作原理(端到端的内部运行流程、数据流转路径与执行的计算操作,包括矩阵表示形式)
  3. 多头注意力机制 —— 本文主题(注意力模块在 Transformer 架构中的完整工作机制)
  4. 注意力机制提升性能的原因(不仅阐述注意力机制的功能,更解析其高效发挥作用的原理,以及该机制如何捕捉句子中词语间的关联关系)

此外,如果你对自然语言处理(NLP)的各类应用感兴趣(in general:译为各类,而非直译 “通常来说”),我还撰写了一些其他文章,或许也会符合你的需求(you might like:意译为或许也会符合你的需求,比直译 “你或许会喜欢”)。

  1. 束搜索(语音转文字与自然语言处理应用中常用的一种提升预测效果的算法)
  2. BLEU 值(BLEU 值与词错误率是衡量自然语言处理模型性能的两项核心指标)

注意力机制在 Transformer 中的应用

正如我们在第二部分所探讨的,注意力机制在 Transformer 架构中共有三处应用场景:

  1. 编码器中的自注意力机制—— 输入序列对自身执行注意力计算
  2. 解码器中的自注意力机制—— 目标序列对自身执行注意力计算
  3. 解码器中的编解码注意力机制—— 目标序列对输入序列执行注意力计算

注意力机制的输入参数 —— 查询向量、键向量与值向量

注意力层接收的输入包含三个参数,分别称为查询向量(Query)、键向量(Key)和值向量(Value)。

这三个参数的结构均相似,序列中的每个词都由一个向量来表示。

编码器自注意力机制

输入序列被送入输入嵌入层位置编码层,这两个模块会为输入序列中的每个词生成一份编码表示,这份表示同时涵盖了每个词的语义信息与位置信息。该编码表示会被输入至第一个编码器的自注意力机制中,分别作为查询向量(Query)、键向量(Key)和值向量(Value)这三个参数;随后,自注意力机制会再次为输入序列中的每个词生成新的编码表示,这份新表示还融入了每个词对应的注意力分数。当该表示流经编码器堆叠层中的所有编码器时,每一个注意力模块都会将自身计算得到的注意力分数,进一步整合到每个词的编码表示中。

解码器自注意力机制

再来看解码器堆叠层,目标序列会被送入输出嵌入层位置编码层,这两个模块会为目标序列中的每个词生成一份编码表示,这份表示同时涵盖了每个词的语义信息与位置信息。该编码表示会被输入至第一个解码器的自注意力机制中,分别作为查询向量(Query)、键向量(Key)和值向量(Value)这三个参数;随后,自注意力机制会再次为目标序列中的每个词生成新的编码表示,这份新表示同样融入了每个词对应的注意力分数。

经过层归一化处理后,该输出会被送入第一个解码器的编解码注意力机制中,作为查询向量(Query)参数

编解码注意力机制

与此同时,编码器堆叠层中最后一个编码器的输出,会被送入编解码注意力机制中,分别作为值向量(Value) 与键向量(Key) 参数。

因此,编解码注意力机制会同时获取两种编码表示:一种来自解码器自注意力机制,是目标序列的表示;另一种来自编码器堆叠层,是输入序列的表示。基于这两种表示,该机制最终生成的新编码表示中,会包含目标序列每个词对应的注意力分数,同时也会体现出输入序列注意力分数对目标序列词的影响作用。

当该表示流经解码器堆叠层中的所有解码器时,每一个自注意力模块与每一个编解码注意力模块,都会将自身计算得到的注意力分数整合到每个词的编码表示中。

多头注意力机制

在 Transformer 架构中,注意力模块会并行重复执行多次计算。其中每一次独立的计算过程,都被称为一个注意力头。注意力模块会将自身的查询向量(Query)、键向量(Key)和值向量(Value)这三组参数按 N 路拆分,并把拆分后的每一组子参数,分别送入一个独立的注意力头中进行计算。随后,所有注意力头输出的计算结果会被整合合并,生成最终的注意力分数。这种机制就被称为多头注意力机制,它能让 Transformer 具备更强的能力,为序列中的每个词编码多种关联关系与语义细节。

为了准确理解数据在模型内部的处理方式,我们将结合一个翻译任务的训练场景,详细拆解注意力模块的工作流程。我们会选取一组训练样本展开说明,该样本包含一个输入序列(英文句子 You are welcome)和一个目标序列(西班牙文句子 De nada)。

注意力机制超参数

有三个超参数用于决定数据的维度:

  1. 嵌入维度—— 嵌入向量的维度宽度(本示例中我们设置的宽度为 6)。该维度会贯穿整个 Transformer 模型的计算流程,因此也被赋予了其他名称,例如模型维度
  2. 查询维度(与键维度、值维度大小相同)—— 三组线性层分别用于生成查询矩阵、键矩阵和值矩阵,此参数指的就是这些线性层所使用的权重参数维度(本示例中我们设置的查询维度为 3)。
  3. 注意力头数量(本示例中我们设置的头数为 2)

此外,我们还会设置批次大小,这一参数对应样本数量维度。

输入层

输入嵌入层与位置编码层会生成一个维度为 (样本数量,序列长度,嵌入维度) 的矩阵,该矩阵会被送入编码器堆叠层中第一个编码器的查询向量(Query)、键向量(Key)和值向量(Value)。

为了便于直观呈现,我们会在示意图中省略批次维度(drop,省略),仅聚焦其余维度展开说明。

线性层

查询向量(Q)、键向量(K)、值向量(V)分别对应三个独立的线性层,每个线性层都配有专属的权重参数。输入数据经这三个线性层处理后,会生成对应的查询矩阵、键矩阵与值矩阵。

在各注意力头间拆分数据

此时,数据会被分配至多个注意力头中,以便每个注意力头都能独立地对其进行处理。

但需要重点理解的是,这只是一种逻辑层面的拆分。查询向量(Q)、键向量(K)和值向量(V)并不会在物理上被切分成多个独立矩阵,为每个注意力头各分配一份。实际上,查询向量、键向量和值向量各自对应一个独立的数据矩阵,只是矩阵内包含了为每个注意力头划分好的、逻辑上相互独立的区域。同理,也不存在为每个注意力头单独配置的线性层—— 所有注意力头共享同一组线性层,只是各自作用于数据矩阵中属于自己的逻辑区域。

线性层权重按注意力头进行逻辑划分

这种逻辑拆分的实现方式是,将输入数据与线性层权重在各注意力头间均匀分配。我们可以通过如下方式设定查询维度,来实现这一目标(logically partitioned per head 译为按注意力头进行逻辑划分,其中 per head 对应前文的每个注意力头,保持表述一致;partition 译为划分,比 “拆分” 更突出 “按比例分配” 的含义。):

查询维度 = 嵌入维度 ÷ 注意力头数量

在我们的示例中,这就是查询维度 = 6 ÷ 2 = 3 的原因。尽管线性层的权重(以及输入数据)是一个单独的矩阵,但我们可以将其理解为 ——把每个注意力头对应的独立层权重 “堆叠” 在了一起

因此,所有注意力头的运算均可通过单次矩阵操作完成,而无需执行 N 次独立运算。这种设计在保留各注意力头独立运算能力的同时,不仅提升了运算效率,还因所需线性层数量更少,让模型结构得以简化。

重塑查询、键、值矩阵

线性层输出的查询(Q)、键(K)、值(V)矩阵会被重塑维度,新增一个显性的注意力头维度。此时,矩阵中的每个 “分片” 就对应着单个注意力头的专属矩阵(slice:译为分片,贴合矩阵按维度切分的语境,指重塑后沿注意力头维度划分出的子矩阵。)。

该矩阵会通过交换注意力头维度与序列维度,再次进行维度重塑。尽管批次维度未被画出,但此时查询矩阵(Q)的维度已变为 (批次大小,注意力头数量,序列长度,查询维度)。

在下方的示意图中,我们可以看到示例查询矩阵(Q 矩阵) 经线性层输出后,完成拆分的完整流程。

最终步骤仅用于直观展示—— 尽管查询矩阵(Q 矩阵)在物理上是一个独立的矩阵,但我们可以将其理解为每个注意力头在逻辑上各自拥有对应的查询矩阵(visualization 译为直观展示,logically separate 译为在逻辑上各自拥有)

我们已准备好计算注意力分数

为每个注意力头计算注意力分数

此时,查询(Q)、键(K)、值(V)这三组矩阵已按注意力头完成划分,我们将通过它们来计算注意力分数。

我们将仅选取最后两个维度(序列长度维度与查询维度),演示单个注意力头的运算过程,并省略前两个维度(批次维度与注意力头维度)。本质上,我们可以将当前关注的运算逻辑理解为:它会在每个注意力头、以及批次中的每个样本上 “重复执行”(显然,实际运算并非以循环的方式逐次进行,而是通过单次矩阵操作并行完成)。

第一步是对查询矩阵(Q)和键矩阵(K)执行矩阵乘法运算。

现在需要向运算结果中加入掩码值。在编码器自注意力机制中,掩码的作用是对填充值进行掩码处理,使其不参与注意力分数的计算。

在解码器自注意力机制与解码器 - 编码器注意力机制中,会应用不同的掩码策略 —— 这两部分内容我们将在后续的流程中展开介绍。

此时,需将运算结果除以查询维度的平方根进行缩放(scaled by dividing by 译为除以…… 进行缩放,the square root of the Query size 译为查询维度的平方根),随后对其执行Softmax 操作

Softmax 的输出结果值矩阵(V 矩阵) 执行矩阵乘法运算。

编码器自注意力机制中注意力分数的完整计算流程如下:

合并各注意力头的注意力分数

此时,我们已得到每个注意力头各自对应的注意力分数,需要将它们整合为一个完整的分数。这个合并操作本质上是拆分操作的逆过程。

实现这一步的方法很简单,只需对结果矩阵进行维度重塑,移除其中的注意力头维度即可。具体步骤如下:

  • 对注意力分数矩阵执行维度重塑,交换注意力头维度与序列维度的位置。换句话说,矩阵的维度会从 (批次大小,注意力头数量,序列长度,查询维度) 变为 (批次大小,序列长度,注意力头数量,查询维度)
  • 通过维度重塑将矩阵调整为 (批次大小,序列长度,注意力头数量 × 查询维度) 的形状,以此消去注意力头维度。这一操作会将每个注意力头对应的注意力分数向量高效拼接,最终得到一个合并后的完整注意力分数(Collapse the Head dimension 译为消去注意力头维度,effectively concatenates 译为高效拼接)。

由于嵌入维度 = 注意力头数量 × 查询维度,因此合并后的分数矩阵维度为 (批次大小,序列长度,嵌入维度)。在下方的示意图中,我们可以看到示例分数矩阵完成合并的完整流程。

端到端多头注意力机制

综上所述,以下即为多头注意力机制的端到端完整流程。

多头拆分可捕捉更丰富的语义信息

嵌入向量用于表征单词的语义。在多头注意力机制中,如前文所述,输入序列(以及目标序列)的嵌入向量会在逻辑层面被拆分到多个注意力头中。这一操作的意义何在?

这意味着,嵌入向量的不同分段能够学习每个单词语义的不同层面 —— 这些语义层面均与序列中的其他单词存在关联。这一特性使 Transformer 模型得以捕捉到序列中更丰富的语义信息(separate sections of the Embedding 译为 嵌入向量的不同分段,different aspects of the meanings 译为 语义的不同层面,richer interpretations 沿用前文译法 更丰富的语义信息)。

这或许算不上一个贴合实际的例子,但有助于建立直观认知。例如,嵌入向量的某一段可能捕捉名词的 **“性” 属性 **(阳性、阴性、中性),而另一段则可能捕捉名词的 **“数” 属性 **(单数与复数)。这一点在翻译任务中可能至关重要,因为在诸多语言里,动词的选用均取决于这些因素(gender-ness:译为 **“性” 属性 **,对应语言学中名词的 “性” 范畴。cardinality:此处结合语境译为 **“数” 属性 **,而非数学领域的 “基数”,精准匹配名词 “单数 / 复数” 的语法概念。build intuition:译为建立直观认知)。

解码器自注意力机制与掩码操作解码器自注意力机制的工作原理与编码器自注意力机制完全一致,区别在于前者作用于目标序列中的每一个词。

同理,掩码操作会对目标序列中的填充词进行掩码处理。

解码器 - 编码器注意力机制与掩码操作

编码器 - 解码器注意力机制的输入来源于两个不同的部分。因此,与 “计算输入序列中各词之间关联关系” 的编码器自注意力机制、以及 “计算目标序列中各词之间关联关系” 的解码器自注意力机制不同,编码器 - 解码器注意力机制计算的是目标序列中每个词与输入序列中每个词之间的关联关系

因此,最终注意力分数矩阵中的每个元素(cell:结合矩阵语境译为元素,而非字面的 “单元格”,均对应着一个查询向量(即目标序列中的词)所有键向量(即输入序列中的词)所有值向量(即输入序列中的词) 之间的关联关系(corresponds to:译为对应着)。

同理,掩码操作会对目标输出序列中后续的词进行掩码处理,这一点在本系列第二篇文章中已有详细阐述。

总结

希望本文能帮助你清晰理解 Transformer 架构中各注意力模块的工作原理。结合我们在第二篇文章中梳理的 Transformer 整体端到端流程,至此,我们已完整介绍了整个 Transformer 架构的详细运行机制。

至此,我们已经准确理解了Transformer的工作机制。但我们尚未完全解答一个问题:为什么 Transformer 的注意力机制要采用这样的计算方式? 它为何要引入查询(Query)、键(Key)、值(Value) 这三个概念,又为何要执行我们刚刚介绍过的那些矩阵乘法操作?(notions 译为概念,performs the calculations that it does 意译为采用这样的计算方式

我们隐约能凭直觉理解它是 **“捕捉序列中每个词与其他所有词之间的关联”,但这到底意味着什么?这种关联捕捉能力,又是如何让 Transformer 的注意力机制得以理解序列中每个单词的细微语义差别 ** 的呢?(vague intuitive idea 译为 隐约能凭直觉理解

这是一个很有意思的问题,同时也是本系列最后一篇文章将要探讨的主题。一旦弄明白这一点,我们便能真正领会 Transformer 架构的精妙之处。

最后,如果你喜欢这篇文章,或许也会对我撰写的其他系列内容感兴趣,主题涵盖音频深度学习、地理定位机器学习以及图像描述生成架构。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐