大模型是怎么读懂一句话的,Attention 注意力机制拆解
你天天让大模型帮你写周报、改代码、总结文档。
有一天你随手丢给它一句话,那只猫追着球跑,它跑得很快。
然后你问,中间那个它,指的是猫还是球。
模型秒回,是猫,不是球。
那它是怎么知道的呢。
这句话里没有任何一条明文规定它等于猫。模型也没长眼睛,看不到画面里那只猫在撒欢。它拿到的只是一串文字,却能稳稳地把它和猫绑在一起。
这个问题往下挖,挖不到两层就会撞上一个词,Attention,注意力机制。
它是所有大模型的地基。GPT、Claude、DeepSeek、Llama,名字随便换,地基都是这一套。你后面要学的微调、RAG、Agent,拆到底全是它。
之前我们用一顿饭的功夫聊过大模型大概是怎么回事,这篇往下硬一层。真正拆开 Attention 里的三个字母,Q、K、V,看看「它指的是猫」这件事,在模型内部是怎么一步步算出来的。
在本篇里,我会按照六层来讲。
先看模型眼里的词长什么样,也就是 token。
然后看一个词的意思怎么由邻居决定。
接着是本文的主菜,注意力打分,以及打分背后的 QKV 机制。
再往上是多头注意力,模型同时用好几个角度看同一句话。
最后看这些层怎么堆起来,变成一个能理解整句话、还能往下生成回答的模型。
不过话说回来,这只是一个过度简化的流程。我隐藏了很多实现细节,公式只给直觉,不做数学推导。想深挖数学的话,可以去看 Transformer 的原始论文,或者各家的公开讲解课,这篇的目标是帮你把直觉先立起来,直觉立起来了,再看公式就不晕了。
Token
我们从最底层开始。
模型是不认识字的。
你眼里的那只猫追着球跑,在模型眼里是一串数字。
中间有个转换过程。
首先,文字会被切成一小段一小段,这个一小段就叫 token。
token 可能是一个字,可能是一个词,也可能是半个词。中文里那只猫追着球跑,大致会被切成那只、猫、追着、球、跑这样的几块。英文里一个 running,可能被切成 run 和 ning 两块。
切完之后,每个 token 去词表里查一个编号。猫可能是 4517,球可能是 892,编号是我随便举的,具体是几各家模型都不一样。
有了编号,再去查一张大表,把编号换成一个向量。
向量你可以理解成一排数字,比如二百个数字排在一起,或者更多。具体多少维不是很重要,记住一排数字就行。
到这里,一句话就变成了模型能处理的东西,一排向量,一个 token 对应一个。
就像这样。
你可能会想,为什么不直接一个字一个块,或者一个词一个块,搞得这么碎干什么。
一个词一个块的话,词表会爆炸。英文的词汇量是几十万的量级,再加上人名、品牌名、错别字,全塞进词表不现实。而且新词一旦没收录,模型就完全不认识。
切成小块就没这个问题。常用的组合整块收,生僻的词用小块拼。这样词表控制在几万到十几万的量级,就能拼出几乎所有的词,没见过的新词也能用旧块现拼出来。
这个切分有专门的算法,细节这篇不展开,记住结论就行。模型眼里的最小单位不是字,是 token,而且每个 token 进门时都会被换成一个向量。
这个进门换好的向量,有个名字,叫词嵌入,embedding。
顺带一提,token 这个词你在别处也会撞见。模型的上下文窗口按 token 算,API 的计费按 token 算,处理速度也常拿每秒多少 token 来讲。今天弄明白 token 是什么,以后看到这些描述就都有画面了。
好,token 就讲到这里。每个词都变成了一排数字,可以进模型了。
但进门向量有个问题,而且问题还不小。
邻居
问题在于,进门换好的向量是死的。
同一个 token,不管出现在哪句话里,进门时那个向量一模一样。
猫在「那只猫追着球跑」里,和在「猫趴在沙发上一动不动」里,进门向量是同一个。
这就不太对了。
你想想看,bank 这个英文词,放在 I sat on the bank of the river 里是河岸,放在 I deposited money in the bank 里是银行。字面一样,意思完全不同,区别全在旁边的词上。
中文也一样。苹果在「今天吃了个苹果」里是水果,在「苹果发布了新手机」里是一家公司。单个词自己是分不出来的,全靠邻居带。
语言学里有个说法,叫分布式语义,一个词的意思由它的上下文决定。孤立的词没有意义,意义是邻居给的。
人也这样。你在一句话中间看到一个熟词拿不准意思,怎么办,看前后文,猜。
在向量空间里,这个事可以看得更具体一点。意思相近的词,向量挨得近,猫和狗挨得近,猫和资产负债表离得远。进门向量编码的,是一个词孤零零存在时的大概意思,它在词表这个静态世界里是固定的。
你可能会想,那干脆一步到位,直接给每个词在不同句子里准备不同的向量,不行吗。
不行的,组合是爆炸的。同一个词出现在无穷无尽的句子里,你不可能提前把每种上下文的向量都存好。
所以只能分两步走。进门向量负责给出这个词的底子,一个静态的、大概的意思。然后由另一个机制,在具体句子里现场加工,把上下文揉进去。
这个现场加工的机制,得让每个词看一眼自己周围的词,然后根据看到的东西,把自己的向量更新一遍。
比如 bank 看一眼邻居,发现是河、是坐、是风景,那就往河岸的方向更新。发现是钱、是存、是利息,那就往银行的方向更新。
这个机制,就是注意力。
好,铺垫到这,地基就算打完了。下面进入主菜。
打分
注意力的直觉,特别朴素。
每个词环顾四周,给周围的词挨个打分。
分高的,多吸收一点信息。分低的,少吸收一点。跟谁都不太相关的,基本不看。
我们拿那句话来走一遍,就是开头那句,那只猫追着球跑,它跑得很快。
重点看它这个 token。
它是个代词,自己几乎不带信息。它指的是谁,完全取决于前面说了什么。所以它比句子里任何词都更需要环顾四周。
我们假设它给前面的每个词打分,就像这样。
看这里,箭头上的数字就是打出来的分。
猫拿到了大头,零点五八。球也分到了一点,零点一九。剩下的那只、追着、跑,都是零星。
这个分布是不是很合理。
它说的是一个在跑的东西。猫会跑,球也会滚,所以球能分到一点分。但真正在追着跑、跑得很快的主语是猫,所以猫拿大头。
顺便说一句,图上五个数加起来正好是一,这一点是怎么保证的,后面讲 softmax 的时候会说。
有了分数,接下来就是吸收信息。
它的新向量,等于句子里所有词的向量按这个分数加权混合。猫的向量乘零点五八,球的向量乘零点一九,依次算下去,全部加起来。
混合完,它这个位置上的向量,就不再是那个干巴巴的代词向量了,里面混进了一大半猫的信息。
后面模型再处理到它时,看到的就是一个带着猫影子的向量。
它指的是猫,这件事到这一步就已经埋下了。
是不是很简单。
而且注意,打分这件事不是它一个人的特权。同一时刻,句子里每个 token 都在做同样的事,各自环顾四周,各自打分,各自更新。这套机制有个名字叫自注意力,self-attention,意思就是一句话内部自己看自己。
比如猫这个位置,它也在打分。它大概会把高分给追着,给跑,因为一个名词需要知道自己前后的动作,才能明确自己在句子里扮演什么角色。跑这个位置也在打分,它需要知道自己前面是猫还是球,才能确定动作的发出者是谁。
也就是说,一句话过完一层注意力,每个位置上的向量都换了一遍血,每个向量里都不再只有自己,还混进了它认为重要的邻居。整句话的理解,就是这么一个词一个词攒出来的。
那你可能会问,分数是从哪来的。总不能模型拍脑袋随手写个零点五八吧。
对,分数不是拍的,是算的。
这里正好有个天真方案,可以先拿来试错一下。
一个很容易想到的做法是,直接拿两个词的进门向量算相似度,谁跟它挨得近,谁分高。
没错,这能算出一个分。但进门向量是死的,它作为一个代词,向量落在词表世界里一个很泛的位置,跟猫、跟球、跟桌子、跟椅子的距离都差不多远。算出来的分数会是一锅粥,它对谁都三分热情。
看来光靠进门向量打分是不行的。
需要一个机制,让每个词能针对性地提问。它得能问出,我是代词,我在找我的先行词,谁是我的先行词。
这就轮到 QKV 登场了。
QKV
QKV 是三个字母,Q、K、V。
对应的英文是 Query、Key、Value,查询、键、值。
很多讲注意力的文章到这就直接上公式了,我们不急,先拿一个你天天在干的事来类比,查字典。
你查字典的时候,手里有一个想查的词,这是你的 Q,你的查询。字典里每个词条头上有个词目,这是 K。词目底下的释义内容,这是 V。
你拿着 Q 去扫一遍所有词条的 K,匹配上了,就把对应的 V 拿走。
注意力干的就是这个事,只不过把查字典搬到了词和词之间。
我们还是用那句话。
那只猫追着球跑,它跑得很快。
首先,每个词的向量会各自生成三份东西。
一份 Q,表示我想找什么。它的 Q 大概在说,我是代词,我在找我指代的那个东西,最好是个会动的名词。
一份 K,表示我是什么。猫的 K 大概在说,我是名词,是一种动物,是这句话的主语。球的 K 大概在说,我是名词,是个物体,是被追的那个。
一份 V,表示如果你选中了我,我真正交给你的信息是什么。
这里多说一句,为什么 K 和 V 要分成两份。
门面和干货分开,是有好处的。被检索到的理由,和被拿走的内容,本来就可以不是一回事。就像招聘网站上,你被搜索命中靠的是简历上的关键词,但公司真正拿走的是你这个人的能力,两者相关但不等同。K 管命中,V 管交付。
然后,「它」拿着自己的 Q,去跟句子里每个词的 K 做匹配。
Q 和 K 怎么算匹配度呢,在数学上是拿两个向量做点积。你可以理解成,两个向量的方向越对齐,点出来的分就越高,方向拧着的,分就低,接近零。
它那个找先行词的 Q,跟猫的 K 一碰,方向很合,高分。跟球的 K 一碰,也算合但稍弱,中分。跟追着的 K 一碰,对不上,低分。
每个词都打完分之后,再过一个叫 softmax 的操作。
softmax 干的事一句话能讲清,把一堆有大有小的分数,变成一组零到一之间、加起来正好等于一的比例。分数高的占比大,分数低的占比小。
这也是为什么前面那张图上,五个箭头的数字加起来正好是一。不是巧合,是 softmax 保证了的。
最后一步,按这个比例,把句子里所有词的 V 加权混合,混合出来的结果,就是它更新之后的新向量。
整个过程串起来,是这样的。
我们再从它的视角重走一遍。
它生成 Q,句子里所有词各自亮出 K 和 V。Q 和每个 K 算出分数,分数过 softmax 变成权重,权重把所有 V 加权混在一起,混出来的就是它的新向量。
对,注意力就是这样的了。
讲到这里,有两件事我必须诚实一下。
第一,上面说的猫的 K 在说我是名词是动物是主语,这是一种拟人化的讲法。真实的 K 向量就是一排数字,里面没有这么清晰的语义标签。这些向量是模型在训练中自己学出来的,每一维具体在干什么,人类看不懂。这个拟人只是为了帮你在脑子里把流程立起来,别把它当成模型内部的真实描述。
第二,Q、K、V 是怎么从词向量里生成出来的呢,大致是用三套不同的变换矩阵去乘,每套矩阵里的数值都是训练学出来的。矩阵运算的细节这篇不展开,你只需要记住,每个 token 一份 Q、一份 K、一份 V,三份各管各的,就够用了。
还有一个小细节。刚才说每个词给句子里所有词打分,包括前面的和后面的。在 GPT 这类生成式模型里其实有个限制,一个词只能看它前面的词,不能偷看后面的。这个限制叫因果掩码,知道有这个东西就行,不影响直觉。我们例子里它只给前面五个词打分,正好也是这么处理的。
讲到这你可能还有一个疑问,那三套变换矩阵里的数值,是谁定的呢。
没人定,是模型自己学的。
训练开始的时候,这些矩阵里全是随机数,模型的输出一塌糊涂。训练的做法是让它玩一个巨大的填空游戏,给一段文本遮住下一个词,让模型猜,猜错了,就根据错误微调所有矩阵里的数值,包括 QKV 的变换矩阵,也包括进门向量的取值。这个游戏在海量文本上重复几万亿次之后,矩阵们就学会了各司其职。
为什么这么练就能学会打分,这个问题牵扯到梯度下降和反向传播,属于深水区,这篇不往下挖。想深挖的话,任何一个深度学习的入门课都会讲。
好,QKV 就讲到这里。单个注意力头的工作方式,你已经全都知道了。
多头
但模型实际用的不是一套 QKV,而是很多套并行。
这个设计叫多头注意力,Multi-Head Attention。
为什么要很多套呢。
你想想看,一句话里词和词的关系不止一种。它和猫是指代关系,追着和跑是动作的呼应关系,那只和猫是修饰关系,很快和跑是程度描述关系。
一个注意力头只有一套 QKV 变换,也就是说它只能以一种视角打分。
一个头如果专门盯着指代,它能把分集中砸向猫。但如果让这同一个头同时还得负责语法呼应、还得负责修饰搭配,几件事挤在一套打分逻辑里,就会互相干扰,最后哪件都打不准。
所以干脆多来几个头,各自带一套变换,各打各的分。
一个头管指代,把它连向猫。一个头管语法,把跑得很快连向前面的追着跑。一个头管修饰,把那只连向猫。可能还有头在看句子整体的情感倾向,在看标点,在看一些你猜不到的角度。
每个头算完,各自得到一份更新后的向量,然后把所有头的结果拼在一起,压一压,作为这个词在这一层的最终输出。
就像这样。
头的数量,大的模型里是几十个的量级。具体的数字各家不一样,也不是很重要。值得一提的是,头多了计算量并没有翻几十倍,因为每个头的向量维度比整体小,总的计算量大致是持平的。这是工程上的巧思,这里大致说一下,不展开。
这里也要诚实一下。
指代头、语法头这种叫法,是研究者把训练好的模型拆开观察之后总结出来的直觉。真实的头不一定分工这么干净,有的头确实明确对应某种语法关系,有的头到现在也没人完全弄明白在干什么,还有的头看起来是好几件事混在一起做。你把它理解成一群各怀绝技但分工模糊的观察员,比理解成一个组织严密的委员会,更接近实情。
回到我们的例子。
在那只猫追着球跑,它跑得很快这句话里,指代角度的头把它的注意力砸向了猫,语法角度的头把跑得很快和前面的追着跑对上了。多个角度的观察汇总到一起,模型对这句话的理解就立体了。
单层多头的机制讲完了,还剩最后一块拼图。
堆叠
最后一个问题是,怎么从理解一个词,变成理解一整句话,还能接着往下生成。
答案是简单粗暴的,堆。
一层注意力做完,句子里每个词的向量都更新了一次。它里面混进了猫的信息,很快里面混进了动作的信息,每个位置都比原来更懂上下文。
然后,把这一层输出的那排新向量,整个喂给下一层注意力。再打一遍分,再更新一遍。
关键在于,下一层的头看到的输入,已经不是原始的进门向量了,是被上一层加工过的向量。所以第二层的打分,是建立在第一层的理解之上的。
第一层把它和猫关联上之后,第二层在处理很快时,看到的它已经带着猫的影子,就能进一步理解到,跑得很快说的是猫跑得快,不是球滚得快。
也就是说,第一层认的是邻居,第二层认的是邻居的邻居。层数越深,每个位置上揉进来的上下文越远,理解也越抽象。
这里可以插一段历史背景。
在注意力机制流行之前,处理文本的主力是循环神经网络,RNN。它的做法是按顺序读,读一个词更新一次记忆,读完整个句子。听起来自然,但有个毛病,句子的信息要像接力赛一样一站一站传下去,句子一长,开头的信息传到结尾就稀释了,第一棒说了什么,最后一棒记不太清。
注意力的做法不一样,它让任意两个词直接连边。句子开头和结尾的词想建立联系,一步到位,不用接力。而且所有词的打分可以同时并行计算,对硬件也友好得多。
正因为这两点,Transformer 这个以注意力为核心的架构,才取代了 RNN,成了今天所有大模型的地基。你天天听到的这个词,Transformer,指的就是这套以注意力为主角的搭建方式。
回到堆叠这件事上。实际的模型里,层数是几十层的量级。每一层里除了注意力,还有一个叫前馈网络的部件,让每个词的向量各自再加工一遍。前馈网络这篇不展开,你只需要记住一个分工,注意力负责词和词之间交换信息,前馈网络负责每个词自己消化信息,两者交替出现,一层一层往上垒。
垒到最顶层,每个位置上都得到了一个深度浸泡过上下文的向量。
这时候在出口接一个预测头,把最顶层的向量变成一张概率表,表上列着词表里每个 token 作为下一个词的概率。模型生成回答的过程,就是从这张表里挑一个词蹦出来,蹦出来的词拼回输入末尾,整句话重新走一遍上面的全套流程,再蹦下一个。
你看到模型一个字一个字往外吐,每吐一个字,背后都是全套注意力重新跑了一遍。工程上有一些缓存优化,让已经算过的词不用重算,这块属于实现细节,这里就不展开了。
好,堆叠讲到这里,注意力机制的所有主要零件就都过完了。
我们把整条链路从头重走一遍。
首先,一句话进来,那只猫追着球跑,它跑得很快,被切成一个一个 token。
然后,每个 token 查词表换成向量,这时候的向量还是死的,不含任何上下文。
然后,每个 token 各自生成 Q、K、V 三份东西。
然后,每个词拿着自己的 Q,去和句子里其他词的 K 匹配打分,分数过 softmax,变成一组加起来为一的权重。同一个词的所有权重里,猫占零点五八,球占零点一九。
然后,按权重把所有词的 V 加权混合,每个词都得到一个融入了上下文的新向量,它的新向量里,一大半是猫。
同一层里,几十个头各自用自己的角度并行做一遍,结果拼接压缩。
然后,这一层的输出喂给下一层,再注意一遍,再更新一遍,几十层垒上去,理解一层比一层深。
最顶层的向量接上预测头,算出下一个 token 的概率,蹦一个词出来,拼回去再跑一遍,直到一句话生成完。
到这里,「它」指的是猫这件事,就完整地走通了。
到这里,注意力机制的大图就算是拼完整了。如果你想系统地学习这些内容,从大模型原理一路讲到 Agent、RAG 和模型部署,可以看看这门0基础Agent开发课,220 个课时,从零基础一路讲到生产部署。
评论区
暂无评论,快来抢沙发吧