01
引言
这是我关于Transformer系列的第三篇文章。在在前两篇文章中,我们了解了什么是Transformer、Transformer的架构以及其各组件的工作原理。在本文中,我们将进一步深入探讨多头注意力机制,它是Transformer的大脑。
闲话少说,我们直接开始吧!
02
Transformer中的Attention
正如我们在第二部分中所看到的,Transformer架构中的Attention主要有三个:
编码器中的自注意力层 - 关注输入序列本身 解码器中的自注意力层 - 关注输出序列本身 解码器中的交叉注意力层 - 输出序列关注输入序列
Transformer中的Attention
Attention层的输入参数一共有三个,即Query,key和Value。这三个参数的结构相似,序列中的每个词都由一个向量表示。
03
编码器中的自注意力层
输入序列被送入嵌入层和位置编码层 ,它为输入序列中的每个单词生成相应的编码表示,以捕捉每个单词的含义和位置。该编码表示被送入第一个encoder中的自注意力层的三个参数Query,Key和Value中,通过计算注意力为输入序列中的每个单词添加相应的注意力得分;当这一过程通过编码器stack中的所有encoder时,每个注意力模块都会将自己的注意力分数添加到每个单词的编码表示中。
04
解码器中的自注意力层
在解码器stack中,目标序列被送入输出嵌入层和位置编码层,为目标序列中的每个单词生成编码表示,捕捉每个单词的含义和位置。在第一个解码器中,这将被送入自注意力层中的三个参数:Query、Key和Value,然后解码器也会为目标序列中的每个单词生成一个编码表示,现在也包含了每个单词的注意力分数。
接着经过LayerNorm层后,该编码表示将送入第一个解码器中的交叉注意力层中的 参数Query中。
05
解码器中的交叉注意力层
与此同时,最后一个编码器的输出也会送入给解码器中的交叉注意力层中的 Value 和 Key 参数中。
因此,解码器中的交叉注意力层同时获得了目标序列的表示(来自解码器自注意力层的输出)和输入序列的表示(来自最后一个编码器的输出)。因此,解码器会为每个目标序列中的单词生成一个包含注意力分数的表征,该表征也会捕捉到输入序列中注意力分数的影响。
当它通过解码器stack中的所有解码器时,每个自注意力层和每个交叉注意力层也会将自己的注意力分数添加到每个单词的编码表示中。
06
多头注意力
07
Attention中的超参数
Embedding Size : 嵌入向量的宽度(我们在示例中使用的宽度为 6)。这个维度贯穿整个Transformer模型,因此有时也被称为模型尺寸。
Query Size: Query大小等于Key和Value的大小 -- 三个线性层分别用于生成Query、Key和Value矩阵的权重大小(我们在示例中使用的Query大小为 3)。 注意力头的数量(我们在示例中使用 2 个注意头)。
此外,我们还有参数batchsize,这为我们提供了一个样本数量维度。
08
输入层
输入嵌入层和位置编码层产生一个形状矩阵(Samples,Seq_len,Embedding_size),并将其输入送到编码器stack中第一个编码器的Query、Key和Value中。
为了简单直观,我们将放弃图片中的Batch这一维度,转而关注其余维度。
09
线性层
Query、Key和Value有三个独立的线性层。每个线性层都有自己的权重矩阵。输入通过这些线性层产生 Q、K 和 V 三个矩阵。
10
根据注意力头划分数据
现在,数据被分割到多个注意力头,以便每个注意力头都能独立处理部分数据。
11
根据注意力头划分线性层权重
这种逻辑分割是通过将输入数据和线性层权重均匀地划分到各注意头来实现的。我们可以通过选择Query Size来实现这一目标:
Query Size = Embedding Size / Number of heads
在我们的例子中,这就是QuerySize = 6/2 = 3 的原因。尽管权重(和输入数据)是一个单独的矩阵,但我们可以将其视为 "堆叠 "了每个头的单独权重。如下所示:
因此,所有注意力头的计算都可以通过单一的矩阵运算实现,而不需要 N 个单独的运算。这不仅提高了计算效率,还减少了所需的线性层,使模型保持简单,同时还能发挥单个注意力头的威力。
12
重塑QKV矩阵
线性层输出的 Q、K 和 V 矩阵经过reshape操作,包含了一个明确的head的维度。现在,每个切片都对应于每个head的一个矩阵。
通过交换heads维度和seq维度,该矩阵再次重塑。虽然没有绘制Batch维度,但现在Q的维度已经变成了(Batch、heads、seq_len、query_size)。
在下图中,我们可以看到从线性层出来后拆分示例 Q 矩阵的完整过程。
最后一个阶段只是为了形象化--虽然 Q 矩阵是一个单一的矩阵,但我们可以把它看作是逻辑上每个头互相独立的Q 矩阵的集合。
13
计算每个头的注意力得分
现在我们有了 3 个矩阵,即 Q、K 和 V,它们被分割到各个head中去。这些矩阵用于计算注意力得分。
我们将仅使用后两个维度(seq_len和query_size)来显示单个head的计算过程,而跳过前两个维度(batch和heads)。从本质上讲,我们可以想象,我们正在查看的计算正在 "重复 "批次中的每个头和每个样本。
第一步是在 Q 和 K 之间进行矩阵乘法运算。
现在会在结果中添加一个掩码值。在编码器的自注意力层中,掩码用于掩盖填充值,使其不参与注意力得分的计算。在解码器的自注意力层和解码器的交叉注意力层中会应用不同的掩码,我们稍后会在流程中介绍。
14
合并多头注意力得分
我们现在有了每个head的独立注意力得分,我们需要将它们合并成一个分数。合并操作本质上与拆分操作相反。
其方法是对结果矩阵进行简单reshape。具体步骤如下:
通过交换维度heads和维度seq_len来重塑注意力得分矩阵。换句话说,矩阵形状从(batch、heads、seq_len、query_size)变为(batch、seq_len、heads、query_size)。 通过reshape操作将其重塑为(batch、seq_len、heads * query_size)来折叠维度。这样就能有效地将每个头部的注意力分数向量合并成一个单一的合并注意力分数。
由于embedding_size=heads *query_size,因此合并后的注意力分数为(batch、seq_len、embedding_size)。在下图中,我们可以看到注意力得分矩阵合并的完整过程。
15
端到端的多头注意力
综上所述,这就是多头注意力的端到端处理流程。
16
多头注意力的优势
嵌入向量用以捕捉单词的含义。在多头注意力的情况下,正如我们所看到的,输入(和目标)序列的嵌入向量在逻辑上被分割成多个head。这有什么意义呢?
这意味着嵌入的不同部分可以学习每个单词不同方面的含义,因为它与序列中的其他单词相关。这使得Transformer可以捕捉到序列中更丰富的语义信息。
为了帮助大家理解,我们来看个例子:一个head可以捕捉名词的 "性别"(男、女、中性),而另一个head则可以捕捉单词的 "词性"(单数与复数)。这在翻译过程中可能很重要,因为在许多语言中,需要使用的动词取决于这些因素。
17
解码器中的自注意力和Mask
解码器中的自注意力层的工作原理与编码器中的自注意力层的功能相同,只是它对目标序列的每个单词进行操作。
同样,掩码mask也会屏蔽掉目标序列中的 "填充 "词。
18
解码器中的交叉注意力和Mask
解码器的交叉注意力层有两个输入来源。因此,与计算每个输入词与其他输入词之间交互作用的编码器自注意力层和计算每个目标词与其他目标词之间交互作用的解码器自注意力层不同,解码器中的交叉注意力层会计算每个目标词与每个输入词之间的交互作用。
因此,交叉注意力得分中的每个单元格都对应于一个 Q(即目标序列词)与所有其他 K(即输入序列)和所有 V(即输入序列)之间的相互作用。
同样,掩码mask也会屏蔽掉目标输出中后面的单词,这在本系列的第二篇文章中有详细说明。
19
结论
希望这篇文章能让大家对 Transformer 中的注意力模块有一个很好的了解。结合我们在第二篇文章中介绍的整个 Transformer 的端到端流程,我们现在已经介绍了整个 Transformer 架构的详细操作。
您学废了吗?
点击上方小卡片关注我
添加个人微信,进专属粉丝群!