1. 前言
transformer结构是google在2017年的Attention Is All You Need论文中提出,在NLP的多个任务上取得了非常好的效果,可以说目前NLP发展都离不开transformer。最大特点是抛弃了传统的CNN和RNN,整个网络结构完全是由Attention机制组成。 由于其出色性能以及对下游任务的友好性或者说下游任务仅仅微调即可得到不错效果,在计算机视觉领域不断有人尝试将transformer引入,近期也出现了一些效果不错的尝试,典型的如目标检测领域的detr和可变形detr,分类领域的vision transformer等等。 本文从transformer结构出发,结合视觉中的transformer成果(具体是vision transformer和detr)进行分析。
1.1. Transformer 概览
首先,让我们先将 Transformer 模型视为一个黑盒,如下图所示。在机器翻译任务中,将一种语言的一个句子作为输入,然后将其翻译成另一种语言的一个句子作为输出。
![](https://img-blog.csdnimg.cn/58e1ef290f0643dea705420b00f8cad1.png)
Transformer 本质上是一个 Encoder-Decoder 架构。因此中间部分的 Transformer 可以分为两个部分:编码组件和解码组件。如下图所示:
![](https://img-blog.csdnimg.cn/e989a5cfab0f4ddf9b387bd064463838.png)
其中,编码组件由多层编码器(Encoder)组成(在论文中作者使用了 6 层编码器,在实际使用过程中你可以尝试其他层数)。解码组件也是由相同层数的解码器(Decoder)组成(在论文也使用了 6 层)。如下图所示:
每个编码器由两个子层组成:Self-Attention 层(自注意力层)和 Position-wise Feed Forward Network(前馈网络,缩写为 FFN)如下图所示。每个编码器的结构都是相同的,但是它们使用不同的权重参数。
编码器的输入会先流入 Self-Attention 层。它可以让编码器在对特定词进行编码时使用输入句子中的其他词的信息(可以理解为:当我们翻译一个词时,不仅只关注当前的词,而且还会关注其他词的信息)。后面我们将会详细介绍 Self-Attention 的内部结构。然后,Self-Attention 层的输出会流入前馈网络。
解码器也有编码器中这两层,但是它们之间还有一个注意力层(即 Encoder-Decoder Attention),其用来帮忙解码器关注输入句子的相关部分(类似于 seq2seq 模型中的注意力)。
1.2. 引入张量
现在我们已经了解了模型的主要组成部分,让我们开始研究各种向量/张量,以及他们在这些组成部分之间是如何流动的,从而将输入经过已训练的模型转换为输出。
和通常的 NLP 任务一样,首先,我们使用词嵌入算法(Embedding)将每个词转换为一个词向量。在 Transformer 论文中,词嵌入向量的维度是 512。
![](https://img-blog.csdnimg.cn/7a974f28ce5e43dfb6ef09450ffbd0c8.png)
嵌入仅发生在最底层的编码器中。所有编码器都会接收到一个大小为 512 的向量列表——底部编码器接收的是词嵌入向量,其他编码器接收的是上一个编码器的输出。这个列表大小是我们可以设置的超参数——基本上这个参数就是训练数据集中最长句子的长度。
对输入序列完成嵌入操作后,每个词都会流经编码器的两层。
接下来,我们将换一个更短的句子作为示例,来说明在编码器的每个子层中发生了什么。
上面我们提到,编码器会接收一个向量作为输入。编码器首先将这些向量传递到 Self-Attention 层,然后传递到前馈网络,最后将输出传递到下一个编码器。
1.3. Transformer 整体结构
![](https://img-blog.csdnimg.cn/26fd3ea6ba5745ea8b5a4e6c708b4992.png)
可以看到 Transformer 由 Encoder 和 Decoder 两个部分组成,Encoder 和 Decoder 都包含 6 个 block。Transformer 的工作流程大体如下:
第一步:获取输入句子的每一个单词的表示向量 X,X由单词的 Embedding(Embedding就是从原始数据提取出来的Feature) 和单词位置的 Embedding 相加得到。
![](https://img-blog.csdnimg.cn/img_convert/0f47aff892ddb895bbf7f0f8a4c5d928.webp?x-oss-process=image/format,png)
第二步:将得到的单词表示向量矩阵 (如上图所示,每一行是一个单词的表示 x) 传入 Encoder 中,经过 6 个 Encoder block 后可以得到句子所有单词的编码信息矩阵 C,如下图。单词向量矩阵用表示, n 是句子中单词个数,d 是表示向量的维度 (论文中 d=512)。每一个 Encoder block 输出的矩阵维度与输入完全一致。
![](https://img-blog.csdnimg.cn/img_convert/be6c36ffcdde13615ff91c7aeec58e35.webp?x-oss-process=image/format,png)
第三步:将 Encoder 输出的编码信息矩阵 C传递到 Decoder 中,Decoder 依次会根据当前翻译过的单词 1~ i 翻译下一个单词 i+1,如下图所示。在使用的过程中,翻译到单词 i+1 的时候需要通过 Mask (掩盖) 操作遮盖住 i+1 之后的单词。
![](https://img-blog.csdnimg.cn/img_convert/6a032473f343bfcea2e5291717ae9114.webp?x-oss-process=image/format,png)
上图 Decoder 接收了 Encoder 的编码矩阵 C,然后首先输入一个翻译开始符 "<Begin>",预测第一个单词 "I";然后输入翻译开始符 "<Begin>" 和单词 "I",预测单词 "have",以此类推。这是 Transformer 使用时候的大致流程,接下来是里面各个部分的细节。
2. Transformer 的输入
Transformer 中单词的输入表示 x由单词 Embedding 和位置 Embedding (Positional Encoding)相加得到。
![](https://img-blog.csdnimg.cn/img_convert/453cc9d8e8fb82faa04a37d65fa40e62.webp?x-oss-process=image/format,png)
2.1. 单词 Embedding
单词的 Embedding 有很多种方式可以获取,例如可以采用 Word2Vec、Glove 等算法预训练得到,也可以在 Transformer 中训练得到。
2.2. 位置 Embedding
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)。使用这种公式计算 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 相加,就可以得到单词的表示向量 x,x 就是 Transformer 的输入。
3. Self-Attention(自注意力机制)
![](https://img-blog.csdnimg.cn/img_convert/9d769f0572f3c555a846e59f74e691ee.webp?x-oss-process=image/format,png)
上图是论文中 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 结构
![](https://img-blog.csdnimg.cn/img_convert/3fdc6b66e4f34e9cba7be675eac2c147.webp?x-oss-process=image/format,png)
上图是Self-Attention的结构,在计算的时候需要用到矩阵Q(查询), K(键值), V(值)。在实际中,Self-Attention接收的是输入(单词的表示向量x组成的矩阵X) 或者上一个 Encoder block 的输出。而Q, K, V正是通过 Self-Attention 的输入进行线性变换得到的。
3.2. Q, K, V 的计算
Self-Attention 的输入用矩阵X进行表示,则可以使用线性变阵矩阵WQ, WK, WV计算得到Q, K, V。计算如下图所示,注意 X, Q, K, V 的每一行都表示一个单词。
![](https://img-blog.csdnimg.cn/img_convert/6f93eac720670810a7db1ce9380ca226.webp?x-oss-process=image/format,png)
3.3. Self-Attention 的输出
得到矩阵 Q, K, V之后就可以计算出 Self-Attention 的输出了,计算的公式如下:
![](https://img-blog.csdnimg.cn/img_convert/a182205d39070ed933ad38d7d7bd2aa0.webp?x-oss-process=image/format,png)
公式中计算矩阵Q和K每一行向量的内积,为了防止内积过大,因此除以的平方根。Q乘以K的转置后,得到的矩阵行列数都为 n,n 为句子单词数,这个矩阵可以表示单词之间的 attention 强度。下图为Q乘以
,1234 表示的是句子中的单词。
![](https://img-blog.csdnimg.cn/img_convert/b3e1345d80815d13a58b0e17a5fcf852.png)
得到之后,使用 Softmax 计算每一个单词对于其他单词的 attention 系数,公式中的 Softmax 是对矩阵的每一行进行 Softmax,即每一行的和都变为 1.
![](https://img-blog.csdnimg.cn/img_convert/3ac679919ed55b7641adfeb4d109e008.png)
得到 Softmax 矩阵之后可以和V相乘,得到最终的输出Z。
![](https://img-blog.csdnimg.cn/img_convert/fa3588b1be4253f99677c12d8b4507d2.png)
上图中 Softmax 矩阵的第 1 行表示单词 1 与其他所有单词的 attention 系数,最终单词 1 的输出
等于所有单词 i 的值根据 attention 系数的比例加在一起得到,如下图所示:
![](https://img-blog.csdnimg.cn/img_convert/d1c7e7025c14bcb3c030dadede77fa98.webp?x-oss-process=image/format,png)
3.4. Multi-Head Attention
在上一步,我们已经知道怎么通过 Self-Attention 计算得到输出矩阵 Z,而 Multi-Head Attention 是由多个 Self-Attention 组合形成的,下图是论文中 Multi-Head Attention 的结构图。
![](https://img-blog.csdnimg.cn/img_convert/795186de862c48231d528739afa5f9a7.webp?x-oss-process=image/format,png)
从上图可以看到 Multi-Head Attention 包含多个 Self-Attention 层,首先将输入X分别传递到 h 个不同的 Self-Attention 中,计算得到 h 个输出矩阵Z。下图是 h=8 时候的情况,此时会得到 8 个输出矩阵Z。
![](https://img-blog.csdnimg.cn/img_convert/3f89c4893a8b2b356f1ff04564643d76.webp?x-oss-process=image/format,png)
得到 8 个输出矩阵到
之后,Multi-Head Attention 将它们拼接在一起 (Concat),然后传入一个Linear层,得到 Multi-Head Attention 最终的输出Z。
![](https://img-blog.csdnimg.cn/img_convert/f4acddd52ee528fa352e6a75ba1ec2e4.webp?x-oss-process=image/format,png)
可以看到 Multi-Head Attention 输出的矩阵Z与其输入的矩阵X的维度是一样的。
4. Encoder 结构
![](https://img-blog.csdnimg.cn/img_convert/506e155722ca008657aa29da0690acf5.webp?x-oss-process=image/format,png)
上图红色部分是 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 两部分组成,其计算公式如下:
![](https://img-blog.csdnimg.cn/img_convert/f382a1278ac532e2a51de2b448f98fcd.png)
其中 X表示 Multi-Head Attention 或者 Feed Forward 的输入,MultiHeadAttention(X) 和 FeedForward(X) 表示输出 (输出与输入 X 维度是一样的,所以可以相加)。
Add指 X+MultiHeadAttention(X),是一种残差连接,通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到:
![](https://img-blog.csdnimg.cn/img_convert/c9203c0edc8f3a403caa0e774fa473a3.png)
Norm指 Layer Normalization,通常用于 RNN 结构,Layer Normalization 会将每一层神经元的输入都转成均值方差都一样的,这样可以加快收敛。
4.2. Feed Forward
Feed Forward 层比较简单,是一个两层的全连接层,第一层的激活函数为 Relu,第二层不使用激活函数,对应的公式如下。
![](https://img-blog.csdnimg.cn/img_convert/6004a6b6067142f19ab249748b024fb3.png)
X是输入,Feed Forward 最终得到的输出矩阵的维度与X一致。
4.3. 组成 Encoder
通过上面描述的 Multi-Head Attention, Feed Forward, Add & Norm 就可以构造出一个 Encoder block,Encoder block 接收输入矩阵,并输出一个矩阵
。通过多个 Encoder block 叠加就可以组成 Encoder。
第一个 Encoder block 的输入为句子单词的表示向量矩阵,后续 Encoder block 的输入是前一个 Encoder block 的输出,最后一个 Encoder block 输出的矩阵就是编码信息矩阵 C,这一矩阵后续会用到 Decoder 中。
![](https://img-blog.csdnimg.cn/img_convert/6f66f1f84e9b3a8e5f8d9e2ac4ec1b67.webp?x-oss-process=image/format,png)
5. Decoder 结构
![](https://img-blog.csdnimg.cn/img_convert/ea62b846323ac1146ee73867ddf09d93.webp?x-oss-process=image/format,png)
上图红色部分为 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. 第一个 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 的时候,是需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 "<Begin>" 预测出第一个单词为 "I",然后根据输入 "<Begin> I" 预测下一个单词 "have"。
![](https://img-blog.csdnimg.cn/img_convert/7030a7d4ca02bd234cac42f1d63921ab.png)
Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 (<Begin> I have a cat) 和对应输出 (I have a cat <end>) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面用 0 1 2 3 4 5 分别表示 "<Begin> I have a cat <end>"。
第一步:是 Decoder 的输入矩阵和 Mask 矩阵,输入矩阵包含 "<Begin> I have a cat" (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。在 Mask 可以发现单词 0 只能使用单词 0 的信息,而单词 1 可以使用单词 0, 1 的信息,即只能使用之前的信息。
![](https://img-blog.csdnimg.cn/img_convert/2efad0e10aeba6bffa3959c30961c437.png)
第二步:接下来的操作和之前的 Self-Attention 一样,通过输入矩阵X计算得到Q,K,V矩阵。然后计算Q和的乘积
。
![](https://img-blog.csdnimg.cn/img_convert/d7ee75cf7b830da7611e1d4bf66628aa.webp?x-oss-process=image/format,png)
第三步:在得到之后需要进行 Softmax,计算 attention score,我们在 Softmax 之前需要使用Mask矩阵遮挡住每一个单词之后的信息,遮挡操作如下:
![](https://img-blog.csdnimg.cn/img_convert/b2286fc2354a283f197a3b80db3eba4e.webp?x-oss-process=image/format,png)
得到 Mask 之后在 Mask
上进行 Softmax,每一行的和都为 1。但是单词 0 在单词 1, 2, 3, 4 上的 attention score 都为 0。
第四步:使用 Mask 与矩阵 V相乘,得到输出 Z,则单词 1 的输出向量
是只包含单词 1 信息的。
第五步:通过上述步骤就可以得到一个 Mask Self-Attention 的输出矩阵,然后和 Encoder 类似,通过 Multi-Head Attention 拼接多个输出
然后计算得到第一个 Multi-Head Attention 的输出Z,Z与输入X维度一样。
5.2. 第二个 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.3. Softmax 预测输出单词
Decoder block 最后的部分是利用 Softmax 预测下一个单词,在之前的网络层我们可以得到一个最终的输出 Z,因为 Mask 的存在,使得单词 0 的输出 Z0 只包含单词 0 的信息,如下:
![](https://img-blog.csdnimg.cn/img_convert/c563c6852622418f24f4a6af219cf55a.webp?x-oss-process=image/format,png)
Softmax 根据输出矩阵的每一行预测下一个单词:
![](https://img-blog.csdnimg.cn/img_convert/78e2abe4db5ce580f923c0de65434841.png)
这就是 Decoder block 的定义,与 Encoder 一样,Decoder 是由多个 Decoder block 组合而成。
6. Transformer 总结
- Transformer 与 RNN 不同,可以比较好地并行训练。
- Transformer 本身是不能利用单词的顺序信息的,因此需要在输入中添加位置 Embedding,否则 Transformer 就是一个词袋模型了。
- Transformer 的重点是 Self-Attention 结构,其中用到的 Q, K, V矩阵通过输出进行线性变换得到。
- Transformer 中 Multi-Head Attention 中有多个 Self-Attention,可以捕获单词之间多种维度上的相关系数 attention score。