Transformer中的Attention机制

要讲attention,肯定就离不开Attention is All You Need这篇文章,虽然我之前都是看的视觉任务,但是视觉任务比如ViT中基本用的都是自注意力(self-attention),而原论文是做机器翻译的,除了self-attention在encoder-decoder之间还用到了attention机制,就是说kv是来自于编码器,q是来自于解码器。这里我建议不论是不是做nlp中的任务,只要用到transformer,都要看一看这篇原始文章,收获颇丰

Self-Attention的原理

首先给出计算公式,如下,QKV分别代表Query,Key以及Value,即查询、键以及值,他们通过X乘上一个权重得到,即, , ,得到QKV之后,我们来计算attention如下

首先计算attention矩阵,也就是计算QK相关性,随后除上一个数是为了避免QK的值过大,如果QK计算的值之间差距过大的话,他们通过softmax之后最大值和最小值会无限接近于1和0,导致产生的梯度较小,模型无法更新

下面参考“我想吃酸菜鱼”这个例子,看一下self-attention是如何一步步计算的(例子来源于网络)

对于“我想吃酸菜鱼”这个例子,每一个字都被嵌入到768维的空间中,因此的输入维度为[6, 768]

接下来我们用分别计算,以为例,同理

随后我们通过计算attention矩阵,我们用“我”对“我想吃酸菜鱼”中的每一个字分别计算相似度,这里用的是内积的操作(dot product),计算后的权重如下图,计算完之后,我们需要对每一行进行softmax操作来归一化权重(即“我”对“我想吃酸菜鱼”每一个字的关注度和为1)

最后就是执行attention操作了,如下图,最终的“我”这一维度依然有768个元素,他的每一个元素都是“我想吃酸菜鱼”这六个字的特征加权得到

为什么是加权和?因为“我”这个位置的结果等于“我”对“我”的注意力乘上“我”这一维度的第一个元素,“我”对“想”的注意力乘上“想”的第一个元素,以此类推乘6次,768维的每一维都是如此。也就是说“我”这个位置的最终结果就是“我想吃酸菜鱼”每一个特征的加权和,只是他们的权重有所不同(当然“我”对“我”的注意力会强一些,所以这个权重就大,至于其他的权重,就看网络的关注度了

Attention的原理

上面我们讲解了self-attention机制,其实就是自己更新自己,看看自己对自己的哪些地方关注度高,哪些地方关注度低,而最初我们就说过,attention is all you need这篇文章中的encoder和decoder交互不是self-attention,而是attention,如下图

这里decoder只是提供了Q,KV需要encoder提供,下面我们详细解释一下到底如何理解transformer中的qkv,知乎

在attention中,Q是一组查询语句,V是数据库,里面有若干数据项。对于每一条查询语句,我们期望从数据库中查询出一个数据项(加权过后的)来。如何查询?这既要考虑每个q本身,又要考虑V中每一个项。如果用K表示一组钥匙,这组钥匙每一把对应V中每一项,代表了V中每一项的某种查询特征,(所以K和V的数量一定是相等的,维度则没有严格限制,做attention时维度和q一样只是为了在做点积时方便,不过也存在不用点积的attention)。

然后对于每一个Q中的q,我们去求和每一个k的attention,作为对应value的加权系数,并用它来加权数据库V中的每一项,就得到了q期望的查询结果。所以query是查询语句,value是数据项,key是对应每个数据项的钥匙。名字起得是很生动的。不过和真正的数据库查询不一样的是,我们不仅考虑了查询语句,还把数据库中所有项都加权作为结果。所以说是全局的。

那么就会存在一个问题,为什么在transformer这篇文章中,encoder的输出作为KV,decoder的作为Q呢?

  1. 对于一个文本,我们希望找到某张图片中和文本描述相关的局部图像,怎么办?文本作query(查询),图像做value(数据库)
  2. 对于一个图像,想要找一个文本中和图像所含内容有关的局部文本,如何设计?图像作query,文本作value.
  3. 自注意力(我查我自己):我们想知道句子中某个词在整个句子中的分量(或者相关文本),怎么设计?句子本身乘以三个矩阵得到Q,K,V,每个词去查整个句子。
  4. 交叉注意力(查别人):transformer模型的decoder中,由decoder的输入经过变换作为query,由encoder的输出作为key和value(数据库)。value和query来自不同的地方,就是交叉注意力。可以看到key和value一定是代表着同一个东西。即:[Q,(K,V)]。如果用encoder的输出做value,用decoder的输入做key和query 那就完完全全不make sense了。所以从宏观上就可以判断谁该作query,谁该作value和key 。而不是乱设计。

对self-attention和attention做一个总结
(1)传统的Attention机制在一般任务的Encoder-Decoder model中,输入Source和输出Target内容是不一样的,比如对于英-中机器翻译来说,Source是英文句子,Target是对应的翻译出的中文句子,Attention机制发生在Target的元素Query和Source中的所有元素之间。简单的讲就是Attention机制中的权重的计算需要Target来参与的,即在Encoder-Decoder model中Attention权值的计算不仅需要Encoder中的隐状态而且还需要Decoder 中的隐状态。
(2) 而Self Attention顾名思义,指的不是Target和Source之间的Attention机制,而是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力计算机制。例如在Transformer中在计算权重参数时将文字向量转成对应的KQV,只需要在Source处进行对应的矩阵操作,用不到Target中的信息。
比如翻译I love china这句话,我们已经翻译了"我",那么这里的source就是指的I love china这句话,target就是"我"

Multi-head Attention

attention的最后我们讲解multi-head attention的方法,如下图

一句话概括multi-head是怎么做的,参考”我想吃酸菜鱼“那个例子,我们将每一个字的特征映射到768维的空间中,我们将768等分为3份,即每份256,然后分别对每一份进行attention操作,最后将分别attention的结果concat起来。这里的3就是head数量

为什么要用multi-head?

这来自于作者的猜测,在conv中,我们有channel的概念,不同的channel代表不同的形式,因此作者也希望在transformer中包含这种范式,所以取了多个head,希望transformer也能学习到conv中的那种形式,即不同的channel(head)学习到不同的东西