引言

这是李宏毅老师讲的机器学习视频中与自然语言处理有关的,本文主要关注Self-attention。

处理序列的模型

我们已经知道如何处理输入是一个向量的问题。假设我们遇到了更加复杂的问题。比如,输入是一系列向量。

李宏毅自然语言处理——Self Attention_点乘

并且输入的这一系列向量的长度不是固定的。

比如,在文字处理的场景中。输入是一个句子,将每个句子的词汇描述成一个向量,这样模型的输入就是一排向量。

李宏毅自然语言处理——Self Attention_Self attention_02

如何把词汇表示成向量呢,我们知道有one-hot编码。

李宏毅自然语言处理——Self Attention_Self attention_03

但是这种表示方法不能表示语义信息,并且维度还非常大。另一种表示方法是词嵌入向量。

李宏毅自然语言处理——Self Attention_点积_04

还有图结构也可以是一系列向量。

李宏毅自然语言处理——Self Attention_点乘_05

那这些模型的输出是什么?

有三种类型:

每个输入的向量都有一个标签,比如词性标注任务。

李宏毅自然语言处理——Self Attention_窗口大小_06

第二种类型是整个序列的输出就是一标签。

比如情感分类任务、或者是输入一段语言,识别说话者是谁等。

李宏毅自然语言处理——Self Attention_Self attention_07

最后一种类型就是,我们也不知道有多少个输出,由机器自己决定要输出多少。比如机器翻译任务。

我们今天关注第一种类型。这种任务又叫序列标注(Sequence Labeling)。

李宏毅自然语言处理——Self Attention_Self attention_08

我们不能直接用全连接网络(FC)来做,因为全连接网络同样的输入会得到同样的输出。而我们上面的例子中“I saw a saw”,第一个“saw”是动词,第二个“saw”是名词。

如果能考虑到上下文就好了。这是有可能的,可以设定一个窗口大小,把窗口大小内的输入都串起来,一起丢到FC中。但是窗口大小始终是有限的,如何考虑整个句子呢,难道设定一个巨大的窗口包含整个语句?

有没有更好的考虑整个序列的模型呢?有,那便是Self-atention!

Self-attention

李宏毅自然语言处理——Self Attention_Self attention_09

Self-attention可以接收一整个序列的输入,序列中有多少个输入,它就可以得到多少个输出。

比如上面输入4个向量到Self-attention中,我们就得到了4个输出向量。这4个输出向量特别之处在于,它们都是考虑了整个序列得到的结果。

在把这些特别的向量丢给FC之后,再来决定要输出什么样的东西。

李宏毅自然语言处理——Self Attention_点积_10

Self-attention的输出还可以叠加。比如,我们得到考虑整个序列的向量后,喂给FC,得到FC的输出后,再过一次Self-attention,重新考虑FC的所有输出,再丢给另一个FC之后,得到最终的输出,如下图:

李宏毅自然语言处理——Self Attention_窗口大小_11

所以可以把FC和Self-attention交替使用,用Self-attention处理整个序列的信息,FC专注于处理某个位置的信息。

​Attention is all you need.​

Self-attention的原理

那Self-attention是怎么运作的呢?

李宏毅自然语言处理——Self Attention_Self attention_12

它的输入就是向量序列,这些向量可能是整个网络的输入,也可能是某个隐藏层的输出。

再强调一次,每个Self-attention的输出,都是考虑了所有的输入向量才生成出来的。

下面我们来看一下是如何生成这些向量的。

李宏毅自然语言处理——Self Attention_点乘_13

我们重点来看一下是如何产生李宏毅自然语言处理——Self Attention_Self attention_14这个向量的,其他的李宏毅自然语言处理——Self Attention_点乘_15同理。

① 我们想要根据李宏毅自然语言处理——Self Attention_点积_16找出序列中其他与李宏毅自然语言处理——Self Attention_点积_16有关的向量。每个向量与李宏毅自然语言处理——Self Attention_点积_16关联程度用一个数值李宏毅自然语言处理——Self Attention_点乘_19来表示。第一步要考虑的问题就是,Self-attention模型如何决定两个向量的关联性呢。

那么就需要一个计算关联性(注意力)的模组。

李宏毅自然语言处理——Self Attention_窗口大小_20

它拿两个向量作为输入,直接输出它们的相关性李宏毅自然语言处理——Self Attention_点乘_19数值。如何计算这个数值,就有各种各样的做法。

常用的做法有使用点乘(Dot product)的方式。

李宏毅自然语言处理——Self Attention_点乘_22

点乘的方式做法如下:

  1. 把输入的向量分别乘上两个不同的权重矩阵,分别得到两个向量,记为李宏毅自然语言处理——Self Attention_窗口大小_23李宏毅自然语言处理——Self Attention_Self attention_24
  2. 然后让李宏毅自然语言处理——Self Attention_Self attention_25就可以得到一个标量,这个标量就作为李宏毅自然语言处理——Self Attention_点乘_26数值。

还有一种叫作Additive的方式:

李宏毅自然语言处理——Self Attention_Self attention_27

也是乘上两个权重矩阵,得到李宏毅自然语言处理——Self Attention_点积_28之后,再拼接起来,然后输入到一个激活函数中,激活函数输出的结果通过矩阵进行线性变换得到李宏毅自然语言处理——Self Attention_点乘_19

我们下面的讨论基于点乘的方式。

② 计算李宏毅自然语言处理——Self Attention_点积_16和其他向量的关联性。

李宏毅自然语言处理——Self Attention_点积_31

怎么做呢? 先用李宏毅自然语言处理——Self Attention_点积_16乘上矩阵李宏毅自然语言处理——Self Attention_点积_33得到李宏毅自然语言处理——Self Attention_Self attention_34,这个李宏毅自然语言处理——Self Attention_Self attention_34被称为为query。

李宏毅自然语言处理——Self Attention_窗口大小_36

然后李宏毅自然语言处理——Self Attention_Self attention_37乘上李宏毅自然语言处理——Self Attention_窗口大小_38分别得到李宏毅自然语言处理——Self Attention_点乘_39,这些李宏毅自然语言处理——Self Attention_点乘_39被称为key。

李宏毅自然语言处理——Self Attention_点积_41

接下来用李宏毅自然语言处理——Self Attention_Self attention_34李宏毅自然语言处理——Self Attention_点乘_43做点积得到李宏毅自然语言处理——Self Attention_点积_44值,表示李宏毅自然语言处理——Self Attention_点积_16李宏毅自然语言处理——Self Attention_点乘_46之间的关联性,李宏毅自然语言处理——Self Attention_点积_44这种记法说明query是李宏毅自然语言处理——Self Attention_点积_16提供的,key是李宏毅自然语言处理——Self Attention_点乘_46提供的。这个李宏毅自然语言处理——Self Attention_点乘_19值还被称为attention score。计算出李宏毅自然语言处理——Self Attention_点积_16李宏毅自然语言处理——Self Attention_点乘_46的关联性后,还要跟李宏毅自然语言处理——Self Attention_Self attention_53计算一下。

李宏毅自然语言处理——Self Attention_点积_54

计算李宏毅自然语言处理——Self Attention_窗口大小_55的时候也是乘以李宏毅自然语言处理——Self Attention_窗口大小_38矩阵得到的,然后再与李宏毅自然语言处理——Self Attention_Self attention_34做点积得到李宏毅自然语言处理——Self Attention_点乘_58

李宏毅自然语言处理——Self Attention_Self attention_59

实际上,我们还要利用同样的公式计算李宏毅自然语言处理——Self Attention_点积_16自己与自己的关联性李宏毅自然语言处理——Self Attention_点积_61

③ 计算Softmax

李宏毅自然语言处理——Self Attention_点积_62

我们还要对这些attention score做一个Softmax,得到李宏毅自然语言处理——Self Attention_点乘_63,相当于与李宏毅自然语言处理——Self Attention_点积_16与各个向量的相关性系数,我们就可以知道哪些向量与李宏毅自然语言处理——Self Attention_点积_16相关性比较大。

④ 计算最终的李宏毅自然语言处理——Self Attention_Self attention_14

在计算李宏毅自然语言处理——Self Attention_Self attention_14之前呢,还有一个向量要计算。

李宏毅自然语言处理——Self Attention_Self attention_68

这个向量就是李宏毅自然语言处理——Self Attention_点积_69向量,把李宏毅自然语言处理——Self Attention_点积_16李宏毅自然语言处理——Self Attention_点积_71都乘上李宏毅自然语言处理——Self Attention_Self attention_72,得到李宏毅自然语言处理——Self Attention_Self attention_73李宏毅自然语言处理——Self Attention_窗口大小_74。这个李宏毅自然语言处理——Self Attention_点积_69可以看成是向量李宏毅自然语言处理——Self Attention_点乘_76所携带的信息编码。

然后与Softmax之后的李宏毅自然语言处理——Self Attention_窗口大小_77做一个加权求和就得到了李宏毅自然语言处理——Self Attention_Self attention_14

李宏毅自然语言处理——Self Attention_窗口大小_79

假设李宏毅自然语言处理——Self Attention_点乘_46李宏毅自然语言处理——Self Attention_点积_16关联性较大,那么得到的权重系数就很大,在计算李宏毅自然语言处理——Self Attention_Self attention_14时,就会有很大程度的信息来自于李宏毅自然语言处理——Self Attention_窗口大小_83,也就是来自于李宏毅自然语言处理——Self Attention_点乘_46

下面得到李宏毅自然语言处理——Self Attention_窗口大小_85是一样的,虽然这里先探讨李宏毅自然语言处理——Self Attention_Self attention_14的产生,其实李宏毅自然语言处理——Self Attention_点乘_87是可以同时产生的。

李宏毅自然语言处理——Self Attention_点乘_88

不过计算李宏毅自然语言处理——Self Attention_Self attention_89时,是以李宏毅自然语言处理——Self Attention_点乘_46计算的李宏毅自然语言处理——Self Attention_点积_91作为query的。同理以李宏毅自然语言处理——Self Attention_点乘_92得到李宏毅自然语言处理——Self Attention_Self attention_93就可以计算李宏毅自然语言处理——Self Attention_点乘_94

刚才我们说是可以同时计算的,是怎么回事呢。其实计算李宏毅自然语言处理——Self Attention_点乘_95无非就是不同的向量与矩阵相乘得到的,如果把这些向量写在一起,写成一个矩阵,不就可以一次得到了吗。

李宏毅自然语言处理——Self Attention_窗口大小_96

李宏毅自然语言处理——Self Attention_窗口大小_97拼成一个矩阵李宏毅自然语言处理——Self Attention_点积_98,然后让矩阵李宏毅自然语言处理——Self Attention_点积_33乘以它得到矩阵李宏毅自然语言处理——Self Attention_点乘_100,矩阵李宏毅自然语言处理——Self Attention_点乘_100可以分解为李宏毅自然语言处理——Self Attention_点积_102

剩下的李宏毅自然语言处理——Self Attention_点积_103也是同样的道理。

接下来,计算attention score也可以写成矩阵运算。

李宏毅自然语言处理——Self Attention_窗口大小_104

我们先看李宏毅自然语言处理——Self Attention_Self attention_34,它计算李宏毅自然语言处理——Self Attention_点乘_106可以写成矩阵李宏毅自然语言处理——Self Attention_窗口大小_107与向量李宏毅自然语言处理——Self Attention_Self attention_34的乘积。

李宏毅自然语言处理——Self Attention_窗口大小_109

同理不难理解李宏毅自然语言处理——Self Attention_Self attention_110也可以写成李宏毅自然语言处理——Self Attention_窗口大小_111

这样把李宏毅自然语言处理——Self Attention_点积_102拼成矩阵李宏毅自然语言处理——Self Attention_点乘_100,然后通过李宏毅自然语言处理——Self Attention_点乘_114得到李宏毅自然语言处理——Self Attention_点积_115李宏毅自然语言处理——Self Attention_点积_115经过Softmax之后就得到了李宏毅自然语言处理——Self Attention_窗口大小_117矩阵,李宏毅自然语言处理——Self Attention_窗口大小_117矩阵中每列之和为李宏毅自然语言处理——Self Attention_点乘_119李宏毅自然语言处理——Self Attention_窗口大小_117也被成为Attenion Matrix。

李宏毅自然语言处理——Self Attention_点乘_121

接下来看李宏毅自然语言处理——Self Attention_Self attention_14

李宏毅自然语言处理——Self Attention_Self attention_123

相当于是把李宏毅自然语言处理——Self Attention_点积_124组成的矩阵李宏毅自然语言处理——Self Attention_Self attention_125乘以李宏毅自然语言处理——Self Attention_窗口大小_117的第一列,就得到了李宏毅自然语言处理——Self Attention_Self attention_14

李宏毅自然语言处理——Self Attention_点乘_128

那么用矩阵李宏毅自然语言处理——Self Attention_Self attention_125乘以李宏毅自然语言处理——Self Attention_窗口大小_117就可以得到李宏毅自然语言处理——Self Attention_点乘_87得到的矩阵李宏毅自然语言处理——Self Attention_窗口大小_132

李宏毅自然语言处理——Self Attention_Self attention_133

总结下来,这一连串的操作,其实就是矩阵的乘法而已。我们用一个图来总结一下。

李宏毅自然语言处理——Self Attention_Self attention_134

李宏毅自然语言处理——Self Attention_点积_98是Self-attention模型的输入组成的矩阵,李宏毅自然语言处理——Self Attention_窗口大小_136是三个可以学习的权重矩阵;

用这三个矩阵乘李宏毅自然语言处理——Self Attention_点积_98分别得到李宏毅自然语言处理——Self Attention_点乘_138矩阵;

然后用李宏毅自然语言处理——Self Attention_窗口大小_107矩阵做个转置乘上李宏毅自然语言处理——Self Attention_点乘_100就得到了李宏毅自然语言处理——Self Attention_点积_115,经过Softmax得到李宏毅自然语言处理——Self Attention_窗口大小_117

李宏毅自然语言处理——Self Attention_Self attention_125乘上李宏毅自然语言处理——Self Attention_窗口大小_117得到最终的输出李宏毅自然语言处理——Self Attention_窗口大小_132

其中需要学习的参数,就是李宏毅自然语言处理——Self Attention_窗口大小_136

Multi-head Self-attention

Multi-head Self-attention是Self-attention的一个升级版本,Multi-head指的是计算出多个李宏毅自然语言处理——Self Attention_点乘_138矩阵。

为什么这样有意义呢? Self-attention是用query去找相关性,但是相关性可能有很多个种类,这时得到多个李宏毅自然语言处理——Self Attention_点乘_138就分别代表不同种类。多出来的李宏毅自然语言处理——Self Attention_点乘_138矩阵是怎么计算的呢。

李宏毅自然语言处理——Self Attention_Self attention_150

以2 head为例,我们还是用李宏毅自然语言处理——Self Attention_Self attention_151来得到李宏毅自然语言处理——Self Attention_Self attention_152,然而,此时用这个李宏毅自然语言处理——Self Attention_Self attention_152乘以一个不同的矩阵,得到李宏毅自然语言处理——Self Attention_点乘_154

同理用李宏毅自然语言处理——Self Attention_窗口大小_155乘上不同的矩阵分别得到李宏毅自然语言处理——Self Attention_窗口大小_156李宏毅自然语言处理——Self Attention_点积_157

然后计算李宏毅自然语言处理——Self Attention_点乘_19李宏毅自然语言处理——Self Attention_点积_159的时候,把李宏毅自然语言处理——Self Attention_Self attention_160这些第二个上标为李宏毅自然语言处理——Self Attention_点乘_119的进行计算,最后得到李宏毅自然语言处理——Self Attention_窗口大小_162;上标为李宏毅自然语言处理——Self Attention_窗口大小_163的进行计算,得到李宏毅自然语言处理——Self Attention_点积_164

李宏毅自然语言处理——Self Attention_点积_165

接下来也可以拼接李宏毅自然语言处理——Self Attention_Self attention_166,然后乘以一个矩阵李宏毅自然语言处理——Self Attention_点积_167得到李宏毅自然语言处理——Self Attention_Self attention_168

Positional Encoding

现在整个过程看下来,Self-attention少了一个重要的信息,即位置信息。

我们上面在进行矩阵运算的时候,对于不同的位置的输入,都是同等对待的。没有说像RNN那样后面的输入考虑了前面输入的信息。也没有考虑输入的距离远近。

本小节介绍的Positional Encoding就是用于解决这个问题的。具体就是为每个位置的输入设置一个独立的位置向量李宏毅自然语言处理——Self Attention_点积_169

李宏毅自然语言处理——Self Attention_点乘_170

Attention is all you need中用的李宏毅自然语言处理——Self Attention_点积_169是下图中的每一列:

李宏毅自然语言处理——Self Attention_Self attention_172

即每一列代表一个位置向量李宏毅自然语言处理——Self Attention_点积_169,这些位置向量李宏毅自然语言处理——Self Attention_点积_169是通过sin和cos函数形成一个公式生成的。当然还有其他的生成方法,甚至可以当成一个可以学习的参数。

李宏毅自然语言处理——Self Attention_窗口大小_175

Self-attention的应用

比较出名的两个就是​​Transformer​​​和​​BERT​​。

李宏毅自然语言处理——Self Attention_窗口大小_176

这两个家伙常年活跃在自然语言处理领域。

当然还可以应用在语音和图像上。

李宏毅自然语言处理——Self Attention_点积_177

李宏毅自然语言处理——Self Attention_Self attention_178

对于图像来说,把每个位置的输入看成一个3维(rgb)的向量。

Self-attention vs CNN

李宏毅自然语言处理——Self Attention_点乘_179

CNN在做卷积的时候,考虑的是感受野之内的信息,而Selt-attention考虑的是整个输入的信息。

因此CNN可以看成是一个简化版的Self-attention。

这样好像是让Self-attention自己去决定感受野的形状是什么样的。

李宏毅自然语言处理——Self Attention_点乘_180

Self-attention只要设置合适的参数,就可以做到CNN能做到的事情。

李宏毅自然语言处理——Self Attention_点积_181

Self-attention在数据量大时表现更好,而CNN在小数据量时表现更好。这样不难理解,显然Self-attention比CNN更复杂,需要的数据量显然更多。

Self-attention vs RNN

Self-attention和RNN都可以处理序列数据,RNN得到结果时必须按照时间步的顺序(正序或逆序)来生成,利用双向RNN的设计可以考虑整个序列信息。

李宏毅自然语言处理——Self Attention_窗口大小_182

而Self-attention每个输出都也是考虑了整个序列信息。虽然双向RNN也考虑了整个序列信息,但是如果将它俩进行对比,还是可以发现一些不同。

李宏毅自然语言处理——Self Attention_点乘_183

比如考虑上面红框框出来的(深蓝色)输入,RNN要考虑这个输入,必须保存到内存中,然后一步一步传递到最后,得到红框框出来的黄色输出。所以是很难考虑到比较远的输入。

而Self-attention就没有这个问题,只要它们的query和key比较相关,得到的相关性系数较大,不管多远,都能轻易地抽取出信息。

还有一个重要的不同就是并行化,这影响到训练效率。RNN是不能并行化的,而Self-attention可以。这样Self-attention完胜RNN。

想要了解更多Self-attention和RNN的关系,可以参考这篇论文☞​​Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention​

Self-attentioni for Graph

李宏毅自然语言处理——Self Attention_点乘_184

Self-attention还可以用在图结构上,图中每个节点看成一个输入,图结构中边的概念,可以看成有关联的向量。可以形成一个稀疏矩阵,其中白色空白区域代表无关联性。

比如上图节点1和5,6,8相连,所以我们只需要计算它们之间的attenion score。

Self-attention的变体

Self-attention有很多种变体,Self-attention最早用在Transformer上面,后来就出来了各种“xxformer”。

李宏毅自然语言处理——Self Attention_点乘_185

​Long Range Arena: A Benchmark for Efficient Transformers​

​Efficient Transformers: A Survey​