《鞋匠的AI之旅》- 4. 自注意力

4.自注意力

【内含公式,建议PC端浏览器阅读】

现在鞋匠为AI模型量化了文本世界的基本单元,这是一个很好的开局。接下来鞋匠就需要设计一种方法,让AI模型自行抓取各个字词在一段文本中的关系,从而更好的理解这段文本所携带的真实意义。

朦胧间,鞋匠觉得在朦胧的尽头,大模型的世界变得些许清晰。鞋匠怀着敬畏之心伸手又敲开了大模型世界的第二扇门 --- 自注意力。

注意到每个词嵌入的细节

鞋匠很满意词嵌入的向量表示,它直观、自然、信息丰富。如何让AI模型注意到这些丰富的细节呢?鞋匠陷入了沉思,他觉得每个词嵌入的每个维度的值都对整句话(即由多个词嵌入排列在一起的序列)的意义都有一定的贡献,但每个维度的贡献度是多少,他一时半会还没有办法确定。但他觉得这一点还是比较直观的,比如说,人类会用“梨”这个字来表达一种水果,它不会代表“苹果”的意思,而“梨”这个字的词嵌入的每个维度都会对整个词嵌入序列(含有“梨”这个字的某一句话)所表达的意思有一定的贡献,比如词性、所代表的事物属性等。随着思考的深入,鞋匠觉得每个词嵌入与词嵌入序列中其它词嵌入的关系,也应该影响到了整句话所表达的意思,比如说“你我之间天涯咫尺”,这句话里所描述的情感关系是“你”“我”之间的而不是“天”“涯”之间的关系。于是,鞋匠想到,需要一种处理方法,或者说一种框架,让AI模型能够注意到每个字词的意义还要注意到其与这个词嵌入序列中的其它词嵌入的关系所携带的含义。

为了便于观察,他在纸上重重地写下了“你我之间天涯咫尺”这句话,并从词汇表中抄下了每个字的词嵌入,如下。

你:x1 = [a(1)(1), a(1)(2), a(1)(3), …, a(1)(256)]

我:x2 = [a(2)(1), a(2)(2), a(2)(3), …, a(2)(256)]

之:x3 = [a(3)(1), a(3)(2), a(3)(3), …, a(3)(256)]

间:x4 = [a(4)(1), a(4)(2), a(4)(3), …, a(4)(256)]

天:x5 = [a(5)(1), a(5)(2), a(5)(3), …, a(5)(256)]

涯:x6 = [a(6)(1), a(6)(2), a(6)(3), …, a(6)(256)]

咫:x7 = [a(7)(1), a(7)(2), a(7)(3), …, a(7)(256)]

尺:x8 = [a(8)(1), a(8)(2), a(8)(3), …, a(8)(256)]

这里鞋匠还是继续使用前面词嵌入的例子,整张词汇表里有10000个词嵌入,每个词嵌入256个维度。x1代表“你”的词嵌入,x2代表“我”的词嵌入,以此类推。其中每个词嵌入的各维度元素的值,都是用a(1)(2)这样的形式来表示,其中第一个下标数字表示这个元素值对应哪个字,第二个下标数字表示这个字对应的词嵌入的哪个维度值。所以a(1)(2)就代表着x1这个词嵌入的第2个维度的取值。

假设上面的这8个token的一句话是AI模型的输入,接下来应该如何处理呢?首先,鞋匠用一个大写的X 来表示这句话,即这个AI模型的输入是:

X  = [[a(1)(1), a(1)(2), a(1)(3), …, a(1)(256)]

     [a(2)(1), a(2)(2), a(2)(3), …, a(2)(256)]

     [a(3)(1), a(3)(2), a(3)(3), …, a(3)(256)]

     [a(4)(1), a(4)(2), a(4)(3), …, a(4)(256)]

     [a(5)(1), a(5)(2), a(5)(3), …, a(5)(256)]

     [a(6)(1), a(6)(2), a(6)(3), …, a(6)(256)]

     [a(7)(1), a(7)(2), a(7)(3), …, a(7)(256)]

      [a(8)(1), a(8)(2), a(8)(3), …, a(8)(256)]]

这是一个形状为8 ×  256的矩阵。它包含了整句话的所有词嵌入的所有维度的量化值,以及词嵌入的排列顺序。这里每一行对应一个词的词嵌入,有些人可能会用每一列来代表一个词嵌入,相应的后面章节的计算形式就要做相应的调整,但鞋匠觉得这不重要,因为大家的算法和理念是相通的。鞋匠喜欢把一个词嵌入放到一行来处理。

看着这个矩阵,鞋匠看到了每个词每个维度的细节,他觉得这里一定有一种方法可以把每个词每个维度对整句话意义的贡献度(或者信息量)提取出来,另外还要找到词与词之间的关系,对这种关系进行度量,由此衡量词与词之间的关系对整句话所表达的意义的贡献度(或者信息量),这样就可以提取到以这两个方面所表示的整句话的意义。比如前面的那句话“你我之间天涯咫尺”,它表达的是一种情感关系,“你”和“我”之间的情感关系,这里的情感关系这个含义是这句话中最重要的,应该被高度的注意到。对于这个“情感关系”的进一步描述是“天涯咫尺”,它也应该要被注意到。这个“关系”是“你”“我”之间的关系,这是对这个“关系”的进一步限定,它也应该被注意到。人类去理解这句话,注意到这些文字所携带的含义是自然而然的过程,大脑知道哪些是重要的,哪些是不太重要的,哪些甚至看到就忘了。鞋匠不清楚在人类的大脑里这些“运算”是如何进行的,但鞋匠知道应该从这里找到突破,那就是如何抓住这些应该被注意到的含义,并且AI模型的注意力又是怎么被分配的,以便注意到这些各类含义。

还记的前面的词嵌入吗?鞋匠设计了一种框架,让神经网络按照一定的规则在茫茫辞海中基于人类文明总结的现有文本自动的把每个词嵌入到一个高维的词嵌入空间中。鞋匠想,这里对“注意力”的度量也应该设计一种框架,让神经网络,或者说承载它的计算机来自动完成这种度量。鞋匠坚信这是可行的。仔细想想,这种“注意力”框架所形成的注意力分配模式应该是固定的,不应该随着每句话的不同而变化的,就像语言学上人类研究出来的模式一样,可以用主、谓、宾、定、状、补来分析一句话。同样的,计算机应该以计算机擅长的方式来形成自己的处理模式。

以上面的输入X 为例,这个例子中每个词嵌入有256个维度,假设为每个词的每个维度随机量化一个量来表示这个词的每个维度应该得到多少注意力,就得到了一个256维的量化了的注意力向量。具体这个注意力向量各维度对这个词来说精确代表着什么,鞋匠不清楚,但鞋匠知道这样可以很好的代表“注意力”对每个维度的关注。就像一个训练好的词嵌入一样,鞋匠知道词嵌入可以很好的代表对应的词,但具体每个维度的精确含义已经很难解释了。对于一个词嵌入来讲,除了关注其每个维度外,维度和维度之间也应该是有关联的,即每个词嵌入的维度以及维度之间的关系也会决定AI模型应该如何“注意”它以及“注意力”应该是多少。每个词的这些信息(词嵌入维度本身以及维度间的关系)加在一起,就形成了对这个词本身的全面理解。

x1(“你”的词嵌入)这个词嵌入为例,鞋匠首先要做的就是对每个维度进行“注意力”量化,比如,首先创建一个256维的随机向量,暂且称之为WQ1,利用WQ1的每个维度对x1的每个维度进行量化,得到256个量化后的数值。为了体现维度间的关系,鞋匠又把这256数值加在一起,得到1个数值。这个数值里包含了每个维度应该被注意到的信息,以及维度间的关系,但这也意味着原来256维的词嵌入降维到了1维,这会丢失很多很多信息。为了保持256维不变,鞋匠又创建了其它255个随机向量,也是256维的,用WQ2,WQ3,…, WQ256来表示。每一个都按照WQ1的方式对x1进行运算,于是鞋匠得到了256个数值。把它们顺序排列就会得到了一个新的256维向量,鞋匠把它表示为[q1,q2,q3,…,q256],这个向量与x1是不同的,但它又是对x1的更新。这个更新里体现了对x1每个维度AI模型应注意到些什么,也包含了对于x1的维度间的关系AI模型应注意到些什么。怎么理解这个q1,q2,q3,…,q256呢?鞋匠想,可以把q1想象成从x1的第1个维度看AI模型应该如何抓取x1所代表的信息,q2代表从x1的第2个维度看AI模型应该如何抓取x1所代表的信息,依次类推。是的,应该是这样的,鞋匠很坚定的认为他正走在正确的路上。

于是,鞋匠得到了这样一个矩阵:

        WQ = [WQ1,WQ2,WQ3,…, WQ256]

              = [[q(1)(1), q(1)(2), q(1)(3), …, q(1)(256)]

                 [q(2)(1), q(2)(2), q(2)(3), …, q(2)(256)]

                 [q(3)(1), q(3)(2), q(3)(3), …, q(3)(256)]

                                    …

                 [q(256)(1), q(256)(2), q(256)(3), …, q(256)(256)]]

这是一个形状为256 ×  256的矩阵。这里把第1列看作是WQ1,第2列看作是WQ2,以此类推,这个矩阵就可以完整的代表WQ1,WQ2,WQ3,…, WQ256这256个256维的向量。上面鞋匠所想象的计算过程就是数学上矩阵的点积运算过程,如下。

x1·WQ = [a(1)(1), a(1)(2), a(1)(3), …, a(1)(256)]·[[q(1)(1), q(1)(2), q(1)(3), …, q(1)(256)]

                                                                               [q(2)(1), q(2)(2), q(2)(3), …, q(2)(256)]

                                                                               [q(3)(1), q(3)(2), q(3)(3), …, q(3)(256)]

                                                                                             …

                                                                               [q(256)(1), q(256)(2), q(256)(3), …, q(256)(256)]]

            = [q1,q2,q3,…,q256]

x1·WQ的第1行第1列的元素值q1 = a(1)(1)·q(1)(1) + a(1)(2)·q(2)(1) + a(1)(3)·q(3)(1) + … + a(1)(256)·q(256)(1)

x1·WQ的第1行第2列的元素值q2 = a(1)(1)·q(1)(2) + a(1)(2)·q(2)(2) + a(1)(3)·q(3)(2) + … + a(1)(256)·q(256)(2)

以此类推,就得到了对应x1的一个新的256维的向量。

于是,上面的例子中,“你我之间天涯咫尺”这句话新的向量表示就是X点积WQ,即X·WQ,用Q来表示这个结果的化,有

Q = X·WQ

这是一个形状为8 × 256的矩阵,代表了对于输入的每个词嵌入AI模型应该如何注意到它的各个方面的含义,即各维度自身含义的量化以及维度间的关系所携带含义的量化。而WQ则代表的是AI模型注意力的一种分配模式。它对于每个输入的词嵌入来说,都是一样的。正如上文所说,分配模式像人类的语法一样是固定的。想到这里,鞋匠有个拓展想法,这里的WQ的列数是256,这个列数决定了最后得到的Q的列数,为了保持每个词嵌入经过WQ点积运算后得到一个相同维度的新的词嵌入表示,这里WQ列数定为256.如果WQ列数定为小于256的数呢?这就相当于对原词嵌入进行了降维处理。如果WQ列数定为大于256的数呢?这就相当于对原词嵌入进行了升维处理。鞋匠觉得可以利用这个升降维机制灵活设计AI模型解决不同的现实问题,但现在鞋匠只希望继续自己的主要研究,即如何抓取一句话的含义。

查询矩阵、键矩阵和值矩阵

还记得前面鞋匠的分析吗?鞋匠觉得需要衡量每个词对整句话意义的贡献度,并找到词与词之间的关系,对这种关系进行度量,由此衡量词与词之间的关系对整句话所表达的意义的贡献度,这样就可以提取到以这两个方面所表示的整句话的意义。上面计算Q矩阵的过程提取了每个词嵌入本身(各维度所代表的含义以及维度间的关系所代表的含义)所携带的含义。而要理解整句话,还需要提取词与词之间的关系所携带的含义。

如何才能抓住这种词与词之间的关系所携带的含义呢?鞋匠想到刚毕业的时候做过一段时间信息检索工作。如果要检索一个信息,一般都这样来做:在工程师数据表里(from 工程师数据表),查询(select)年龄在25到35之间的男性工程师(25<年龄<35 and 性别=男性),这样就得到了若干符合条件的工程师信息。仔细想想,“查询”代表了要找什么,年龄、性别则是查找的依据,是存储在数据库中的索引或“键”,输出则是与这些条件相匹配的信息的具体内容(“值”)。在这个检索的过程中,“查询”和“键”组合在一起,决定了找到什么样的“值”。类比过来,AI模型也应该“注意”到类似的信息,从一句话中提取正确的“理解”,就像从数据库中找到正确的“值”一样。AI模型先要做的是提取“查询”的信息,再提取“键”的信息,通过“查询”和“键”的某种交叉计算,衡量出“查询”和“键”之间的关系,把这种关系的度量与“值”结合在一起,形成最终的想要的“值”。这也就是对整个输入序列的理解,这里包含了对输入序列中每个词嵌入的维度、每个词嵌入维度之间、词嵌入和词嵌入之间的信息提取,形成对整个输入序列的理解,并最终把这种理解以新的形式表示出来。

上面的WQ可以看作是为了从一句话中(一个输入的词嵌入序列)提取“查询”信息而构建的权重矩阵,现在还需要确定“键”权重矩阵,以便提取“键”信息(查询依据),以及“值”权重矩阵,以便提取“值”信息(这句话的正确理解)。鞋匠用Q、K、V来分别代表提取出来的“查询”矩阵、“键”矩阵、“值”矩阵。上面的计算Q的过程其实就是提取“查询”的过程。同样的,鞋匠创建了另外两个256 × 256的矩阵,其各元素值是按照一定的规则或者一定的模式进行随机初始化的,分别以WK和WV来表示,它们就是为了计算“键”和“值”的权重矩阵。按照前面对WQ的理解,WK和WV是AI模型注意力的另外的两种分配模式。通过前面同样的运算可以得到“键”和“值”,即K和V,与Q一样,它们都是8 × 256的矩阵。把Q、K、V放到一起,得到如下算式:

Q = X·WQ

K = X·WK

V = X·WV

现在Q、K、V之间是孤立的,各自按照自身所能表征的模式,为每一个词嵌入重新计算出了一个新的向量。每个词嵌入的3个新的向量所表征的含义,可以按照上面“查询”、“键”、“值”来理解,但就像词嵌入的各个维度一样,鞋匠预感到一旦WQ、WK和WV随着模型的训练被优化并完全固定下来以后,其真实的含义可能已经完全不是“查询”、“键”、“值”本身所代表的含义了。鞋匠觉得理解这一点很重要,这样才不会执着于它们本身的含义。忘掉我是谁,才能在更高一层理解系统是怎么工作的。这里鞋匠觉得有必要重复下:专家以其对现实的深邃洞察所形成的对某种现实意义的深刻认知给AI模型定了一个必须遵守的法则,但训练结果只能是现实情况在AI模型的空间中被这个法则具象化的表示出来了,它并不一定按照专家们所设想的那样反映其在人类的物理空间中被赋予的现实意义。

正像前面鞋匠的拓展思考那样,这里WK、WV的列数和WQ的列数一样都是可变的,可以根据需要在提取“键”、“值”和“查询”信息时对原词嵌入进行降维和升维处理。不过在鞋匠的这个例子里,列数都定为和词嵌入维度数一致,即256.

注意力分值矩阵与注意力权重矩阵

为提取到输入的词嵌入序列中的不同词嵌入间的关系,可以把两个独立表征各个词嵌入的Q和K进行交叉计算,使其捕捉到词与词之间的关系,这种关系包含了词嵌入本身所携带的信息,还包含了词与词之间的关系所携带的信息。通常这种交叉计算是通过矩阵点积的运算来实现的,但需要注意的是Q和K的形状是都8 × 256,按照点积的运算规则,如果两个矩阵可以进行点积运算,那么前一个矩阵的列数要正好是后一个矩阵行数,以Q和K为例,点积运算要求Q的列数是K的行数。但当前Q和K的行列数不满足点积要求。

还记得上面用来表示输入序列的X矩阵吗?鞋匠约定了每一行代表一个词嵌入,其实按照列来表示一个词嵌入也是一样的,新的矩阵有多少个词嵌入,每个词嵌入的每个维度的值,这些都不会改变,那么这个新的输入序列矩阵形状就会变为256 × 8。经过这种变化正好就符合了前面点积运算的要求。鞋匠把这种行列互换的转换称为这个矩阵的转置,以K为例,它经过行列互换后得到的新矩阵即是K的转置,表示为K^{T} 。这里K^{T}的形状就是256 ×  8。

所以,鞋匠希望得到的Q和K的交叉运算可以表示为,

Aorig = Q·K^{T} = [8 ×  256]·[256 ×  8] = [8 ×  8]

这里Aorig表示的就是Q和K首轮点积的结果,它是一个8 ×  8的矩阵。8是AI模型输入的token的个数,8 ×  8 = 64,正好表征的就是每个token与其自身和其它各个token两两关系的一个矩阵。这个矩阵各元素的值就是对这种两两关系的度量。显然这里每个元素的值可大可小,完全依赖于这8个词嵌入本身以及权重矩阵WQ和WK的各元素值。为了避免数值过大可能导致后面将要用到的softmax函数进入到梯度很小的区域,常用的做法是进行缩放处理。用来做缩放的这个数一般定为WK的列数的平方根,鞋匠用d_{K}来表示列数,则平方根可以表示为\sqrt{d_{K}} ,称之为缩放因子。在鞋匠的例子里,WK的列数与词嵌入向量维度数相等,都是256。通过这个缩放因子进行缩放后,得到:

Ascore = Aorig / \sqrt{d_{K}}

鞋匠称这个矩阵Ascore为注意力分值矩阵,是对Aorig所表征的token与token之间的关系的进一步优化处理,描述的是对于每个token来说它应该关注到所有输入token的原始分值。它的每一行对应的是X 的同一行所代表的token,它每一列对应的也是X 的同样数值的行所代表的token,即Ascore的第3行对应X 的第3行所对应的token3(“之”字对应的token),Ascore的第4列对应X 的第4行所对应的token4(“间”字对应的token),交叉位置的元素,即Ascore的第3行第4列的元素值所表示的就是token3注意到token4的原始分值。

如果把一个token注意到所有输入的token的注意力看成单位1(包括自己对自己的关注),那么Ascore每个元素值所代表的就是关注程度,也就是该行所对应的token应该注意到该列所对应的token的注意力占比,这样的占比之和为1,即每一行元素之和都是1,表示一个token的总的注意力。以x3为例,它在X 里是第3行的词嵌入,在上面这个[8 × 8]的矩阵里,x3所对应的token3与其它token的两两关系也在第3行,表示的是token3把自己的注意力分配到其它token上的占比,这一行共有8列,这一行每一列所对应的元素值都是token3对于其它token的注意力占比。比如,第4列的值为0.15,那么token3对token4的注意力占比就是15%,即token3用自己15%的注意力来关注token4.

但这里的Ascore里的元素并不是每行之和为1,而是提取的“查询”矩阵和“键”矩阵的点积进行缩放之后的结果。让每行的元素之和为1的方法,鞋匠比较熟悉了,这里还是要用softmax函数,则有:

                                                A = softmax(Ascore)

                                                   = softmax(Aorig /\sqrt{d_{K}} )

                                                   = softmax((Q·K^{T})/\sqrt{d_{K}} )

                                                   = softmax((Q·K^{T})/\sqrt{256} )

这是一个8 × 8的矩阵。鞋匠称这个矩阵为注意力权重矩阵。它是通过对提取到的“查询”Q和“键”K的信息进行交叉运算从而得到词与词之间的关注关系的一种度量。

上下文向量矩阵

有了上面的注意力权重矩阵,接下来就是对“值”信息按照这个注意力权重矩阵进行加权求和处理,得到输入序列的新的向量表示。这个新的表示考虑了每个词嵌入的每个维度、维度间的关系、词与词之间的关系。AI模型就是通过这些细节信息捕获到输入序列X 所承载的含义。鞋匠把这个输入序列的新的表示称之为上下文向量矩阵,用C来表示这个矩阵,则有:

C = A·V = [8 × 8]·[8 × 256] = [8 × 256]

所以C是一个形状为8 × 256的矩阵,形状同X ,即为X 新的表示。C就是输入序列X所承载的含义的一种量化。这个量化就是AI模型捕捉到的词嵌入序列中每个词嵌入自身代表的信息以及词嵌入之间的关联信息。鞋匠觉得在此基础上,进一步处理输入序列,即理解整个输入序列就有了充足的信息基础。

鞋匠收了收思绪,他看到在生成C的过程中,用到了WQ、WK和WV权重矩阵。就像前一个章节生成词嵌入的词汇表一样,这些矩阵各元素的值需要在整个AI模型的训练过程中来优化。鞋匠把上面这种上下文向量的生成过程称为自注意力机制。这就是鞋匠找到的方法,这种方法可以量化一个词嵌入序列,为后续AI模型进一步处理输入序列做好了准备。具体AI模型怎么利用这个新的向量表示,模型的输出是什么,依赖于鞋匠设计这个AI模型的初衷。

一切准备就绪,鞋匠觉得应该翻开更为激动人心的篇章了!

>>下一章节:《鞋匠的AI之旅》- 5. Transformer【上】

<<上一章节:《鞋匠的AI之旅》- 3. 词嵌入

<<内容总章:《鞋匠的AI之旅》- 总章【一段从神经元到GPT的AI之旅】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值