transformer系列:#3 深度解析多头注意力

原文:https://mp.weixin.qq.com/s/Cg-BR4oSAvvoZsvQGTfmCQ

欢迎关注公zh: Al-Frontiers

transformer往期文章推荐

收藏! transformer学习资源汇总

transformer进阶之路:#1 整体概述

transformer进阶之路:#2 工作原理详解

这是 Transformer 系列文章的第三篇,沿用前期「自上而下」的方式讨论 Transformer。前两篇里,我们了解了 Transformer 是什么、架构,工作机制。

本篇文章我们将深入探索 「多头注意力」--------- Transformer 的大脑。

Transformer 中如何使用注意力

正如我们在第二篇 transformer进阶之路:#2 工作原理详解所讨论的,Transformer 在三个地方用到了注意力:

  1. 编码器中的自注意力:输入序列关注自己

  2. 解码器中的自注意力:目标序列关注自己

  3. 解码器中的编码器-解码器注意力:目标序列关注输入序列

注意力输入的参数:Query、Key 和 Value

注意力层接收三个参数形式的输入,分别叫做 Query(查询)Key(键)Value(值)

这三个参数的结构很相似:序列里的每个词都由一个向量来表示。

编码器自注意力

输入序列首先被送入输入嵌入和位置编码,该模块为输入序列中的每个词生成一个编码表示,该表示捕捉了每个词的含义和位置。然后,该编码表示被送入第一个编码器中的自注意力机制,该机制接收查询(Query)、键(Key)和值(Value)三个参数。自注意力机制随后也为输入序列中的每个词生成一个编码表示,但这次编码表示中加入了每个词的注意力分数。随着编码结果依次经过堆栈中的所有编码器,每个自注意力模块都会将自身的注意力分数添加到每个词的编码表示中。

解码器自注意力

在解码器栈中,目标序列首先被送入输出嵌入和位置编码,该过程会为目标序列中的每个词生成一个编码表示,该表示捕捉了每个词的含义和位置。

然后,该编码表示被送入第一个解码器中的自注意力机制,该机制会接收查询(Query)、键(Key)和值(Value)这三个参数。自注意力机制随后也会为目标序列中的每个词生成一个编码表示,该表示还包含了每个词的注意力得分。

经过层归一化之后,这个表示被传给第一个解码器中「编码器-解码器注意力 」的 Query 参数。

编码器-解码器注意力

与此同时,编码器堆栈最后一个编码器的输出 ,被传给编码器-解码器注意力的 Value 和 Key 参数。

这样一来,编码器-解码器注意力同时拿到了两样东西:一个是目标序列的表示 (来自解码器自注意力),另一个是输入序列的表示(来自编码器堆栈)。所以,它生成的表示里,既包含了目标序列每个词的注意力分数,也融入了输入序列注意力分数带来的影响。

当这个表示流过解码器堆栈里的所有解码器时,每一个自注意力层和每一个编码器-解码器注意力层,也都会把自己的注意力分数继续加进每个词的表示里。

多个注意力头

在 Transformer 里,注意力模块会把自己的计算并行地重复多次 。每一次重复,就叫做一个「注意力头 」。注意力模块会把它的 Query、Key 和 Value 参数按 N 份切开 ,然后把每一份独立地传给一个单独的头。所有这些相似的注意力计算结果,最后会被组合在一起,生成一个最终的注意力分数。这就是「多头注意力 」的由来------它给了 Transformer 更强大的能力,去为每个词编码多重关系细微差别

为了准确理解数据在内部是怎么处理的,我们还是以训练一个翻译任务为例,一步步走过注意力模块的工作流程。我们只用一个训练样本:输入序列是英文 "You are welcome",目标序列是西班牙语 "De nada"。

注意力的超参数

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

  • 嵌入尺寸 :嵌入向量的宽度(我们的例子里用了宽度 6)。这个维度会贯穿整个 Transformer 模型,所以有时候也会被叫成别的名字,比如模型尺寸等等。

  • 查询尺寸(等于 Key 和 Value 的尺寸):三个线性层用来生成 Query、Key 和 Value 矩阵时,各自使用的权重的尺寸(我们的例子里,查询尺寸用了 3)。

  • 注意力头的数量(我们的例子里用了 2 个头)。

除此之外,还有个批量大小,它给了我们一个表示样本数量的维度。

输入层

输入嵌入和位置编码层,产出一个形状为 (样本数, 序列长度, 嵌入尺寸 ) 的矩阵。这个矩阵被喂给堆栈里第一个编码器的 Query、Key 和 Value。

为了让图示更简单,我们会丢掉批量这个维度,只关注剩下的维度。

线性层

Query、Key 和 Value 各自有一个独立的线性层,每个层有自己的权重。输入数据穿过这些线性层,就产生了 Q、K 和 V 矩阵。

把数据拆分到各个注意力头

现在,数据要被拆分到多个注意力头上,这样每个头才能独立处理。

但有个关键点需要理解:这仅仅是逻辑上的拆分 。Query、Key 和 Value 并不是真的被切成了一个个独立的物理矩阵------每个头一个。相反,Query、Key 和 Value 各自仍然是一个单独的数据矩阵,只不过矩阵里为每个头划出了逻辑上独立的分区 。同样,也不是每个头都有自己独立的线性层。实际上,所有注意力头共享同一个线性层 ,只不过它们在自己的那一块逻辑数据分区上操作。

线性层的权重也按头做了逻辑分区

这种逻辑拆分,是通过把输入数据线性层的权重,都均匀地划分到各个注意力头上实现的。我们可以通过设定下面的查询尺寸来达到这一点:

查询尺寸 = 嵌入尺寸 / 头的数量

在我们的例子里,查询尺寸 = 6 / 2 = 3。尽管线性层的权重(和输入数据)是一个单独的矩阵,但我们可以把它想象成:每个头的独立层权重,被堆叠在了一起。

因此,所有头的计算可以通过一次矩阵运算完成,而不需要 N 次独立的运算。这让计算更高效,也让模型保持简洁,因为需要的线性层更少了,但同时仍然保留了独立注意力头的强大能力。

重塑 Q、K 和 V 矩阵

线性层输出的 Q、K 和 V 矩阵会被重塑,以显式地包含一个「头」的维度 。现在,每个切片就对应每个头的矩阵。

这个矩阵会被再次重塑------这次是交换「头」和「序列」这两个维度 。尽管图示里没有画出批量维度,但 Q 现在的维度变成了 (批量, 头, 序列, 查询尺寸)。

下面的图示展示了我们的示例 Q 矩阵,从线性层出来后,被拆分的完整过程。

最后一个步骤只是为了可视化------虽然 Q 矩阵仍然是一个单独的矩阵,但我们可以把它想象成每个头有一个逻辑上独立的 Q 矩阵。

现在,我们准备好计算注意力分数了。

为每个头计算注意力分数

现在我们有了 Q、K 和 V 这三个矩阵,并且它们已经被拆分到了各个头上。下面就用它们来计算注意力分数。

我们会只使用最后两个维度(序列和查询尺寸)来展示单个头的计算,同时跳过前两个维度(批量和头)。本质上,我们可以想象这些计算会为每个头、批量里的每个样本重复 进行------尽管很明显,它们实际上是作为一次矩阵运算发生的,而不是一个循环。

第一步,是做一个 Q 和 K 之间的矩阵乘法。

然后,一个掩码值 会被加到结果上。在编码器自注意力里,这个掩码用来屏蔽掉填充值,让它们不参与注意力分数的计算。

在解码器自注意力和解码器的编码器-解码器注意力里,会用到不同的掩码,我们稍后在流程中会讲到。

接下来,结果被缩放 :除以查询尺寸的平方根。然后对它应用一个 Softmax

最后,再做一次矩阵乘法:这次是 Softmax 的输出 与 V 矩阵相乘。

编码器自注意力里,完整的注意力分数计算如下所示:

把每个头的注意力分数合并起来

现在我们为每个头都得到了独立的注意力分数,需要把它们合并成一个单一的分数。这个合并操作,基本上就是拆分操作的逆过程。

它的做法很简单:重塑 结果矩阵,去掉这个维度。具体步骤是:

  • 通过交换序列 维度,来重塑注意力分数矩阵。换句话说,矩阵形状从 (批量, 头, 序列, 查询尺寸 ) 变成 (批量, 序列, 头, 查询尺寸)。

  • 再通过重塑成 (批量, 序列, 头 * 查询尺寸),来坍缩掉「头」维度 。这实际上就是把每个头的注意力分数向量,拼接成了一个合并后的注意力分数。

因为 嵌入尺寸 = 头数量 * 查询尺寸 ,所以合并后的分数维度是 (批量, 序列, 嵌入尺寸)。下面的图示展示了示例分数矩阵合并的完整过程。

端到端的多头注意力

把上面所有步骤整合起来,就是多头注意力的端到端流程。

多头拆分如何带来更丰富的解读

一个嵌入向量承载了一个词的含义。正如我们所见,在多 head 注意力里,输入(和目标)序列的嵌入向量会被逻辑上拆分到多个头上。这有什么意义呢?

这意味着:嵌入向量的不同部分 ,可以去学习每个词含义的不同方面------特别是当这个词与序列里其他词产生关联的时候。这让 Transformer 能够捕捉到对序列更丰富的解读

下面的例子可能不太现实,但能帮你建立直觉。比方说,嵌入向量的一个部分可能捕捉名词的性别属性 (阳性、阴性、中性),而另一个部分可能捕捉名词的数量属性(单数 vs 复数)。这在翻译时可能很重要------因为很多语言里,到底该用哪个动词,取决于这些属性。

解码器自注意力和掩码

解码器自注意力的工作方式,与编码器自注意力几乎一样,唯一的区别是,它操作的是目标序列的每个词。

类似地,这里的掩码,也是用来屏蔽掉目标序列里的填充词。

解码器中的编码器-解码器注意力和掩码

编码器-解码器注意力,从两个来源接收输入。因此,它和那两种自注意力都不一样:

  • 编码器自注意力计算的是:每个输入词 与其他输入词之间的交互。

  • 解码器自注意力计算的是:每个目标词 与其他目标词之间的交互。

  • 编码器-解码器注意力 计算的是:每个目标词 与每个输入词之间的交互。

所以,最终注意力分数矩阵里的每个格子,都对应一个 Query(也就是一个目标序列词)与所有 Key(输入序列词)和所有 Value(输入序列词)之间的交互。

同样,这里的掩码也会屏蔽掉目标输出中位置靠后的词。关于这一点,在系列的第二篇文章里已经详细解释过了。

结语

希望本篇文章能让读者对 Transformer 中的注意力模块的功能有一个大致的了解。结合我们在前面文章中介绍的 Transformer 整体端到端流程,现在已经详细阐述了整个 Transformer 架构的运作方式。

现在我们已完全理解了 Transformer 的工作原理。但是,我们还没有完全解答 Transformer 的 Attention 为什么要执行这些计算。为什么会使用查询、键和值的概念,以及为什么会执行我们刚才看到的矩阵乘法?

我们从直觉上介绍了 Transformer是捕捉每个词与其他词之间的关系,但这究竟是什么意思?是如何赋予 Transformer 的注意力机制理解序列中每个词的细微差别的能力的?

我们将在下篇文章给大家回答上述问题。

感谢阅读文章,如果觉得不错,欢迎点赞、关注、转发!

参考资料

Transformers Explained Visually (Part 3): Multi-head Attention, deep dive

相关推荐
AIGC安琪2 小时前
Transformer 和 LLM 到底是什么关系?
人工智能·深度学习·ai·语言模型·程序员·大模型·transformer
数智工坊16 小时前
【RT-DETR论文阅读】:首个实时端到端Transformer检测器,DETR正式超越YOLO
论文阅读·yolo·transformer
MATLAB代码顾问17 小时前
Transformer时序预测:PatchTST原理与PyTorch实现
pytorch·深度学习·transformer
YBAdvanceFu1 天前
拆解 MusicGen:Meta 开源音乐大模型,到底是怎么跑起来的?
人工智能·深度学习·机器学习·数据挖掘·transformer·agent·智能体
生物信息与育种1 天前
黄三文院士领衔植物星球计划(PLANeT)发表Cell
人工智能·深度学习·算法·面试·transformer
码农的神经元1 天前
从论文复现到模型升级:Transformer-Attention-WOA-XGBoost 在含新能源配电网故障诊断中的实现
人工智能·深度学习·transformer
庞轩px1 天前
Transformer的核心思想——Attention机制直观理解
人工智能·rnn·深度学习·transformer·attention·q-k-v
码农的神经元2 天前
拆解 SDGT 算法:图神经网络 + Transformer 如何做短期电力负荷预测
神经网络·算法·transformer
数智工坊3 天前
【Mask2Former论文阅读】:基于掩码注意力的通用分割Transformer,大一统全景/实例/语义分割
论文阅读·深度学习·transformer