AI中的线性代数——从数学的角度深入浅出看Transformer

0 阅读1小时+

写在前面

现在AI、大模型等十分地火,就连我妈都知道,但是应该有不少同学跟我一样,实际上基本没怎么接触过AI领域,如机器学习、深度学习、神经网络、LLM等等相关的知识,几乎可以说是一张白纸,以前学生时代学过一点孱弱的数学,在考完试后也基本就马上忘得一干二净(当然可能考试的时候也不太会)。

我在刚开始学习的时候,去到处搜各种教程看,然后经常会看见类似的标题:“5分钟带你读懂transformer”,“大模型,就是这么简单”,“transformer,是个简单到让人困惑的模型”,“用大白话带你轻松理解transformer, 我奶奶都能听懂”......等等之类的,其实我基本上每个视频都看了,但是这种快餐式的视频,不管看多少,很多问题还是不明白,只能给你一个对于这个东西大体的印象,经不起细想和推敲,稍微一想深入理解,又总会被卡住,很痛苦。但是,整个AI体系的知识又十分复杂,从头学的话还需要补很多数学知识,很难让人下定决心去这么做,所以我又很需要这些视频来帮我快速上手。

当然,除此之外,也有很多带着你一步步计算一步步细节讲解的质量还不错的教程,但是又苦于过于专业,很多地方自己实在跟不上,句句都是中文,却又句句都听不明白。但是也正常,有句话叫,会者不难,难者不会,你学会了、或者说你相关专业知识储备够丰富、数学基础够扎实,你回头看起来肯定会比较轻松,就像我们组里的资深前端大佬,看代码跟看大白话似的,我就不行。或许这个东西真的很简单,但是作者在说的时候,总是会不经意间默认某些东西你是懂的、默认你已经具备某些“小儿科”基础知识,然后不去具体补充、一笔带过,然后可能是考虑到专业性,又会说很多专业名词,但是我觉得我恰恰就是被这些基础知识、以及各种各样的专业名词卡住了,特别是里面的很多数学、线性代数相关的,比如:梯度消失、爆炸、正则化、线性变换、高维向低维的投影、潜空间之类的话我确实看不太明白。这些东西就很容易让人看不下去,有点劝退,因为我们会潜意识觉得,太“高大上”了,从而否定自己,觉得不是我们朝夕能看懂的。

当然我也确实怀疑过自己,可能确实是因为我有的笨,理解能力不行,看不懂,或者说我确实基础太差,那我显然不适合一上来就跑,那我就先从爬开始一点点来,一点点补充知识吧。所以,这里我才想按照我自己的学习心路历程,带着大家从最基础的线性代数开始,补课,或者说复习,写一篇即快又慢,即深入又肤浅的,不管大家啥基础都能看得懂的文章。正所谓磨刀不误砍柴工,在上战场前,也就是去学习理解真正的模型前,我们先把自己武装起来,然后再去攻坚克难。

就我们要讨论学习的Transformer的核心部分注意力机制而言,一些基础的线性代数知识其实足矣,虽然基础,但是对于我们理解模型很重要,所以我们先进入磨刀环节,学一点线性代数。

AI领域常用的线性代数基础知识

数学往往被大家妖魔化了,大家总是谈“数”色变,甚至哪怕是在学习人工智能这样一个数学浓度99%的领域,大家依然会有意无意地去尽量避开它,因为一看到数学相关的就再也没有看下去的动力,这可能也就是为什么网上搜到的教程总是试图尽量用大白话和举例子类比来讲,并且很多人内心也潜意识觉得自己看不懂、要学也吃力,打上“高端”的标签,其实不然。

数学实际上是一个很好的工具,只不过很可惜应试教育阶段把它扭曲成了每个人的内心阴影,就好比我们在做前端开发时用到的各种组件库是一个很好的工具一样,可能一开始不熟悉的时候用起来会有些痛苦,但是当用熟练了之后就会成为我们很好的帮手。在AI领域一定会有数学,但是大部分用到的其实都是很基础的东西,大家在大学阶段或多或少一定学过,只需要花点时间复习一下概念,哪怕是没有学过,简单的介绍一下知识点也可以很快学会,并不需要像以前上学时候那样去做那些又痛苦又没卵用数学题。

今天我们将会讨论到当下最火的LLM模型的鼻祖transformer(不是变形金刚),所以这里先讲一下一些会用到的数学知识,其实都是线性代数的知识,然后我们在解构完transformer注意力机制的核心结构之后,再站在数学的角度去尝试深入地洞悉下它。

提到线性代数,我其实也都是痛苦的回忆,学生时代的我对于线性代数的印象就是,听了一堆晦涩难懂且绕口的定义,然后记了一堆蛮复杂的公式,拿来算各种各样复杂的计算,什么行列式、特征值、矩阵的秩这那的,然后通过一些公式,又推导出新的一堆公式,然后接着算,为了算而算,但是真不知道这玩意能有啥用,稍微扩展一下就完全听不懂了。但是在慢慢去学习一些机器学习相关的东西,然后回过头来再去看这个东西,确实又是有了新的不一样的感受,某些东西在结合实际应用再去深度思考以后,也发现蛮有意思的。

总的来说,线性代数,是研究“空间”的 ,在学线性代数的时候,肯定绕不开空间,但是这个空间和我们常说的空间不太一样,是一个比较抽象的概念,大家在看各种定义时肯定会看到一句话:“在N维 向量空间 中,xxxxxxx...”,大家可以不用纠结这个学术的叫法被绕进去,到了实际工程运用中,大家可以把我们这里说的“空间”,理解成,一个“世界”,这个世界中,有着一定的运作法则,或者说含有一定量的特定信息,这里把这个放在最前面说给大家留个印象,后面也会再次说。

向量

一切还是需要从向量开始说起。在物理、数学以及线性代数中其实都有向量,本质其实是一种东西,一个既有大小又有方向的“东西”,它可以表示为一条从一个点(起点)指向另一个点(终点)的有向线段。向量的“大小”通常被称为其长度或模,而“方向”则是它所指向的方向。虽然各自学科里的精确的官方定义有所不同,为了避免混淆,我们这里就只用专注线性代数就好。

线性代数中向量的定义:

向量是向量空间(也称为线性空间)中的一个元素。那么什么是向量空间呢?一个向量空间 V是一个集合,其中的元素(我们称之为向量)定义了两种运算:向量加法 和 标量乘法。说实话,定义还是很抽象很难懂,那就不用懂,看看就行了,其实我们在机器学习中接触到的向量的形式都是一个有序数组,咱们就先简单这么理解就行,例如向量a=[a1,a2.....an],这组数里面的n个数就是向量的各个分量,然后n就是向量的维度,有n个分量就是n维向量。

那么问题来了,n个数的集合怎么就有方向了,别急,我们不是经常又会把向量画在坐标系里面看看几何意义啥的嘛,这n个分量其实就是在n维坐标下的一个点在各坐标轴,也就是各个维度的分量,比如我们最熟悉的三维坐标,那就是x y z的值,然后由原点画一个箭头指向这个坐标确定的点,就是向量在n维坐标系下的表示了,方向就是坐标原点朝这个点的方向,大小就是这个箭头长度,也就是向量的模长。比如下面这个a向量,向量坐标就是[ax ,ay, az] ,因为向量其实是可以平移的,平移之后也还是和原向量相等,所以方便研究我们就都把向量起点挪到坐标原点,那么其实既然起点是固定的,n维坐标空间中向量也可以抽象为就用终点来表示,所以终点坐标就是向量坐标

这里我们也给出向量模长的计算公式:各分量平方后相加,然后开根号,很简单的计算

进一步补充思考:

前面只是站在纯数学、纯线性代数定理角度去说了下向量,但是,还是那句话,数学,只是工具,所以我们还是要放在实际工程运用中去看待它,首先,在实际运用中,向量往往是某种实际事务的抽象,或者说编码,取决于具体的任务,比如我们要讨论的transformer里,就是对字词的编码和抽象(词向量),为什么需要这样呢,因为碳基生物(人类)的语言和硅基生物(计算机/AI)的语言存在着天然的鸿沟屏障,需要一个进行数字化的桥梁媒介,所以要将人类语言编码为数字符号的形式,也就是向量,计算机才能去处理

那么,既然实际工程运用中,向量是某事物的抽象,我们如何去看待向量的维度呢,在前面我们说过,向量的这些个维度,或者说分量,是其在坐标空间中的坐标,但是我们不能单纯的这样理解,因为不管是向量、还是我们画出来的空间坐标系等,其实都是一种抽象的数学模型,我们把向量放到坐标系里面,是为了更好地地去研究和讨论它,使其具有“几何意义”,然后再将我们的结论推广到高维度的空间(大于3维),因为3维是我们人类能画出的极限,在高维度画不出来了,所以大家学的时候要借助坐标系,但是不能被坐标系捆绑,而是站在更高的角度去看。

在实际工程运用中,我的理解是:向量的某个维度,应该是对该事物某方面、某种特征的描述和表征。

比如,举个最简单的例子,我们从4个维度去描述一个人,那人就可以抽象为(身高、体重、年龄、性别)这样的一个四维向量,每个维度都代表了这个人的某特征,带有这个人的某项信息。 如果维度越多,比如还是拿人来举例子,我们再多加入职业、性格、学历等等之类的维度,那么对这个人的描述就会越具体和全面、信息越丰富。那么类比过来,在处理语言的模型如transformer中,是将字词编码抽象为向量,词向量的各个维度是啥呢,大家类比一下凭直觉去想,应该就是一些语义信息,并且向量维度越高,能够编码进去的语义信息就越丰富、越复杂,也就提升了处理复杂任务的潜力,所以我们可以看到transformer中的词向量维度都很高,当然,你要问我具体是啥,那是超高纬度的机器的语言,以我3维碳基生物的大脑确实也是没法进行细微颗粒度的描述,大家就像上面这样宏观的去理解即可。

向量的运算:

向量加法规则:

毕竟现在有了方向了,和普通的数相加就不一样了,这里我们用最简单的二维的做例子讲,推广到n维也是一样的规则。

我们看下图,就是向量x和向量y相加,下面这种起点放一块的向量的相加符合平行四边形法则,也就是这两个向量作为邻边,可以确定一个平行四边形,相加后得到的向量就是这个平行四边形的对角线,从坐标看的话就更简单了,其实就是对应维度的分量相加即可,x的第一个维度分量加y的第一维分量,x的第二维度分量加y的第二维度分量......以此类推。

数乘运算规则:

数乘运算很简单,顾名思义就是向量乘以一个数,运算规则呢就是给向量的每一个分量都乘上这个数,简单举个二维例子,不赘述:

向量的乘法:

其实向量涉及到两种乘法,最常见的就是点乘,又叫点积(因为运算符号是个点),学术点叫内积,还有就是叉乘(因为运算符号是个x),学术点又叫外积,外积我们用不上不管了,简单讲讲要用到的内积,也很简单,我们一笔带过,一看就懂:

简单说的话,就是两个向量对应位置的分量相乘之后再相加,所以得到的结果其实是一个数。这里先知道怎么算就好,具体两个向量相乘还能带来啥我们下面再讨论。

向量的相关度度量(余弦相似度):

有时候我们需要去计算两个向量的相似度,或者说关联程度。向量的相似度或者说相关度的度量有多种度量方法,比如曼哈顿距离、欧氏距离、切比雪夫距离等等,但是前面说的,我也没咋看,跟我们之后要讨论也无关所以我们就忽略,我们这里看一下余弦相似度。

首先,正如前面所介绍的,不同于一般的数只需要比较大小即可比评判二者的相近程度,向量是一个既有大小,又有方向的量,所以,判断两个向量是否相似,不仅要看大小(也就是向量模长),更要看二者的方向,所以我们引入了余弦相似度这一度量手段:

向量的余弦相似度(Cosine Similarity) 是衡量两个向量 “方向相似性” 的核心指标,本质是通过计算两个向量夹角的余弦值来判断它们的方向接近程度 —— 余弦值越接近 1(夹角为0),向量方向越一致,那么他们相关度越高;越接近 - 1,方向越相反(cos180度为-1),相关度越低,但是要注意,相反并不是无关,而是负相关,那么什么是无关呢,两个向量垂直(正交),那么这两个向量无关,没有方向上的关联。

可以看到计算公式,其实就是用两个向量的内积(之前介绍过,对应分量相乘后相加),然后除以两个向量的模长的乘积,其实这就是什么呢,简单来说,余弦相似度其实就是归一化(同时除以了二者的模长)的向量内积。

这个东西在AI领域被使用到的也很多,比如:

  • 人脸识别中,讲人脸图像编码成向量,将当前人脸图像数据与数据库中的所有人脸向量比较,也就是计算相似度,有相似度超过一定阈值的,说明识别成功
  • 推荐系统中,将用户以及行为习惯抽象编码为向量,然后度量这些向量的相似度来分析用户之间的相似性,从而进行分析推荐
  • 以及我们后面会说的transform中的注意力机制同样会用到

进一步思考:Why?为什么这么多种度量,偏偏选择余弦相似度用的更多,为啥可以不考虑模长,模长不是同样含有信息量吗? 举一个类比的例子,现在有一张吴彦祖的照片,这张照片里的“他”,和他本人的真实大小自然是差了很多,而我,身高体型都和他差不多,那我能说自己比吴彦祖的相片更像他吗,当然不可能,也就是说什么呢,相比起尺寸大小,其他信息更加关键重要,放在向量也是一样,向量的大小或者说模长自然也是含有一定信息量的,但是,向量的这么多个维度中蕴含的方向上的,或者说空间上的信息,无疑是更多的。

向量空间的基

也就是说,其实在坐标系中,向量都是各个基向量的线性组合(也就是基向量乘以一个数拉伸/缩放后相加,乘和加的动作,都是线性的)。

像我们最熟悉的三维坐标空间中的基向量,就是上面图中看到过的i,j,k这三个向量,模长为1,各自指向各自坐标轴的正方向。N维空间中的就一般写作e1,e2,e3....en,但是其实无所谓, 只是起个名字。

同时,为了方便运算,我们通常选择的都是相互正交(也就是相互垂直)的基向量,这样的话每个向量投影到基向量方向上的投影长度就是这个向量在这个维度上的分量的大小,这样子计算向量的模长也可以变得很简单(上文介绍过),各分量平方后相加然后开根号,其实本质就是勾股定理在二维以上空间的推广(因为坐标轴都是相互垂直的,所以算向量长度就会变成算直角三角形斜边长:

我们常见的二维、三维坐标系里也是这样处理的,这样的基底又叫标准正交基(相互正交且模长为1) ,当然了,其实理论上这个基底可以不用正交(相互垂直),但是没必要给自己找麻烦。

那么,既然向量空间中的所有向量,都可以由这些基向量线性组合而来,那我们是否可以这样思考,这个空间,或者说这个“世界”,就是被这组基决定的,可以被它表征出来,这个基,顾名思义,就是这个世界的基础,是造物者,就像变形金刚里那个火种源魔方。这个世界的所有东西(向量的世界只有向量),都有这些基组成,就像计算机世界里都是0和1二进制构成,人都是由4个DNA符号组成一样。

矩阵与矩阵乘法

矩阵定义:

说完了向量,我们再来说另一个必不可少的工具,矩阵,什么是矩阵呢,顾名思义就是由数构成的矩形阵列:

我们可以看出,其实矩阵也可以理解为,向量的集合,比如上面这个,就可以看作是m个n维行向量(横着的一行n数),或者是n个m维的列向量(竖着的一列m个数),所以,其实向量也是一种特殊的矩阵,一个行向量其实就是行矩阵,列向量就是列矩阵,然后行、列数量,也就是m = n的矩阵我们就叫方阵。

矩阵乘法:

矩阵同样也是可以相乘的,只不过不是每个矩阵都能相乘,需要能“匹配”上才行,我们先从一个简单的小例子看下具体怎么乘,然后再说定理,如下面组图就是两个2*2的矩阵做乘法的过程:

所以,简单来说,矩阵乘法的运算规则就是用前面矩阵的某一行行向量,去与后面矩阵的每一列向量相乘,然后得到结果矩阵的某一行,依次这样前面矩阵的所有行去乘后面矩阵的每一列得到最终的结果矩阵,写成数学公式定义的形式就是下面这样,公式其实很简单,只是不如看图片例子来的直观:

好了,说了怎么乘,现在要引出前面留下的问题,也就是什么样的两个矩阵才能匹配得上进行相乘呢,其实很简单,就是前面矩阵的列数要等于后面矩阵的行数,这也很好理解,因为根据前面所说的,实际做乘法的过程中是前面矩阵的行向量去乘后方矩阵列向量,那什么样的向量才能做内积,当然要维度一致才能玩得到一块,所以需要前面的列数等于后面的行数。

也就是说,需要mn的矩阵和nk的矩阵才能相乘,m k是多少无所谓,但是这个n必须匹配上,并且得到的结果矩阵的大小是:(前面矩阵行数)(后面矩阵列数),用上面的mn和nk的例子说的话最终结果就是mk的矩阵(简单记忆就是把中间维度消掉),看两个简单的例子。

矩阵乘法的一些性质:

  • 满足结合律:(AB)C=A(BC),三个矩阵相乘,顺序不影响结果(先乘前两个或后两个,结果一致)
  • 满足分配律:A(B+C)=AB+AC, (A+B)C=AC+BC,分配律就跟普通的数的乘法一致,但是还是那句话,能相乘的需要维度上能匹配得上
  • 不满足交换律:一般情况下,AB≠BA,因为首先,交换之后前后维度可能就匹配不上了,没法相乘,除非是方阵,就算能匹配上,也只是偶尔碰巧可能乘出来相等,但是不具备一般规律性

矩阵转置:

这里再提一嘴矩阵的另一种操作叫做转置,这个其实是个很简单很好理解的操作,虽然简单,但是被应用到的时候其实挺多的。

矩阵的转置(Transpose) 是线性代数中最基础的矩阵操作之一,本质是将矩阵的 “行” 与 “列” 互换位置,说的通俗点,就是把当前矩阵,调个个儿,转置后的矩阵,行列维度会交换,也就是mn的矩阵会变成nm的。

数学定义如下:

不得不说,数学定义就是有一种把简单的东西说得很复杂且很催眠的魅力在里面,还是那句话,千言万语不如看一个例子:

大家一看就明白了,就这啊,不就是把以前的第i行竖过来变成现在的第i列了么,又或者说把以前的第i列都横过来了变成现在的第i行。

这个东西的用处是啥呢,我们举个最简单的用途,我们前面不是说两个矩阵相乘要匹配么,那我两个32的矩阵肯定是乘不到一块去了,那我把其中一个转置一下,比如后面的变成23的,就能乘到一块了,当然,这种例子只是为了乘而乘,算而算进行的变换,感觉很鸡肋,但是到后面实际说到工程应用,到说注意力机制的时候就变得有用了。

线性变换

大家在学习人工智能相关的各种知识、各种经典的模型架构的时候,肯定是绕不开这个东西的,不管你是具体去处理什么任务、什么场景,大语言模型也好,CV的也好,哪怕你只是很浅的读过一些相关的文章,没有深入去系统性地学习,肯定脑海里会有类似的语句的印象:什么xxx东西经过线形层处理后,或者xxx经过xxx线性变换之后,亦或者是xx在经过xx线性映射(投影)之后,亦或者是直接在模型的架构图上画一个框,里面写上“Linear”等等......可能不一定完全就是准确的原文描述,但是大家肯定是见过类似的语句的,我说实话我看到这些话的时候,我只觉得,高大上,不明觉厉,但是说是话不懂是啥。

但是这些其实就是线性变换了,我们将要探讨的transformer中也充满了它的身影。以前学校里学线性代数的时候,说实话我完全不知道啥叫线性变换,不知道为啥叫这个名字,只知道算就完事了,但是通过学习transformer的时候反过来又重新学了一遍之后,觉得这玩意挺有意思的,也很想和大家分享下。

线性变换(Linear transformation),是不是这名字看起来就跟transformer很有关系,当然可能命名也和这个无关,只是一种猜测。

我们经常见到的,都是说:“对某某向量进行线性变换”,所以我们第一反应会觉得,线性变换,变的是具体的被处理的这个向量,但是其实线性变换,变的是空间本身

矩阵乘法和线性变换

其实,先前我们已经接触过线性变换了,作用于向量(特殊的矩阵)的矩阵乘法就是线性变换,给向量乘上一个矩阵,就是对其做线性变换,在看一些严谨但是枯燥的理论推导和证明之前,我们先来看几个线性变换的例子,先给大家感受和理解到线性变换是空间的变换这一点。

首先我们看一个最简单的变换:

这里,我们给α向量左乘一个矩阵A,就相当于对这个α向量做了个A变换,然后得到的了β向量,这是数学上的描述,我们来看几何上,把这个向量画在坐标系中,其实也可以看出来,从(a,b)到(2a,b),β向量就是α向量的横坐标变成原来的两倍了,而纵坐标不变,所以A变换就是做了这个事,也就是横向的拉伸。

当然,我们想对空间变换建立一个直观的认识的话,我们不能光看一个向量,我们还得再多找一些向量,比如我们就来看这个正方形吧,那在二维平面中,我们这个正方形是由什么组成的呢,是一堆的点,但是在向量空间中,我们之前有提到过,我们不把点看作点,而是看作以原点为起点,这个点为终点的向量,所以这个正方形其实就是一堆向量聚集在一起形成的,那么,我们对这堆向量都进行A变换呢,那就是把所有向量横坐标翻倍,那么这个正方形就会被拉伸为长方形。所以我们常说A这个矩阵对应的就是一个拉伸变换。

然后我们再看另一个比较有意思的矩阵和变换,下面这个B矩阵,他的作用就是让某个向量逆时针旋转θ度,比如还是下图中α向量,左乘B矩阵就是让其如图进行旋转,再次用刚才那个正方形距离的话就是整个图形的旋转。这里为什么这个B矩阵可以带来旋转某个角度的效果就暂时不进行数学计算推导了,等后面说另一个原理的时候一下就懂了。

然后其实从线性变换的角度也可以反过来看为什么交换律不适用,ABα,BAα这两个矩阵乘法,其实本质上就是两种变换对于向量α(不特指某个向量,而是任意向量)执行的顺序先后不同,我们都知道了A矩阵是拉伸2倍,B矩阵是逆时针旋转45度,那么我们先拉伸后旋转和先旋转后拉伸带来的效果可以看到是截然不同的,这就好比,你穿鞋穿袜子都是对你的脚做变换,但是先穿袜子后穿鞋,和先穿鞋后穿袜子,那是完全不一样的。所以从变换的几何意义角度来看,矩阵乘法也是不可交换的。

我们再来看另一个比较有意思的线性变换,比如A这个矩阵是四个1,然后对某个向量做这个A变换,可以看到,得到的结果向量,x,y轴两个分量都是相等的,也就是y=x,那么,再一次拿正方形例子距离,如果对正方形中所有向量进行这个变换,所有的点,都会被“压缩”到y=x这条线上,本来是个面,现在被变成线了,大家如果看过三体的话,里面有一个东西叫降维打击,那这个其实就是一个降维打击了,二维变成1维了。

其实也很好理解,原本是由x,y两个维度的,但是现在y和x相等了,变成一个东西了,相当于损失掉了一个维度,也就变成一维了。

生动的例子先看到这,然后我们还是需要看一定定理证明,因为大家或许会有和我一样的疑问,那么,是不是每个矩阵乘法,都是线性变换呢,这里先给出结论,然后再看证明:

在有限维向量空间中,矩阵乘法本质上是线性变换的 “数值表示” —— 每个矩阵都对应一个线性变换,且每个线性变换都可通过矩阵乘法表示。

那么,怎么证明呢,线性变换要满足两个性质,可加性和数乘性,以此来证明其线性:

好的,那么看到这里,大家或许还是觉得有疑问,因为看起来,变换好像还是针对向量来变的,为什么会说是空间的呢,我们再来看一个例子,然后再结合上之前的知识大家就能理解了。

我们先考虑这样一个线性变换:对向量(2,2)做线性变换(下面图中我们写成列向量竖着的形式),变换后得到的向量是(4,0)

我们先把(2,2)在坐标系中画出来,这里我还画了两个橘黄色的,长度为1的向量,这两个是什么呢,这就是我们之前说过的,标准正交基,我们分别记作i,j向量,i就是我们常说的x轴上的,j就是y轴上的基向量。

好的,接下来我们来扭曲空间,直接开始拉伸和旋转整个坐标系,直至这个(2,2)向量变到原坐标系中(4,0)的位置:变换后的向量是图中红色的向量,然后可以看到,由于是整个空间的变换,所以两个基向量也被跟着旋转拉伸了,变化后的基向量是两个紫色的向量。

这里我们注意看,这两个紫色的基向量的坐标,i变为(1,-1),j变为了(1,1), 这两个向量坐标,是不是看起来很眼熟啊,我们把这个线性变换拿回来看,这下看懂了,其实这个变换矩阵的两个列向量,分别就是变换后的两个基向量,在原坐标空间中对应的坐标。

这下我们再回过头来想,其实一切就都能连起来,都说得通了,我们之前在讲基向量的时候说过,向量空间中的所有向量,都是由这些基向量线性组合而来的,说人话就是每个向量,都是各个维度对应的基向量乘以向量在这个维度的分量然后做向量加法生生成的,拿二维来说就是ai+bj=(a,b),比如上面这个(2,2)就是2i+2j得到的,并且,由于是线性变换,所以这样的基向量的组合关系,在空间变换后,在新的空间仍然成立,仍然没改变,也就是,(变换后的目标向量)=2*(变换后的i向量)+2*(变换后的j向量),仍然成立。

那么这样的话,既然正如之前我们说到过的,这些基向量就是一个向量空间,一个“世界”的基础,这个向量空间可以被这些基向量所代表或者说表征出来,那么基向量的变换,实际上就是整个空间变化的外在显示,基向量变了之后,整个向量空间中的所有向量就都会跟随基向量一起改变,因为它们是由基向量的某种线性组合而来这一事实依然没有改变。

所以说,一个不会使得向量空间维度改变的线性变换,其必然,也肯定只能是变换后的基向量在原坐标系下的坐标表示的列向量组成的方阵,为啥一定是方阵呢,因为N维向量空间,一定有N个基,每个基也是N维的,所以它们组成的必然是N*N的方阵。

但是,需要补充的是,线性变换并不说是一定只能是方阵,也只能是进行维度不变的变换,线性变换同样可以把维度进行变换,我们都知道,矩阵乘法,乘出来的结果,其行维度是由前方矩阵决定的,列维度是后方矩阵决定的,所以,举两个例子:

  1. 如果说,我们用一个24的矩阵去乘了一个41的行维度为4的向量,得到的是一个2*1的向量,那么这个向量就因为这个线性变换降维了,个人理解,这应该就是我们在有的文章里看到的比较“优雅的”学术描述:将高维向量投影到低维。
  2. 如果说,我们用一个42的矩阵去乘了一个21的行维度为2的向量,得到的是一个4*1的向量,那么这个向量就因为这个线性变换升维了,个人理解,这应该就是我们在有的文章里看到的比较“优雅的”学术描述:将低维向量映射到高维。

思考:这些升降维度的操作有什么用处呢?

  高维到低维投影:降维,为了 “简化” 与 “聚焦”:

  • 核心意义:剔除冗余信息、降低计算成本,同时保留原向量的核心特征。
  • 新空间中向量的含义:代表原高维向量在低维 ,也就是其“关键方向” 上的投影结果,即原向量最核心的信息浓缩,因为向量不可能每个维度都很重要,也会存在侧重,就像我们评估两个人长得像不像主要看五官。
  • 例子:用手机拍 3D 的杯子(3 维空间物体),照片是 2 维平面投影。照片里的 2 维向量,就是杯子在 “拍摄平面” 这个低维空间的表达,保留了杯子的形状、颜色等核心信息,却剔除了深度(3 维中的 z 轴)这个冗余维度,方便我们快速识别杯子。

  低维到高维映射:升维,为了 “拓展” 与 “适配”

  • 核心意义:补充必要的维度信息,让低维向量能适配高维空间的任务(如运算、建模)。
  • 新空间中向量的含义:代表原低维向量在高维空间的 “扩展形态”,新增维度通常是补充的关键属性或适配规则。
  • 例子:把 2 维的 “点坐标”(x,y)升维成 3 维的 “空间点坐标”(x,y,0)。这里的 3 维向量,就是原 2 维点在 3D 空间中的表达 —— 新增的 z 轴维度(值为 0),明确了它在 “高度” 这个维度上的位置,让它能和其他 3D 物体(如 z=5 的球体)一起进行距离计算、碰撞检测等 3D 任务。

这时候我们在回过头来看之前的线性变换的例子就非常直观了,比如之前留的一个伏笔,为啥这个B矩阵代表的变换就是旋转θ度,现在以变换后的基向量视角看的话就很一目了然的,根本不用再去算了,x轴基向量,模长为一,逆时针转θ度,那新坐标肯定就是(cosθ,sinθ),y轴的基向量坐标的变换也是同理。

再看咱们的降维打击,这稍微特殊点,虽然是个方阵,但是还是导致维度变了,为啥变了,也很容易理解了,两个维度的基向量一模一样,重合在一起了,那这样肯定就是二维压成一维了呀,所以维度不变的线性变换的方阵还要保证各基向量是线性无关的。

那么看了这么多证明、定理也好,什么样的变换能叫做线性变换呢,其实很简单,变换能保证原点不变,且各维度的坐标系网格线保持平行且等距分布即可。

比如下面这样的,就能匹配上述描述,就是线性变换,虽然看着很歪很斜:

但是下面这些例子,就不是线性的了,可以看到坐标系不知道被扭成啥样了,坐标网格完全不等距也不平行了:

总的来说,线性变换是一种操纵空间的手段,我个人喜欢把它看作一个传送门,把向量空间看作一个世界的话,这些线性变换就像是平行世界的传送门一样,把其他世界的东西,传送,或者说投影到当前世界(原坐标系) ,就像漫威的多元宇宙里的人来回穿越一样,为什么是说投影到当前世界呢,因为我们研究的时候需要固定在原坐标系中,作为唯一参照,否则没有意义,这也就是为什么,线性变换的结果给我们带来的直觉上的感受是向量本身被变换了,这就是因为我们始终都是在原坐标系下研究的,所以看上去就是各个向量被扭来扭去、变来变去。

这些各种线性变换(Linear transformation),会在Transformer中被多次用到。

简单,却又极其有效的Transformer

众所周知,Transformer是一个处理语言的模型,那么在语言文字处理这块,一定也有其发展史,也有前世今生,我们简单说一下:

首先,处理文字,最原始粗暴的一种方案,把所有词编码为词向量后直接全部丢到全连接神经网络中训练,当甩手掌柜开始炼丹。显然,这样每个词都没有上下文信息且长度只能一一对应(无法处理输入输出序列长度不同的任务,比如翻译),同时神经网络神经元规模会暴增(因为词向量维度都很高,每个维度都会对应一个神经元,比如有5个100维的词向量输入就会有500个神经元)、计算量暴涨,并且对于变长输入的任务也很麻烦,因为输入长度变了的话,神经网络结构也要跟着变,所以是个典型的效果差差,吃力不讨好的模型,所以这样的方案不太行。

那么,就有了进一步的,RNN(循环神经网络),这个架构,简单来说就是,之前不是没有上下文信息吗,也就是每个词向量的计算过程没有其他词参与进来,那么我们就想办法让它参与进来,那我们就把前一个向量的计算结果暂存到一个隐藏层,再与后面的向量结合,一起作为输入进行计算,这样把每次的计算结果都作为下一次计算的输入的一部分,然后一步步往下顺序传递,以此融入上下文信息,但是这样的弊端也很明显,首先,这是个串行计算的结构,因为每一步都依赖上一步的结果,这样会使得算力没法充分发挥,训练和运行缓慢,并且,这样子的上下文信息会随着时间步的增多而逐渐丢失,无法捕捉长期的依赖(长距离上下文),而有的语句恰恰又是距离当前词很远的地方起到的关键作用, 所以RNN仍然有缺陷,虽然后面也针对这些有了改进的模型,但是基本也都过气了,所以就不谈了

那么这也不行,那也不中,那咋办,我们是否能有一种彻底抛弃RNN这种顺序计算,能一眼把信息尽收眼底的新方案呢,小孩子才做选择,成年人全都不要,我直接另起炉灶,重新弄个新的架构单干,就像我们用组件库里的组件,这个也不满足需求,那个库也不太行,那就自己封装一个把,于是有了transformer。

Transformer这一模型架构首次提出于论文《Attention is all you need》中,那这个文章题目大概翻一下意思就是,“注意力,就是你所需要的一切”,不得不说,任何东西可能都还是需要一个引人注目的外壳的,即便是归为Transformer这种现代大语言模型教父,一上来就来了个看着很厉害的标题,告诉你,万能钥匙来了,并且也开门见山强调了架构的核心————注意力机制

arxiv.org/abs/1706.03…

我把论文原文的地址贴在上面。我相信包括我在内的很多人,会觉得读论文是件很费劲、伤神,并且专业的科研人员才去干的事,有的晦涩难懂的论文确实是这样,但是这篇文章,相比于一些又臭又长且晦涩难懂的论文(xue shu la ji)来说,确实可以说是短小精悍,相信大家静下心来看的话是可以看懂的,并且也能确实加深自己的理解,全文只有几页,重点核心内容也就3页左右,其提出的模型结构也很简洁且高效,可能这就是为何其会被奉为新时代“圣经”一般的角色,确实是大道至简,而那些我刷到过的科普博主标题获取其实也真的没写错,Transformer,可能的确是一个“简单到让人困惑的模型”———不过得在你充分理解它了之后。

Transformer结构浅析

下图是论文原文中截取的Transformer的结构图总览:

从上面的图中可以看到,总的来说,transformer可以分为两部分,即左边的Encoder(编码器)部分和右边的Decoder(解码器)部分,这是一个经典的解决Seq2Seq问题的架构,也就是sequence to sequence,说中文就是输入一个序列文本,然后输出另一个序列文本,关键是输入输出的长度是可变的,而不是像我们之前说过的那种直接把序列一个个扔到神经网络中的例子,输入输出序列长度要一致。

这样的seq2seq的架构最典型的应用就是不同语言文字间的翻译,当然其应用领域很广,还有如:

  • 文本摘要:长文本-->短摘要
  • 阅读理解:文章+问题-->答案

那么解码器和编码器分别做什么工作呢:

  • Encoder(编码):把输入转成机器能懂的“代码”,做的是理解的工作
  • Decoder(解码):把机器“代码”转成人能懂的输出,做的是生成的工作

需要注意的是,Transformer并不就是我们现在熟知的大语言模型,现在的大语言模型是由其演化改造而来。总的来说可以分成两个演化分支,一派是Encoder-only,顾名思义就是只由左侧的编码器部分改进而来,另一派就是Decoder-only,也就是只由右侧解码器部分改进而来,既然说到这了就简单也介绍下这两个东西。

Encoder-only:Transformer的编码器本身是强大的“语义理解器”,其核心功能是通过注意力机制捕捉文本中的上下文关联,将输入转为机器可理解的“数字化”语义标识。Encoder-only最著名的一个模型就是BERT,我们把Transformer编码器部分拆出来,增强一下就成了著名的BERT模型,其核心又是就是“低适配成本”,无需为每个NLP(自然语义处理)任务单独搭建模型,只需简单调整就能处理多种任务,真正实现“一次训练多次复用”,很契合互联网快速迭代的节奏。它能应用到如:文本分类任务(包括情感分析、主题分类、垃圾邮件检测等),问答与检索任务(抽取式问答(QA),文本检索/匹配等),序列标注任务(标注出输入文本中的某类东西,比如标出地名、标出不同词性的词,动词名词形容词等)。像美团有把其用在客户的意图识别中,其能分清“霸王别姬”是电影/戏剧,而“霸王茶姬”是奶茶,因此就能知道用户是想看电影还是想喝奶茶。

Decoder-only:大家最耳熟能详的GPT其实就是一个Decoder-only模型,所以这玩意其实是大家的老熟人了,前面说过,编码器主要负责理解,而解码器是生成,这也就是我们会常听到的一种说法,GPT是生成式大语言模型,也就是说它是一个字一个字往外蹦的,每个测只能看到前文,比如写“猫在追老鼠”这句话,预测“鼠”时只能依靠“猫在追老”,这样其实就完全模拟了人的写作逻辑,所以其实它是个因果语言模型,根据前文去预测(说不好听点就是猜,只不过猜的够准) 下一个字,是一个概率性的东西,所以也有人会觉得这玩意不靠谱。

这样也导致了其和BERT这类模型有一个不同点就是,BERT是双向的注意力机制,说大白话就是其能同时看到所有上下文,因为其核心集中在语义理解上,而GPT这种的注意力机制是单向的,它只能看前文,不能“作弊”,然后依赖前文来文本生成。这就是为什么我们看到的解码器中的注意力机制叫Masked Attention,掩码注意力机制,简单说就是加上了把后续上下文遮住这一步。

注意力机制,大家要注意啦~

上面我们提到过,直接把词向量往全连接神经网络(FCN)里扔的话,每个词都没有任何上下文信息,然后进一步地,我们有了循环神经网络(RNN),让每个词能获取到一些上下文信息,但是这类上下文信息比较局限,所以要进一步地找更好的方案。其实大家应该能发现,一切的关注点似乎都指向了上下文(context)这个点,也就是说,对于文字,或者说自然语言处理的任务来说, 如何去让每个字词能够获取、编码、感知到更多、更全、更丰富的上下文语义,或者说语境信息,就成了决定任务执行效果好坏的关键因素,也就是前面提到过的,站在任何词的角度都能一眼把上下文信息尽收眼底的方案·,而Transformer中的注意力机制就是来做这个事情的。

同时仔细观察Transformer的结构也能发现,模型中其余的像词嵌入、位置编码、经典神经网络、残差连接、归一化、softmax等等,大家有接触过一些深度学习相关的都知道其实都是老东西了,而就这个模型来说真正创新的就是注意力这部分的结构,所以我们后面也主要集中在这部分的刨析和思考上。

首先我们先来看看注意力机制这部分的结构图,这里我们直接把论文原文里的图拿出来看,其实整个结构不算复杂,就是一些简单的乘法和缩放,原文的图其实已经描述的很清晰,左边这部分其实就是右边这个图里紫色的这部分的详细结构,也就是单头注意力的结构,而右边的是多头的,其实原理都是一样的,只不过多头的是拆分成了多个头来进行计算,为了方便解释和方便初学理解我们就按单头的先来讲。

说一千道一万不如一个例子,所以我们还是先举一个例子,带着大家按着这个结构来走一遍计算的全流程,然后我们再一点点拆开来研究每一步具体都在干啥,这里面我们举一个三个字的输入,然后每个输入的词向量维度是3,原文维度是150的高纬度向量,正如开篇讲向量维度时提到的,维度越高,能够编码进去的语义信息就越丰富、越复杂,也就提升了处理复杂任务的潜力,但是这里为了方便就拿低维度的向量来过一遍计算,不过原理是一样的,所以并不影响。

同时,这里我们不展开去讲词嵌入和位置编码这部分,这里我们就假设已经做了词嵌入,将人类语言进行了向量化、数字化,并且已经做了位置编码使得每个词向量拥有了其在上下文中的位置信息,然后得到了即将作为注意力机制的输入向量。

这里原文里面还稍微少了点东西,那就是Q,K,V是怎么来的,这里我们在图里给它自己补上,让整个流程完整,Q,K,V,都是由输入的词向量,也就是上一步我们进行了词嵌入和位置编码之后得到的产物,乘Wk,Wq,Wv矩阵以后得来的。

然后我们就来进行第一步的计算,也就是先把输入给乘上这三个向量,然后把Q,K,V都给算出来,这里为了方便看把属于某个词的都给弄成同样的颜色,方便大家看,这一步就是简单的矩阵乘法,和我们先前讲的一样,就是用前面矩阵的每一行的行向量去和后面矩阵每一列的列向量做向量点积后得到结果,不过要注意这里是把W矩阵左乘在输入左侧:

好的,在得到我们的Q,K,V之后我们接着进行下一步,计算Q矩阵和K矩阵转置的乘积,矩阵乘法之后再进行一个缩放,也就是除以根号3(根号下维度数),之后再经过softmax处理,其实这一步主要是这个矩阵乘法,其他两步操作都是为了利好训练,所以这里在理解时去暂时忽略掉这两步也是可以的,不过为了完整走一遍流程这里还是加上。其实可以发现,这个矩阵乘法的目的就是让“猫”“追”“狗”这三个词对应的向量分别和其他词(包括自身)的向量去相乘,只不过写成了矩阵运算的形式。

上述计算后得到的结果矩阵其实就是我们的“注意力分数了“,结果矩阵中每一行的数的含义,都是从当前词的角度出发去看其他词,相对于自己的”注意力“ ,或者说语义上的关联程度的大小,下面这条横线的颜色就代表了是跟这个颜色对应的那个字词去进行关联得到的注意力分数,方便大家观看的时候追踪数据。

大家或许注意到我还漏了一个方框,就是这个粉色的Mask,这里面后面这个opt.的意思就是说这个模块是可选的,我们知道,Transfomer的编码器和解码器部分都有注意力模块,而只有解码器部分有这个掩码。我们之前说过,解码器部分负责的是生成任务,这部分也是我们现在GPT这种生成式大语言模型的前身,而生成任务中,是和我们人类说话方式一样的,是根据前面已有的词一个一个生成的,也就是只有已生成词的信息,看不到后面的,所以我们训练的时候,也需要模拟这一情况,所以我们得先把对于后面词的注意力分数遮住,就好比我们做作业时,我们不会一来就看答案,而是先自己做,然后再对答案,这就是掩码干的事。

好了,现在我们就差最后一步了,也就是把上面得到的这个和咱们的V矩阵做乘法,得到最终的结果,一组“新”的猫追狗词向量构成的矩阵。

如果说上面矩阵运算的形式看着有点难受的话,我们就把矩阵拆开来再过一遍,把大象装冰箱,总共分三步:

第一步:计算Q,K,V,我们之前的图中是矩阵计算,但是其实我们知道,输入矩阵其实是由输入的词向量组成的一个矩阵,其每一列都是一个词向量,所以在输入矩阵X左乘Wq,Wk,Wv矩阵,就相当于每个向量左边乘上了这三个矩阵,所以可以画作下图:

第二步:词向量之间两两相乘,猫和猫,猫和追,猫和狗,追和猫,追和追,追和狗.......然后就可以得出每个词的视角下,相对于其他词的注意力分数,注意是每一个词的Q去乘其他所有的V,然后进行缩放和softmax,图中省略不画。

第三步:按照上一步计算出的注意力分数,将所有向量以对应的注意力分数为权重进行加权求和,得到的新的向量就表示,也就是在某词的视角下,按照和他相对注意力分数大小作为权重,把每个词的词V向量都加到了一起,这样就是最终这个词集成了上下文信息后的产物。

然后再看我们论文原文中的注意力的核心公式就会发现,是完全一致的:

可以看到,其实每一步都是在针对向量进行的运算,只不过把它们写成了矩阵的形式,也就是说其实这些矩阵是上述向量运算处理的批量化执行的算子,这样可以充分地发挥GPU的算力,因为我们知道,GPU本身就是做图形计算的东西,而图像本身就是r,g,b三通道的数值矩阵,所以做这种大规模的矩阵运算有天然的优势,这也就是为何现在图形计算卡如此昂贵,大模型、人工智能本质还是算力和数据驱动的科学,所以自然英伟达股票也嘎嘎涨。

对于注意力机制的一些疑问和思考

看到这好像觉得注意力机制好像是懂了,理解了,绝大多数比较好的教程其实也是讲到这个程度,但是细想之后我还是有几个没法说服自己的地方,还是有几个问题卡住了我还是没法看到最本质的东西,这里我也分享给大家,还有自己对这些问题的思考:

问题一:为何做向量点积,而不是别的运算

上述第二步这里计算注意力分数的时候,为什么是把各个词向量去进行两两相乘,而不是相加亦或者是别的操作?选择做乘法一定是有其意义在的,那么到底是啥?

其实这个问题的答案,我们之前已经学过了,我们回忆下哪里有过向量乘法,答案就是余弦相似度,我们之前说过,余弦相似度,其实就是归一化的向量内积。这样看的话其实就合理了,首先,所谓注意力分数,其实换句话说就是从某个词的角度出发,去计算其他的词在上下文中与自己的关联程度,而如何来表征两个向量化之后的词的关联程度呢,使用余弦相似度来表征他们的相关程度。

那么可能有的同学还会有疑问,那人家这里余弦相似度是做了归一化,除了模长的,你这里注意力计算的时候似乎并没有?

首先,归一化的作用其实是把两个向量的关联程度分数限定在了[-1,1]这个区间内,结合我们最终的目的来看,我们最终是要使用这个注意力分数来作为权重值来使用的,从对语言的理解来分析,我们肯定是希望在上下文语义中关联大的词相比于其他关联小的词有更大的权重的,这样才说得通,所以其实我们更关注的是词与词之间相对关系,也就是说即便这里不作归一化,其结果的相对大小依然能体现出我们想要的东西,这就好比我们是百分制还是10分制,100分和90分的对比,换做用10分和9分来进行对比同样可以。并且我们其实在这之后做了缩放(避免乘法导致某些分数过大造成梯度消失),然后进行了softMax处理,将这个相对的分数大小转为了概率分布(分数大的映射到概率上也更大),而概率的取值其实就限定在1以内了,所以某种程度上,我们也做了我们的归一化。

问题二:关于Q,K,V以及Wq,Wk,Wv的疑惑

在第二步中,明明是Q和K相乘,但是为什么在讲的时候还是说是各个词去进行两两相乘,并且还是说的猫和猫,猫和追,猫和狗,追和猫,追和追,追和狗相乘......如果说是原始的输入尚可理解,因为那确实就是把猫追狗这三个字向量化了,但是明明Q和K已经是它们各自左乘了一个矩阵之后的产物了,并且还是左乘的不同的矩阵,经过了那么大量的计算之后,你还说这些个向量是“猫追狗”这三个字未免让人难以信服。

那么要搞清这个问题,其实就是拆解为要搞清楚以下两个问题:左乘Wq,Wk,Wv矩阵是干啥?以及Q,K,V是啥?

在前面的讲解中我们知道了,在输入矩阵X左侧分别乘上Wq,Wk,Wv实际上相当于分别对每个词向量左乘了这三个矩阵,那么在更早之前我们其实也说到过,给一个向量左乘一个矩阵,实际上是对这个向量做了一次线性变换,或者有的地方称之为线性投影。

那么再代入回我们的例子,其实这一步就是对“猫追狗”这三个字分别做了三个不同的线性变换,我们之前说过,线性变换是空间变换,所以就是把这三个字对应的向量变到了不同的空间上去,并让它们展现出了在语义上不同的一面,起到不同的作用,然后又投影放到同一坐标系下进行运算。

所以,的确,即便是经过了这么大一个矩阵乘法之后,“猫追狗”这三向量还是对应“猫追狗”,他们只不过是被线性变换了,展示出不同面的“猫追狗”,那么具体起了啥作用呢,展示的是哪一面呢,那就来看看它们的产物各自的定义,也就是我们在很多地方都能看到的关于Q,K,V的讲解:

  • Q向量:Query,查询向量,负责“查询需求”,也就是“我”(当前所处视角的词向量),要找什么
  • K向量:Key,也就是键向量,类比到我们的编程语言里的key,Key一般就是作为索引标识符,所以其是负责“匹配”上面的需求的,也就是向“别人”(其他向量的Q向量)介绍自己
  • V向量:Value,顾名思义,价值向量,那就是负责提供和价值,那么这里的价值是什么呢,个人理解,在语言处理任务中自然就是其对应的词向量(线性变换前的)所能提供给其他向量作参考的语境上下文,或者说语义信息。

那么,理解到这里,再把所有东西串起来的话就清晰一些了,首先输入经过词嵌入和位置编码生成了最终的词向量输入,然后把输入的词向量经过三个不同的的线性变换,得到各个向量的q,k,v三个向量,一个负责找,一个负责展示自己,一个负责提供价值。

那么根据上文我们知道,我们期望的的是语义上关系更加紧密的词的q和k相乘后得到的注意力分数更高,从而在价值加权时给与对应的词更高的权重,那么我们知道了向量内积的含义是余弦相似度之后,从几何上理解就是我们希望的是,存在两个线性变换Wq,Wk,在词向量经过这两个线性变换之后,语义上关联强烈的词对应的Q和K向量能在他们所处的高维空间我们确定的那个坐标系中,呈现出一定趋势的聚拢,这样他们之间夹角小,余弦相似度才会高,那么注意力分数才会大。

问题来了,要怎么进行这两个线性变换才能做到这一点呢,Wq,Wk要弄成啥样才可以呢,答案是,不知道,作者也不知道,因为我们没法精准地解读和研究那么高维度的空间,更没法做到人为干预调控,不知道就对了,那么没法显式求解的东西怎么办呢,当然是交给万能的神经网络,所以Wq,Wk,Wv这三个矩阵实际上是训练出来的,它们是模型的一部分参数,所以这里所做的工作就是通过神经网络不断地进行前向传播计算损失和反向传播梯度下降调整参数,不断地调整这三个线性变换,使得这三个线性变换中各自蕴含了大量的训练数据中的语义信息,进而不断调整每个词向量对应的Q,K,V向量在我们固定坐标系中的位置,直到到达某一合适的位置,使得每个词可以很好地 “注意” 到和它语义上关系紧密的其他词(词之间对应Q,K的聚拢),并且每个词也能很好地去对外提供自己在上下文语义信息中的价值或者说语义线索(V向量作用),使得在被根据注意力加权之后能使得来收集自己信息的词能收集到最有价值的语义信息,从而使得模型很好地工作。

当然你要问我,这个合适的位置是哪里,这三个线性变换具体是怎么变的,具体发生了啥变化,这个我确实不得而知,因为人类确实没办法像研究3维空间那样精准地去研究和解读高维空间,我们即画不出来,也想象不出来,更无法显式地去求解,这是机器的世界了,这些数,它能“理解”。

这里我还是举个例子来给大家能够有个更直观的理解,我们假设两个比较极端点的情况,第一种,我们的训练数据有且只有“苹果是种好水果”,第二种是训练数据有且只有“我爱用苹果手机”,那么这里我们最终训练完毕后,Q,K向量的分布会是啥样的呢,假设我们从“苹果”视角出发:

那么根据我们人类对于自己语言的理解,我们其实可以推测:

在第一个语境下,“苹果”肯定是和“水果”靠的更近的,而“好”是用来形容“苹果”的,相对也会比较近。

而在第二个语境下,虽然同样是“苹果”,但是含义完全变了,这的“苹果”指的是手机,所以距离手机会更加,而“爱用”是说“我”爱用,所以他们两应该会挨得近些,然后“爱用”是说关于手机的动作,所以相对来说也比较靠近“手机”些。

注意:只是一个帮助大家能更直观、更生动地理解的例子,并不具备准确性,同时,超高维度空间的运作和低维空间不同,我们也没法去精确地解读和透析。

问题三:我们为何能解读出它们是Query, Key 和Value

为什么同样的词向量,在经过了三个同样维度的线性变换之后,他们的作用和性质就变了,然后,既然这些线性变换,我们前面说过,是无法精准解读的,也就是我们实际是不清楚具体怎么变,那作者又是怎么知道这这些变换的产物,也就是这三种向量分别能代表什么,有什么作用呢,既然我们并不清楚具体的变换,他怎么知道得到的产物是Q(Query),K(Key),V(Value),并且还以此为基础继续往后设计模型,让其确实很好地工作,并能达到自己的目的呢?这似乎前后矛盾。

但是这里其实是我自己思考的方向错了,我把问题和答案搞反了,我们思维惯性上习惯了从输入一路往前看到输出的这样一个前向的过程,总觉得输出才是答案,但是我们忘了我们是在训练视角。所以实际上,生成Q,K,V的方式,也就是他们对应的Wq,Wk,Wv,这三个线性变换矩阵,其实才是我们要找的答案,要理解这个,我们首先要理解这些AI模型是什么:

一切的起点其实都源于一个认知观点,那就是这个世界上的所有逻辑和知识,都可以用一个函数(给一些输入,会给你相应输出的映射规则)来表示,也就是Functions describe the world,我们只需要将现实世界的东西抽象为符号,然后再设置好一些运算规则,也就是函数,最后,算出的结果反过来解释现实世界就可以了,就比如输入质量与加速度,根据牛顿第三定律就可以算出物体受力,这其实就是人工智能早期的思路符号主义。

其实这些人工智能模型,就是一个函数(有输入有输出),但是这个函数里面有大量的未知的量,也就是模型的参数,我们没法确定,我们需要去想办法求解,这其实本质上就是一个解方程的过程,比如最简单的一元函数y=f(x)=ax+1,求a,这里面就a一个未知数,一个参数,然后我们通过知道几组(y,x),也就是输入输出,比如x=1,y=3,代入就能解出a=2。

但是不同于一些简单的任务,我们人类可以明确地写出函数运算规则 ,我们需要模型能进行更复杂的任务,就比如图像识别任务,对人类来说可能很简单,但是要让一段计算机程序去执行,就是要让其去拟合非常非常复杂的函数,也就是未知数的个数和他的次幂,都会很高, 这样的复杂函数我们显然是无法人工求出精确的解析解的,所以我们就需要借助深度学习,然后使用庞大的算力和足够多的答案(也就是训练数据)来一点一点的“连蒙带猜”把这个方程里的未知数都给猜出来,来暴力破解这个方程。就好比小时候父母给电脑设了密码不让我打游戏,而我其实对于计算机知识、数学、密码学等完全不懂,但是趁父母不在家,我有足够的时间和耐性以及无数次尝试的机会,我就一点一点的试,把所有可能性都遍历一遍,最终把这个密码找出来。

当然我们训练出来的其实并不是精确的解,我们只是在无限地去接近它,然后让准确率足够的高,所以本质上其实是个近似解,也就是我们其实在做的,是去拟合某个未知的函数,所以有时候我们才会听到过拟合这种说法。

好了,说到这了,再回到我们之前的问题,既然是解方程,那其实这些未知数参数,本身其实才是我们训练所要找的答案,也就是说,这三个Q,K,V向量,我们在事前就已经定好了,他们就是各自要去发挥query key value的作用的,只是我们不能明确地知道应该怎么样做变换才让他们发挥出这个作用。但是,只要我们想,它们就一定会是这样,为什么,Because they are trained to do that,怎么理解呢,我们其实都知道训练的过程,首先我们是知道模型的正确输出是什么的,那是我们的目标,而这个目标已经已经明确订好了,然后我们通过前向传播得到一个值,拿这个值和真实值根据损失函数计算损失,然后通过梯度下降反向传播回来调整所有的被训练的参数,而这个模型巧妙的地方就在于,这三个线性变换矩阵Wq,Wk,Wv是训练出来的,也就是说,除非它们能够把输入的词向量,经过自己变换后,Q能起到去找自己想要的,K能起到键的作用也就是向别人介绍自己,然后V能把自己的重要线索提供出去,否则最终得到的结果都会和预期相距甚远,损失会很高,那么就还会被接着“调教”,直到它们乖乖听话,模型才收敛,举个很好理解的例子,这就好比,领导(此处领导为虚构人物,如有雷同纯属巧合)找我说,小王啊,你必须要狠狠地加班,不然我就不给你发年终奖,我不照做的话,我的损失就大了,所以我只能照做,一定能成为领导心目中的样子,而计算机和程序实际上则比我更听话,所以其实这一切都是一开始已经确定好的,我们已经想好了Q就是用来查,K就是键而V就是价值体现,它们非得那样不可,所有的计算都已经设计好了,整个模型是一个紧密的系统,只有他们发挥了它们该发挥的作用,整个系统才能正常运作(组织和领导已经决定了,你们自己看着办吧)。我们不关心也不干预其具体内部的东西,不会去手动控制,也不去试图解读这三个线性变换,但是我们用最终目标去对其进行了宏观的干预和管控。

当然这听起来简单,但是实际训练其实是十分复杂的,因为这项科学本质还是算力和数据驱动的科学,训练时如何调参、如何处理数据,才能够让模型训练出更好的效果,都是有很大学问和技巧在里面的,模型的设计只是模型最终效果的一方面,所以经常会听见做算法的同学抱怨说“机器厌学”了。

问题四:Transformer是否是万能的

那这样说下来的话,Transformer岂不是无敌了,那我们人类简直成了上帝,想训练它做什么都可以?那当然是不可能的,要不然人类的科技不就发展到头了。

的确,Transformer在NLP和CV领域都取得了巨大的成功,注意力机制似乎无所不能,但是最近有刷到一篇文章叫《Why Attention Fails》,意思就是为什么注意力机制失败了,文章里有提到,根据实验来看,注意力模块在时间序列任务中几乎不起作用,Transfomer的表现还不如简单的线性模型。

比如预测任务就是一种时序任务,基于历史时间序列数据预测未来的数值。例如,根据过去几年的每日气温数据,预测未来一周的气温;根据过去几个月的股票价格,预测未来一个月的股票价格走势;根据历史电力消耗数据,预测未来某一时刻或某段时间的电力负载等。

但是文章中也指出,问题并不是出在注意力机制本身,而是出在另一个地方,另一个之前我们没有提到的很容易被忽略的地方————潜空间(latent space),或者叫隐空间。

这里首先补充下潜空间是啥,我们知道,在最开始,我们进行了词嵌入和位置编码从而将输入文字向量化了,我们最初也提到过,线性代数是研究空间的学科,我们在研究向量时也总是结合着它所处的空间来说的,从来没有离开过,也就是说这些我们生成的词向量,其实也构成了,或者说处于一个高维空间中,这个空间,就是潜空间,即便后面经过了线性变换变成了Q,K,V,依然是在某个潜空间中的,因为我们固定了研究的坐标系。

好的,那我们现在也已经知道了,Transformer的注意力机制本质上是通过Q与K的点积来计算相似度,或者说相关度的,这点其实是其根基,这意味着:只有当Q, K所在的潜空间具有有意义的几何结构时,点积才能反映这种“相似性”,或者说相关性,否则在此之上的一切都是不成立的。也就是说,如果潜空间,本身就是混乱的、无结构的,那么Q和K做点积就不再具有意义,此时的注意力机制,也最终退化为了随机的加权。

词嵌入同样在被训练,如何看待训练好的词向量

在说这个话题之前需要先把之前没说的一个点给补上,之前我们反复提到过Wq,Wk,Wv是训练出来的,它们是整个模型参数的一部分,那么还有哪些地方是训练出来的呢?这里我在图里标出来:

前馈神经网络自然不必说,肯定是需要学习训练的,此外除了我们之前提到过的Wq,Wk,Wv之外,词嵌入这一步也是需要训练学习的,因为初始化的时候是通过某些库随机初始化的,然后在多头注意力机制中的这些拆分多头再到最后多头合成后的Linear,其实也是线性变换,也是需要训练学习的,关于多头注意力我们后续再谈,因为在理解了单头之后再去看多头会非常快,这里我们先来说词嵌入这部分,神经网络部分我们不做讨论。

我们之前说过,词嵌入之后,这些高维度的词向量构成了高维度的潜空间,并且随着训练,这些原本杂乱的词向量会呈现出一定的分布趋势,词语词之间会具有一定的距离,不过高维空间我们想象不出来,但是我们又很希望有一种方式能让我们相对直观地能看到这种趋势,哪怕不那么准确,所以其实有了一些可视化的方法,将这些高维向量朝着较为重要的维度投影,投影到三维空间中进行可视化,就像下面这个图一样,还是挺有趣的。

进一步思考:

到了这,我们知道了,除了进行调整Wq,Wk,Wv这三个线性变换在调整向量位置外,词嵌入的这个阶段的训练,其实也在进行向量位置的调整,那么如果说上述三个线性变换的调整可以理解为之前说的那样,把上下文语义上相近的词的Q,K调的比较近,那么怎么看待词嵌入这一步的调整呢,怎么看待这些高维度的,看起来似乎人类仍然没法去理解和解读的词向量呢?

这里我谈谈个人的理解:人类的语言,其实很多时候是对现实世界的描述或者说反应,也就是说语言间其实潜藏着很多现实世界的规则或者说现象,现实世界里一些经常关联到一起的东西,在语言上也经常会同时出现,如果说向量化是一个编码的过程的话,那么对语言进行词嵌入这一步编码进到这些向量的,其实我理解很大程度上就是这些蕴含在语言里的世界的规则,以及一些语言上的固定搭配。这里给大家分享一个比较有趣的说法,据说被训练好的词向量当中,“桌子”词向量减去“椅子”词向量=“键盘”词向量减去“鼠标词向量”,大家可以好好体会下这里面的东西,这个时候,其实似乎就已经出现某种“智能”的苗头了,因为这时候我们就会意识到,此时,这个被训练的人工智能,具备了造出“椅子之于桌子,相当于鼠标之于键盘”这样的句子,或者更准确的说,是对现实世界的一种解释和描述,因为在人类世界中,桌椅和键盘鼠标,的确基本上就是形影不离成对出现的东西,并且也反应到了语言中,而可能也正是源于语言的这一特性,才使得我们进行语言处理任务时,语言向量化之后的潜空间是具有一定结构的,是有序有规律的,这才使得注意力机制被完美地发挥出来,也才使得这个模型的效果如此的出色

那么,既然词嵌入这里,已经学到了这么大量的信息,似乎已经给这个潜空间里的向量位置调整的还算不错了,那为啥还需要后续的线性变换和Q,K,V以及注意力机制等等呢?

我的理解是因为,虽然现实世界的规则、事物、现象相对固定,但是语言的变化却是极为灵活的,尤其是中文,更是变换无穷,很多词,你放到某个语境下,含义会完全变掉,举个最简单的例子,“水”这个字,放到现实世界中,99%的时间它是个名词,就是指水这个东西,但是,如果放到这句话中“这篇分享很水”,此时联系上下文,“水”字成了形容词,这样的变换是词嵌入这里很难学习得到,也体现不出来的,必须通过注意力机制去把上下文的语义信息都结合起来,才能Get得到这一层信息。

多头注意力机制

有的时候,一个词和另一个词的关系,可能从不同的视角下看是不一样的,对于注意力机制来说,如果只通过一种方式计算一次相关性,即单头的情况,那么灵活性就会大大降低,所以我们需要再做一些改进,所以有了多头注意力机制,简单来说多头就是把向量切成几个小的向量,然后每个都再去做一遍和单头的时候一样的操作,最后再把这些头拼回去一个向量,但是实际肯定不是这么生硬地切和拼接。

之前是我们每个词只计算一组Q K V,现在我们再进过一组权重矩阵进行线性变换(降维度的线性变换,因为根据矩阵乘法,行数由坐标的矩阵行数决定),将Q K V拆分为多个更低维度的Q K V小头,也就是图中的这堆Linear。

结合前面说的潜空间来看,其实这一步就相当于把原本在单一潜空间中的Q K V向量,投影到了很多个较低维度的潜空间中,让它们有了更多次的机会去学习到更多不一样的上下文语义信息,来增加语言的灵活性,并且最终多头拼接的时候,也不是生硬的首尾相连,而是还要经过最后一个线性变换权重矩阵,也就是图中的最上方这个Linear完成最终的合成。

但是这里会产生一个疑问,之前我们有说过,之所以把词向量做成很高维度的向量,就是为了使得其可以处理更复杂的任务,编码更多更复杂的特征,那这里的降维度岂不是自断手脚。那么这里其实就又回到了我们之前说过的,使得向量维度下降的线性变换的意义,其实是为了“聚焦”,浓缩我们更想要的核心信息,通俗的来说就是单头看得“广”,多头的每一个头视角有限但是看得“细”。

打个比方就好比,单头注意力机制是一个相亲大会,会上每个人都只能了解到其他人的大概的一些信息,虽然你可以接触了解很多的人,但是了解必定有限。而多头的情况,就好比参会的男女嘉宾们,各自私下再去各种地方,咖啡馆、游乐场、酒吧、电影院等等不同的地方约会(投影到不同的潜空间中),然后由不同的约会再去对彼此的各方面会有更深层次的了解(在不同的潜空间进行注意力计算,最后获得更多更丰富的上下文),其实多头注意力就是这个意思。

————————————没有了—————————————

感谢大家耐心看到这,希望对大家有所帮助。现在的主流的大模型,其实都是在Transformer之上已经又进行了大量的改造和迭代发展了,所以和大家看到的新模型的结构或许不太一样,所以就这个模型而言,或许直接拿来用已经不行了,但是希望这篇文章能够帮你打好基础,这样在以后大家去研究甚至说微调更多更复杂的模型时能有一个参考,能站在一个更高的起跑线和认知线上。