selfattention
1.self-attention
在之前处理序列问题的时候,用的是rnn,现在我们把rnn层换成self-attention层。
那么具体是怎么做的呢?我们首先以上图为例讲解。
a1,a2,a3,a4是一个sequence(序列),我们首先为每一个input a1,a2,a3,a4 乘以一个矩阵,通过embedding变成 x1,x2,x3,x4。然后我们把 x1,x2,x3,x4丢进self-attention layer.
每一个input(x1,x2,x3,x4)分别乘三个不同的矩阵,产生三个不同的向量,我们把三个向量分别命名为q k v。
q代query,是要去match其他人的。k代表key,被query match。v是被抽取出来的信息。其中:
现在,我们每一个a 都有不同的q k v。接下来,拿每一个不同的query q去对每一个不同的key k 做attention(attention的式子可以采用很多不同的方法,这里我们采取图片中的式子,先内积再除根号d。d是k和v的维度,因为k和v可以做内积,因此k和v的维度是一样的。至于为什么除以根号d,论文中有证明,这里不再细说。为什么要内积呢?因为我们做attention的目的就是衡量两个向量的相似度,而内积的几何意义就是:两个向量越相似,则两个向量的内积越大)。attention的过程是把q和k做匹配,看一下两个向量有多相似,衡量两个向量相似程度的是,该数越大,两个向量越相似。比如:狗和猫和梨分别做匹配,我们发现狗和猫更相似一些。
接下来我们把得到的经过一个softmax层,得到
,然后我们把
跟每一个对应的
相乘,然后累加,就得到一个向量,该向量就是输出序列的第一个向量。如下图所示:
注意:我们在产生b1的时候,用了整个sequence(a1,a2,a3,a4)的信息,意思就是当你产生b1之后,你就看到了a1到a4的信息。如果你整个序列很长(a1a2…a1000),你不想考虑整个句子的信息,你只想考虑局部的信息,只需要把不想考虑的部分所产生的α变成0即可。
截至到目前为止,我们算出来了b1,其实再同一时间,我们还可以计算b2b3b4,b1-b4计算是可以进行的。下图展示一下b2的计算过程:
大体来说,输入序列a1 a2 a3 a4,得到输出序列b1 b2 b3 b4。 该过程和rnn是一样的,只是attention-layer可以使得b1 b 2 b3 b4同时运算。
接下来我们把刚刚一连串的过程用矩阵表示。
- 首先,我们知道
,
,
-
Wq乘以a1得到q1
-
Wq乘以a2得到q2
-
Wq乘以a3,a4分别得到q3,q4
-
把a1 a2 a3 a4拼起来变成一个矩阵,用I表示,我们用Wq*I 就可以得到Q,Q里面的第i列,就代表
-
同理,我们可以得到:K = Wq*I 、V = Wq*I
-
、
、
、
- 为了简便计算,我们先忽视根号d
K的转置乘以Q得到矩阵A,矩阵A经过softmax得到A^ -
输出矩阵O = V*A
b1 = v1*a11+v2*a12+v3*a13+v4*a14,同理算出b2,b3,b4,b1—b4构成输出矩阵O
我们再把刚刚的过程简化以下:
2.muti-head-self-attention
以head=2为例 ,在self-attention中,每一个ai都会得到一个qi,ki,vi。在有两个head的前提下,会把每个qkv分裂,变成两个。以q为例,做法可以是把qi乘上两个不同的矩阵,得到qi1和qi2。接下来做跟刚才一样的self-attention。只是qi1只会对跟他同样是第一个的k做attention(看图就能明白)。q和k相乘得attention之后,与各自的v相乘、累加。得到bi1,bi2。然后我们把bi1和bi2 concat起来,然后把他乘以一个矩阵进行降维得到bi。设置多个head的好处就是,不同的head关注的点是不同的,举个例子,有的head关注局部信息,而有的head关注全局信息,当你有muti-head的之后,每个head的关注的点不一样,他们各司其职。
3.positional encoding
如果我们仔细回顾一下刚才的两个过程就会发现,对于self-attenton来说,我们输入的序列的顺序是不重要的,因为对于input来说,只需要跟每个人都做self-attention,所以对于每个input的来说,无论其他input的距离是近还是远,对他来说,作用都是一样的。但是我们希望把input的顺序考虑到self-attention里面,原始的论文里采用positional encoding方法。
当原始输入xi经过embedding得到ai之后,需要再加上一个和ai的维度相等的ei,ei是人手设的,ei表示ai的位置。接下来就跟前面两个过程一样。