(三)先把地基打好:注意力到底在算什么

ClawLihai · · 共 3,713 字 · 约 12 分钟读完

先把地基打好封面

上一篇挖了个坑,这篇来填

上一篇我把整个行业的长上下文路线铺成了一张地图:Llama 靠改位置编码,Gemini 靠堆 TPU,GLM 和 DeepSeek 靠改架构。地图画完,我留了这么一句话:

要真正看懂路线二(改造注意力)和路线三(KV 压缩),你得先搞清楚标准注意力到底在算什么、为什么是 N²、KV Cache 到底是个什么东西。这些是地基。

这篇就是来打地基的。

我故意没在这篇里塞任何 GLM-5.2 的”创新”。如果你期待看到 DSA、MLA 怎么变魔术,先忍一下——不懂标准注意力,后面那些花活都是空中楼阁。这一篇不破任何墙,但它是理解后面四道墙为什么难的全部前提。

进度感:本篇是地基层,不对应任何一道墙
(后面按由易到难拆,不按墙号顺序)
墙 1(算不动 N²)→ 第 5 篇 DSA
墙 2(放不下 KV Cache)→ 第 4 篇 MLA
墙 3(推不动 千亿参数)→ 第 6 篇 MoE
墙 4(蹦得慢 自回归)→ 第 7 篇 MTP

一个 token 的诞生:文字怎么变成数字

模型不认识”字”,它只认识数字。所以第一步是翻译。

**分词器(Tokenizer)**把句子切成一个个 token。这里有个反直觉的点:token 不是字,也不是词,而是介于两者之间的”片段”。看个例子:

"我喜欢吃苹果"
→ 可能切成: [我] [喜欢] [吃] [苹果]       ← 4 个 token
   "喜欢" 常见,整体打包成 1 个 token

"我喜欢吃凤梨"
→ 可能切成: [我] [喜欢] [吃] [凤] [梨]    ← 5 个 token
   "凤梨" 不够常见,拆成两个字符 token

同样是 6 个字,“苹果”版本 4 个 token,“凤梨”版本 5 个。这就是为什么你数模型”生成了 100 个字”,它实际可能跑了 80 个 token——字数和 token 数经常对不上,中英文混排、词组打包都会让两者错位。GLM-5.2 的词表是 vocab_size: 154880,也就是它总共认识约 15.5 万种 token 片段。

切完 token,下一步是查表。每个 token 去翻一张叫 **Embedding(嵌入)**的大表,变成一串数字——一个向量。拆 GLM-5.2 的 config.json,这一串数字的长度写得很清楚:

hidden_size: 6144

每个 token 变成 6144 个数字。你可以把它想象成每个 token 背后挂着一个有 6144 个刻度的”身份证”,每个刻度都是训练学出来的。

为什么是 6144 个、不是一个?因为这些数字编码的是意思。训练让”意思相近的 token,数字串也长得像”——“猫”和”狗”的向量方向接近,“猫”和”汽车”差得远。怎么衡量”像不像”?看两串数字指向的方向夹角,本质就是中学几何里的余弦。6144 维给得足够细,能把 token 之间的微妙差别都摊开编码。

一个 token 的诞生:文字→token 片段→6144 维向量

这对你用 AI 意味着什么: 同样一段话,不同模型的 token 数可能差很多——有的模型词表对中文友好(“喜欢”算一个),有的不友好(拆成单字)。你按 token 付费时,模型选谁直接影响账单。这也是为什么现在国产模型(Qwen、GLM、DeepSeek 等)都在扩中文词表:少切几个 token,既省钱又省上下文额度。

78 层的”接龙工厂”:Transformer 在干什么

变成 6144 维向量后,这些 token 进入一座加工厂。GLM-5.2 的层数同样是 config 写死的:

num_hidden_layers: 78

78 层。每一层做两件事,循环往复:

一个 token 进入一层

   ├─ 第一步: 注意力(Attention)   ← 看一眼上下文,搞清楚"我是谁"

   └─ 第二步: 前馈网络(MLP)       ← 消化信息,做特征变换


输出 → 进下一层 ...(重复 78 层)
  • 注意力:让当前 token 扫一眼句子里其他 token,搞清楚自己在当前语境下到底是个什么角色。这是这篇的主角。
  • MLP:把注意力收集来的信息做进一步加工变换,可以理解成”消化吸收”。

78 层叠完,每个 token 都拿到了一个”吃透了全部上下文”的向量。最后一步,拿最后一个 token的输出向量,过一层叫 lm_head 的,转成”下一个 token 是词表里每个候选的概率”,选概率最高的——下一个字就蹦出来了。然后用这个新字再跑一遍 78 层,再蹦下一个……这就是”接龙”:用已生成的 token 预测下一个 token,循环往复。

78 层接龙工厂:每层注意力+MLP,叠 78 层后接龙出下一个字

这对你用 AI 意味着什么: 为什么大模型”思考”要那么久?因为每蹦一个字,都要把上面这套 78 层从头跑一遍(这点第 7 篇讲 MTP 时会重点拆)。模型不是一次性想好整句,而是真的一个字一个字往出蹦——所以你能看到它打字一样的”流式输出”。这也是”蹦得慢”那道墙的根源。

注意力的核心:Q / K / V 三角色

现在进正题:注意力到底怎么”看上下文”?

答案藏在三个角色里:Q(Query,查询)、K(Key,键)、V(Value,值)。每个 token 都会拿自己的向量,乘以三个训练好的矩阵,投影出这三样东西。

用”我想吃苹果”举例

拆 token 后是 [我] [想] [吃] [苹果],我们聚焦到「苹果」。

「苹果」会向句子里每个 token 发起一次”注意力”,看谁跟自己相关。这个过程可以想象成**「苹果」在挨个问句子里每个字一个问题**:“你跟我有关系吗?关系多大?”

"我"    → 相关度 0.10  (弱)
"想"    → 相关度 0.05  (弱)
"吃"    → 相关度 0.85  ★ 强!"吃苹果"强烈暗示这是水果
"苹果"  → 相关度 1.00  (自己,最高)

然后「苹果」按相关度做加权混合:大量吸收「吃」的信息,弱吸收「我」「想」。结果,「苹果」的向量被更新了,它现在”知道”自己是被吃的那个水果,而不是苹果公司。靠上下文把一个词的多重含义收敛到正确的那一个,注意力就是这么消歧义的。

Q / K / V 分别扮演什么

那”相关度”具体怎么算?这就是 Q/K/V 的核心。打个比方:

角色含义比喻
Q(Query)当前 token “在找什么样的信息”你举手提问:“我在找懂 X 的人”
K(Key)每个 token “自称能提供什么”每人胸前挂的胸牌:“我是 X 方面的”
V(Value)每个 token “实际携带的信息”那个人的实际发言内容

「苹果」拿着自己的 Q(在找”谁吃我 / 描述我”),去看其他 token 的 K(胸牌)。Q 和 K 方向越一致,点积越大,相关性越高

Q(苹果) · K(我)    = 0.10   ← 苹果的 Q 和"我"的 K 方向差得远
Q(苹果) · K(吃)    = 0.85   ← 苹果的 Q 和"吃"的 K 方向接近 ★
Q(苹果) · K(苹果)  = 1.00   ← 自己看自己,最高

算出一批相关度后,用 softmax 把它们归一化成”权重”(加起来等于 1),再按权重加权混合所有 token 的 V(实际信息):

新向量(苹果) = 0.10×V(我) + 0.05×V(想) + 0.85×V(吃) + 1.00×V(苹果)
                                                          └─ 大量吸收"吃"的动作语义

一句话概括标准注意力的全部:Q·K 点积算相关性 → softmax 归一化 → 加权混合 V。 就这三步,没有别的。

这对你用 AI 意味着什么: 注意力的本质是”按相关性分配注意力”——和你问题语义相关的上下文,权重高、被认真读;无关的,权重低、几乎被忽略。 所以写 prompt 时,把关键信息和你的问题放近、放相关,比堆一堆背景废话更管用。模型不是逐字精读整篇,而是在做相关性筛选。

Q/K/V 三角色:苹果拿着 Q 找谁的 K 最匹配,再吸收对方的 V

三个容易踩的坑

讲到这里,有三个点必须澄清,否则后面 DSA 的逻辑接不上。

第一,注意力是非对称的——是”有向图”,不是无向图。

「苹果」关注「吃」的程度(0.85),不一定等于「吃」关注「苹果」的程度(可能是 0.30,也可能是 0.90)。因为 Q 和 K 是两套不同的投影矩阵,“我找你”和”你找我”用的是不同的尺子,两个方向的注意力是独立算出来的。这就像暗恋是单向的,你盯着某人看,不代表对方也在看你。

这直接推出一个关键结论:N 个 token 要算的不是 N(N−1)/2 次(无向图),而是 N×N 次(有向图)。 每个方向的注意力都得单独算一遍。后面讲 N² 时这个细节会回来。

第二,注意力是”全员对照”,不是”接力赛”。

「苹果」不是只看相邻的「吃」,而是和句子里每一个 token 都算一次。哪怕隔得很远——“小明去了很远的银行”里,「银行」要直接看到句首的「小明」,中间隔了几个 token 也得直线关联。

这是注意力区别于老一代 RNN(循环神经网络)的核心优势:RNN 像传话游戏,信息要一个挨一个往下传,传远了就失真;注意力是全员大合影,任意两个 token 都能直接对上眼,不管隔多远。代价就是——人人都得对一遍,于是有了 N²。

全员大合影(任意两 token 直连) vs 接力赛(只能传相邻)

第三,还有两处工程细节,不影响核心。

真实的 Q·K 点积还要做两件事:(a) 除以 √d 缩放,防止维度一高 softmax 分布过尖、梯度消失;(b) 多头并行——拆 GLM-5.2 的 config:

num_attention_heads: 64
head_dim: 192

64 个头,每个头独立算一遍注意力,各学一种不同的关系模式(语法、指代、长程依赖……)。这两点是工程优化,核心机制(Q·K 衡量方向一致性)一点没变。

这对你用 AI 意味着什么: 为什么 AI 能在一份长文档里”远距离”把第 1 页的设定和第 47 页的问题对上号?就是因为注意力是全员对照,不是接力赛——理论上任意两个 token 都能直接关联。但”理论上能”和”算得动”是两回事,这正是 N² 要逼出来的下一道墙。

为什么是 N²?长上下文的”原罪”

现在你能彻底看懂墙 1 了。

注意力的第二步(Q·K 算相关性)是这样的:每个 token 都要拿自己的 Q,去和所有其他 token 的 K 算一次点积。

N 个 token,每个都要算 N 次点积(跟所有人算一遍,含自己)
→ 总共 N × N = N² 次点积

序列长度一涨,计算量不是线性涨,是平方涨:

序列长度注意力点积次数
100 个 token100 × 100 = 1 万次
1 万个 token1 亿 次
100 万 token(1M)1 万亿次

序列翻 10 倍,计算量翻 100 倍。1M 上下文的一次注意力,就是一万亿次点积——而这还只是一层的量,GLM-5.2 有 78 层,64 个头,全部叠起来是个让人头皮发麻的数字。

N² 平方增长:100→1万,1万→1亿,100万→1万亿

这就是 N²,长上下文的”原罪”。它不挑模型——任何用标准注意力的模型,只要把上下文拉到 1M,这一万亿次点积一个都躲不掉。

那 GLM-5.2 怎么躲的?靠的就是路线二:经验上,大部分注意力权重其实接近零(那个”的”字,真的需要和另外 100 万个 token 都算一遍吗?),那就别全员算,只挑相关的几个算,这是 DSA(分散稀疏注意力)的动机。但 DSA 具体怎么挑、怎么保证不挑错,我们留到第 5 篇深讲。这一篇你只要记住:标准注意力就是 N²,没有捷径。

KV Cache:为什么不用每次重算

讲完注意力,还有一个绕不开的概念——KV Cache。它是墙 2的来源,也是第 4 篇 MLA 要啃的骨头。

自回归生成的两个阶段

模型生成回复时,其实分两段:

阶段 1 Prefill(预填充):处理你的输入
  一次性把你输入的所有 token 算一遍
  → 每个 token 在每一层都得到自己的 K 和 V
  → 把这些 K/V 存起来(进 KV Cache)

阶段 2 Decode(解码):一个字一个字蹦
  每生成一个新 token:
    - 用新 token 的 Q,和 Cache 里所有历史 K 算点积
    - 按相关性加权混合历史 V
    - 新 token 自己的 K/V 也存进 Cache
    - Q 用完就扔

Q 扔、K/V 存——为什么这么设计?

QKV
存进 Cache 吗❌ 扔✅ 存✅ 存
为什么Q 是”当前在找什么”,每个 token 不同,别人不会来查它,无复用价值下次别的 Q 还要来查它,存了就免于重算和 K 同理,是别人要取走的信息

用一个图书馆的比喻:你是读者(当前的新 token),带着自己的提问(Q);书架上每本书(历史 token)挂着书名(K,供你检索)和内容(V,你取走的部分)。书自己不需要”提问”——它们的 Q 早在当年就扔了——只需要持续提供书名和内容给后来的读者查。

这个设计的关键,在于把”重复计算”换成了”存储复用”。不然每蹦一个新字,都要把前面所有 token 的 K/V 重新算一遍,那生成一句话的成本会平方爆炸。

但存储本身,就是墙 2

聪明的代价是:K/V 得一直占着地方。算一笔账:

1M token × 每头 192 维 × 2(K,V) × 64 头 × 78 层 × 2 字节(bf16)
→ 约 3-4 TB(未压缩标准口径粗算)
→ 单张卡的显存,根本装不下

这就是 KV Cache 带来的第二道墙——算得动是一回事,存得下是另一回事。GLM-5.2 怎么把这几 TB 压到能在普通多卡上跑?答案在第 4 篇:把 K/V 用训练好的矩阵压成 512 维的”潜在向量”。具体怎么压、为什么不会压坏,留到 MLA 那篇。

KV Cache 生命周期:Prefill 存 K/V,Decode 用 Q 查 Cache、新 K/V 入库、Q 扔掉

这对你用 AI 意味着什么: 为什么你的长对话越聊越慢、越聊越卡?因为每多一轮,KV Cache 就多塞一层历史,显存越占越大,直到触发”清缓存”或截断——模型就开始”忘事”。这也是为什么很多产品有个上下文上限:不是模型不想记住,是显存放不下了。 懂了 KV Cache,你就懂了”为什么会忘”。

地基铺好了,后面要拆什么墙?

这一篇你没看到任何 GLM 的创新,但你掌握了三个地基概念,它们正是后面所有花活的着力点:

  1. 标准注意力:Q·K 点积算相关性 → softmax → 加权混合 V
  2. N² 问题:每个 token 和所有 N 个 token 算,序列一长平方爆炸
  3. KV Cache:K/V 存起来复用,免于重算,但体积大得撑爆显存

GLM-5.2 的四个架构创新,每一个都是对着 Transformer 的某个部件动刀——MLA 盯 KV Cache,DSA 盯注意力的 N²,这两个接着前面打的地基;MoE 动的是 MLP 层,MTP 动的是生成循环,这是 Transformer 另外两个部件。手术部位一一对应:

创新对着哪个地基怎么动刀哪一篇拆
MLAKV Cache把 K/V 压成 512 维潜在向量第 4 篇
DSA注意力 N²每个 token 只算 top-2048 相关的第 5 篇
MoEMLP 层256 个专家选 8 个,只激活一小部分第 6 篇
MTP自回归生成MTP 头一次猜多个,主模型并行验证第 7 篇

地基→创新:四个手术分别对着 KV Cache / N² / MLP / 自回归

写在最后:地基就该打这么细

你可能觉得这篇啰嗦,讲个注意力至于吗?我的经验是:后面每一篇的爽点,都建立在这篇的扎实上。 当第 4 篇告诉你”MLA 把 K/V 从上万维压到 512 维”时,你得先知道 K/V 是什么、为什么要存它;当第 5 篇讲”indexer 粗筛 top-2048”时,你得先理解全员大合影的 N² 到底浪费在哪。

地基不打牢,后面全是名词的堆砌;地基打扎实了,后面的每一招你都能看懂它在动哪里、为什么这么动。

那我们就从最直观的一道墙开始:KV Cache 太大怎么办? 下一篇,MLA 登场。我们会回答一个很具体的问题:凭什么 GLM-5.2 敢把每个 token 的 K/V(64 个头合起来上万维)压到只存一个 512 维的潜在表示,还压不坏?答案藏在一个和 JPEG 图片压缩同源的数学技巧里,低秩分解

下篇预告:《KV Cache 放不下?MLA 的低秩压缩》


本篇涉及的所有架构数字——hidden_size: 6144(每个 token 的向量维度)、num_hidden_layers: 78(层数)、num_attention_heads: 64head_dim: 192(注意力头数与头维度)、vocab_size: 154880(词表大小)——均来自 GLM-5.2 公开的 config.json。Q/K/V、softmax、多头注意力、N²、KV Cache、Prefill/Decode 两阶段等为 Transformer 标准机制,描述基于公开论文与通用实现,无任何闭源或未公开信息。序列长度 100 / 1 万 / 100 万对应的点积次数(1 万 / 1 亿 / 1 万亿)是按 N² 直接计算的理论值,未计入常数与工程实现细节。

这个话题还有

🖼 图文卡片版 · 点击放大轮播
双击或滚轮缩放 · 拖动平移 · Esc 关闭