Transformer的简单总结

  

  对Jay Alammar的The Illustrated Transformer做一个简单总结。

整体结构

  从宏观上来看,Transformer是由一堆编码器与解码器组成。其各自的数量没有具体要求,不过编码器和解码器的数量是相同的。

Transformer基础结构

  其中编码器由自注意力层和前馈神经网络组成。解码器也同样有这两层,但是在这两层之间还有一个注意力层——帮助解码器关注输入序列的相关部分。

编码器和解码器

自注意力机制 Self-Attention

  通过自注意力机制,能够让模型在处理每一个token时候能够关注输入序列其它位置的token,从而达到更好的编码效果。例如“The animal didn’t cross the street because it was too tired”这句话,模型在处理时候需要知道“it”指代的是什么,是“animal”还是“street”。

实现自注意力的几个步骤

  1. 准备工作:对于每一个输入向量进行词嵌入,通过word embedding得到可以处理的输入数据,记为\(X_i\)向量

  2. 第一步:对于每一个输入\(X_i\)计算其\(Q\)\(K\)\(V\)向量(Query、Key、Value)。具体计算步骤如下:

    • \(Q_i = X_i \times W^Q\)
    • \(K_i = X_i \times W^K\)
    • \(V_i = X_i \times W^V\)
    • 其中这三个\(W\)矩阵都是在模型训练过程中得到的模型参数 QKV计算
  3. 第二步:对于每一个\(X_i\),都计算一个其相对于所有\(X\)的分数(包括自己),用于评估\(X_i\)需要在其它\(X\)上付出多少关注程度。针对某个\(X\)的具体做法如下:

    • 将自己的\(Q\)向量与所有的\(K\)向量做点积运算 \[score = Q \cdot K_i\]
    • \(X_1\)相对于自己的\(score\)就是\(Q_1 \cdot K_1\)\(X_1\)相对于\(X_2\)\(score\)就是\(Q_1 \cdot K_2\) …… score的运算
  4. 第三步:将计算出来的\(score\)除以\(\sqrt{d_k}\),并通过一个\(Softmax\)。其中\(d_k\)\(K\)向量的维度。其目的是为了能够让模型有更加稳定的梯度。

  5. 第四步:将每一个\(V\)向量乘以经过\(Softmax\)\(score\),以提高相关词的关注度,降低不相干的。

  6. 第五步:将所有加权后的\(V\)向量求和,最后得到的结果就是自注意力层在输入序列当前位置上的结果。

  我们需要对每一个输入\(X\)都计算一遍自注意力。

自注意力的矩阵运算

  在实际应用中,并不是对每个\(X\)都单独计算一遍自注意力。而是采取矩阵运算的方式提高计算效率。

  1. 首先,将输入向量组成一个整体的矩阵\(X\),然后让其乘上三个训练好的权重矩阵\(W^Q, W^K, W^V\)
  2. 然后,直接通过以下公式计算得到自注意力层的输出 \[Z = Softmax(\frac{Q \times K^T}{\sqrt{d_k}})V\]

多头注意力 multi-head

  通过多头注意力,能够让模型关注输入序列的不同特征。简单来说就是让不同的头关注不同的信息。它能够提升模型在注意力层的表现。

1787411014326

  在Transformer中使用了8个注意力头。所以需要将输入矩阵分别计算八个不相同的注意力头\(Z_0\)\(Z_7\)

1787411208776

  为了将注意力层的结果能够成功送入下一前馈层,需要将这8个注意力头先拼接成一个矩阵,然后让其乘上一个权重矩阵\(W^O\),最终的输出\(Z\)包含了所有注意力头的信息,然后将其送入前馈层。

\[Z = concat(Z_0...Z_7) \times W^O\]

1787411621864

  以下是注意力机制的完整图:

1787411720896

位置编码

  通过位置编码将输入序列的空间信息进行表征。最终送给模型进行注意力处理的输入是将词嵌入和位置编码信息的和。

1787412645750

残差

  在编码器和解码器的子层中还通过残差连接以降低梯度的退化。

1787414428555
1787414453178

解码器

  输入在经过多个编码器之后的输出会将其转换成一系列注意力向量\(K_{encdec}\)\(V_{encdec}\),这些注意力向量会在解码器的每个“编码-解码 注意力层”中使用。

1787414966694

  然后,在解码器中会不断根据上一轮的输出作为输入重新送入解码器中,不停循环直到输出最终结束符。需要注意的是,在解码器中的注意力层仅允许关注输出序列中较早的位置,也就是让当前位置只允许看自己的左边。

  “编码-解码 注意力层”的工作方式和多头自注意力基本相同,但是其\(Q\)矩阵来源于下面的注意力层,而不是编码器的输出,\(K\)\(V\)矩阵来自编码器的输出。

最后的回归

  最后,将解码器的输出通过一个全连接层以及一个softmax得到最终单词(token)的概率分布,选取其中概率最高的一个。

1787415954636

为什么是这样?

  Transformer的架构设计并非空穴来风,设计者并非一拍脑袋就这样设计,一切有迹可循。

Transformer之前

  在Transformer之前,机器理解一句话的主流架构RNN。但是其存在一个效率问题:单词必须一个一个生成。因此后面的计算必须依赖前面的计算结果,这就导致计算效率很低,而GPU擅长的是并行计算。

  另外还有一个长距离依赖问题:仍以“The animal didn’t cross the street because it was too tired”这个句子为例。当句子过长时候,后面的“it”和前面的主体距离较远,而RNN中信息又需要一步一步传递,这就导致很久之前出现的信息会在传递过程中丢失。

Self-Attention的出现

  简单来说,自注意力就是让token能够直接查看整个序列信息,并且让其自己决定应该关注哪些内容。

  通过\(Q\)\(K\)\(V\)三个矩阵,让这个问题数学化。\(Query\)代表“我现在想要寻找什么信息”;\(Key\)代表“我这里有什么信息可以提供”;\(Value\)代表“如果你要关注我,你需要从我这里拿走哪些信息”

  于是,这就解决了一个非常具体的问题:如何让一个token根据当前的需求,有选择地从其它token中获取信息。

为什么要多个头

  使用多个头的目的就在于从多个不同的视角来看待信息。由于输入语句中会同时存在多个不同的关系,例如语义、语法、指代等等关系。通过多个头的设计让模型能够同时从不同角度观察句子。

Attention后为什么有一个MLP

  在Attention中,模型做的事是从输入中获取信息。但是在获取完成这些信息后,还需要处理这些信息。因此在编码器和解码器中有两个阶段:信息交流和信息加工。

为什么要位置编码

  这是由Attention的本身特点决定的。它并不关注token的顺序。例如:“我喜欢你”和“你喜欢我”这两个在Attention眼中并没有太大的区别,其本身并不知道前后。

  因此就需要位置编码,在输入中加入位置信息。因为位置本身也传递信息。

总结一下

遇到的问题 设计
RNN 必须顺序处理 Self-Attention 并行处理
长距离依赖困难 任意 token 可以直接关注其他 token
不知道关注谁 Q/K/V
一个视角不够 Multi-Head
获取信息后还要加工 MLP
深层网络难训练 Residual
数值/训练不稳定 LayerNorm
Attention 不知道顺序 Position Information
GPT 不能偷看答案 Causal Mask