注意力机制为什么work
embedding
输入的文本被切分成小块,通常是单词或者单词片段。我们通过embedding将向量关联到高维空间。在所有嵌入的向量中,方向可以对应语义。其中GPT3有12288个维度。由多少个方向就可以代表有多少个语义。Transformer的目标是逐步调整这些嵌入,让它们不单单只编码单个单词,还可以融入更多上下文语义。
比如一个mole在不同的语境下有不同的含义比如
- take a biopsy of the mole
- American shrew mole
- one mole of carbon dioxide
但在经过embedding之后它们对应的都是同一个向量。因为初始的token嵌入本质上是没有上下文本的查找表。只等到Transformer的这一步,周围的信息才能传递到该嵌入向量。可以这样想象,嵌入空间中由多个方向,编码了mole一次的不同含义,训练好的注意力模块能计算出需要给初始的泛型嵌入加什么向量。才能把它移动到上下文具体的方向上。
比如输入的是tower,如果前面跟的是塔可能指的就是非常广泛的方向,而不是具体的方向。如果塔的前面是Eiffel。就应该有一个机制来更新这个向量。让它具体指向埃菲尔铁塔的方向。新向量也需会关联上巴黎法国与钢铁制品。如果前面还有微型一次,那么模型应该再次更新使其不再与高大的事物相关连。注意力模块不止是精细化了一个词的含义。还允许模型相互传递这些嵌入向量所蕴含的信息。甚至可以传递的很远。且信嵌入向量的信息,可以比单个词丰富很多。
在经过许多层的注意力模块之后,此时产出下一个token的预测,完全基于序列中最后一个向量。比如你输入了一本小说,从头输入到块结尾的,因此凶手是。。。到这里结束该序列中的最后一个向量,即 “是”的嵌入向量。必须经过所有注意力block的更新,以表示更多超过单个词的信息量。
单头注意力机制
假设输入包含以下短语,一个毛茸茸的蓝色生物漫步于葱郁的森林,假设我们只关心形容词调整对应名词的含义,这种类型的更新。
还记得,每个词的初始嵌入是一个高维的向量,其中编码了词的位置信息。假如用E表示这些嵌入向量。最后的目标是通过一系列的计算。产生一组新的更为精准的嵌入向量。比如经过形容词修饰的名词所对应的向量。在深度学习中,我们希望多数的计算都是矩阵向量乘法(products)。其中矩阵填满了可调的权重,需要模型学习数据来调整。这里的形容词更新名词只是为了方便理解。
每个词都想知道他前面形容词,我们它为这个词的查询。GPT3中每个query的维度是128(模型的query维度是多个头并行的输出拼接后的结果,即96头x1288。如果不是多头注意力的话,查询的维度应该和嵌入是一样的。)。我们将其记作$W_Q$。我们将Q乘以嵌入的向量,也就是把查询矩阵分别与文中所有嵌入的向量相乘。每个token算出一个查询向量。Q内的数值都是模型参数,具体的模式是从数据中学到的。假设这个查询空间将嵌入空间中的查询矩阵,映射到较小的查询空间中的某个方向,用向量来编码 “寻找前置形容词”的概念。
同时我们还需要第二个矩阵叫做键矩阵。也会与每个嵌入向量相乘,产生第二个向量序列称为键。概念上,可以把key视为想要回答query。这个键矩阵也充满了可调减参数,它也会将嵌入向量映射到相同的低维度空间。当键与查询的方向对其时,就能认为它们相匹配。就本例而言,键矩阵将会把毛茸茸和蓝色映射到名词生物对应的查询向量高度对其的位置上。为了衡量每个键与每个查询的匹配程度,我们要计算所有可能的键查询查询对之间的点击。可以想象如下一张表格,如果点击的大小越大,就说明它们越对齐。
回到上面的例子,creature应该和fluffy和blue俩个有最大值。用机器学习的属于来说就是毛茸茸的和蓝色的嵌入注意到了生物的嵌入。
反过来the等词的键与生物的查询之间的点积,就会很小因为他们不相关。网格中的值可以是负无穷到正无穷的任何实数。这个分数代表的是每个词语更新它词含义有多关联。我们对每一列进行加权求和。这样一来数值就是0-1之间。而且每一列的总和为1,就像概率分布一样。我们使用softmax进行归一化。将每一列重新填充会表格我们就能将每一列看做是权重。权重的意义就是相关新性。我们将这个填充回去的网格叫做注意力模式(attention pattern)。原论文中使用了非常简洁的写法
$$ \text {Attention}(Q,K,V)=\text{softmax}(\frac{QK^\top}{\sqrt{d_k}})V $$
这里的Q和K分别包含了所有的查询向量和键向量,此外为了数值稳定性,将所有的点积除以键查询空间维度的平方根。这里为了进行掩码,我们将矩阵左下角的词语设置为负无穷。这样在计算softmax之后,它们的权重就可以为0了。有的注意力机制不使用掩码(编码器)。此外gpt的大小是长度的平方。这就是为什么上下文的长度会成为大语言模型的瓶颈。
为了渴求更大上下文的窗口。近些年来注意力机制出现了一些变体。目的是让上下文更具扩展性。这里不做展开。
注意力会用到的第三个值矩阵。把它乘以上面那个词嵌入。得到的结果就是值向量。这就是单头注意力机制。
我们来看一下GPT3模型本身的总参数量。
键矩阵和查询矩阵格优12288列,对应了嵌入的维度。以及128行,对应较小键的查询空间和键空间的维度。这样就有各1572864个参数。而值矩阵是一个1228列12288行的方阵。因为它的输入输出都在于高为的嵌入空间。大约为1.5个亿。
设计上值矩阵的参数量 可以比键矩阵和查询矩阵多几个数量级。但是更高效的做法是,让值矩阵所需的参数量, 等于键矩阵和查询矩阵的参数量之和。对于并行多个注意力头来说这一点尤为重要。具体的做法是将值矩阵分解为俩个小矩阵相乘。概念上可以视为一个线性映射。输入输出都在这个高维度的嵌入空间。例如把蓝色的嵌入向量映射相加后把名词变蓝的向量。只不过实际上会分俩个步骤。
右边行较少的那个矩阵(12288x128)通常等于键空间和查询空间的维度。可以看做是将较大的嵌入向量,降维到较小的空间。左侧的第二矩阵是从小空间映射回嵌入空间(128x12288)。这种操作的实质就是对大矩阵进行低秩分解。
低秩分解(Low-Rank Decomposition)是一种矩阵分解技术,用于将一个高维矩阵表示为两个或多个低维矩阵的乘积。通过这种分解,可以减少数据的维度,捕捉重要的特征,同时降低计算复杂度和存储需求。
这样的话一共四个矩阵,key,query俩个value一共的参数大概为630w个。
我们目前讨论的都是self-attention。与cross-attention(交叉注意力)有所不同。这与GPT无关,交叉注意力设计的模型会处理俩中不同类型的数据。比如原文与正在翻译出来的译文。或许是语音音频与正在被转录出来的文字。交叉注意力和自注意力几乎相同。唯一的区别是键和查询矩阵作用于不同的数据集。这样就可以描述一种语言的单词对应另一种语言的那种单词。在这种情况下通常不会用到掩码,因为不存在后面token影响前面token的问题。
多头自注意力
在例子中关注的是形容词更新名词的含义。但实际上,上下文影响词义的方式多种多样。比如如果巫师和harry一起出现,那么它很可能指的是哈利波特。
Transformer的模块由多头注意力组成。大量并行的执行这些操作。每个头都有不同的键,查询,值矩阵。GPT3中每个模块使用96个注意力头。意味着有96个key和查询矩阵产生96中不同的注意力模式。每个注意力头独特的值矩阵。用来产生96个值向量矩阵。全都会议对应的注意力模式作为权重分别进行加权求和。也就是说每个头都会给出每个token,要加入到该位置的嵌入中的变化量。我们要做的就是把这些头给加起来,然后给改位置的初始位置加上这个加和。这个综合就是多头注意力模块的输出的一列。因此能得到更近准的嵌入。
这样模型能学到不同上下文中改变语义的多种方式。继续统计参数乘以96个头之后参数量大概为。6亿。理论上多头注意力的value矩阵每个都可以分解为俩个低维度查询矩阵。但Transformer中会有些不同的。
这些值矩阵。我们先将左边的矩阵记为value up矩阵右边的矩阵记为value down。这些value up矩阵会被合在一起称作输出矩阵(output)。与整个多头注意力模块相关联。而单头的值矩阵实际上单指down value的矩阵。
gpt会重复多次这种操作。经过多层感知机,attention,多层感知机,attention。意味着吸收了上下文的语义之后还有多次机会被周围含义细致的词语影响。越是网络的深处,每个嵌入就会从其他嵌入中吸收越多的含义。让嵌入本身也越来越丰富。gpt中包含了96个不同的层。因此qkv的数量还得乘以96。使得所有注意力参数的总数有580亿个。虽然很多了,但这仍然只占网络总数量的三分之一。因此有非常多的操作是来自其他模块的。其实大部分参数都在多层感知机之中。