本文根据月来客栈的文档进行学习并通过博客记录一下自己学习的过程
Multi-Head Attention原理
1 当前问题
目前主流架构基本都是由RNN和CNN构造而来的encoder-decoder模型,在这些框架中,下一步的计算往往依赖于上一步的输出,这种属性让传统的encoder-decoder模型不能以并行的方式去计算,如下图所示:
2 解决的思路
作者提出了Transformer架构来解决上述中提到的问题,摈弃了传统的循环结构,只通过注意力机制去计算模型输入与输出的representation,这种机制也就是下文中的Multi-Head Attention模块。
该模块中所使用的自注意力机制(Scaled Dot-Product Attention),简单来说就是通过运算直接计算得到句子在encoding过程中每个位置的注意力权重,然后再通过权重和的形式来计算整个句子的隐含向量表示(上下文相关向量 c)。最终,Transformer架构就是基于self-attention机制构建的Encoder-Decoder模型。
3 实现的方法
3.1 总体的描述
对应于原文中,Scaled Dot-Product Attention就是上述的自注意力机制,可以理解为将query和一系列的key-value对映射到某个输出的过程,而这个输出就是根据query和key计算得到的权重作用于value上的权重和。
自注意力机制的核心过程就是通过 Q 和 K 计算得到注意 力权重;然后再作用于 V 得到整个权重和输出。具体的,对于输入 Q、K 和 V 来 说,其输出向量的计算公式为:
在上述公式中,Q,K,V分别为三个矩阵,且第二个维度分别为
d
k
,
d
q
,
d
v
d_k,d_q,d_v
dk,dq,dv。公式中除以
s
q
r
t
(
d
k
)
sqrt(d_k)
sqrt(dk)的过程即为上文中Scale(缩放)的过程。
进行缩放的原因=> 如果 d k d_k dk的值较大,那么计算 Q k T Qk^T QkT后将会得到很大的值,直接进行softmax进行操作后梯度会较小,对训练效果会有一定的负面影响。
3.2 实现的小例子
输入序列: 我 是 谁
提前的预处理:经过Embedding后得到了形状为3x4的矩阵
具体过程:
Q,K,V其实是X分别乘上不同的矩阵
W
Q
,
W
K
,
W
V
W^Q,W^K,W^V
WQ,WK,WV(该过程仅用于Encoder和Decoder在各自输入时利用自注意力机制进行编码的过程)
因此,Q,K,V其实可以看作是同一个输入进行了三次不同的线性变化来表示三种不同的状态。得到Q,K,V后就可以进一步得到权重,计算过程如下所示:
不过从上图中可以看到,模型对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置 => 因此在后面使用了multi-head attention机制。
在得到计算权重后,就可以使用权重乘上V, 就可以得到最终的输出,计算过程如下图所示:
单独将中间的一行拿出来,可以转换成如下图所示的计算过程。
从图 1-7 可以看出,对于最终输出“是”的编码向量来说,它其实就是原始 “我 是 谁”3 个向量的加权和,而这也就体现了在对“是”进行编码时注意力权重分配的全过程。当然,对于整个图 1-5 到 1-6 的过程,我们还可以通过如图 1-8 所示的过程来进行表示。
通过这种机制就可以解决传统模型中所说的“训练必须顺序进行”的问题,如果使用上述的自注意力机制,仅需要对原始输入进行矩阵比那换就能得到包含不同位置的attention信息的向量。
4 使用Multi-Head Attention的理由
当前自注意力的缺陷=> 会过度集中于自身的位置
所以需要使用多头自注意力机制
同时,使用多头注意力机制还能够给予注意力层 的输出包含有不同子空间中的编码表示信息,从而增强模型的表达能力。
从下图中,我们可以看到多头自注意力机制其实只是将输入序列进行多组的自注意力处理过程,然后再拼接起来,最终经过一次线性变化即可完成。
具体的公式如下:
ps:这里的Q,K,V和上图中的Q,K,V不一样,需要进行最后一步linear能得到上文中的Q,K,V。
在论文中,作者使用了head = 8 的并行自注意力模块,并且限制了维度,具体按如下公式进行计算:
d
k
=
d
v
=
d
m
o
d
e
l
/
h
=
64
d_k = d_v = d_{model} / h = 64
dk=dv=dmodel/h=64
从中可以看出,多头自注意力机制其实就是将一个大的单头,拆分成了h个多头,因此可以将上述的图用下面的方法进行表示。
如图所示,根据输入序列X和权重
w
1
k
,
w
1
q
,
w
1
v
w_1^k,w_1^q, w_1^v
w1k,w1q,w1v可以得到对应的
Q
1
,
K
1
,
V
1
Q_1,K_1, V_1
Q1,K1,V1, 再根据前文的计算方法可以得到其中一个自注意力模块
z
1
z_1
z1。以此类推可以得到其他自注意力模块的
z
z
z。最终,我们可以通过concat将
z
1
,
z
2
.
.
.
z
n
z_1,z_2...z_n
z1,z2...zn水平拼接成
Z
Z
Z,然后乘上
w
o
w^o
wo就能够得到整个多头自注意力层的输出。
5 多头自注意力和单头注意力机制的区别
首先,作者对head的个数与多头自注意力机制中做出了限制如下:
d
q
=
d
k
=
d
v
=
d
m
o
d
e
l
/
h
d_q = d_k = d_v = d_{model} / h
dq=dk=dv=dmodel/h
如果这里的head的数量为2,则
d
m
o
d
e
l
=
2
∗
d
q
d_{model}= 2*d_q
dmodel=2∗dq
最后,再将
z
1
,
z
2
z_1,z_2
z1,z2进行水平拼接得到最终的
z
z
z,这一步也可以用如下这一张图进行表示。
如果结合公式,可以转化为如下所示:
当h未知时,可以将注意力机制抽象为如图所示的过程:
当h不固定时,h=2和3的情况可以表述如下:
可以发现,无论h如何修改,在进行计算时其实没有任何区别,但从结果上来看,h越大,
k
,
q
,
v
k,q,v
k,q,v将会被切分得越小,得到的权重的分配方式也越多。
从上图中可以看出,如果h=1时,得到的attention将会聚焦于每个单词本身;如果h = 2 时,那么就还可能得到另外一个注意力权重稍微分配合理的权重矩阵; 3 h= 同理如此。因而多头这一做法也恰好是 论文作者提出用于克服模型在对当前位置的信息进行编码时,会过度的将注意力 集中于自身的位置的问题。
综上所述,当 d m o d e l d_{model} dmodel固定时,h越大,整个模型的表达能力就越强,越能提高模型对于注意力权重的合理分配。