简介
近年来,注意力(Attention)机制被广泛应用于计算机视觉和自然语言处理领域,仅 CVPR2020 接收的论文中,使用到 Attention 的文章达到 60 篇。随着 Attention 的广泛研究,很多不同的 Attention 方法被提出,谷歌机器翻译团队于 NIPS2017 发表的《Attention is all you need》引起了广泛的关注,其中,大量使用自注意力机制(self-attention)构建 Transformer 结构,自此自注意力成为了研究热点并在各种任务取得了相当优秀的结果。
-
论文标题
Attention Is All You Need
-
论文地址
https://arxiv.org/abs/1706.03762
-
论文源码
https://github.com/tensorflow/tensor2tensor
背景
Attention 机制最早出现于计算机视觉领域,《Neural Machine Translation by Jointly Learning to Align and Translate》首次利用 Attention 机制在机器翻译上将翻译与对齐同时进行,这也是首次 Attention 机制被用在 NLP 任务中,随后以 Attention 机制为基础的神经网络模型被广泛使用在 NLP 领域。2017 年,谷歌机器翻译团队提出 Transformer,大量使用自注意力机制而放弃 RNN 和 CNN 学习文本表示。到 2020 年,已经有不少研究将自注意力机制带回到计算机视觉任务中,并取得了不错的成果,如 2020 年的《End-to-End Object Detection with Transformers》将自注意力引入目标检测中。
注意力机制(Attention Mechanism)
Attention 机制本质上来自于人类的注意力感知机制,当人们用肉眼感知事物的时候并不会从头到尾每次都看全视野中的内容,而是根据需求注意特定的一部分,当习惯于场景中某个部分出现自己感兴趣的东西时,人类会学会下次出现类似场景时将注意力移到该部分(由于这个学习过程几乎就是本能,我们很少注意这个过程)。例如,看到如下这个图像,我们第一眼关注的就是图像中的猫而不是周围的绿叶和露水,这就是我们的大脑长期“训练”的结果。
视觉注意力分好几种,其核心思路是基于原有的数据找到其之间的关联性然后突出某些重要的特征(即将有限的注意力放到更重要的信息上从而防止“遗忘”),一般有通道注意力、像素注意力、自注意力等,其应用领域如用于捕捉图像上的感受野等。在实现上,attention 算法其实有很多种,但其本质都可以概括为加权求和。
Transformer 解读
Transformer 是一个典型的基于自注意力的 seq2seq 模型,就如论文题目《Attention is all you need》所说,在该模型中不会出现 RNN 和 CNN 结构,有的只是一个自注意力模块以及前馈网络。
自注意力模块
此前,对于针对序列数据的建模多采用 RNN 及其变种(包括 LSTM、GRU 等)或采用复杂堆叠的 CNN,RNN 的思路如下图左侧,其中 a i a^i ai表示输入序列的第 i 个位置的特征, b i b^i bi表示对应的输出,RNN 的原理这里不多提及,它的后一个输出是依赖前面输出的信息的,所以 RNN 的推理只能串行,self-attention 能得到同样的输出,但可以并行计算。
上图右侧就是自注意力模块的黑盒模型,显然,各个输入之间的关系通过自注意力模块捕捉到了,那么具体是如何实现的呢?
首先,基于原始的 Attention,定义三个向量,分别为 q q q(query,与其他的进行匹配), k k k(key,被其他的匹配)以及 v v v(value,提取到的特征信息),它们的计算式如下。
q
i
=
W
q
a
i
q^{i}=W^{q} a^{i}
qi=Wqai
k
i
=
W
k
a
i
k^{i}=W^{k} a^{i}
ki=Wkai
v
i
=
W
v
a
i
v^{i}=W^{v} a^{i}
vi=Wvai
其示意图如下图,注意,对每个不同的输入提取 q , k , v q,k,v q,k,v的权重 W W W是相同的,这就是为什么后面使用multi-head结构的一个原因。
通过权重学习到了每个输入的 q , k , v q,k,v q,k,v向量后,接着就是计算当前输入的 a i a^i ai得到的 q i q^i qi与所有 k j ( j ∈ ( 1 , 4 ) ) k^j(j\in{(1,4)}) kj(j∈(1,4))之间的Attention α 1 , j \alpha_{1,j} α1,j,计算方式采用Scaled Dot-Product Attention,计算式如下,其中dot-product表示点积,scaled表示通过除以 d \sqrt{d} d进行尺度调整,原因是如果query和key的每一个元素是mean=0,std=1,那么dot product的 m e a n = 0 , s t d = d 0.5 mean=0,std=d^{0.5} mean=0,std=d0.5,所以除以 d 0.5 d^{0.5} d0.5可以normalize dot product。
α 1 , i = q 1 ⋅ k i / d \alpha_{1, i}=q^{1} \cdot k^{i} / \sqrt{d} α1,i=q1⋅ki/d
这样,每个输入的 a i a^i ai都会输出 n n n(n是输入a的数目或者维度)个输出 α 1 , j \alpha_{1, j} α1,j,将它们softmax后得到的 α 1 , j ^ \hat{\alpha_{1, j}} α1,j^与对应的 k j k_j kj点乘后求和,即得到对应的输出 b i b^i bi,示意如下图。
所有的 b i b^i bi组合到一起,就是类似上面RNN的输出,可以看到,每个 b b b都与其他的输入产生运算后得到输出,也就是说每个位置的输出都包含了与其他位置的相关性信息,这就是自注意力。
多头注意力
显然,上述这个过程是可以矩阵化的,也就是可以GPU并行加速。上面这个这么复杂的操作其实引入的权重只有提取 q , k , v q,k,v q,k,v的三个 W W W矩阵,这显然只能学到一种任务有关的信息,要想学到更丰富的信息,就是使用更多的 q , k , v q,k,v q,k,v提取方法,这就是multi-head self-attention,它的思路就是反复进行自注意力,得到多种 b b b输出。因此,就像李宏毅老师PPT中下图所示的,不同的head只有输入 a a a是共享的,每个head的 q q q只能与当前head的 k k k和 v v v进行运算得到不同的 b j b^{j} bj(j最大为head数),最后concat到一起再通过一个权重矩阵进行降维得到 b i b^i bi。
位置编码
使用multi-head有助于捕获不同类型的特征,但是纵观上面的attention过程,可以发现,每个输入都会去与所有的输入得到的信息进行运算,所以这个过程可以矩阵化并行,但是,这就带来一个很严重的问题:顺序信息的丢失,也就是位置信息的丢失。 举个例子,对某个单词而言,只使用词嵌入作为输入的话,它与某个单词计算attention,那无论这个单词在它前面还是后面,得到的信息都是一样的(本质上没有区别),这显然不合理,对NLP而言,语序是一个非常重要的信息。
那么Transformer如何解决这个问题的呢?它通过引入一个与词嵌入同维的位置编码向量来补充位置信息。这个位置编码怎么理解呢,其实也就是对一个输入的不同位置对应的不同的embedding加上一个表示位置信息的向量 e i e_i ei(若对 ( n , 512 ) (n,512) (n,512)的输入,那n个位置每个位置的位置编码都是一个512维的向量),此时 e i + a i e_i+a_i ei+ai才是真正的输入,它参与后面自注意力计算的过程。
不过,在原始的论文中,这个 e i e_i ei是人为设定的,关于为什么直接加到输入上而不是concat到输入上,李宏毅老师以onehot形式的位置编码进行了推导,本质是它们就等同于concat的计算,不会导致位置信息混入特征向量中。
不过,这个位置编码(Position Encoding,PE)在原始的Transformer中式手工设计的,它的计算公式如下,其中pos表示当前token在序列中的位置,如第一个词位置为0。下面的
i
i
i(严格来说
2
i
2i
2i和
2
i
+
1
2i+1
2i+1)则表示位置编码某个位置的元素,
i
i
i的取值范围是
(
0
,
d
m
o
d
e
l
/
2
)
(0, d_{model} / 2)
(0,dmodel/2),以位置1为例,其位置编码则为下面第三个式子。
P E ( p o s , 2 i ) = sin ( p o s / 1000 0 2 i / d model ) P E ( p o s , 2 i + 1 ) = cos ( p o s / 1000 0 2 i / d mode l ) \begin{aligned} P E_{(p o s, 2 i)} &=\sin \left(p o s / 10000^{2 i / d_{\text {model }}}\right) \\ P E_{(p o s, 2 i+1)} &=\cos \left(p o s / 10000^{2 i / d_{\text {mode }} l}\right) \end{aligned} PE(pos,2i)PE(pos,2i+1)=sin(pos/100002i/dmodel )=cos(pos/100002i/dmode l)
P E ( 1 ) = [ sin ( 1 / 1000 0 0 / 512 ) , cos ( 1 / 1000 0 0 / 512 ) , sin ( 1 / 1000 0 2 / 512 ) , cos ( 1 / 1000 0 2 / 512 ) , … ] P E(1)=\left[\sin \left(1 / 10000^{0 / 512}\right), \cos \left(1 / 10000^{0 / 512}\right), \sin \left(1 / 10000^{2 / 512}\right), \cos \left(1 / 10000^{2 / 512}\right), \ldots\right] PE(1)=[sin(1/100000/512),cos(1/100000/512),sin(1/100002/512),cos(1/100002/512),…]
上面这个式子有一个玄学的地方,就是这个底为什么选10000,这点论文中也没有说明白。位置编码的好处是,序列中每个位置会有一个唯一的编码,且对于超出设定的位置也可以通过计算得到对应编码。不过后来也诞生了一些新的位置编码设计思路,如可学习的位置编码、二维位置编码等。
Transformer
上面理清了Transformer的一些核心结构和模式,下面就来看看这个模型究竟是怎样的。
通过上述的自注意力模块堆叠而成的Transformer模型如下图,左侧表示编码器,右侧表示解码器。首先看编码器部分,词嵌入和位置编码相加后输入多头注意力模块,该模块上文已经分析了,随后的Add和Norm分别表示残差连接和layer norm,该结构重复N次后输入一个feed forward网络得到encoder 的输出。显然,encoder的输出和输入是一样的,即都是
(
n
,
d
m
o
d
e
l
)
(n,d_{model})
(n,dmodel),encoder的结果可以理解为对各个位置信息的一次集成精炼后的内容信息。
再看右侧的解码器部分,输入的outputs是前一个time step得到的output,它和位置编码相加之后进入重复N次的一个块中。这个块首先是一个Masked Multi-Head Self-attention(这个所谓的mask是限制模型只会对已经产生的序列做attention,这是很常规的思路,不存在的东西怎么能做attention呢),经过这个Masked Multi-Head Self-attention得到的输出作为query进入后面的Multi-head Attention中,这里的key和value就是encoder的输出,之后就是正常的前向网络和做任务的head了。这里需要注意的是,编码(encoder)可以并行计算,一次性全部计算出来,但解码(decoder)不是一次把所有序列解出来的,而是像RNN一样一个一个解出来的,因为要用上一个位置的输出当作attention的query。
虽然是这么说,但是实际上训练时是可以直接并行计算出来的,但是推理时不行,否则就是一种作弊(向后看),因此引入mask来隐藏不该看到的信息。
因此,从整体上看,Decoder的Key和Value其实来自Encoder的输出,所以可以看做句子或者图像的内容信息,而Query表达了一种查询要求,即我希望查到什么结果,可以看做引导信息。将它们通过多头注意力结合在一起的过程就相当于是把我们需要的内容信息通过指导信息查询出来。显然,聪明的同学已经发现了,这个Query其实可操作空间非常大(赋予其特殊的含义),事实上后来不少工作都是在Query上做的文章,如DETR等。
DETR
Facebook提出的《End-to-End Object Detection with Transformers》,简称DETR,将目标检测直接作为一个集合预测问题来处理,它由一个Transformer encoder-decoder作为核心结构,通过基于集合的全局损失进行训练,该全局损失通过二分匹配强制进行唯一预测。给定固定的学习对象查询集,则DETR会依据对象与全局图像上下文之间的关系,并行输出最终的预测集,因而效率很高。
其pipeline如下图,官方开源代码链接给出。
总结
虽然 Transformer 的本质结构就是 Attention 和全连接的堆叠,但是其设计思路因彻底放弃 RNN 和 CNN 已经足够超前了,值得深度学习研究者品味;在速度方面,并行化的推理速度远超了 RNN 这种模型,同时也使得以超长序列为输入的任务成为可能,并行结构也是最适合 GPU 加速运算的结构。
虽然粗暴地放弃 RNN 和 CNN 是挺“狂妄”的,但是 local 信息的获取能力就丢失了,因此现在,组合 Transformer 和 RNN 及 CNN 是更好的选择;而且,Transformer 通过位置编码补充了顺序信息,该位置编码是固定得出的,对其他任务未必合适,这也是该结构优化需要考虑的。
补充说明
本文对近几年在 CV 中有着不错潜力的 Transformer 结构进行较为通俗粗浅的分析,文章部分的示例图片参考了李宏毅老师关于 Transformer 的解读视频这是一节非常不错的课,相对 Transformer 深入理解的可以去看看该视频或者Transformer的原论文,相关 PPT 可以评论或者私信我获取。国外一个大佬也写了很不错的解析博客,英文不错的也可以看看。