一、自注意力机制
(一)序列与模型
哪些场景是用向量作为输入呢?
首先是词的表示,表示词的方式:One-hot Encoding(词向量很长,并且词之间相互独立)、Word Embedding。
然后是语音向量和图(Graph)也是由一堆向量组成。
输出可能是什么样的?
- 每个向量对应一个输出标签(一对一的情况,Sequence Labeling)
例子:词性标注(POS tagging)、语音、社交网络等等。
- 整个序列只有一个输出的标签
例子:文本情感分析(sentiment analysis)、识别语音来源、分析分子的性质等等。
- 模型自决定输出标签的数量
这种任务叫做Seq2Seq,输入一段话,输出一段文字。
Sequence Labeling
对于输入输出一对一的情况,最直接的想法是用很多全连接网络(FCN)来逐个击破输入向量,如下图。
考虑这样做的缺陷,这样做没办法分析上下文关系(context),比如这里的第一个saw是动词“看见”,第二个saw是名词“锯子”,只用上图所示的FCN是没办法识别出两种不同的词性的(在网络中认为两个输入的词向量一模一样,学到的东西也会一样)。
于是,考虑到如下的交叉模型,这样每个词就能考虑到上下文了。
但是这样做有上限,我们如果不考虑一个window,而是考虑整个sequence,应该怎么做呢。可以很直接想到,那单纯把window开大点,不就考虑到了整个sequence吗,但是每次sequence的长度是不同的,这个window需要人为去设定,另外,开太大的window会导致FCN运算量太大并且过拟合(overfitting)。
为了利用好整个sequence的信息,就有了接下来要学习的Self-attention。
(二)Self-attention
1. 架构设计
在如上的架构中,带黑框的向量是考虑了整个sequence的信息,再输入FCN中得到对应输出。
Self-attention可以堆叠很多次,再输入FCN得到输出。
有关Self-attention最经典的论文就是Transformer,源于Google的模型:《Attention is all you need》。
首先要找到每个输入向量与其他输入向量的关系,如下图。
2. Dot-product
现在考虑如何去计算这个相关系数,这里用到的方法是Dot-product,也就是点乘,计算过程如下。将两个向量分别乘上两个权重矩阵
W
q
W^q
Wq和
W
k
W^k
Wk得到
q
q
q和
k
k
k向量,然后将两者做dot-product(element-wise相乘求和)得到相关系数。
除此之外,还有Additive的方式计算相关系数,不同点在于
q
q
q和
k
k
k做加性运算,然后通过activation function(tanh)。
3. Self-attention计算原理
在后面的学习中,采用的是常见的Dot-product方法计算相关性。接下来学习如何计算两两向量之间的相关系数。 如下图,将需要查询的向量
a
1
a^1
a1乘上
W
q
W^q
Wq得到
q
1
q^1
q1,待查询的向量
a
2
a^2
a2乘上
W
k
W^k
Wk得到
k
2
k^2
k2,然后两者做dot-product(element-wise相乘求和)得到attention score
α
1
,
2
\alpha_{1, 2}
α1,2。
同理,可以计算另外两个向量与第一个向量的相关系数,当然,每个向量也会和自己计算相关系数,如下所示。
计算出向量与向量之间的相关系数后,通过Softmax函数计算出归一化(normalization)后的相关系数,作为每一个向量对该向量的重要程度(权重)。
下一步,基于计算出的attention score(前文的相关系数)来抽取信息。用每个输入向量乘上
W
v
W^v
Wv权重矩阵得到
v
v
v,然后用每个归一化后的相关系数(
a
1
,
1
′
,
a
1
,
2
′
.
.
.
a'_{1, 1}, a'_{1, 2}...
a1,1′,a1,2′...)乘上
v
v
v的值,最后全部求和得到
b
1
b^1
b1,作为
a
1
a^1
a1在考虑了整个sequence的情况下经过self-attention模块得到的输出值。
回顾一眼,
a
1
a^1
a1在考虑了所有输入的情况下,经过self-attention机制计算得到输出
b
1
b^1
b1的过程。
4. 并行化处理——矩阵乘法角度(向量化)
值得注意的是,计算每个向量的输出时,不需要一个一个顺序执行,而是并行处理的(parallel)。
现在从矩阵乘法的角度来看,是将多个输入向量拼起来成为一个大的输入矩阵,乘上
W
W
W后得到
Q
Q
Q,后面同理,用这种矩阵乘法的思想实现了多输入的并行计算,最终得到所有输出值和attention score矩阵(就是吴恩达老师讲的“向量化”)。
下面是得到q、k、v的计算过程:
下面是得到相关系数的计算过程:
下面是得到输出的计算过程:
总结一下上面self-attention机制的矩阵乘法的计算过程:
其中,只有三个W是需要学习的矩阵。
5. Multi-head Self-attention
多头注意力是现在广泛应用的自注意力方式。用多个不同的 W q W^{q} Wq, W k W^k Wk, W v W^v Wv可学习矩阵去学习不同的相关系数(可能关注点不同,所以更全面地考虑了相关性)。
得到多个head的输出后,再通过一次线性计算,得到最终的输出。
6. Positional Encoding
前面学会了self-attention的原理,但是存在一个问题——没有考虑词之间的位置关系,比如第一个向量和第二个向量并没有近,第一个向量和第四个向量并没有远(天涯若比邻),每个向量计算相关系数时是等价的、位置无关的。
在不同的位置加上一个唯一的位置向量(positional vector)
e
i
e^i
ei,如下所示:
在Transformer中,人为(hand-crafted)地设定位置向量如下,每一列就是一个位置向量的表示。
这个positional encoding实际上还是有待研究的问题,可以hand-crafted,也可以生成,可以learn from data。
7. 应用
Transformer和BERT类似的NLP模型广泛应用self-attention机制,还有语音领域、图像领域、Graph…
由于语音问题的向量非常长,所以采用truncated self-attention来关注小范围的相关系数。
图像问题同样可以看作是长向量问题,如图的1x1x3三维向量就可以作为每个像素点的向量表示,然后用self-attention来处理图片。
要比较self-attention和CNN,那么CNN实际上是简化版的self-attention,因为只考虑到了局部视野(receptive field)。self-attention可以看作是可学视野的CNN(全局性)。
在下面这篇论文中,详细阐述了CNN是self-attention的特例这一观点,如果参数设置合适,self-attention可以做到和CNN完全一样的效果。
上面的实验结果可以说明,训练数据少的时候,self-attention容易过拟合,而CNN效果好,数据多的时候,self-attention利用了更多的信息,而CNN效果相对差一些。
要比较self-attention和RNN(当然是可以双向Bi-RNN,所以RNN是可以考虑到左右侧的上下文关系),self-attention的主要优势是可以考虑到很远很远的输入向量(只需要KQV的矩阵运算,而RNN需要将memory依次计算推进),并且能做到并行处理,同时计算所有向量之间的相关系数。
在Graph上可以用self-attention,计算出节点之间边的权重值,其实就是GAT。