*注:本博客参考李宏毅老师2020年机器学习课程. 视频链接
在前面的学习过程中,我们所使用到的模型的输入和输出都具有以下特点:
那么如果我们要解决的问题可能具有更复杂的结构,例如输入/输出不定长,或者输入是一组向量而非单个向量。根据模型输入和输出的形式不同,可以分为如下几类:
我们先来讨论第一种类型,即词性标记任务。
假设有这样一条语句:“I saw a saw”,意为“我看到了一把锯子”,那么输出中值得注意的是,两个单词saw的词性不一样。如果我们按照以往的步骤,将每一个单词都输入一个神经网络中,由于两个saw是完全一样的,网络必然给出完全一样的结果。要解决这样的问题,我们需要让网络不仅仅考虑单词本身,还要结合语句上下文,根据当前单词前后几个单词的内容,判断其词性。
自注意力机制将考虑模型的整个序列a,然后输出与输入序列长度相等的向量b,如图1所示:
(图1,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
在图1中,self-attention模块的输入,既可以是整个网络的输入,也可以是经过一定的隐藏层之后的中间输出。
在图1中,每个输入 a i a^i ai产生一个输出 b i b^i bi。以 a 1 a^1 a1为例,要得到 b 1 b^1 b1,第一步是计算 a 1 a^1 a1与其他输入之间的关联程度。
我们将 a 1 a^1 a1与 a j a^j aj的关联度记为 α 1 , j \alpha_{1,j} α1,j,计算 α 1 , j \alpha_{1,j} α1,j有多种不同的做法,在这里我们使用一种名为dot-product的方式来计算:
(图2,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
将 a 1 a^1 a1与矩阵 W q W^q Wq做矩阵乘法得到向量 q q q,将 a j a^j aj与矩阵 W k W^k Wk做矩阵乘法得到向量 k k k,然后将 q q q和 k k k两个向量做内积,即可得到 α 1 , j \alpha_{1,j} α1,j。按此方法,可以计算出 a 1 a^1 a1与所有输入的关联性:
(图3,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
经过第一步计算得到的关联度分数 α \alpha α,在通过一个soft-max层,得到输出,记为 α ′ \alpha' α′。soft-max这一步并不是必须的,也可以使用如ReLU等其他函数。
用另一个矩阵 W v W^v Wv乘以输入 a a a,得到一个新的向量 v v v,再将 v v v与 α ′ \alpha' α′相乘再求和,得到向量 b 1 b^1 b1。
(图4,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
如何从 b 1 b^1 b1的值看出 a 1 a^1 a1与哪一个输入最接近呢?假设 a 1 a^1 a1与 a 2 a^2 a2最接近,那么计算所得的 α 1 , 2 ′ \alpha'_{1,2} α1,2′就会很大,那么 b 1 b^1 b1的值就会与 v 2 v^2 v2最为接近,因此就得到了输入中与 a 1 a^1 a1最接近的是 a 2 a^2 a2。
上述三个步骤在实际的运算中史以矩阵的形式表达的。
由于各个不同的输入向量 a i a^i ai都会与三个相同的矩阵 W q , W k , W v W^q,W^k,W^v Wq,Wk,Wv相乘,得到对应的 q i , k i , v i q^i,k^i,v^i qi,ki,vi,因此我们可以将各个输入拼接维一个矩阵,再分别与 W q , W k , W v W^q,W^k,W^v Wq,Wk,Wv相乘,得到三个输出矩阵:
(图5,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
接下来将 q i q^i qi与 k i k^i ki做内积得到关联度分数 α i , i \alpha_{i,i} αi,i,表示为矩阵乘法则是: k i T q i {k^i}^Tq^i kiTqi(也可以写成 q i T k i {q^i}^Tk^i qiTki),表示为矩阵操作则是:
(图6,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
最后将矩阵 A A A与矩阵 V V V相乘,得到输出:
(图7,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
尽管在上述过程中看似进行了很复杂的计算,但实际上我们发现,只有 W q , W k , W v W^q,W^k,W^v Wq,Wk,Wv是需要训练的参数,因此整个Self-Attention机制是一个相对较小的模块。
在某些任务中,我们认为各个输入之间的关联性并非只有一种,以语音为例,假设输入是若干段不同的语音,由于声音信号具有频率、响度等属性,那么两端语音可能在频率上关联性很强,但是在响度上关联度不强,而对于另外一组输入则可能存在相反的情况。为了应对这样多层次的关联性,需要对上述的注意力机制进行改进。
多头注意力机制(Mutil-Head Self-Attention)就是一种对多层次的关联性建模的方法,该方法在原始的注意力机制产生 q , k , v q,k,v q,k,v的基础上,对这三个矩阵再分别做 n n n次矩阵乘法,得到 n n n组不同的 q , k , v q,k,v q,k,v,每组对应于一个关联性。
(图8,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
在图8中计算得到 n n n个 b b b之后,可以将这些 b b b构成一个向量,在令其乘上一个可学习的向量,得到最后的输出,该输出的维度与单头的输出一致。
上述的注意力机制中,没有考虑到输入在序列中的位置信息,有时这种信息是很重要的。为此,可使用一种名为Positional Encoding的技术,该方法将输入的位置信息表达为一个唯一的矩阵 e e e,将 e e e与输入 a a a的和作为注意力机制的输入。
产生矩阵 e e e的方法有很多种,如何产生最优的矩阵 e e e仍是一个有待研究的问题。可以参考Learning to Encode Position for Transformer with Continuous Dynamical Model,该文章介绍了一些方法来生成 e e e。
NLP领域中有一个名为BERT的技术,使用到了Self-Attention。
在语音上使用Self-Attention时,由于语音数据的序列往往较长,例如一个1秒钟的语音数据,如果将其以10毫秒的长度切片,那么该数据将产生100个向量,而计算Self-Attention的输出矩阵大小为100*100,计算量较大。因此根据问题的不同,往往选择一个较小的范围应用Self-Attention,称为Truncated Self-Attention。
一张rgb彩色图片有三个颜色通道,将每个像素点视为一个三维向量,也可以应用Self-Attention。使用Self-Attention处理图片的例子:
Self-Attention对每一个输入都要进行处理,而CNN可以将相邻的几个输入一并处理,因此,可以说CNN是简化版的Self-Attention。事实上,经过合理地设置,Self-Attention就可以达到与CNN一样的效果,相关文献参考On the Relationship between Self-Attention and Convolutional Layers。
经过实验,CNN在数据量较少的时候表现更好,而Self-Attention在数据量很大的时候能取得比CNN更好的结果。
RNN是循环神经网络的简称,也是一种考虑了输入序列中多个输入信息的一种网络结构。
在RNN中,存在一个memory的概念,用于存储RNN过去所输出的结果,memory中的数据将会作为RNN下一次运算的输入的一部分,RNN的每次运算的输入包含两个部分,一个是训练数据样本的输入序列中的当前时间点的输入,另一个就是memory。RNN的输出既用于后续网络结构的输入,也用作RNN下一个时间点的的输入。因此不难发现,RNN会利用当前时间点之前的数据信息。
(图9,来源:https://www.bilibili.com/video/BV1Wv411h7kN?p=24)
尽管RNN能够考虑当前时间点之前的数据信息,但是对于RNN最后一次计算而言,输入序列最开始的信息很难经过若干次计算之后还能保留其特征,对于RNN的最后一次计算,最初的输入数据的特征往往丢失了。
除此之外,RNN产生输出的过程是非同步的,必须在上一个数据产生之后,才能计算下一个输出。
而在Self-Attention中就不存在这样的缺陷。由于Self-Attention的每一个输出都考虑了输入序列中所有输入的信息,序列中的每一个输入的特征都能容易的获取,并且产生各个输出是可以并行计算的。
在Transformers are RNNs:Fast Autoregressive Transformers with Linear Attention中,作者表示,只要在Transformer中添加一些组件,就可以将其变为RNN。