论文:https://arxiv.org/abs/1706.03762v5
代码:https://github.com/jadore801120/attention-is-all-you-need-pytorch
1 摘要
序列转换模型一般基于RNN和CNN进行实现,包含了encoder和decoder,性能更好的模型还会在encoder和decoder之间使用attention模块进行连接。本文作者提出了一种新的称为Transformer的结构,完全使用attention块构建网络,不需要使用任何的RNN和CNN模块。在机器翻译任务上的实验证明了Transformer可以并行化,训练速度更快,且在多个数据集上取得了更好的效果。实验结果也说明了Transformer可以应用到其他的任务中,说明了其具有良好的泛化能力。
2 核心思想
2.1 模组总体组成
seq2seq的模型一般都包含encoder和decoder部分,对于输入序列 ( x 1 , x 2 , ⋯ , x n ) (x_1,x_2,\cdots,x_n) (x1,x2,⋯,xn),encoder之后的输出为 ( z 1 , z 2 , ⋯ , z n ) (z_1,z_2,\cdots,z_n) (z1,z2,⋯,zn)作为对输入的特征表示。给定z,decoder每次计算一个输出,最终得到总体输出 ( y 1 , y 2 , ⋯ , y n ) (y_1,y_2,\cdots,y_n) (y1,y2,⋯,yn)。decoder部分使用auto-regressive算法,将前一个输出用作下一个输出计算时的输入。
Transformer中的encoder和decoder部分都用到了self-attention层、逐点操作和全连接层。如下图所示:
Encoder:如上图左侧部分所示,使用了6个一样的网络块。每个网络块又包含两个子块,第一个子块由Multi-Head Attention + Add + Layer Norm
组成,第二个子块由Feed Forward层 + Add + Layer Norm
组成,其中Add
是指类似于ResNet的残差结构。每一个子块可以表示成LayerNorm(x + Sublayer(x))
的形式。
Decoder:如上图右侧部分所示,也是由6个完全相同的结构组成。其中第一个和第三个子块与Encoder中的完全相同,中间新增了一个multi-head attention结构,以encoder的输出作为输入。三个子块都使用了Add Layer Norm
层。注意第一个子块使用的是Masked Multi-Head Attention结构,目的是保证对第
i
i
i个输出只能使用其左边的输出,即index < i
的输出。
2.2 Attention
从Fig. 1中可以看出Transformer深度依赖Attention结构,下面对其进行解释。
Attention结构是根据query、keys、values和输入得到输出,输入、输出、Q、K、V都是向量。输出是V的加权和,加权系数由Q和K计算得到。具体请参考:李宏毅self-attention。
2.2.1 Scaled Dot-Product Attention
常用的attention函数有Dot-Product attention和additive attention,当
d
k
d_k
dk值较小时,additive attention和dot-product attention效果相似,但是对于较大的
d
k
d_k
dk值,addtive attention要比dot-product attention效果更好。additive attention是使用一个单隐藏层的前向网络计算相关度值。additive attention和Dot-Product attention理论上的计算复杂度相近,但实际上Dot-Product attention效率更高,更省内存,主要得益于其可以采用高度优化的矩阵乘法进行实现。
作者提出了Scaled Dot-Product Attention结构实现attention。输入是Q,K和V,Q和K的维度为
d
k
d_k
dk,V的维度为
d
v
d_v
dv。实现过程为:
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
)
V
\text{Attention}(Q,K,V)=\text{softmax}(\frac{QK^T}{\sqrt{d_k}})V
Attention(Q,K,V)=softmax(dkQKT)V
- 计算Q和K的点积,表示为 Q K T QK^T QKT;
- 除以 d k \sqrt{d_k} dk;
- 应用
softmax
函数,得到归一化后的score值; - score值和V加权求和得到输出。
本文作者提出的Scaled Dot-Product Attention是在Dot-Product attention的基础上多除了一个 d k \sqrt{d_k} dk。
为什么要对点积后的输出进行scale操作:
本文作者假设
q
q
q和
k
k
k中的元素服从均值为0,方差为1的分布,那么对于它们的点积
q
.
k
=
∑
i
d
k
q
i
k
i
q.k=\sum_i^{d_k}q_ik_i
q.k=∑idkqiki,其均值为0,方差为
d
k
d_k
dk。所以
d
k
d_k
dk越大,
Q
K
T
QK^T
QKT的方差越大,softmax结果的幅值也越大。而softmax在i=j
时的梯度为
s
o
f
t
m
a
x
(
x
i
)
(
1
−
s
o
f
t
m
a
x
(
x
i
)
)
softmax(x_i)(1-softmax(x_i))
softmax(xi)(1−softmax(xi))。
s
o
f
t
m
a
x
(
x
i
)
softmax(x_i)
softmax(xi)的幅值越大,梯度值就越可能接近于0。所以作者对点积后的输出乘了
1
d
k
\frac{1}{\sqrt{d_k}}
dk1,保证softmax输入的方差仍然为1。综上,scale操作可以看做是一种归一化处理以避免梯度值过小。
2.2.2 Multi-Head Attention
如果按照single model进行处理,k,q和v的维度为
d
m
o
d
e
l
d_{model}
dmodel,共n个输入。那么
Q
∈
R
n
×
d
m
o
d
e
l
,
K
∈
R
n
×
d
m
o
d
e
l
,
V
∈
R
n
×
d
m
o
d
e
l
Q \in R^{n \times d_{model}},K \in R^{n \times d_{model}},V \in R^{n \times d_{model}}
Q∈Rn×dmodel,K∈Rn×dmodel,V∈Rn×dmodel。但是按照上图中构建的共h
个head的multi-head结构,自下向上的操作依次为:
- 进行 h h h组线性变化,每组使用的矩阵分别是 W i Q ∈ R d m o d e l × d k , W i K ∈ R d m o d e l × d k , W i V ∈ R d m o d e l × d k W_i^Q \in R^{d_{model} \times d_k},W_i^K \in R^{d_{model} \times d_k},W_i^V \in R^{d_{model} \times d_k} WiQ∈Rdmodel×dk,WiK∈Rdmodel×dk,WiV∈Rdmodel×dk,其中 d k = d v = d m o d e l / h d_k=d_v=d_{model}/h dk=dv=dmodel/h,起到了降维的作用;
- 对每个降维后的输出应用attention操作,得到的输出维度是 d v d_v dv;
- 对 h h h个head的输出进行concat,得到维度为 d m o d e l d_{model} dmodel的输出;
- 再进行一次线性变换,使用的矩阵为 W O ∈ R d m o d e l × d m o d e l W^O \in R^{d_{model} \times d_{model}} WO∈Rdmodel×dmodel,得到的输出维度和single head时一致为 d m o d e l d_{model} dmodel。
总体处理过程可以表示为:
Multi-Head的好处:
Multi-head Attention的本质是,在参数总量保持不变的情况下,将同样的query, key, value映射到原来的高维空间的不同子空间中进行attention的计算,在最后一步再合并不同子空间中的attention信息。这样降低了计算每个head的attention时每个向量的维度,在某种意义上防止了过拟合;由于Attention在不同子空间中有不同的分布,Multi-head Attention实际上是寻找了序列之间不同子空间的关联关系,并在最后concat这一步骤中,将不同子空间中捕获到的关联关系再综合起来。
2.2.3 Attention在Transformer中的使用
- 在"encoder-decoder attention"层(即encoder和decoder之间的箭头部分,也叫作cross-attention),query来自于前面的decoder层的输出,key和values来自于encoder的输出。这样设计允许了每一层的decoder可以从输入的所有位置计算attention。
- encoder中使用的是self-attention层,keys,values和query都来自于前一层的输出。计算的是前一层的任一位置输入和其他输入之间的attention。
- decoder中使用的也是self-attention层,也是计算任一位置和其他位置之间的attention。但是由于使用的是autoregressive方法,以前一个输出时作为了当前的输入,所以使用了特殊的加了mask的attention,即只计算当前位置和其左侧位置之间的attention值。在实现上,是对scale dot-product中不应该存在的连接的softmax的输入设置为 − ∞ - \infty −∞,这样softmax之后的输出无限解决于0。
2.3 Feed-Forward层
encoder和decoder中还包含了feed-forward层,实现上使用的是两个全连接层和ReLU激活函数,具体为:
F
F
N
(
x
)
=
max
(
0
,
x
W
1
+
b
1
)
W
2
+
b
2
FFN(x)=\text{max}(0,xW_1+b_1)W_2+b_2
FFN(x)=max(0,xW1+b1)W2+b2
2.4 Embedding和Softmax
使用学习的embedding将输入和输出转换为长度为 d m o d e l d_{model} dmodel的向量,也就是应用 W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV将输入转换为embedding向量。
对于decoder的输出,也使用了线性变换和softmax函数将其转换为每个输出值的概率。
按照惯例,两个embedding层和softmax之前的线性变换层使用相同的权重矩阵,在embedding层中,对权重矩阵额外乘了 d m o d e l \sqrt{d_{model}} dmodel。
2.5 Positional Encoding
attention中对所有的输入是同等对待的,并未考虑不同输入之间的位置关系。所以作者使用Positional Encoding给输入添加了位置信息,如Fig. 1中encoder和decoder的输入部分所示。Positional Encoding是把每一个位置编码为长度是 d m o d e l d_{model} dmodel的向量,可以直接加到embedding上。
Positional Encoding可以有多种方法,通过训练学习到的或者是人为定义的。本文作者使用了人为定义的sine和cosine函数:
上式中的pos
表示输入的绝对位置,即token在sequence中的位置,例如第一个token “我” 的 pos = 0;i
表示向量的索引位置,即Positional Encoding的维度,取值范围为[0,
d
m
o
d
e
l
/
2
d_{model}/2
dmodel/2);
d
m
o
d
e
l
d_{model}
dmodel为向量总体长度。可以看出,在偶数位置,使用正弦编码,在奇数位置,使用余弦编码。
上面的位置编码是绝对位置编码。但是词语的相对位置也非常重要。这就是论文为什么要使用三角函数的原因!
正弦函数能够表达相对位置信息,主要数学依据是以下两个公式:
s
i
n
(
p
o
s
+
k
)
=
s
i
n
(
p
o
s
)
c
o
s
(
k
)
+
s
i
n
(
k
)
c
o
s
(
p
o
s
)
c
o
s
(
p
o
s
+
k
)
=
c
o
s
(
p
o
s
)
c
o
s
(
k
)
−
s
i
n
(
p
o
s
)
s
i
n
(
k
)
sin(pos + k)=sin(pos)cos(k)+sin(k)cos(pos) \\ cos(pos+k)=cos(pos)cos(k)-sin(pos)sin(k)
sin(pos+k)=sin(pos)cos(k)+sin(k)cos(pos)cos(pos+k)=cos(pos)cos(k)−sin(pos)sin(k)
上面的公式说明,对于词汇之间的位置偏移k,PE(pos+k)
可以表示成PE(pos)
和PE(k)
的组合形式,这就是表达相对位置的能力。
参考:
李宏毅2021机器学习-Transformer
https://juejin.cn/post/6844903680487981069
https://zhuanlan.zhihu.com/p/104393915