参考来源:
Transformer 模型详解
CSDN:Transformer 模型详解
The Illustrated Transformer
论文:Attention Is All You Need

简介

Transformer 是 Google 的团队在 2017 年提出的一种 NLP 经典模型,现在比较火热的 Bert 也是基于 Transformer 。Transformer 模型使用了 Self-Attention 机制,不采用 RNN 的顺序结构,使得模型可以并行化训练,而且能够拥有全局信息。这个模型广泛应用于 NLP 领域,例如机器翻译,问答系统,文本摘要和语音识别等等方向。

1. Transformer 总体结构

和 seq2seq 模型一样,Transformer 模型中也采用了 encoer-decoder 架构。但其结构相比于 Attention 更加复杂,论文中 encoder 层由 6 个 encoder 堆叠在一起,decoder 层也一样。
首先介绍 Transformer 的整体结构,下图是 Transformer 用于中英文翻译的整体结构。
Transformer(Attention Is All You Need) - 图1

Transformer 整体结构

可以看到 Transformer 由 Encoder 和 Decoder 两个部分组成,Encoder 和 Decoder 都包含 6 个 block。
每一个encoder和decoder的内部简版结构如下图:
Transformer(Attention Is All You Need) - 图2

对于 encoder,包含两层,一个 self-attention 层和一个前馈神经网络,self-attention 能帮助当前节点不仅仅只关注当前的词,从而能获取到上下文的语义。
decoder 也包含 encoder 提到的两层网络,但是在这两层中间还有一层 attention 层,帮助当前节点获取到当前需要关注的重点内容。

Transformer 的工作流程大体如下:
第一步:获取输入句子的每一个单词的表示向量 XX 由单词的 Embedding 和单词位置的 Embedding 相加得到。
Transformer(Attention Is All You Need) - 图3

Transformer 的输入表示

第二步:将得到的单词表示向量矩阵 (如上图所示,每一行是一个单词的表示 X) 传入 Encoder 中,经过 6 个 Encoder block 后可以得到句子所有单词的编码信息矩阵 C,如下图。单词向量矩阵用 X(n×d) 表示, n 是句子中单词个数,d 是表示向量的维度 (论文中 d=512)。每一个 Encoder block 输出的矩阵维度与输入完全一致。
Transformer(Attention Is All You Need) - 图4

Transformer Encoder 编码句子信息

第三步:将 Encoder 输出的编码信息矩阵 C 传递到 Decoder 中,Decoder 依次会根据当前翻译过的单词 1~ i 翻译下一个单词 i+1,如下图所示。在使用的过程中,翻译到单词 i+1 的时候需要通过 Mask (掩盖) 操作遮盖住 i+1 之后的单词。
Transformer(Attention Is All You Need) - 图5

Transofrmer Decoder 预测

上图 Decoder 接收了 Encoder 的编码矩阵 C,然后首先输入一个翻译开始符 ““,预测第一个单词 “I”;然后输入翻译开始符 ““ 和单词 “I”,预测单词 “have”,以此类推。这是 Transformer 使用时候的大致流程,接下来是里面各个部分的细节。

2. Transformer 的输入

Transformer 中单词的输入表示 X 由单词 Embedding 和位置 Embedding 相加得到。
Transformer(Attention Is All You Need) - 图6

Transformer 的输入表示

2.1 单词 Embedding

单词的 Embedding 有很多种方式可以获取,例如可以采用 Word2Vec、Glove 等算法预训练得到,也可以在 Transformer 中训练得到。

2.2 位置 Embedding/Positional Encoding

Transformer 中除了单词的 Embedding,还需要使用位置 Embedding 表示单词出现在句子中的位置。因为 Transformer 不采用 RNN 的结构,而是使用全局信息,不能利用单词的顺序信息,而这部分信息对于 NLP 来说非常重要。所以 Transformer 中使用位置 Embedding 保存单词在序列中的相对或绝对位置。
位置 Embedding 用 PE表示,PE 的维度与单词 Embedding 是一样的。PE 可以通过训练得到,也可以使用某种公式计算得到。在 Transformer 中采用了后者,计算公式如下:

其中,pos 表示单词在句子中的位置,d 表示 PE 的维度 (与词 Embedding 一样),2i 表示偶数的维度,2i+1 表示奇数维度 (即 2i≤d, 2i+1≤d),即在偶数位置,使用正弦编码,在奇数位置,使用余弦编码,这里提供一下代码。

  1. position_encoding = np.array(
  2. [[pos / np.power(10000, 2.0 * (j // 2) / d_model) for j in range(d_model)] for pos in range(max_seq_len)])
  3. position_encoding[:, 0::2] = np.sin(position_encoding[:, 0::2])
  4. position_encoding[:, 1::2] = np.cos(position_encoding[:, 1::2])

使用这种公式计算 PE 有以下的好处:

  • 使 PE 能够适应比训练集里面所有句子更长的句子,假设训练集里面最长的句子是有 20 个单词,突然来了一个长度为 21 的句子,则使用公式计算的方法可以计算出第 21 位的 Embedding。
  • 可以让模型容易地计算出相对位置,对于固定长度的间距 k,PE(pos+k) 可以用 PE(pos) 计算得到。因为 Sin(A+B) = Sin(A)Cos(B) + Cos(A)Sin(B), Cos(A+B) = Cos(A)Cos(B) - Sin(A)Sin(B)。

将单词的词 Embedding 和位置 Embedding 相加,就可以得到单词的表示向量 XX 就是 Transformer 的输入。

3. Self-Attention

Transformer(Attention Is All You Need) - 图7

Transformer Encoder 和 Decoder

上图是论文中 Transformer 的内部结构图,左侧为 Encoder block,右侧为 Decoder block。红色圈中的部分为 Multi-Head Attention,是由多个 Self-Attention组成的,可以看到 Encoder block 包含一个 Multi-Head Attention,而 Decoder block 包含两个 Multi-Head Attention (其中有一个用到 Masked)。Multi-Head Attention 上方还包括一个 Add & Norm 层,Add 表示残差连接 (Residual Connection) 用于防止网络退化,Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化。
因为 Self-Attention是 Transformer 的重点,所以我们重点关注 Multi-Head Attention 以及 Self-Attention,首先详细了解一下 Self-Attention 的内部逻辑。

3.1 Self-Attention 结构

Transformer(Attention Is All You Need) - 图8

Self-Attention 结构

上图是 Self-Attention 的结构,在计算的时候需要用到矩阵 Q(查询), K(键值), V(值)。在实际中,Self-Attention 接收的是输入(单词的表示向量 x 组成的矩阵 X) 或者上一个 Encoder block 的输出。而 Q, K, V 正是通过 Self-Attention 的输入进行线性变换得到的。

  1. 首先,self-attention 会计算出三个新的向量,在论文中,向量的维度是 512 维,我们把这三个向量分别称为 Query、Key、Value,这三个向量是用 embedding 向量与一个矩阵相乘得到的结果,这个矩阵是随机初始化的,维度为(64,512)注意第二个维度需要和 embedding 的维度一样,其值在 BP 的过程中会一直进行更新,得到的这三个向量的维度是 64 低于 embedding 维度的。

image.png
那么 Query、Key、Value 这三个向量又是什么呢?这三个向量对于 attention 来说很重要,当你理解了下文后,你将会明白这三个向量扮演者什么的角色。

  1. 计算 self-attention 的分数值,该分数值决定了当我们在某个位置 encode 一个词时,对输入句子的其他部分的关注程度。这个分数值的计算方法是 Query 与 Key 做点乘,以下图为例,首先我们需要针对 Thinking 这个词,计算出其他词对于该词的一个分数值,首先是针对于自己本身即 q1·k1 ,然后是针对于第二个词即 q1·k2 。

image.png

  1. 接下来,把点成的结果除以一个常数,这里我们除以 8 ,这个值一般是采用上文提到的矩阵的第一个维度的开方,即 64 的开方 8 ,当然也可以选择其他的值,然后把得到的结果做一个 softmax 的计算。得到的结果即是每个词对于当前位置的词的相关性大小,当然,当前位置的词相关性肯定会会很大。

image.png

  1. 下一步就是把 Value 和 softmax 得到的值进行相乘,并相加,得到的结果即是 self-attetion 在当前节点的值。

image.png

在实际的应用场景,为了提高计算速度,我们采用的是矩阵的方式,直接计算出 Query, Key, Value 的矩阵,然后把 embedding 的值与三个矩阵直接相乘,把得到的新矩阵 Q 与 K 相乘,乘以一个常数,做 softmax 操作,最后乘上 V 矩阵。
image.png
image.png
这种通过 query 和 key 的相似性程度来确定 value 的权重分布的方法被称为 scaled dot-product attention

3.2 Q , K , V 的计算

Self-Attention 的输入用矩阵 X 进行表示,则可以使用线性变阵矩阵 WQ, WK, WV 计算得到 Q , K , V 。计算如下图所示,注意 X , Q , K , V 的每一行都表示一个单词
Transformer(Attention Is All You Need) - 图15

Q, K, V 的计算

3.3 Self-Attention 的输出

得到矩阵 Q , K , V 之后就可以计算出 Self-Attention 的输出了,计算的公式如下:

d**kQ、K** 矩阵的列数,即向量维度。

Self-Attention 的输出

公式中计算矩阵 QK 每一行向量的内积,为了防止内积过大,因此除以 dk 的平方根。Q 乘以 K 的转置后,得到的矩阵行列数都为 n,n 为句子单词数,这个矩阵可以表示单词之间的 attention 强度。下图为 Q 乘以 K 的转置,1234 表示的是句子中的单词。
Transformer(Attention Is All You Need) - 图16

QKT 的计算

得到 QK**T** 之后,使用 Softmax 计算每一个单词对于其他单词的 attention 系数,公式中的 Softmax 是对矩阵的每一行进行 Softmax,即每一行的和都变为 1。
Transformer(Attention Is All You Need) - 图17

对矩阵的每一行进行 Softmax

得到 Softmax 矩阵之后可以和 V 相乘,得到最终的输出 Z
Transformer(Attention Is All You Need) - 图18

Self-Attention 输出

上图中 Softmax 矩阵的第 1 行表示单词 1 与其他所有单词的 attention 系数,最终单词 1 的输出 Z**1 等于所有单词 i 的值 V**i 根据 attention 系数的比例加在一起得到,如下图所示:
Transformer(Attention Is All You Need) - 图19

Zi 的计算方法

3.4 Multi-Head Attention

在上一步,我们已经知道怎么通过 Self-Attention 计算得到输出矩阵 Z 。
这篇论文更牛逼的地方是给 self-attention 加入了另外一个机制,被称为“multi-headed” attention ,该机制理解起来很简单,Multi-Head Attention 是由多个 Self-Attention 组合形成的,也就是说不仅仅只初始化一组 Q、K、V 的矩阵,而是初始化多组,tranformer 是使用了 8 组,所以最后得到的结果是 8 个矩阵。
下图是论文中 Multi-Head Attention 的结构图。
Transformer(Attention Is All You Need) - 图20

Multi-Head Attention

从上图可以看到 Multi-Head Attention 包含多个 Self-Attention 层,首先将输入 X 分别传递到 h 个不同的 Self-Attention 中,计算得到 h 个输出矩阵 Z 。下图是 h=8 时候的情况,此时会得到 8 个输出矩阵 Z
Transformer(Attention Is All You Need) - 图21
Transformer(Attention Is All You Need) - 图22

多个 Self-Attention

得到 8 个输出矩阵 Z**1Z**8 之后,这给我们留下了一个小的挑战,前馈神经网络没法输入 8 个矩阵呀,这该怎么办呢?所以我们需要一种方式,把 8 个矩阵降为 1 个,首先,我们把 8 个矩阵连在一起,这样会得到一个大的矩阵,再随机初始化一个矩阵和这个组合好的矩阵相乘,最后得到一个最终的矩阵。

得到 8 个输出矩阵 Z**1Z**8 之后,Multi-Head Attention 将它们拼接在一起 (Concat),然后传入一个 Linear 层,得到 Multi-Head Attention 最终的输出 Z

Transformer(Attention Is All You Need) - 图23

Multi-Head Attention 的输出

可以看到 Multi-Head Attention 输出的矩阵 Z 与其输入的矩阵 X 的维度是一样的。

4. Encoder 结构

Transformer(Attention Is All You Need) - 图24

Transformer Encoder block

上图红色部分是 Transformer 的 Encoder block 结构,可以看到是由 Multi-Head Attention , Add & Norm , Feed Forward , Add & Norm 组成的。刚刚已经了解了 Multi-Head Attention 的计算过程,现在了解一下 Add & Norm 和 Feed Forward 部分。

4.1 Add & Norm

Add & Norm 层由 Add 和 Norm 两部分组成,其计算公式如下:

Add & Norm 公式

其中 X 表示 Multi-Head Attention 或者 Feed Forward 的输入,MultiHeadAttention(X) 和 FeedForward(X) 表示输出 (输出与输入 X 维度是一样的,所以可以相加)。

AddX+MultiHeadAttention(X),是一种残差连接,通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到。
Transformer(Attention Is All You Need) - 图25

残差连接

Norm 指 Layer Normalization,通常用于 RNN 结构,Layer Normalization 会将每一层神经元的输入都转成均值方差都一样的,这样可以加快收敛。
说到 normalization,那就肯定得提到 Batch Normalization。BN 的主要思想就是:在每一层的每一批数据上进行归一化。我们可能会对输入数据进行归一化,但是经过该网络层的作用后,我们的数据已经不再是归一化的了。随着这种情况的发展,数据的偏差越来越大,我的反向传播需要考虑到这些大的偏差,这就迫使我们只能使用较小的学习率来防止梯度消失或者梯度爆炸。
BN 的具体做法就是对每一小批数据,在批这个方向上做归一化。如下图所示:
image.png
可以看到,右半边求均值是沿着数据 batch_size 的方向进行的,其计算公式如下:

那么什么是 Layer normalization 呢?它也是归一化数据的一种方式,不过 LN 是在每一个样本上计算均值和方差,而不是 BN 那种在批方向计算均值和方差!
image.png
下面看一下 LN 的公式:

4.2 Feed Forward

Feed Forward 层比较简单,是一个两层的全连接层,第一层的激活函数为 Relu,第二层不使用激活函数,对应的公式如下。
Transformer(Attention Is All You Need) - 图28

Feed Forward

X 是输入,Feed Forward 最终得到的输出矩阵的维度与 X 一致。

4.3 组成 Encoder

通过上面描述的 Multi-Head Attention , Feed Forward , Add & Norm 就可以构造出一个 Encoder block,Encoder block 接收输入矩阵 X(n×d),并输出一个矩阵 O(n×d)。通过多个 Encoder block 叠加就可以组成 Encoder。
第一个 Encoder block 的输入为句子单词的表示向量矩阵,后续 Encoder block 的输入是前一个 Encoder block 的输出,最后一个 Encoder block 输出的矩阵就是 编码信息矩阵 C ,这一矩阵后续会用到 Decoder 中。
Transformer(Attention Is All You Need) - 图29

Encoder 编码句子信息

5. Decoder 结构

Transformer(Attention Is All You Need) - 图30

Transformer Decoder block

上图红色部分为 Transformer 的 Decoder block 结构,与 Encoder block 相似,但是存在一些区别:

  • 包含两个 Multi-Head Attention 层。
  • 第一个 Multi-Head Attention 层采用了 Masked 操作。
  • 第二个 Multi-Head Attention 层的 K, V 矩阵使用 Encoder 的编码信息矩阵 C 进行计算,而 Q 使用上一个 Decoder block 的输出计算。
  • 最后有一个 Softmax 层计算下一个翻译单词的概率。

5.1 Mask

mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask。

其中,padding mask 在所有的 scaled dot-product attention 里面都需要用到,而 sequence mask 只有在 decoder 的 self-attention 里面用到。

Padding Mask

什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0 。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的 attention 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。
具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax ,这些位置的概率就会接近 0 !
而我们的 padding mask 实际上是一个张量,每个值都是一个Boolean,值为 false 的地方就是我们要进行处理的地方。

Sequence mask

文章前面也提到,sequence mask 是为了使得 decoder 不能看见未来的信息。也就是对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。
那么具体怎么做呢?也很简单:产生一个下三角矩阵。把这个矩阵作用在每一个序列上,就可以达到我们的目的。
对于 decoder 的 self-attention,里面使用到的 scaled dot-product attention,同时需要padding mask 和 sequence mask 作为 attn_mask,具体实现就是两个 mask 相加作为 attn_mask 。
其他情况,attn_mask 一律等于 padding mask。

5.2 第一个 Multi-Head Attention

Decoder block 的第一个 Multi-Head Attention 采用了 Masked 操作,因为在翻译的过程中是顺序翻译的,即翻译完第 i 个单词,才可以翻译第 i+1 个单词。通过 Masked 操作可以防止第 i 个单词知道 i+1 个单词之后的信息。下面以 “我有一只猫” 翻译成 “I have a cat” 为例,了解一下 Masked 操作。
下面的描述中使用了类似 Teacher Forcing 的概念,不熟悉 Teacher Forcing 的童鞋可以参考以下上一篇文章Seq2Seq 模型详解。在 Decoder 的时候,是需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 ““ 预测出第一个单词为 “I”,然后根据输入 “ I” 预测下一个单词 “have”。
Transformer(Attention Is All You Need) - 图31

Decoder 预测

Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 ( I have a cat) 和对应输出 (I have a cat ) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面用 0 1 2 3 4 5 分别表示 “ I have a cat

第一步:是 Decoder 的输入矩阵和 Mask 矩阵,输入矩阵包含 “ I have a cat” (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。在 Mask 可以发现单词 0 只能使用单词 0 的信息,而单词 1 可以使用单词 0, 1 的信息,即只能使用之前的信息。
Transformer(Attention Is All You Need) - 图32

输入矩阵与 Mask 矩阵

第二步:接下来的操作和之前的 Self-Attention 一样,通过输入矩阵 X 计算得到 Q , K , V 矩阵。然后计算 QK**T 的乘积 QK**T
Transformer(Attention Is All You Need) - 图33

QKT

第三步:在得到 QK**T 之后需要进行 Softmax,计算 attention score,我们在 Softmax 之前需要使用 Mask** 矩阵遮挡住每一个单词之后的信息,遮挡操作如下:
Transformer(Attention Is All You Need) - 图34

Softmax 之前 Mask

得到 Mask QKT 之后在 Mask QKT 上进行 Softmax ,每一行的和都为 1 。但是单词 0 在单词 1, 2, 3, 4 上的 attention score 都为 0。

第四步:使用 Mask QKT 与矩阵 V 相乘,得到输出 Z ,则单词 1 的输出向量 Z1 是只包含单词 1 信息的。
Transformer(Attention Is All You Need) - 图35

Mask 之后的输出

第五步:通过上述步骤就可以得到一个 Mask Self-Attention 的输出矩阵 Zi ,然后和 Encoder 类似,通过 Multi-Head Attention 拼接多个输出 Zi 然后计算得到第一个 Multi-Head Attention 的输出 ZZ 与输入 X 维度一样。

5.3 第二个 Multi-Head Attention

Decoder block 第二个 Multi-Head Attention 变化不大, 主要的区别在于其中 Self-Attention 的 K , V矩阵不是使用 上一个 Decoder block 的输出计算的,而是使用 Encoder 的编码信息矩阵 C 计算的。
根据 Encoder 的输出 C 计算得到 K , V,根据上一个 Decoder block 的输出 Z 计算 Q (如果是第一个 Decoder block 则使用输入矩阵 X 进行计算),后续的计算方法与之前描述的一致。
这样做的好处是在 Decoder 的时候,每一位单词都可以利用到 Encoder 所有单词的信息 (这些信息无需 Mask)。

5.4 Softmax 预测输出单词

Decoder block 最后的部分是利用 Softmax 预测下一个单词,在之前的网络层我们可以得到一个最终的输出 Z,因为 Mask 的存在,使得单词 0 的输出 Z0 只包含单词 0 的信息,如下。
Transformer(Attention Is All You Need) - 图36

Decoder Softmax 之前的 Z

Softmax 根据输出矩阵的每一行预测下一个单词:
Transformer(Attention Is All You Need) - 图37

Decoder Softmax 预测

这就是 Decoder block 的定义,与 Encoder 一样,Decoder 是由多个 Decoder block 组合而成。

5.5 输出层

当 decoder 层全部执行完毕后,怎么把得到的向量映射为我们需要的词呢,很简单,只需要在结尾再添加一个全连接层和 softmax 层,假如我们的词典是 1w 个词,那最终 softmax 会输入 1w 个词的概率,概率值最大的对应的词就是我们最终的结果。
image.png

6. Transformer 总结

Transformer 与 RNN 不同,可以比较好地并行训练。
Transformer 本身是不能利用单词的顺序信息的,因此需要在输入中添加位置 Embedding,否则 Transformer 就是一个词袋模型了。
Transformer 的重点是 Self-Attention 结构,其中用到的 Q, K, V 矩阵通过输出进行线性变换得到。
Transformer 中 Multi-Head Attention 中有多个 Self-Attention,可以捕获单词之间多种维度上的相关系数 attention score。