Attention(注意力)作为Transformer中核心部分,弄明白其原理是有必要的。参考中列出的第一篇博客写的非常清晰,这篇中文文章的内容主要结合参考1中论文的图,来给大家普及注意力机制。(如有能力读原文是最好)
注意力是什么?
首先我们要回答的问题是:注意力机制是什么?
这个问题,举个句子的例子来解释最容易理解。比如我们有这么一个句子:
”The animal didn't cross the street because it was too tired”
从语言要表达的意思来说,这里的“it”指的是“animal”,也就是说“it”和“animal”之间的关系更加密切。
我们再看下面的句子:
”The animal didn't cross the street because it was wet”
这里的“it”指向“street”更好一些,也就是说“it”和“street”之间的关系更加密切。
上面提到的“关系”就是“注意力”,它反馈的是对象之间联系程度的高低,而注意力机制就是帮助我们将联系度高的对象找出来。现实中,很多任务其实就是在寻找高关联度的对象,比如:NLP中的完形填空,CV中的检测、分割,图像生成,智能对答。
这里的对象可以是单词、句子、一段内容、像素点等任何待处理的信息。
如何计算注意力?
这里先贴出一张整体流程图:
具体的步骤,大致分成8步:
1)准备输入
2)初始化权重
3)计算所有输入的k(key)、q(query)和v(value)
4)q*k计算当前第i个输入的注意力分数
5)计算softmax
6)将分数与每个输入的v相乘
7)对加权值求和以获得当前第i个输出
8)对第i+1个输入重复步骤 4)–7)
以上的说明可能还不够清晰,下面根据上图,对每个步骤做详细的介绍:
1)准备输入
上图例子中,我们的输入有三个,分别是:
input1 : [1,0,1,0]
input2 : [0,2,0,2]
input3 : [1,1,1,1]
(你当然可以定义更多,更长或者更短,甚至更多维的矩阵来表示一个输入。)
2)初始化权重
在深度学习中,这些权重,是需要通过训练获取的,但这里我们只是解释如何使用,因此认为这些权重已知。假设权重如下:
key weights:
[[0, 0, 1],
[1, 1, 0],
[0, 1, 0],
[1, 1, 0]]
query weights:
[[1, 0, 1],
[1, 0, 0],
[0, 0, 1],
[0, 1, 1]]
value weights:
[[0, 2, 0],
[0, 3, 0],
[1, 0, 3],
[1, 1, 0]]
这里可能会产生疑问:为什么一个输入对象,要计算key、query、value这三个值呢?注意力机制中为什么要分成key、query、value?这些数据对应含义是什么?
以下是ChatGPT对以上问题的回答:
注意力机制中将输入对象拆分成key、query、value这三个向量的主要原因是为了更好地捕捉输入对象的不同方面信息,并在计算注意力权重时对这些信息进行精确建模。
首先,key向量代表了输入对象中的一个特定方面,它可以看作是该输入对象在某个特定维度上的表示。比如,在机器翻译中,输入对象可以是源语言句子中的单词向量,key向量可以表示该单词在某个特定维度上的表示,例如单词的语义信息。
其次,query向量表示了需要进行注意力计算的目标,它也是输入对象的一种表示,但与key向量所表示的方面不同。在机器翻译中,query向量通常是目标语言句子中的某个单词向量,它代表了模型当前需要关注的方面,例如需要翻译的单词。
最后,value向量是用于计算注意力权重的向量,它对应的是key向量所表示方面的具体数值,可以看作是一个向量的实际取值。在机器翻译中,value向量也可以表示源语言句子中单词的具体词向量。
综上所述,通过将输入对象拆分成key、query、value三个向量,注意力机制可以更好地理解输入对象的不同方面,并在计算注意力权重时对这些方面进行精确建模。这种机制使得模型能够更好地关注和利用输入对象中的信息,从而提高了模型在各种任务中的表现。
大家觉得解释的如何?
3)计算所有输入的k、q和v
对于第一个输入[1,0,1,0]来说,其对应的k、q、v计算如下:
同理:
q1 = input1 * query_weights = [1, 0, 2]
v1 = input1 * value_weights = [1, 2, 3]
余下输入的k、q、v计算方式和上面一致。
4)计算当前第i个输入的注意力分数
同理,Input1的query和Input2、Input3的key相乘结果分别为4和4。
5)计算注意力分数的softmax
将上一步计算得到的所有attention_score放在一起,计算softmax:
softmax([2, 4, 4]) = [0.0, 0.5, 0.5]
【注】:以上结果为了计算方便,做了近似操作,实际上 softmax([2, 4, 4]) = [0.0634, 0.4683, 0.4683] 。
6)将注意力分数与每个输入的v相乘
这里的注意力分数是softmax之后的值,对于Input1 value [1, 2, 3]而言就是0.0,相乘如下:
0.0 * [1, 2, 3] = [0.0, 0.0, 0.0]
Input2和Input3相乘过程分别为:
0.5 * [2, 8, 0] = [1.0, 4.0, 0.0]
0.5 * [2, 6, 3] = [1.0, 3.0, 1.5]
7)对加权值求和以获得当前第i个输出
将上一步的结果相加,得到最后的第i个输入的输出结果:
[0.0, 0.0, 0.0] + [1.0, 4.0, 0.0] + [1.0, 3.0, 1.5] = [2.0, 7.0, 1.5]
至此,Input1的输出结果就算了出来。
8)对第i+1个输入重复步骤 4)–7)
重复步骤4)-7),获得每个输入的输出。
如果觉得上面的步骤还是过于复杂,不好记忆,那么下面还有一个更好记的版本:
(1)将Input和weight相乘,得到k、q、v;
(2)将单个q和所有的k相乘获取注意力分数,结果放在一起算softmax,获取每个v的权重;
(3)将每个v权重和对应位置的v相乘后,得到的矩阵相加,得到最后的输出结果。
什么是多头注意力机制?
如果你已经明白了上面关于注意力的含义和计算过程,那么多头注意力机制就很好理解。对于注意力而言,获取每个Input的output,需要计算k、q、v,而k、q、v由Input乘以对应的权重weight获取,因此这里的多头指的就是有多组weight,类似于GoogLeNet中一个Conv层有多个尺度不一的卷积核,都增加了模型的复杂度,从而能够容纳更多不同“角度”的特征。
什么是自注意力机制(self attention)?
这里要结合ChatGPT给出的解释。假如,现在我们有个机器翻译任务,是把英文翻译成中文,翻译的英文是Love这个单词,翻译结果为中文“爱”。对于这个任务而言:
● key是源语言英语中Love的一串语义信息,值可能是[1, 2, 5, ..., 0];
● value是源语言中的Love一词;
● query是目标语言汉语中的“爱”。
这里是一个标准的注意力机制。
那如果不是翻译任务,是个完形填空任务,比如BERT,对于源语言英语来说,目标语言依然是英语,这时候的注意力机制就是一种自注意力机制,即key和query属于同一种语言,同一个句子,或者同一张图片(MAE)。拿上文中出现的一段英文举例:
”The animal didn't cross the street because it was too tired”
现在我们想知道it指的是什么,对于这个任务而言:
● key是it的一串语义信息;
● value是it;
● query是animal。
什么是交叉注意力(cross attention)?
交叉注意力(Cross-Attention)是一种注意力机制的变体,通常用于处理多个输入序列之间的关系。它在自然语言处理(NLP)和计算机视觉(CV)等领域中得到广泛应用。
在传统的自注意力机制中,输入序列中的每个位置都可以作为查询、键和值。而在交叉注意力中,通常有两个不同的输入序列,一个作为查询序列,另一个作为键和值序列。这样可以通过计算查询序列与键序列之间的关联,来为查询序列生成上下文相关的表示。
交叉注意力的计算过程如下:
1)将查询序列和键序列分别映射为查询向量和键向量。
2)计算查询向量与键向量之间的相似度得分,通常使用点积或其他相似度度量。
3)将相似度得分进行归一化,得到注意力权重,用于加权求和键序列的值向量。
4)将注意力权重与值向量相乘,并求和得到最终的上下文表示。
交叉注意力可以捕捉到不同输入序列之间的关联信息,有助于模型理解它们之间的语义关系。在NLP任务中,交叉注意力常用于处理机器翻译、阅读理解等任务,其中一个序列作为源语言句子,另一个序列作为目标语言句子。在CV任务中,交叉注意力可以用于处理图像和文本之间的关联,如图像描述生成、图像问答等任务。
总之,交叉注意力是一种用于处理多个输入序列之间关系的注意力机制。通过计算查询序列与键序列之间的关联,可以生成上下文相关的表示,有助于模型理解序列之间的语义关系。
什么是密集查询?什么是稀疏查询?
在Transformer模型中,密集查询(Dense Query)和稀疏查询(Sparse Query)是指在自注意力机制中对查询向量进行不同的计算方式。
在自注意力机制中,输入序列被分别映射为查询(Query)、键(Key)和值(Value)向量。查询向量用于计算注意力权重,以便将注意力集中在与查询相关的键值对上。
密集查询是指将查询向量与所有的键进行点积计算,得到注意力分数。这意味着每个查询都会与所有的键进行计算,因此称为密集查询。这种方式可以捕捉到全局的关系,但计算复杂度较高,尤其在序列较长时。
稀疏查询是指将查询向量与一部分键进行点积计算,得到注意力分数。这种方式通过选择一部分与查询相关的键来计算,从而减少了计算复杂度。通常,稀疏查询会使用一些近邻搜索的技术,如k-d树或局部敏感哈希(LSH),来选择与查询相关的键。
稀疏查询在处理长序列时可以有效地减少计算量,但也可能引入一定的信息损失。因此,在实际应用中,需要权衡计算复杂度和模型性能之间的平衡,选择适当的查询方式。
总之,密集查询和稀疏查询是Transformer模型中自注意力机制中对查询向量进行不同计算的方式。密集查询计算所有的键,可以捕捉到全局关系,但计算复杂度较高;稀疏查询只计算与查询相关的键,可以减少计算量,但可能引入一定的信息损失。选择哪种查询方式取决于具体的任务和计算资源的限制。
稀疏查询如何确定和查询相关的键?
确定和查询相关的键是稀疏查询的关键问题,通常可以通过以下几种方法来实现:
1)固定窗口:固定窗口方法是最简单的稀疏查询方法之一。它将查询向量与固定大小的窗口内的键进行计算。窗口的大小可以根据任务和数据的特点进行设置。
2)局部敏感哈希(LSH):局部敏感哈希是一种通过哈希函数将键映射到桶中的技术。在稀疏查询中,可以使用LSH来选择与查询相关的键。通过设计适当的哈希函数,可以使得相似的键被映射到相同的桶中,从而实现近邻搜索。
3)k-d树:k-d树是一种二叉树的数据结构,用于对数据点进行分割和组织。在稀疏查询中,可以使用k-d树来存储键的空间分布,并通过搜索k-d树来选择与查询相关的键。k-d树可以将空间划分为不同的区域,从而快速地定位与查询相近的键。
4)近似最近邻(ANN)搜索:近似最近邻搜索是一种通过近似方法找到与查询最相似的键的技术。ANN搜索算法可以根据数据的特点和任务的需求进行选择,如k最近邻(k-NN)、Locality Sensitive Hashing(LSH)等。
需要根据具体任务和数据的特点选择适当的方法来确定和查询相关的键。不同的方法在计算复杂度、精度和存储需求等方面可能有所不同。因此,需要根据具体情况进行权衡和选择。
Position Embedding是什么?
Position Embedding(位置编码)是一种在自然语言处理(NLP)中常用的技术,用于为序列中的每个位置分配一个唯一的编码向量。它的目的是为了将位置信息引入到序列数据中,以帮助模型理解不同位置的语义关系。
在NLP任务中,通常将文本序列转换为词嵌入(Word Embedding),将每个词映射为一个低维的向量表示。然而,词嵌入只考虑了词的语义信息,而没有考虑词在句子中的位置信息。
为了引入位置信息,位置编码被引入。它通过为序列中的每个位置分配一个唯一的编码向量来表示其位置。常用的位置编码方法有两种:
1)绝对位置编码(Absolute Positional Encoding):绝对位置编码使用一组预定义的编码向量,每个向量对应一个位置。这些编码向量可以是固定的,也可以是可学习的参数。通常使用正弦函数和余弦函数来生成绝对位置编码。
2)相对位置编码(Relative Positional Encoding):相对位置编码考虑了序列中不同位置之间的相对关系。它通过计算不同位置之间的距离或相对位置来生成编码向量。相对位置编码可以捕捉到词与词之间的相对位置信息。
位置编码将位置信息融入到序列数据中,使得模型可以根据位置进行更好的理解和推理。在Transformer等模型中,位置编码通常与词嵌入相加或拼接在一起,作为输入传递给模型进行下游任务的处理。
需要注意的是,位置编码只是一种引入位置信息的方法,具体的编码方式可以根据任务和数据的特点进行调整和优化。