BERT基于transformer而成,而transformer的一个核心模块便是self-attention,在这里详细介绍一下self-attention及其计算过程;
一、What is self-attention?
The animal didn't cross the street because it was too tired
举个例子,比如上方的句子中,我们要理解it具体指代的是什么,很容易看出来,但是让机器识别出it的指代含义,却很难。因此,为了解决这种问题,便有了self-attention。
也就是 :Self-attention 关注于句子内部每一个单词对其他单词的权重。(而以前的attention机制则是关注句子之间单词的权重)
如下图,便是一个头的self-attention对上面句子的可视化图:
可以看到 经过了self-attention之后,it更关注于The animal。
二、Self-attention in details
下面详细讲述self-attention的计算过程
1.
首先,self-attention会计算出三个新的向量,在论文中,embedding向量的维度是512维,我们把这三个向量分别称为Query、Key、Value,这三个向量是用embedding向量分别与一个矩阵相乘得到的结果,这个矩阵是随机初始化的,纬度是(512,64),并会随着BP过程,这三个参数矩阵会不断的进行更新
2.
以计算thinking的self-attention值为例
- 计算K、Q、V之后,便如之前的所有的attention机制一样,计算一个单词之间的score,具体计算方法是:score=q ✖️ k,如上图要计算thinking这个单词与自己的score那么就是q1 ✖️ k1 ,要计算machines对thinking对score那么就是q1 ✖️ k2 ;
- 为了避免softmax时,向量之间的点积过大,导致梯度被push到很小的区域,因此在此之前会有一个scaled的操作,也就是把上一步计算的score进行一次缩放,也就是除以根号下dk ,原论文中除以的是8;
- 接着便是softmax操作,将score转化为具体的概率
- 到现在为止,最开始计算的value vector还没有使用,所以这一步也就是将softmax的值✖️v,比如图中,分别将thinking和machine的softmax值✖️对应的value vector,再相加就得到了z1 ,也就是最终输出的thinking的self-attention值。
3.矩阵的self-attention计算形式
事实上,在实际计算过程中,因为不可能一个vector一个vector的计算,因此都是把一个句子的所有单词转换后的embedding vector放到一个矩阵中,进行统一的计算,右边的计算公式,也就是前面整体计算公式的一个总结,在论文中也称之为 scaled dot-product attention
三、Multi-head attention
在attention is all you need(2017)这篇文章中,还提到了一个多头attention的机制,在论文中提到,这样做的优点就是 可以允许模型在不同的表示子空间里学习到相关的信息。
- 具体做法其实很简单,就是把原来单头的参数矩阵WK、WQ、WV,由一份变成了多份,如上图,当有两个head时,就有了两种参数矩阵W0 Q、W0 K、W0 V;W1QW、1K、W1V。然后分别将其与X(句子的单词embedding 矩阵)相乘,得到不同的K、 Q 、V。
- 接着经过了上文所说的scaled dot-product attention得到不同的z0,z1...z7(论文中提到的是八头)
- 最后1)把得到的所有z vector 进行concate;2)将其与一个参数矩阵W0 进行点乘,其中参数矩阵会随着模型一起进行训练;3)得到最终结果Z,其中Z也就捕获了所有头的信息
最后把之前的所有计算过程放在一张图(上图)中,也就十分清楚了。
我们再回过头看一下,文章开头的那个例子,可以看到使用了多头以后(左边是单头的可视化结果,中间的部分是两头的可视化结果,右边是八头的可视化结果),不同的头对于it的相关注的地方是不同的,比如单头attention中比较关注The animal,而两头attention中,有一个头更关注于tired,八头则各在自己对应的子空间有不同的关注部分。
Summery
- self-attention的机制很有效,也有许多值得思考的地方, 比如:multi-head提出有一部分的原因可能是为了单头时可能出现信息的bias现象;
- multi-head attention 中的头其实并没有一个有效的结合机制,彼此之间是独立的,那么因此google在2019年有一篇叫做talking-heads attention的文章中,对此进行了改进;
- 最后一点,self-attention有一个致命的缺点,那就是它的计算量是很大的,尤其是多头的情况下,那么多个头每个进行计算时,时间和空间的复杂度都是O(n2),如果序列长度很长的话,会给gpu会带来很大的负担,因此关于如何减少self-attention的计算复杂性问题,有了很多的研究工作,比如sparse-attention。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。