【自然语言处理】Transformer 讲解

有任何的书写错误、排版错误、概念错误等,希望大家包含指正。

在阅读本篇之前建议先学习:
【自然语言处理】Seq2Seq 讲解
【自然语言处理】Attention 讲解

Transformer

为了讲解更加清晰,约定“预测阶段”被称为“推断阶段”(inference),“预测”用于表示模型根据输入信息输出目标信息的抽象过程。

1. 简介

在 Transformer 出现之前,大部分序列转换(转录)模型是基于 RNNs 或 CNNs 的 Encoder-Decoder 结构。但是 RNNs 固有的顺序性质使得并行计算难以实现,即训练时当前时刻的隐藏状态与前一个时刻的隐藏状态有关,这意味着需要先计算出前一个时刻的状态才能计算下一个时刻的状态,这大大限制了 RNNs 的训练速度;CNNs 可以比较好的解决并行计算的问题,但是对于长序列 CNNs 难以建模,需要设置非常多的卷积层才能将较长距离的部分联系起来,可以想象,大小合理的卷积核只能对序列的某一部分进行关联,当卷积层叠加到一定层数后,才能将序列中最远距离的两个部分相关联。Transformer 彻底摒弃了 RNNs 和 CNNs,是一种完全基于注意力机制的 Encode-Decoder 模型。它的预测效果非常出众,同时优质的并行性使得它的训练时间更短。

序列转换(转录)模型(sequence transduction models)是指将一个序列转换为另一个序列的模型。

2. 模型结构

2.1. 整体框架

上面提到 Transformer 是序列转换模型,将其视为一个黑盒子,那么输入和输出分别是两个序列,如图 1 1 1 所示。

在这里插入图片描述

图 1    Transformer 模型 (黑盒模型)

将黑盒子一步步打开来看内部细节。

Transformer 仍然采用 Encoder-Decoder 框架,如图 2 2 2 所示。

在这里插入图片描述

图 2    Transformer 模型 (Encoder-Decoder 框架)

其中,ENCODERS 部分由多层编码器首尾相连组成,DECODERS 同理;二者之间的连接是将 ENCODERS 最后一层编码器的输出作为 DECODERS 每一层编码器的部分输入。论文中作者规定 ENCODERS 和 DECODERS 均为 6 层,如图 3 3 3 所示。

在这里插入图片描述

图 3    Transformer 模型 (6 层编码器和解码器)

4 4 4 展示了更为具体的内部细节。每个编码器包括两个子层:Multi-Head self-attention(多头自注意力)子层和 Position-wise Feed Forward Network(按位置操作的前馈神经网络,简记 FFN)子层。注意到,两个子层除了主体模块外,还包括 Add & Norm 模块。Add 和 Norm 分别对应 Residual Connection 和 Layer Normalization,即残差连接和层标准化。解码器在编码器的两个子层的基础上还在两层之间添加了一层朴素的多头注意力子层,即非自注意力子层。

图 4    Transformer 中的编码器和解码器

对于图 4 4 4 的理解需要配合图 3 3 3

2.2. 自注意力机制

2.2.1. 作用

对于两个仅有一词不同的语句,The animal didn’t cross the street because it was too tiredThe animal didn’t cross the street because it was too wide,我们可以轻松判断出第一个语句中的 it 表示 animal,第二个语句中的 it 表示 street。从注意力的角度来看,第一个语句中的 it 会更加关注 animal,第二个语句中的 it 会更加关注 street

Query、Key 和 Value 均来自同一个输入序列的注意力机制称为自注意力机制(self-attention mechanism)。自注意力机制对于序列转换任务的意义在于,帮助模型理解序列中元素之间的关系,亦语句中单词的语义关系。正如上面的例子,经过自注意力模块,模型可以学习到不同语境下的 it 与句子中不同的单词有关,这使得模型对语义的理解更加深刻。

2.2.2. 计算

自注意力计算过程如下:

  1. 假设自注意力模块的输入序列为 ( x 1 , x 2 , x 3 ) (x_1, x_2, x_3) (x1,x2,x3) x i x_i xi 表示第 i i i 个单词向量。单词向量可以通过 Word2Vec 等预训练模型得到,即从 lookup table 中查找;也可以随机初始化,训练模型的同时训练单词向量。
  2. 序列中的每个单词共享三个参数矩阵 W Q W_Q WQ W K W_{K} WK W V W_V WV x 1 x_1 x1 分别与三个矩阵相乘,得到向量 Q 1 Q_1 Q1 K 1 K_1 K1 V 1 V_1 V1 x 2 x_2 x2 x 3 x_3 x3 同理。计算第一个单词的自注意力: Q 1 Q_1 Q1 作为注意力机制的 Query, < K 1 , V 1 > \left<K_1,V_1\right> K1,V1 < K 2 , V 2 > \left<K_2,V_2\right> K2,V2 < K 3 , V 3 > \left<K_3,V_3\right> K3,V3 作为 <Key,Value> 对,对 Q 1 Q_1 Q1 K 1 K_1 K1 K 2 K_2 K2 K 3 K_3 K3 的相似度进行 Softmax 后作为权重计算 V 1 V_1 V1 V 2 V_2 V2 V 3 V_3 V3 的加权和,得到 x 1 x_1 x1 的注意力向量 z 1 z_1 z1 z 2 z_2 z2 z 3 z_3 z3 同理。
  3. 矩阵计算形式。将三个单词向量堆叠成矩阵 X = ( x 1 ; x 2 ; x 3 ) X = (x_1;x_2;x_3) X=(x1;x2;x3),与 W Q W_Q WQ W K W_K WK W V W_V WV 相乘分别得到矩阵 Q = ( Q 1 ; Q 2 ; Q 3 ) Q=(Q_1;Q_2;Q_3) Q=(Q1;Q2;Q3) K = ( K 1 ; K 2 ; K 3 ) K=(K_1;K_2;K_3) K=(K1;K2;K3) V = ( V 1 ; V 2 ; V 3 ) V=(V_1;V_2;V_3) V=(V1;V2;V3)。相似度由矩阵 Q K T QK^T QKT 表示,相似度矩阵的大小为序列长度乘以序列长度,表示每个单词与序列中任意一个单词的相关程度,对 Q K T QK^T QKT 的每一行进行 Softmax 后再与 V V V 相乘得到注意力矩阵 Z = ( z 1 ; z 2 ; z 3 ) Z=(z_1;z_2;z_3) Z=(z1;z2;z3)
  4. Q K T QK^T QKT 进行缩放。上面没提到的一部分细节是对 Q K T QK^T QKT 缩放后才计算 Softmax,即自注意力模块的公式为 S o f t m a x ( Q K T d k ) V {\rm Softmax}(\frac{QK^T}{\sqrt{d_k}})V Softmax(dk QKT)V,其中 d k d_k dk 为向量 Q i Q_i Qi K i K_i Ki 的维度。缩放的目的在于,避免因进行内积计算的向量维度过大而出现过大或过小的值,经过 Softmax 会出现权重向极端的 0 和 1 靠拢,使得梯度比较小,难以收敛。对于维度较小的向量,是否缩放无所谓。另外,形式化解释为,当假设 Q i Q_i Qi K j K_j Kj 的均值为 0,方差为 1 时,二者内积的均值为 0,方差为 d k d_k dk,缩放后点积的方差为 1。这样就可以避免梯度消失问题。

计算过程(简化,省略缩放操作)如图 5 5 5 所示。

图 5    自注意力计算

2.2.3. 多头注意力

多头的思想来自多通道卷积,通过不同通道识别不同的模式,从不同角度理解序列。还是以上面提到的语句为例,The animal didn’t cross the street because it was too tired,不同的注意力头关注序列中不同的位置。如图 6 6 6 所示。

区别多头注意力和自注意力,前者强调通道个数,后者强调 Q、K 和 V 的取法。二者是可以融合使用的,这在 Transformer 中有很多体现。

图 6    双头自注意力

可以看出,橙色注意力头更加关注 it 所指代的单词 animal,绿色注意力头更加关注 it 所指代单词的状态 tired

以双头自注意力为例,符号上标表示头序号,第一个单词注意力的计算过程(简化,省略 Softmax 层)如图 7 7 7 所示。

图 7    第一个单词的双头自注意力计算

对比图 5 5 5 和图 7 7 7 可以发现,单头注意力仅涉及一组参数矩阵 W Q W_Q WQ W K W_K WK W V W_V WV h h h头注意力包括 h h h 组参数矩阵 W Q 1 : h W_Q^{1:h} WQ1:h W K 1 : h W_K^{1:h} WK1:h W V 1 : h W_V^{1:h} WV1:h

在 Transformer 模型中,多头注意力保证参数数量保持不变,即 W Q W_Q WQ 元素个数与 W Q 1 : h W_Q^{1:h} WQ1:h 元素个数相等, W K W_K WK 元素个数与 W K 1 : h W_K^{1:h} WK1:h 元素个数相等, W V W_V WV 元素个数与 W V 1 : h W_V^{1:h} WV1:h 元素个数相等,这表明 W Q i W_Q^i WQi 的列数是 W Q W_Q WQ 1 / h 1/h 1/h W K i W_K^i WKi W V i W_V^i WVi 同理。

在代码实现时,完全可以将 W Q 1 : h W^{1:h}_Q WQ1:h 拼接成 W Q W_Q WQ 的样式实现并行计算多头注意力。具体地, X W Q 1 : h = Q 1 : h XW_Q^{1:h}=Q^{1:h} XWQ1:h=Q1:h Q 1 : h Q^{1:h} Q1:h Q 1 = ( Q 1 1 , Q 2 1 , Q 3 1 ) Q^1=(Q_1^1,Q_2^1,Q_3^1) Q1=(Q11,Q21,Q31) Q 2 = ( Q 1 2 , Q 2 2 , Q 3 2 ) Q^2=(Q_1^2,Q_2^2,Q_3^2) Q2=(Q12,Q22,Q32) … \dots Q h = ( Q 1 h , Q 2 h , Q 3 h ) Q^h=(Q^h_1,Q_2^h,Q_3^h) Qh=(Q1h,Q2h,Q3h) 拼接而成,其中 X = ( x 1 ; x 2 ; x 3 ) X=(x_1;x_2;x_3) X=(x1;x2;x3) K 1 : h K^{1:h} K1:h V 1 : h V^{1:h} V1:h 的含义及计算同理。这样多头注意力的计算就完全转换成了单头注意力的计算。显然,最后计算出的注意力也是由多个头的注意力拼接得到,即 Z = ( z 1 , z 2 , … , z h ) Z=(z^1,z^2,\dots, z^h) Z=(z1,z2,,zh) z i = ( z 1 i , z 2 i , z 3 i ) z^i=(z_1^i,z_2^i,z_3^i) zi=(z1i,z2i,z3i)。最后,为了融合每个头得到的信息, Z Z Z 还需要经过一层同维线性映射(映射矩阵的行数与列数相等)。

多头注意力所谓的“参数数量不变”不包括最后的融合线性映射的参数。

多头自注意力模块的理论示意图(并非具体实现的方法)如图 8 8 8 所示。可以发现,自注意力模块的输入和输出序列长度一致,且向量 x i x_i xi z i z_i zi 的维度一致,我们记其维度为 d m o d e l = 512 d_{\rm model}=512 dmodel=512。输入与输出长度和维度相等的模块贯穿整个 Transformer 模型,编码器和解码器由这种模块组成的好处在于前一个编码器(或解码器)的输出可以直接作为下一个编码器(或解码器)的输入,而且最后一个编码器的输出也可以直接输入到每个解码器中,这在一定程度上简化了模型。

在这里插入图片描述

图 8    多头自注意力模块的理论示意图

2.3. 按位置操作的前馈神经网络

参考图 4 4 4 可以知道,在忽略 Add & Norm 层的前提下,无论是在编码器还是解码器中,按位置操作的前馈神经网络(Position-wise Feed-Forward Networks,FFN)都是以注意力模块的输出 z 1 z_1 z1 z 2 z_2 z2 z 3 z_3 z3 作为输入。可以认为 FFN 是单隐层的感知机,FFN 的输入为单个单词对应的注意力向量 z i z_i zi,先映射到高维,再映射回原来的维度输出。输入层神经元个数与注意力向量的维度相等,即 d m o d e l = 512 d_{\rm model}=512 dmodel=512,隐藏层神经元个数为 4 × d m o d e l = 2048 4\times d_{\rm model}=2048 4×dmodel=2048,输出层神经元个数为 d m o d e l = 512 d_{\rm model} = 512 dmodel=512。可见,FFN 层也满足输入和输出的维度相等。

有两点需要注意。一是,注意到 FFN 的输入层神经元个数与单个单词注意力向量维度一致,这说明对于某个 FFN 层而言,虽然一次性接收来自注意力模块的多个单词注意力向量,但不会一次性输入到多层感知机中,否则输入层神经元个数应该是注意力向量维度乘以序列长度,即对于长度为 3 3 3 的序列,输入层神经元个数为 3 × d m o d e l 3\times d_{\rm model} 3×dmodel。在代码实现时,为了并行处理,会将全部单词向量堆叠成的矩阵作为多层感知机的输入。另外一种更简单的理解方式是,注意力模块输出的每个单词注意力向量都对应一个多层感知机,只不过这些同结构的多层感知机共享参数,这正是 FNN 称为 Position-wise 的原因,即每个单词对应一个位置,每个位置对应一个前馈神经网络。两种理解方式如图 9 9 9 10 10 10 所示。二是,FFN 隐藏层的输出会经过 ReLU 激活函数,ReLU 激活函数提供非线性变换,FFN 对应的公式为:
F F N ( z i ) = max ⁡ ( 0 , z i W 1 + b 1 ) W 2 + b 2 {\rm FFN}(z_i) = \max (0, z_i W_1+b_1)W_2+b_2 FFN(zi)=max(0,ziW1+b1)W2+b2

图 9    FFN 示意图(一)

图 10    FFN 示意图(二)

2.4. 残差连接和层规范化

个人习惯,这里统一称为“规范化”,而不称为“标准化”或“归一化”。

Add & Norm 层更严谨的名称应该是 Residual Connection & Layer Normalization,本层的任务是进行残差连接和层规范化(简记为 LN)。假设 Add & Norm 层的前一层的输入记为 X = ( x 1 ; x 2 ; x 3 ) X=(x_1;x_2;x_3) X=(x1;x2;x3),输出记为 Y = ( y 1 ; y 2 ; y 3 ) Y=(y_1;y_2;y_3) Y=(y1;y2;y3)。Add & Norm 层的具体任务是对 X + Y X+Y X+Y 进行层规范化。

残差连接的思想来自 ResNet,属于非常基础的知识,这里不再介绍。重点讲解一下 BN(Batch Normalization)和 LN(Layer Normalization)。

2.4.1. Batch Normalization

对于由向量表示的样本而言,一个 batch 对应一个矩阵( B × D B\times D B×D),比如以身高、体重等为特征来描述一个人,抽象为如图 11    ( 左 ) 11\;(左) 11() 所示的矩形;对于由矩阵表示的样本而言,一个 batch 是由多个样本矩阵堆叠而成的张量( B × N × D B\times N\times D B×N×D),常见于自然语言处理任务中,张量的第一维表示序列个数,第二维表示序列长度,第三维表示序列中每个元素对应向量的维度,抽象为如图 11    ( 右 ) 11\;(右) 11() 所示的立方体。

在这里插入图片描述

图 11    矩阵 batch (左) 和张量 batch (右)

对于矩阵 batch 而言,Batch Normalization 以 batch 内全部样本的同一维度为一组,计算均值和方差并进行规范化,图 11    ( 左 ) 11\;(左) 11() 同箭头跨越的部分为一组规范化对象;对于张量 batch 而言,BN 以 batch 内全部序列,同一位置对应单词的同一维度为一组,计算均值和方差并进行规范化,图 11    ( 右 ) 11\;(右) 11() 同箭头跨越的部分为一组规范化对象。

我们知道 batch 的概念只出现在训练阶段,因此,在训练时会保存每一个 batch 计算的均值和方差,通过如下公式计算全局均值和方差作为推断阶段要使用的均值和方差。
μ g l o b a l = E [ μ i ] σ g l o b a l 2 = m m − 1 E [ σ i 2 ] \mu_{\rm global}= E[\mu_i]\\ \sigma^2_{\rm global}= \frac{m}{m-1} E[\sigma^2_i] μglobal=E[μi]σglobal2=m1mE[σi2]
其中 μ i \mu_i μi σ i 2 \sigma^2_i σi2 为第 i i i 个 batch 对应的均值和方差。需要注意,每组 batch 的每个维度都有一对均值和方差。

2.4.2. Layer Normalization

自然语言处理任务常用 Layer Normalization,而不用 Batch Normalization。这是因为,每个序列的长度往往不一致,使用 BN 会出现一组内进行规范化的元素非常少的情况,此时在训练数据中计算出的均值和方差很可能与推断数据中的均值和方差不匹配。另外,使用 BN 需要保证推断序列长度不超过训练序列最大长度,否则会出现推断序列多出的位置在训练阶段没有均值和方差被计算、被保存,也就无法在推断阶段对这些位置进行 BN。

这两点问题都说明 BN 在自然语言任务中的效果不令人满意,因此引入 LN。LN 以每个单词向量为一组进行规范化,如图 12    ( 右 ) 12\;(右) 12() 所示。

在这里插入图片描述

图 12    BN (左) 和 LN (右) 对比

如果使用 LN,那么不再需要保存任何训练阶段的均值和方差,推断阶段可以根据输入实时计算均值和方差。另外,自然语言处理任务可以使用 LN 的一个关键原因是,词向量的每一维度之间不像身高、体重似的存在非常明显的量纲,这保证了数据无需进行 BN。

目前众多的实验结果表明,BN 在 MLP 和 CNN 上表现优异,但在 RNN 上效果不明显。

【小笔记】在编写对应的代码时发现,Pytorch 中的 torch.nn.LayerNorm 函数是根据总体方差(标准差)进行的层规范化,这与用 Numpy 中的 var 函数手动实现层规范化的结果是一致的;而 Pytorch 中的 var 函数计算的是样本方差,所以手动实现出的结果与前面两者不同。

2.5. 位置编码

Transformer 模型完全基于注意力机制,而注意力机制无法捕获有关时序(词序)的信息,丧失时序信息可能使模型对语句的理解产生歧义。举个例子,He is reading English.Is he reading English ?,两个语句由完全相同的单词组成,词序的不同使得这四个单词产生了陈述句和疑问句。这个例子的问题可以通过对标点符号编码并与其他单词一同输入到模型中的方式解决。那么再看个例子,Even now he doesn’t believe me.Now even he doesn’t believe me.Now he doesn’t believe even me.,这三个语句由完全相同的单词和标点组成,却表达了不同的含义,依次为「甚至到现在他还不相信我(其它时候就更不用说了)」、「现在连他都不相信我了(其他人就更不会相信我了)」和「现在他甚至连我都不相信了(就更不会相信其他人了)」。由此可见,时序信息对于模型理解语义是有帮助的。

为了解决这个问题,需要向模型中注入单词在序列中的绝对或者相对位置信息。Transformer 中第一个编码器的输入和第一个解码器的输入是由位置编码(positional encodings)和词嵌入(embedding)相加得到,因此,序列中第 i i i 个单词的表示为 x i = p i + e i x_i=p_i+e_i xi=pi+ei。显然,需要保证 p i p_i pi e i e_i ei 具有相同的维度 d m o d e l d_{\rm model} dmodel

上面提到过词嵌入 e i e_i ei 可以由预训练模型生成,也可以随机初始化跟随 Transformer 模型学习得到;位置编码同样也可以跟随模型学习确定,但是常用不同频率的正弦和余弦函数对位置进行固定编码。经证明,三角函数编码与学习向量这两种方法效果近似,但是三角函数编码的优势在于,其使得模型能够处理比训练阶段遇到的序列长度更长的序列。

不同频率的正弦函数和余弦函数定义如下:
P E ( p o s , 2 i ) = sin ⁡ ( p o s / 1000 0 2 i / d m o d e l ) P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s / 1000 0 2 i / d m o d e l ) {\rm PE}(pos, 2i) = \sin (pos/10000^{2i/d_{\rm model}})\\ {\rm PE}(pos, 2i+1) = \cos (pos/10000^{2i/d_{\rm model}}) PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)
其中, p o s pos pos 表示单词在序列中的位置,范围 [ 0 , s e q _ l e n − 1 ] [0,\rm seq\_len-1] [0,seq_len1] d m o d e l d_{\rm model} dmodel 必须为偶数,在代码实现中取值为 512 512 512 i i i 表示向量 P E \rm PE PE 某一维度的索引值除以 2 2 2 的商(除以 2 2 2 向下取整),范围 [ 0 , d m o d e l / 2 − 1 ] [0, d_{\rm model}/2-1] [0,dmodel/21] p o s pos pos 处的位置编码向量 P E p o s {\rm PE}_{pos} PEpos 由两个函数交替拼接得到:
P E p o s = [ sin ⁡ ( w 0 ⋅ p o s ) , cos ⁡ ( w 0 ⋅ p o s ) , … , sin ⁡ ( w i ⋅ p o s ) , cos ⁡ ( w i ⋅ p o s ) , … , sin ⁡ ( w d m o d e l / 2 − 1 ⋅ p o s ) , cos ⁡ ( w d m o d e l / 2 − 1 ⋅ p o s ) ] {\rm PE}_{pos} = [\sin(w_0·pos),\cos(w_0·pos),\dots, \sin(w_i·pos),\cos(w_i·pos),\dots, \sin(w_{ {d_{\rm model}/2}-1}·pos),\cos(w_{ {d_{\rm model}/2}-1}·pos)] PEpos=[sin(w0pos),cos(w0pos),,sin(wipos),cos(wipos),,sin(wdmodel/21pos),cos(wdmodel/21pos)]
其中 w i = 1 / 1000 0 2 i / d m o d e l w_i = 1/10000^{2i/d_{\rm model}} wi=1/100002i/dmodel 。可见,位置编码的每个维度对应于一个正弦(或余弦)曲线。这些波长形成一个从 2 π 2\pi 2π 10000 ⋅ 2 π 10000 \cdot 2\pi 100002π 的集合级数。

这样编码不仅能够提供单词的绝对位置信息,还隐含着单词的相对距离信息。讨论 p o s + k pos+k pos+k 处与 p o s pos pos 处的位置编码的关系。 p o s + k pos+k pos+k 处的位置编码可以表示为:
P E ( p o s + k , 2 i ) = sin ⁡ ( w i ⋅ ( p o s + k ) ) P E ( p o s + k , 2 i + 1 ) = cos ⁡ ( w i ⋅ ( p o s + k ) ) {\rm PE}(pos+k, 2i) = \sin (w_i · (pos+k))\\ {\rm PE}(pos+k, 2i+1) = \cos (w_i · (pos+k)) PE(pos+k,2i)=sin(wi(pos+k))PE(pos+k,2i+1)=cos(wi(pos+k))

根据三角函数公式
sin ⁡ ( α + β ) = sin ⁡ α ⋅ cos ⁡ β + cos ⁡ α ⋅ sin ⁡ β sin ⁡ ( α + β ) = cos ⁡ α ⋅ cos ⁡ β − sin ⁡ α ⋅ sin ⁡ β \sin (\alpha+\beta) = \sin \alpha · \cos \beta + \cos \alpha ·\sin \beta \\ \sin (\alpha+\beta) = \cos \alpha · \cos \beta - \sin \alpha ·\sin \beta \\ sin(α+β)=sinαcosβ+cosαsinβsin(α+β)=cosαcosβsinαsinβ
可得
P E ( p o s + k , 2 i ) = sin ⁡ ( w i ⋅ ( p o s + k ) ) = sin ⁡ ( w i ⋅ p o s ) ⋅ cos ⁡ ( w i ⋅ k ) + cos ⁡ ( w i ⋅ p o s ) ⋅ sin ⁡ ( w i ⋅ k ) P E ( p o s + k , 2 i + 1 ) = cos ⁡ ( w i ⋅ ( p o s + k ) ) = cos ⁡ ( w i ⋅ p o s ) ⋅ cos ⁡ ( w i ⋅ k ) − sin ⁡ ( w i ⋅ p o s ) ⋅ sin ⁡ ( w i ⋅ k ) {\rm PE}(pos+k, 2i) = \sin (w_i · (pos+k))= \sin (w_i ·pos) · \cos (w_i ·k) + \cos (w_i ·pos) ·\sin (w_i ·k)\\ {\rm PE}(pos+k, 2i+1) = \cos (w_i · (pos+k)) = \cos (w_i ·pos) · \cos (w_i ·k) - \sin (w_i ·pos) ·\sin (w_i · k) PE(pos+k,2i)=sin(wi(pos+k))=sin(wipos)cos(wik)+cos(wipos)sin(wik)PE(pos+k,2i+1)=cos(wi(pos+k))=cos(wipos)cos(wik)sin(wipos)sin(wik)
代入 P E ( p o s + k , 2 i ) = sin ⁡ ( w i ⋅ p o s ) {\rm PE}(pos+k, 2i) = \sin(w_i·pos) PE(pos+k,2i)=sin(wipos) P E ( p o s + k , 2 i + 1 ) = cos ⁡ ( w i ⋅ p o s ) {\rm PE}(pos+k, 2i+1)=\cos(w_i·pos) PE(pos+k,2i+1)=cos(wipos)
P E ( p o s + k , 2 i ) = cos ⁡ ( w i ⋅ k ) ⋅ P E ( p o s , 2 i ) + sin ⁡ ( w i ⋅ k ) ⋅ P E ( p o s , 2 i + 1 ) P E ( p o s + k , 2 i + 1 ) = cos ⁡ ( w i ⋅ k ) ⋅ P E ( p o s , 2 i + 1 ) − sin ⁡ ( w i ⋅ k ) ⋅ P E ( p o s , 2 i ) {\rm PE}(pos+k, 2i) = \cos (w_i ·k)· {\rm PE}(pos,2i) + \sin (w_i ·k)·{\rm PE}(pos, 2i+1) \\ {\rm PE}(pos+k, 2i+1) = \cos (w_i ·k) · {\rm PE}(pos, 2i+1) - \sin (w_i · k)·{\rm PE}(pos, 2i) PE(pos+k,2i)=cos(wik)PE(pos,2i)+sin(wik)PE(pos,2i+1)PE(pos+k,2i+1)=cos(wik)PE(pos,2i+1)sin(wik)PE(pos,2i)
k k k 是常数,不妨将有关 k k k 是三角函数记为
u = cos ⁡ ( w i ⋅ k ) v = sin ⁡ ( w i ⋅ k ) u = \cos(w_i · k) \\ v = \sin(w_i · k) u=cos(wik)v=sin(wik)
这样, P E ( p o s + k , 2 i ) {\rm PE}(pos+k, 2i) PE(pos+k,2i) P E ( p o s + k , 2 i + 1 ) {\rm PE}(pos+k, 2i+1) PE(pos+k,2i+1) 可以以矩阵的形式表达:
[ P E ( p o s + k , 2 i ) P E ( p o s + k , 2 i + 1 ) ] = [ u v − v u ] × [ P E ( p o s , 2 i ) P E ( p o s , 2 i + 1 ) ] \left[ \begin{matrix} {\rm PE}(pos+k, 2i) \\ {\rm PE}(pos+k, 2i+1) \\ \end{matrix} \right]= \left[ \begin{matrix} u & v\\ -v & u \end{matrix} \right] \times \left[ \begin{matrix} {\rm PE}(pos, 2i) \\ {\rm PE}(pos, 2i+1) \\ \end{matrix} \right] [PE(pos+k,2i)PE(pos+k,2i+1)]=[uvvu]×[PE(pos,2i)PE(pos,2i+1)]

从矩阵乘法的角度可以看出, p o s + k pos+k pos+k 处与 p o s pos pos 处位置编码存在线性关系。进一步,计算两个位置编码向量的内积:

< P E ( p o s ) , P E ( p o s + k ) > = ∑ i sin ⁡ ( w i ⋅ p o s ) sin ⁡ ( w i ⋅ ( p o s + k ) ) + cos ⁡ ( w i ⋅ p o s ) cos ⁡ ( w i ⋅ ( p o s + k ) ) = ∑ i cos ⁡ ( w i ⋅ ( p o s − ( p o s + k ) ) ) = ∑ i cos ⁡ ( w i ⋅ k ) \begin{align} \left<{\rm PE}(pos),{\rm PE}(pos+k)\right> &= \sum_{i} \sin(w_i·pos)\sin(w_i·(pos+k)) + \cos(w_i·pos)\cos(w_i·(pos+k)) \notag\\ &= \sum_i \cos(w_i·(pos - (pos+k)))\notag \\ &= \sum_i \cos(w_i·k) \notag \end{align} PE(pos),PE(pos+k)=isin(wipos)sin(wi(pos+k))+cos(wipos)cos(wi(pos+k))=icos(wi(pos(pos+k)))=icos(wik)
可以看出,如果两个位置距离越远,那么对应的编码向量内积就越小。

可以证明位置编码和词嵌入相加而不是拼接的合理性,证明见 REF[5] 李宏毅视频 30:00 处。

2.6. 掩码机制

Mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 Padding Mask 和 Sequence Mask。Padding Mask 在所有的注意力(Attention)模块里面都需要用到,而 Sequence Mask 只在解码器的自注意力(Self-Attention)模块里面用到。

Padding Mask

为了保证同一 batch 内全部输入序列(输入到编码器和解码器的序列)的长度一致,我们会设定一个固定长度,过长的序列会被截断,多余部分被丢弃;在长度不足的输入序列后面填充 padding。Padding Mask 是一个二值掩码,用于区分序列中每个位置是否为 padding 区域。因为填充 padding 的位置没有任何意义,只是为了方便处理,所以注意力机制不应该把注意力放在这些位置。具体来说,在计算注意力时,进行 Softmax 操作之前需要根据 Padding Mask 将 padding 区域的值设置为负无穷,这样经过归一化后,padding 区域的概率会接近 0 0 0,即目标单词不关注 padding 区域内的单词。

尽管引入 padding 后可以保证输入序列长度一致,但是使用 Batch Normalization 依旧没有意义,这是因为加入的 padding 本身没有意义,只是起到占位符的作用,不改变序列长度不相等的本质。

在代码实现时,每个序列对应自己的 Padding Mask,Padding Mask 是维度为序列长度 s e q _ l e n \rm seq\_len seq_len 的二值向量。Padding Mask 对大小为 s e q _ l e n × s e q _ l e n \rm seq\_len\times seq\_len seq_len×seq_len 的矩阵 Q K T / d k QK^T/\sqrt{d_k} QKT/dk 按行遮盖,设置被遮盖区域的值为负无穷,如图 13    ( 左 ) 13\;(左) 13() 所示。

注意到,并没有遮盖 <pad> 对应的行,这是因为归一化操作以行为单位进行的,<pad> 对应的行表示 <pad> 对序列中单词的关注度,显然,我们不会在意这些关注度,所以 <pad> 对应的行是否计算都无所谓。

Sequence Mask

Sequence Mask 只出现在解码器中的自注意力模块中,这主要是考虑到自注意力机制会提取全局信息,也就是说在预测位置 p o s pos pos 的单词时用到序列中的全部单词信息,包括 p o s pos pos 之前(历史)和之后(未来)的单词,但是,显然在预测时用到未来信息是一种作弊行为,属于信息泄露。在实际推断中,不可能提前知道未来信息。因此,引入 Sequence Mask 对未来信息进行遮盖。具体来说,作用时机与 Padding Mask 一致,二者共同影响矩阵 Q K T / d k QK^T/\sqrt{d_k} QKT/dk ,被遮盖的区域的值设置为负无穷。

在代码实现时,一个 batch 内序列的 Sequence Mask 是完全一样的,Sequence Mask 是大小为 s e q _ l e n × s e q _ l e n \rm seq\_len \times seq\_len seq_len×seq_len 的二值上三角(不含对角线)矩阵,如图 13    ( 中 ) 13\;(中) 13() 所示。一般会先将 Padding Mask 和 Sequence Mask 合并成一个掩码矩阵,再对 Q K T / d k QK^T/\sqrt{d_k} QKT/dk 处理,如图 13    ( 右 ) 13\;(右) 13() 所示。

在这里插入图片描述

图 13    Padding Mask 操作过程(左)、Sequence Mask 操作过程(中) 和合并 Mask(右)

2.7. 线性映射和归一化

这里讲解的线性映射和归一化是图 4 4 4 最后一层解码器连接的 Linear 层和 Softmax 层。这一步的目的在于将解码器输出的每个单词向量映射到 v o c a b _ s i z e \rm vocab\_size vocab_size 维,对 v o c a b _ s i z e \rm vocab\_size vocab_size 维归一化,最高概率值对应该单词的预测标签。其中 v o c a b _ s i z e \rm vocab\_size vocab_size 为模型的输出词汇表大小,即模型的输出单词肯定包含在词汇表中。如图 14 14 14 所示。

在这里插入图片描述

图 14    线性映射与归一化

3. 训练与推断

以汉(机器 学习)译英(machine learning)的机器翻译任务为例讲解训练阶段和推断阶段的整体流程。

训练阶段。batch 内的每个样本对应三个序列:源序列、目标序列和预测序列。源序列是输入到编码器中的序列,即 机器 学习;目标序列是输入到解码器中的序列,需要在序列前加上起始标志 <BOS>,即 <BOS> machine learning;预测序列是模型对于输入为源序列和目标序列的预测结果,也可以认为是最后一层解码器的输出经过线性映射和归一化的结果,预测序列可能与目标序列不一致,比如预测出 machine translation <EOS>love learning <EOS>,但是预测出的序列一定是以结束标志 EOS 结尾。计算 batch 内全部样本的目标序列中单词的独热编码与预测序列中对应单词的概率分布的交叉熵,定义损失函数为交叉熵之和,进而反向传播更新参数。图 15 15 15 展示了单样本训练过程。

图 15    单样本训练过程

推断阶段。在推断阶段,目标序列是未知的,所以不能像训练阶段一样并行输入计算。模型根据当前的目标序列预测出下一个单词,将预测出的单词拼接到目标序列上作为新的目标序列输入到模型中,继续预测下一个单词,直至预测到结束标志 <EOS>,初始目标序列仅由起始标志 <BOS> 构成。图 4 4 4 中“shifted right”表达的正是这种串行的推断方式。图 16 16 16 展示了单样本推断过程。
在这里插入图片描述

图 16    单样本推断过程

注意两点:

  1. Mask 在训练和推断过程都是正常使用。推断阶段的变长目标序列不会影响 Sequence Mask 的使用方法,Sequence Mask 的大小跟随序列长度变化即可。
  2. 变长的目标序列不会使得模型无法正常运行。出现“模型无法正常运行”想法的原因在于,认为模型参数矩阵无法与序列矩阵进行合法的乘法运算。不妨随意调整序列长度,试试注意力模块是否受到影响,答案肯定是正常运算。只要保证训练时的 d m o d e l d_{\rm model} dmodel 与推断时的 d m o d e l d_{\rm model} dmodel 相等,就不会出现运行错误,而与序列长度无关。

REF

[1] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[J]. Advances in neural information processing systems, 2017, 30.

[2] The Illustrated Transformer – Jay Alammar – Visualizing machine learning one concept at a time. (jalammar.github.io)

[3] 68 Transformer【动手学深度学习v2】- bilibili

[4] Transformer论文逐段精读【论文精读】- bilibili

[5] 李宏毅Transformer - bilibili

[6] 深入理解transformer源码_- CSDN

[7] 自然语言处理(十五):Transformer介绍_自然语言处理transformer - CSDN

[8] 《The Annotated Transformer》翻译——注释和代码实现《Attention Is All You Need》- CSDN

[9] 【自然语言处理】Attention 讲解 - CSDN

[10] 超详细图解Self-Attention - 知乎

[11] 深度学习中的batch normalization - 知乎

[12] 层标准化详解(Layer Normalization)- CSDN

[13] 【深度学习】Layer Normalization - CSDN

[14] 标准化、归一化、规范化区别_- CSDN

[15] 什么?是Transformer位置编码 - CSDN

[16] 位置编码详细解读 - bilibili

[17] 碎碎念:Transformer的细枝末节 - 知乎

[18] Properties of Dot Product of Random Vectors - 知乎

[19] Transformer学习笔记一:Positional Encoding(位置编码) - 知乎

[20] Transformer 模型详解_- CSDN

[21] 从训练和预测角度来理解Transformer中Masked Self-Attention的原理 - 知乎

[22] Transformer详解 - 知乎

[23] transformer模型学习路线_transformer怎么训练 - CSDN

[24] ml6 - 博客园

猜你喜欢

转载自blog.csdn.net/weixin_46221946/article/details/129294826