04|注意力机制通俗讲:它怎么知道“它”指的是谁
《大模型原理通俗讲》系列第 4 篇,共 16 篇。不需要数学基础,会用大模型聊天就能读。这是整个系列最关键的一篇:读懂它,后面的结构就都顺了。
先看两句话,只差一个字:
- 奖杯放不进行李箱,因为它太大了。
- 奖杯放不进行李箱,因为它太小了。
第一句里,“它”是奖杯;第二句里,“它”是行李箱。
你读起来毫不费力。可对模型来说,这是个难题:“它”这个字,两句话里一模一样,光看“它”本身,永远看不出它指的是谁。答案藏在别的词里(“大”“小”,还有前面的“奖杯”“行李箱”)。
所以模型读到“它”的时候,必须有一个本事:回头去前面找线索,而且知道该重点看哪几个词。
这个本事,就叫注意力机制。读完这一篇,你能回答三个问题:
- 注意力到底在“注意”什么?它是怎么算出来的?
- 经常听到的 Q、K、V 是什么?
- 为什么说注意力是大模型最核心的零件?
一、一句话讲清楚注意力
回忆上一篇:每个词查表得到一个向量,但这只是起点,同一个词不管出现在哪句话里,起点都一样。
如果每个词只用自己的向量,“它”永远是同一个“它”,什么都分不清。如果让每个词把前面所有词平均地看一遍,信息又会糊成一团。
注意力的做法是第三种:有选择地看。
每个词读到自己的时候,回头给前面的每个词打一个“看重程度”,再按这个程度,把它们的信息“借”过来,混进自己的向量里。
用我们的例子,读到“它”时是这样的:

“奖杯”和“行李箱”各被看重了约 4 成,别的词只有零头。“它”的向量因此带上了“奖杯”“行李箱”这类物体的信息。(图里的数字,是后面那段小代码真实算出来的。)
二、拆成三步:打分 → 变百分比 → 混合
“有选择地看”,在计算机里是怎么做到的?只有三步。
先用一个类比:你带着一个问题去图书馆。
- 你手里有一张写着问题的问题卡;
- 每本书都有一个书名标签;
- 每本书里有自己的内容。
你拿问题卡跟每本书的标签对一对,越对得上的书,越多翻几页;最后把几本书里的内容,按“翻了多少”的比例,合成一份笔记。
注意力就是把这件事变成了数字:

第 ① 步,打分。 “它”拿着自己的问题卡(Q,Query,“我在找什么”),去和每个词的标签(K,Key,“我是什么”)对一对。越匹配,分数越高。(两串数字“对得上”的程度,数学上用“点积”来算,你只需要记住:对得上,分高。)
第 ② 步,变成百分比。 分数有大有小,没法直接当“分配比例”用。于是用一个叫 softmax 的办法,把一排分数变成一排百分比:分数越高,占比越大;每个都不为负,加起来正好是 100%。
第 ③ 步,混合内容。 每个词还有一份“能给你什么”的内容(V,Value)。按上一步的百分比,把大家的内容加权相加,得到的就是“它”的新向量:它已经带上了前文的信息。
这三步,就是注意力的全部。后面不管多复杂的模型,都是在重复它。
三、动手:30 行代码,把三步算一遍
我们做一个迷你版(需要 numpy),句子只有五个词:“奖杯 放不进 行李箱 因为 它”。
先说清楚:这里的数字是我手工设定的,为的是让你看清计算过程。 真实模型里,这些数字是训练出来的,没有这么整齐。为了看得懂,我给每个词的 3 个数起了名字:[是物体, 是动作, 是指代词]。
# mini_attention.py —— 迷你注意力:看清"打分 → 变百分比 → 混合信息"三步
import numpy as np
words = ["奖杯", "放不进", "行李箱", "因为", "它"]
# 每个词的向量,3 个数。为了看得懂,我手工给每一维起了名字:[是物体, 是动作, 是指代词]
x = np.array([[1, 0, 0], # 奖杯
[0, 1, 0], # 放不进
[1, 0, 0], # 行李箱
[0, .3, 0], # 因为
[0, 0, 1]]) # 它
# 三个"变换":把同一个向量,分别变成"问题卡 q""标签 k""内容 v"。真实模型里它们是训练出来的,这里手工设定
Wq = np.array([[0, 0], [2, 0], [3, 0]]) # 动作词想找"物体",指代词更想找"物体"
Wk = np.array([[1, 0], [0, 1], [.3, 0]]) # 物体词的标签是"我是物体"
Wv = np.eye(3) # 内容:原样带走
q, k, v = x @ Wq, x @ Wk, x @ Wv
scores = q @ k.T / np.sqrt(2) # ① 打分:问题卡和每个标签对一对(除以 √2 是为了数字别太大)
scores[np.triu_indices(len(words), 1)] = -np.inf # 接龙:每个词只能看自己和前面的,不许偷看后面
w = np.exp(scores - scores.max(axis=1, keepdims=True))
w = w / w.sum(axis=1, keepdims=True) # ② 变成百分比:每一行加起来是 1
out = w @ v # ③ 按百分比,把内容混合起来
print("词的编号:" + " ".join(f"{i + 1}={t}" for i, t in enumerate(words)))
print("\n注意力权重(行 = 谁在看,列 = 看第几个词)")
print(" " * 8 + "".join(f"{i + 1:>6}" for i in range(len(words))))
for t, row in zip(words, w):
print(t + " " * (8 - 2 * len(t)) + "".join(f"{p:>6.2f}" for p in row)) # 汉字占两格,所以按 2 倍补空格
print("\n『它』看之前:", x[4], " 看完之后:", out[4].round(2))
运行结果(我实际跑过):
词的编号:1=奖杯 2=放不进 3=行李箱 4=因为 5=它
注意力权重(行 = 谁在看,列 = 看第几个词)
1 2 3 4 5
奖杯 1.00 0.00 0.00 0.00 0.00
放不进 0.80 0.20 0.00 0.00 0.00
行李箱 0.33 0.33 0.33 0.00 0.00
因为 0.30 0.20 0.30 0.20 0.00
它 0.41 0.05 0.41 0.05 0.09
『它』看之前: [0. 0. 1.] 看完之后: [0.81 0.06 0.09]
把这张权重表画出来:

逐行读一读,你会发现它在做符合直觉的事:
- “它”这一行: 奖杯 0.41、行李箱 0.41,是最高的两个。因为“它”的问题卡在找“物体”,而这两个词的标签正好是“我是物体”。
- “放不进”这一行: 奖杯 0.80。动词在找它的“东西”,所以重点看了前面的物体。
- “它”的向量变了: 看之前是
[0, 0, 1](只是个指代词),看完之后是[0.81, 0.06, 0.09],“物体”这一维从 0 变成了 0.81。这就是“借信息”。 - 上三角全是“看不到”: 生成文字是接龙,每个词只能看自己和前面的词,不能偷看后面还没写出来的。这叫“因果遮罩”(第 07 篇会再用到)。所以第一个词“奖杯”只能看自己,权重是 1.00。
- “行李箱”这一行是平均的: 因为我没给它设“问题”,它对前面一视同仁。这只是我这个迷你例子的结果,不是规律。
一定要说清楚的一点
看“它”这一行:奖杯和行李箱几乎平分了注意力,这一步并没有分出“它”到底是谁。
这没错,而且这正是真实情况。一次注意力不会“解开谜题”,它只是把相关的信息聚到一起。 真正分出“大 → 奖杯,小 → 行李箱”,要靠后面的词(比如“大”)也去回头看,再靠很多层、很多个“头”反复整理(第 05、06 篇讲),以及训练时学到的常识(比如“大的东西放不进小的容器”)。
把注意力想成“聚光灯”,而不是“答案”,会更准确。
四、Q、K、V 是从哪来的
上面反复出现的 Q(问题卡)、K(标签)、V(内容),是三种不同的东西吗?
不是。它们是同一个词的向量,分别经过三个不同的“变换”得到的三个版本:

- 一个词的向量 x(上一篇查表得到的);
- 乘以变换表 Wq,得到 q:“我在找什么”;
- 乘以变换表 Wk,得到 k:“我是什么”;
- 乘以变换表 Wv,得到 v:“我能给你什么”。
为什么要三个不同的?因为同一个词在注意力里扮演三种不同的角色:有时候它是“提问的人”,有时候它是“被找的人”,有时候它是“被取走信息的人”。三个角色需要的信息不一样,所以要用三张不同的变换表。
这三张变换表(Wq、Wk、Wv)里的数字,和上一篇的向量表一样,都是训练时学出来的。我们的迷你例子里是手工填的,真实模型里没有人填。
五、为什么说它是大模型最核心的零件
三个理由:
- 它解决了“远距离线索”。 回忆第 01 篇:“小明昨天丢了钱包,今天他很……”,线索在很前面。注意力让每个词都能直接看到前面任何位置的词,不管隔得多远。
- 它是一次性算完的。 一个句子里每个词的注意力,可以同时算(一张表一次算出来),不用像人读书那样一个字一个字等。这让用大量 GPU 训练成为可能(第 10 篇)。
- 大模型的主体结构(Transformer)就是用它搭起来的。 提出这个结构的那篇 2017 年的论文,标题就叫《Attention Is All You Need》(注意力就是你所需要的)。第 06 篇我们会看到整个结构。
当然,它也有代价:每个词要和前面所有词打分,句子长度翻倍,要算的分数大约翻 4 倍。这就是为什么模型一次能看的长度(上下文窗口)有上限,而且很长的输入特别耗资源(第 12 篇会讲)。
六、图书馆这个比喻,到哪里为止
- 书名是人起的,Q、K 却是学出来的数字。 真实模型里,q、k 里的每个数没有我们能读懂的含义。“问题卡”“标签”只是帮你建立直觉的名字。
- “注意”不是人那种有意识的注意。 它只是一个数学上的“加权平均”:权重大,就多混一点。模型没有“意识到这里是重点”。
- 权重高,不等于“它就是这么想的”。 注意力权重很容易画成漂亮的热力图,但学界对“权重能不能当作模型的解释”一直有讨论。看图时,把它当成“信息从哪里流过来”,不要当成“模型的内心想法”。
- 一次注意力只“借”一轮信息。 真实模型要借很多轮(很多层),而且每轮同时从好几个角度借(很多个头),这是下一篇的内容。
七、三个常见误解
误解 1:“注意力说明模型理解了。”
不能这么推。注意力只是把相关的信息聚在一起的一种计算。它为什么有效,和模型“懂不懂”,是两个问题。
误解 2:“注意力让模型记住了前面所有内容。”
不是。权重加起来是 1,看重了这个,就少看了那个;而且模型一次能看的长度是有上限的(上下文窗口),超过的部分就看不到了。
误解 3:“只有‘它’这样的代词才需要注意力。”
每个词都在做。“放不进”要找它的物体,“很”要找前面的原因,“钱包”要找“丢了”……每一个位置、每一层,都在回头看。
八、本篇要点
- 注意力 = 每个词回头给前面每个词打“看重程度”,再按这个程度把信息“借”过来。
- 三步:打分(Q 对 K)→ 变成百分比 → 按百分比混合内容(V)。
- Q、K、V 是同一个词的向量,经过三个学出来的变换得到的三个版本。一次注意力是“聚光灯”,不是“答案”。
下一篇预告: 一次注意力,只能从一个角度看。可一句话里,同时有语法关系、指代关系、因果关系……怎么办?而且注意力本身并不分先后:“狗咬人”和“人咬狗”,它怎么分得清?下一篇讲多头注意力和位置编码。
系列目录

文中的图、代码,欢迎自己动手改一改,比如把 Wq 里的数字改掉,看看“它”会去看谁。有哪里没讲明白,欢迎在评论区告诉我。
本文通俗讲解大模型中注意力机制的核心原理,阐明其如何通过Q(查询)、K(键)、V(值)三要素实现有选择地聚合上下文信息;详细拆解打分、softmax归一化、加权混合三步计算流程;指出注意力是Transformer架构基石,解决长程依赖、支持并行计算,并澄清常见误解如‘注意力等于理解’或‘权重即解释’等。

1489

被折叠的 条评论
为什么被折叠?



