Transformer模型详解
前言
Transformer模型现在已经是大语言模型搭建的主流框架,其由谷歌团队在2017年发表的论文《Attention is All You Need》[1706.03762] Attention Is All You Need (arxiv.org)提出,论文相关的Tensorflow代码:tensorflow/tensor2tensor: Library of deep learning models and datasets designed to make deep learning more accessible and accelerate ML research. (github.com)。
本文的目的是在弄懂Transformer模型结构、底层逻辑的基础上,进一步理解当前大语言模型的主要技术架构。
参考链接:
Transformer模型详解(图解最完整版) - 知乎 (zhihu.com)
十分钟理解Transformer - 知乎 (zhihu.com)
【Transformer模型】曼妙动画轻松学,形象比喻贼好记_哔哩哔哩_bilibili
The Annotated Transformer (harvard.edu)
The Annotated Transformer的中文注释版(1) - 知乎 (zhihu.com)
The Annotated Transformer(译) - 知乎 (zhihu.com)
【LLM】万字通俗讲解大语言模型内部运行原理 (qq.com)
Large language models, explained with a minimum of math and jargon (understandingai.org)
自注意力(Self-attention)清晰解释 (qq.com)
一、Transformer模型整体结构
Transformer是一个利用注意力机制来提高模型训练速度的模型,因其适用于并行化计算以及本身模型的复杂程度使其在精度和性能上都要高于之前流行的循环神经网络。
标准的Transformer结构如下图所示,是一个编码器-解码器架构,其编码器和解码器均有一个编码层和若干相同的Transformer模块层堆叠组成。
1.1 Encoder-Decoder结构
神经网络均可理解为是一个黑箱模型,Transformer的黑箱主要由两部分组成:Encoders和Decoders。以文本翻译任务为例,输入文本会先经过Encoders模块(编码器),该模块对文本数据进行编码;编码后的文本数据再被传入Decoders模块(解码器),该模块对文本数据进行解码得到最终的翻译结果。
上述的编、解码模块都是由多个编码器组成的,经典的Transformer模型一般都是6个编码器和6个解码器。在编码模块,每个编码器的输出作为下一个编码器的输入;而在解码模块,则是每个解码器的输出结合整个编码模块的输出作为下一个编码器的输入。
1.2 编解码器组成部分
每个编码器内部又可以分为“self-attention”和“前馈网络”两个部分,每个解码器则分为“self-attention”、“编解码-attention”和“前馈网络”三个部分。
二、模型输入
原始文本 → 令牌化(Tokenization) → 嵌入(Embedding) → 模型
Transformer模型的输入是文本经转换(embedding/嵌入)后的向量矩阵,矩阵则是由输入文本中每个单词的表示向量组成,表示向量由每个单词的词向量和位置向量相加得到。
2.1 词向量
原始输入文本经Word2Vec等方法进行embedding后的得到的长列表称为单词的词向量。例如单词“cat”的词向量可以表示为:[0.0074, 0.0030, -0.0105, …… , -0.0396, -0.0998, -0.0796, …… , 0.0002]。完整向量长度为300(具体使用的维度根据实际情况确定),点击该链接可以查看完整内容:Semantically related words for “cat_NOUN” (nlpl.eu)
对于使用词向量来表示单词的方法,我们可以类比于经纬度坐标的使用,经纬度构成的向量可以表示一个地点的具体位置,通过计算两个向量的距离或相似性可以用于判断两个地点距离的远近。如:
- Washington DC is at [38.9, 77]
- New York is at [40.7, 74]
- London is at [51.5, 0.1]
- Paris is at [48.9, -2.4]
我们可以通过38.9接近于40.7以及77接近于74来判断纽约离华盛顿特区很近。同样的道理,巴黎靠近伦敦,但巴黎离华盛顿特区很远。
语言模型采用类似的方法:每个词向量代表了虚拟的“词空间”中的一个点,具有更相似含义的单词会被放置在彼此更靠近的位置。
例如,在词向量空间中,最接近"cat"的词包括"dog"、“kitten"和"rabbit”。
使用实数向量(而不是像"C-A-T"这样的字母序列)来表示单词的一个关键优势是,数字使得可以进行字母无法实现的操作。
单词太复杂,无法仅用二维空间表示,因此语言模型使用具有数百甚至数千维度的向量空间,如基础Transformer模型使用的512维,GPT3中的Transformer使用的是12288维。人类无法想象具有如此多维度的空间,但计算机完全能够利用矩阵运算对其进行推理并产生有用的结果。
2.2 位置向量
上述的词向量实现了单词的表示、单词间相似性度量等等,但仍无法解决的一个问题是:单词通常具有多个含义,具体的词义取决于上下文。单词在语句中出现的位置对语义理解非常重要,而且Transformer与循环神经网络不同,它不能利用单词的顺序信息,而是使用文本的全局信息。因此,还需要引入位置向量来表示单词在语句中的相对或绝对位置。
位置向量(Positional Encoding, PE)利用如下公式进行求解:
P
E
(
p
o
s
,
2
i
)
=
s
i
n
(
p
o
s
/
1000
0
2
i
/
d
)
PE(pos,2i)=sin(pos/10000^{2i/d})
PE(pos,2i)=sin(pos/100002i/d)
P E ( p o s , 2 i + 1 ) = c o s ( p o s / 1000 0 2 i / d ) PE(pos,2i+1)=cos(pos/10000^{2i/d}) PE(pos,2i+1)=cos(pos/100002i/d)
式中,pos表示单词在语句中的位置,d表示位置向量PE的维度(与词向量维度一样),2i表示偶数维度,2i+1表示奇数维度(2i<d, 2i+1<d)。其中10000这个数值的选择是基于对序列长度和位置编码维度的经验和实验分析得出的。通过选择 (10000) 这个数值,可以确保不同维度之间的周期性变化是不同的,这有助于让模型更好地捕捉输入序列中不同位置之间的关系。
三、自注意力机制
语言模型的核心思想是理解自然语言内部的结构、模式和关联关系。通过建模语句中单词(Token)之间的关系,我们可以捕捉到语句的上下文和含义。自注意力作为一种沟通机制,用来帮助建立这些关系,以概率分数表示。下面会逐步解释概率/注意力分数的计算过程。
3.1 self-attention结构
在上一章节的介绍中提到,模型输入(self-attention的接收)是单词的表示向量或上一编解码器的输出,而下图所示,在进行注意力分数计算的时候需要用到Q,K,V三个向量。Q,K,V也正是通过self-attention的输入经矩阵运算(线性变换)得到的。
3.2 Q,K,V的计算
首先对这三个向量进行字面解释:
- 查询向量(Query Vector)
- 键向量(Key Vector)
- 值向量(Value Vector)
这三个向量都是通过self-attention的输入与三个对应的权重矩阵(
W
Q
,
W
K
,
W
V
W_Q,W_K,W_V
WQ,WK,WV)进行矩阵乘法运算得到的。这三个权重矩阵最开始是随机初始化得到的,在训练过程中依据梯度下降进行更新。
3.3 self-attention输出
在得到输入矩阵对应的Q,K,V之后,就可以进行计算self-attention的输出了,计算公式如下:
A
t
t
e
n
t
i
o
n
(
Q
,
K
,
V
)
=
s
o
f
t
m
a
x
(
Q
K
T
d
k
)
V
Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V
Attention(Q,K,V)=softmax(dkQKT)V
式中,
d
k
d_k
dk为K矩阵的列数,即K向量维度。
Q
K
T
QK^T
QKT这一步矩阵乘法也可看作是Q,K每一行向量的内积计算,结果即为注意力得分,计算结果除以维度的平方根,可以防止内积过大,有助于梯度稳定。
之后再利用softmax函数对每一行分数进行标准化,结果表示每个单词对于其他单词的attention系数,保证系数均为正数且每一行相加结果为1。(注:上图中“Attention scores矩阵第二行存在错误,可以把其中的一个0.05视为0.15”)
将标准化后的注意力分数矩阵与矩阵V相乘,得到最后的self-attention输出,输出矩阵也可以理解为在输入矩阵的基础上编码了其他单词的上下文信息。
总之,self-attention就是基于一系列矩阵操作,实现了单词间的权重计算。
3.4 多头注意力
所谓多头,就是使用了多组(论文中使用8组)不同权重矩阵的Q,K,V进行上述计算,这样做的目的主要是为了消除Q,K,V初始值的影响。
将得到的8个输出矩阵Z0,Z1,…,Z7拼接在一起,然后传入一个Linear层对8组结果进行加权求和,得到Multi-Head Attention的最终输出Z。可以看到最终的输出矩阵Z的维度与输入矩阵X的维度保持一致。矩阵维度为:序列长度×词向量维度。
最终的输出矩阵Z即为前馈神经网络的输入。
四、编码器(Encoders)结构
如上图所示,完整的编码部分由N个encoder组成,每个encoder包含多头自注意力(Multi-Head Attention)->残差与归一化(Add & Norm)->前馈网络(Feed Forward)->残差与归一化(Add & Norm)。
多头注意力在上节中已经进行了介绍,现在了解一下残差与归一化和前馈网络部分。
4.1 残差与归一化(Add & Norm)
为了解决梯度消失的问题,在每个注意力层和每个全连接前馈层后面跟一个残差与归一化层。例如,每一个前馈神经网络的输入不光包含上述self-attention的输出Z,还包含最原始的输入。这种结构有助于在模型层数非常深时保留信息并确保模型性能。
Add & Norm层由Add和Norm两部分组成,其计算公式如下:
L
a
y
e
r
N
o
r
m
(
X
+
M
u
l
t
i
H
e
a
d
A
t
t
e
n
t
i
o
n
(
X
)
)
LayerNorm(X+MultiHeadAttention(X))
LayerNorm(X+MultiHeadAttention(X))
L a y e r N o r m ( X + F e e d F o r w a r d ( X ) ) LayerNorm(X+FeedForward(X)) LayerNorm(X+FeedForward(X))
式中,X表示Multi-Head Attention或者Feed Forward的输入;MultiHeadAttention(X)和FeedForward(X)表示输出(输出与输入维度一样,所以可以相加)。
Add指X+MultiHeadAttention(X),是一种残差连接,通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到:
Norm指Layer Normalization,通常用于 RNN 结构,Layer Normalization 会将每一层神经元的输入都转成均值方差都一样的,实现层归一化这样可以加快收敛。
4.2 前馈网络(Feed Forward)
Feed Forward 层比较简单,包括一个两层的全连接网络和一个非线性激活函数。两层的全连接网络实现两次线性变换,第一层使用激活函数(通常为RELU),第二层不使用激活函数。
前馈网络的输出
F
F
N
(
X
)
FFN(X)
FFN(X)由如下公式进行计算:
F
F
N
(
X
)
=
σ
(
X
W
1
+
b
1
)
W
2
+
b
2
FFN(X)=\sigma(XW_1+b_1)W_2+b_2
FFN(X)=σ(XW1+b1)W2+b2
式中,
X
X
X为前馈网络的输入,
σ
\sigma
σ为激活函数,
W
1
、
b
1
、
W
2
、
b
2
W_1、b_1、W_2、b_2
W1、b1、W2、b2均为全连接网络中的可学习参数。
前馈网络层的输入、输出矩阵的维度一致。
4.3 组成Encoders
通过上面描述的 Multi-Head Attention->Add & Norm->Feed Forward->Add & Norm 就可以构造出一个encoder(编码器),多个encoder叠加就组成了Encoders(编码器部分)。
第一个 encoder 的输入为单词的表示向量矩阵X,后续 encoder 的输入是前一个 encoder 的输出,最后一个 encoder 输出的矩阵就是**编码信息矩阵C**,这一矩阵后续会用到 Decoders 中。
五、解码器结构
上图为 Transformer 的 Decoders 结构,与 Encoders 相似,但是存在一些区别:
- 包含两个 Multi-Head Attention 层。
- 第一个 Multi-Head Attention 层采用了 Masked(掩码) 操作,目的是防止查询矩阵Q 去对序列中尚未解码的后续位置来施加注意力操作。
- 第二个 Multi-Head Attention 层的键矩阵K和值矩阵V是从Encoders的最后一层的输出编码信息矩阵C中进行计算而来的,而查询矩阵Q是使用Decoders中的上一个解码器decoder的输出计算。
- 最后有一个 Softmax 层计算下一个翻译单词的概率。
5.1 第一个Multi-Head Attention
Decoders的第一个 Multi-Head Attention采用了Masked操作,因为在翻译的过程中是顺序翻译的,即翻译完第i个单词,才可以翻译第i+1个单词。通过 Masked 操作可以防止第i个单词知道i+1个单词之后的信息。
下面以 “我有一只猫” 翻译成 “I have a cat” 为例,了解一下Masked操作。
在解码的时候,需要根据之前的翻译,求解当前最有可能的翻译,如下图所示。首先根据输入 “” 预测出第一个单词为 “I”,然后根据输入 “ I” 预测下一个单词 “have”。
Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 ( I have a cat) 和对应输出 (I have a cat ) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面用 0 1 2 3 4 分别表示 “ I have a cat ”。
第一步:Decoder 的输入矩阵和 Mask 矩阵。
输入矩阵包含 “ I have a cat” (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。在 Mask 可以发现单词 0 只能使用单词 0 的信息,而单词 1 可以使用单词 0, 1 的信息,即只能使用之前的信息。
第二步:接下来的操作和之前的Self-Attention一样,通过输入矩阵X计算得到Q,K,V矩阵。然后计算Q和
K
T
K^T
KT的乘积
Q
K
T
QK^T
QKT。
第三步:在得到
Q
K
T
QK^T
QKT之后需要使用Softmax函数进行标准化,计算attention score,我们在Softmax之前需要使用Mask矩阵遮挡住每一个单词之后的信息,遮挡操作如下:
得到Mask Q K T QK^T QKT后进行Softmax标准化,每一行和为1。但是单词0在单词 1, 2, 3, 4 上的attention score都为0,即只能使用之前的信息。
第四步:使用Mask
Q
K
T
QK^T
QKT与矩阵V相乘,得到输出矩阵Z。
第五步:通过上述步骤就可以得到一个Mask Self-Attention的输出矩阵,然后通过Multi-Head Attention拼接多组输出矩阵,经线性变换(加权求和)后得到Multi-Head Attention的最终输出矩阵Z。输出矩阵Z与输入矩阵X维度一致。
5.2 第二个Multi-Head Attention
解码部分Decoders中的每个解码器decoder中第二个Multi-Head Attention在结构上与编码器的多头注意力接近,主要区别的是decoder中的self-attention使用的K, V矩阵不是利用上一decoder的输出进行计算的,而是使用编码部分Encoders输出的 编码信息矩阵C计算的。
根据 Encoders 的输出C计算得到K, V,根据上一个decoder的输出Z计算Q (如果是第一个decoder则使用输入矩阵X进行计算),后续的计算方法与之前描述的一致。
这样做的好处是在Decoders的时候,每个单词都可以利用到Encoders所有单词的信息 (这些信息无需 Mask)。
5.3 Softmax 预测输出单词
decoder最后的部分是利用 Softmax 预测下一个单词,在之前的网络层我们可以得到一个最终的输出 Z,因为 Mask 的存在,使得单词 0 的输出
Z
0
Z_0
Z0只包含单词0的信息,如下:
Softmax 根据输出矩阵的每一行预测下一个单词: