专题一 · 一个 Token 的一生 16 讲 · 67 分钟

这份讲义怎么用

这是老师的草稿,不是给学员的材料。学员看 课件,你看这一份。
课程作者 Chris Yang · Google Cloud AI Infra 架构师。

🎯这一讲要留下什么 讲完之后学员脑子里应该剩的那一句话。 只有一句 —— 讲散了就是没讲到。
🗣讲稿 接近逐字,可以照着念。黄底的是必须说出口的原话, 其余可以按自己的语感改。
🖥屏幕 这一段该滚到课件的哪里、要不要动顶上那条滑块。
可能被问到 真实会被问的问题 + 答法。答不上来的也写清楚 「这个我不确定」比编一个强。
⚠️别讲什么 这一讲最容易跑偏的方向。时间就是被这些吃掉的。

一小时怎么分

课件那一页的信息量大于一小时能讲完的量,这是故意的 —— 课上按下面的节奏走主线,细节留在页面上让人回去自己翻。 左边侧栏的分组跟这张表是同一套时间。

分钟讲什么这一段的落点
0–5开场:先让所有人猜「一次 128K prefill 留下多大 KV cache」,再亮口径和底牌 把「我们要算一笔账」这个框架立住。开场只算 KV cache —— 那笔账是确定的;激活留到合账
5–12第 1 步 · 嵌入:查一次表,1.75 GiB 就没了 第一个「坐直」的时刻
12–27第 2 步 · MLA:四代演进 → 压缩怎么做 → 三个爆点 全课最重的 15 分钟。讲不完别的可以砍,这段不能砍
27–40第 3、4 步 · Dense → MoE:先讲 dense 的四条好性质, 再讲 MoE 拿它们换了什么 「MoE 省算力不省显存」这一条要点破
40–48第 5、6 步 · 层间与出口:残差流是总线;出口 logits 的爆点 顺手立住「激活为什么不能全留」这个问题,不答
48–60第 7 + 7.5 步 · 合账 + 我们真跑过 落到「至少 15 张卡」,然后抛钩子给专题二

现场怎么用课件:投影时把浏览器开到 课件,讲到哪一步滚到哪。 顶上那条控制台随时可以拖 —— 讲 attention 平方项那一段, 当场把序列长度从 4K 推到 1M,比说十句话都管用。

⛔ 站上讲台之前,先记住这一条 不确定的,当场说「这个我不确定,回头查了告诉你」。 绝对不要顺口编一个听起来合理的解释。

编出来的东西最危险的地方在于它看起来最像真的 —— 它总是长得像一条正确的知识,所以自己也发现不了。 而台下只要有一个人真懂,你这一整节课的可信度就没了。

这份讲义里凡是标了「不确定」的地方,都请照原样说出口。 真实的例子:本课曾把「7168 = 128 头 × 56」当成一条架构约束讲出去 —— 128 × 56 数学上确实等于 7168,但 V3 的每头维度是 128 不是 56, 这条约束在 MLA 上根本不成立。这就是编的代价。
00

先定尺子:这门课怎么算账

⏱ 3 分钟
这一讲要留下什么 先定尺子,再量东西,中途不换。 学员要接受一件事:这一课不给结论,只算账;而算账之前必须把口径钉死。

讲稿

课件停在首屏,先不往下滚。

先说这一课的方法,因为它跟大多数讲大模型的材料不太一样。

市面上的材料,绝大多数是在给你结论 —— 告诉你 MoE 省算力、 MLA 省显存、FlashAttention 更快。这些话都对,但它们是听来的。 听来的东西有个问题:换一个场景你就不知道它还成不成立。

这一课只做一件事:算账。

具体怎么算?拿 DeepSeek V3 这个真实的模型,让一个 token 从进去到出来走完全程。 它每经过一个部件,我们就当场问同样的四个问题 —— 这一步的权重有多大、产生的激活有多大、要做多少次乘法, 还有第四个,也是最重要的一个:有没有什么东西在这里爆炸。

你会发现,最后那个问题往往才是重点。

停一拍。下面这段是这一讲真正的内容。

但要算账,就得先有尺子。算账最容易出的问题不是算错,是中途换了尺子。 所以我们先把口径钉死,后面一路不改。

滚到「算账口径」那四张卡片。

五条。第一,序列长度取 128K,也就是十三万一千零七十二个 token —— 差不多是一本小书。第二,batch 等于一,先把单独一条算清楚,要乘再乘。 第三,精度按 bf16,每个数占两个字节。第四,只算前向,不算反向。 第五,参照物是一个 TPU v7 device 的九十四点七四个 GB —— 所有算出来的数,最后都要拿它去除一下,不然没有感觉。

这五条里,最需要解释的是第四条。为什么只算前向?

这里可以问一句「有人猜到为什么吗」,然后自己答。

因为前向的账是事实,反向的账掺着决策

前向就是一步一步的矩阵乘,每一步多大是确定的,没得选。 但反向不一样 —— 你得先决定:算完的中间结果要不要留着、还是等用到时重算; 优化器状态放哪张卡;梯度要不要攒几步再更新一次。 这些都是策略,不同的人选不同,算出来的数就完全不一样。

把决策混进事实里,这笔账就说不清了。所以这一课只算 所有人都必须付的那部分。反向留到专题四单独讲,那时候我们已经会算账了, 再谈策略才谈得动。

如果台下有人显得不耐烦,直接跳到「先猜一个数」那块互动, 让他们先猜完再回来补口径。

好,尺子立完了。下面从第一步开始 —— 一个字,是怎么变成一串数的。

可能被问到

为什么是 128K?不是 4K 或者 1M?

因为 128K 是当下的常态,而且它刚好在拐点上。 4K 的时候很多问题看不出来 —— 后面会算到,4K 下注意力只吃掉一成算力, 一切都很太平。到了 128K,注意力吃掉八成,整个瓶颈换了个地方。

而且课件顶上那条滑块随时可以改。讲到关键处当场拖给他们看, 比解释十句管用。

batch 只有 1,是不是太不真实了?

是不真实,这是故意的。 batch 是一个乘数, 它会把每一项都放大同样的倍数,反而让人看不清各项之间的比例。

先用 batch=1 把结构看清楚,要看真实场景就把滑块拨到 8 —— 顶上就有。

bf16 是什么?为什么不用 fp32 或者 fp8?

简单说,bf16 是现在训练和推理的默认精度,一个数两个字节。 fp32 是四个字节,精度更高但贵一倍;fp8 是一个字节,更省但有数值风险。

这一讲不展开 —— 精度这件事本身够讲一整课,在专题八。 这里只要知道「一个数占两个字节」就够算账了。

九十四点七四这个数,是怎么来的?

这是一个 TPU v7 device 实际能用的 HBM 容量, 是硬件常数,不是我们测出来的。

如果有人追问「为什么是这个数、卡内部长什么样」—— 那正是专题二要讲的,先记下来别展开,这里会把时间吃光。

576 是怎么来的?为什么不是 512

512 是压缩后的 KV,64 是 RoPE 那一截,两段拼起来才是一个 token 每层要存的东西。 配置文件里就是 kv_lora_rank = 512qk_rope_head_dim = 64

为什么 RoPE 那 64 维不能一起压?这里只答一句「它带着位置信息,压了就废了」, 不要展开 —— 完整的理由在第 2 步,那里有一整段专门讲这个不对称。开场展开必然超时。

既然 MQA 只要 3.81 GiB,比 MLA 还省,为什么不直接用 MQA

这是最好的一个提问,因为它就是第 2 步的题眼 —— 但现在不要答完。

只说这一句:MQA 是靠「让所有头共用同一份 K/V」省出来的,省的同时表达力是真丢了、 找不回来;MLA 省得几乎一样多,却没让任何一个头做出牺牲。怎么做到的,第 2 步整节都在讲。

然后把这个问题挂在黑板上,讲到 MLA 那一段回头指给他看。 开场留一个钩子,比开场答完一个问题值钱。

那反向大概要多少?能先说个数吗?

可以给一个方向性的答案压住场:算力大约变成三倍, 显存里最大的一块其实是优化器状态,比模型本身还大。

到此为止,不要展开。这句话的作用是让他们知道「后面有账等着」, 不是现在就把专题四讲了。

别讲什么

这一讲最容易跑偏的三个方向 ① 别开始讲 Transformer 是什么。 台下如果有人不熟, 也是靠后面「跟着 token 走」这条线自然补上的,不要在开场做前置知识扫盲 —— 一讲就是十分钟,整节课的节奏全毁。
② 别解释 bf16 的位布局。 提一句「两个字节」就走。
③ 别在「为什么是 94.74」上停留。 那是专题二。
节奏提示 这一讲三分钟必须讲完。它是地基,但地基不是房子 —— 学员真正会被抓住的是第 1 步那个「还没进第一层,显存已经掉一块」的瞬间。 早点走到那儿。
01

模型底牌:那张表怎么读

⏱ 2 分钟
这一讲要留下什么 这张表里每一个数都来自官方公开的配置文件 —— 没有一个是估的,也没有一个是我们测的。 这句话决定了台下信不信你后面算出来的东西,必须说出口

讲稿

滚到「模型底牌」那三张表。

这张表有二十多行,但千万别逐行念 —— 念完人就走神了, 而且这些数一次也记不住。

整张表你只需要当场挑出四个数,其余的等用到再回头指。

下面四个数,边说边用鼠标在表上点出来。

第一个,hidden_size 等于 7168。 这是残差流的宽度 —— 也就是那条贯穿全程的主干道有多宽。 后面每一步算激活,都是拿 token 数乘以它。 如果台下只能记住一个数,就让他们记这个。

第二个,61 层。 这个数本身不稀奇。稀奇的是紧挨着它的那一行 —— first_k_dense_replace 等于 3。意思是前 3 层是普通的 MLP, 从第 4 层开始才是 MoE。所以准确说是「3 层 dense 加 58 层 MoE」。 这个 3 和 58,后面算参数量的时候要反复用。

第三个数其实是个 false。 tie_word_embeddings = false。 这个字段的意思是:进门那张查词表的大矩阵,和出门那张把向量变回词的大矩阵, 不共享权重,是两份独立的。 很多模型选择共享,省一份参数;V3 选择不共享,代价是多出九亿多个参数。

这里埋一个钩子,别答。

可以顺口问一句:「为什么有的模型共享,有的不共享?」—— 然后说「这个留到最后一步,我们走到出口的时候再说」。不要现在答。

第四个不是一个数,是一句话,而且是这一讲最重要的一句:

这张表里的每一个数,都来自 DeepSeek 官方公开的配置文件。 没有一个是估的,也没有一个是我们自己测的。 后面所有的推导,都从这张表出发。

这句话必须说,因为它决定了台下信不信你后面算出来的东西。 等到最后合账那一步,我们算出总参数六千七百一十亿、激活三百六十六亿 —— 刚好对上官方公布的口径,那一刻这句话就兑现了。

可能被问到

7168 这个宽度是怎么定出来的?

两条硬的:要对齐硬件的矩阵单元(7168 是 128 的整数倍), 要跟层数配平 —— 同样的参数预算,做宽一点还是做深一点。 后面这条取舍每个模型都要重做一次,没有标准答案。

⚠️ 别说「7168 要能被头数整除」——V3 上这条不成立。 128 个头、每头 128 维,注意力内部是 16,384 维,是 7168 的 2.29 倍, 最后靠 o_proj 压回来。标准 MHA 里这两个数必须相等, MLA 把这条约束解除了。

顺带:这就是 o_proj 为什么是 MLA 一层里最大的那块权重 (117.44 M,占一层 63%)—— 它跨着 16384 → 7168 这道口子。 第 2 步讲到那张五行表的时候可以回头点这一句。

词表 129,280 为什么是这么个怪数?

能确定的部分:词表大小是训练分词器时定的,是一个权衡 —— 往大做,一个词占一个 token,序列变短算得快,但进出口那两张大矩阵都跟着变大; 往小做正相反。129,280 是在这两头之间选的一个点。

能自己验的一条:129,280 ÷ 128 = 1,010,整除, 对硬件的矩阵单元是友好的形状。

不确定的部分要老实说:DeepSeek 没有公开解释过这个数具体怎么定的。 被问到就答「官方没说,我不确定」 —— 别顺口编一个听起来合理的理由。

MTP 那一行是什么?

num_nextn_predict_layers = 1,意思是除了预测下一个 token, 还额外预测下下一个。这里提一句就够,第 6 步会展开 —— 它在训练和推理里的用途完全不同,那才是有意思的地方。

为什么前 3 层不做 MoE?

好问题,但先压住 —— 而且这里千万别顺口答「因为前几层比较通用」。 那个说法流传很广,但不是论文给的理由,第 3 步会专门拆它。

现在只答一句:这个做法最早的公开理由是训练稳定性 (DeepSeekMoE 观察到第一层的负载均衡收敛特别慢), 而 V3 为什么从 1 层改成 3 层,论文没有解释。

完整的链条在第 3 步,那里有一张图专门讲这件事。

别讲什么

这一讲最容易跑偏的两个方向 ① 别逐行解释每个字段。 尤其是 MLA 那六个字段 (q_lora_rankkv_lora_rank 那些)—— 它们要在第 2 步才有意义,现在讲等于讲两遍,而且第一遍必然讲不明白。
② 别解释 YaRN。 max_position_embeddings 那一行有人会好奇, 答「靠一种外推方法从 4K 撑到 16 万,专题三讲」就走。
节奏提示 两分钟。 这一讲的功能是「发一份底牌给大家,让后面有据可查」, 不是「把模型讲一遍」。讲超过三分钟就是跑偏了。
02

一个字怎么变成一串数

⏱ 8 分钟
这一讲要留下什么 还没进第一层,显存已经掉了一块。 这一步什么都还没「算」,只是查了张表 —— 但 3.48 GiB 已经没了。 这是全课第一次让台下感到「原来账是这么起来的」。

讲稿

滚到「第 1 步 · 从文字到向量」—— 左边一块词表矩阵、右边一条残差流张量、 中间一根「查表」箭头的那张图。

先说清楚输入端到底发生了什么。一句话进来,先被切成 token。

token 不等于字,也不等于词。 中文常见字大多一个字一个 token, 英文一个长单词可能被拆成两三段。切完之后,每个 token 就是一个整数编号 —— 在 V3 上这个编号的范围是 0 到 129,279,因为词表是 129,280。

滚到「分词:129,280 是怎么来的」下面那张横轴图 —— 上面绿色一条、下面红色一条、中间一根轴,讲的就是词表大小的两头拉扯这张图对着讲,不要念文字。

台下第一个反应通常是「129,280 这个数是怎么来的」。 先给结论:它一点都不特别。 看这根轴 —— Llama 2 是 3.2 万,Llama 3 是 12.8 万,V3 是 12.9 万, Qwen3 15.2 万,gpt-oss 20 万,Gemma 2 是 25.6 万。 V3 和 Llama 3 几乎就是同一个点。

指绿色那条词表往大做,赚的是:同一句话切成更少的 token。 序列变短,后面注意力里那个平方项立刻跟着掉 —— 这条到第 2 步会兑现。

指红色那条赔的有三样:两张表变大、 罕见词分到的样本变少所以学不好、出口那层要在整张词表上算 softmax。

这里有一句要说准 —— 「两张表变大」这一条,在 V3 身上几乎不成立。 因为表的大小只跟「词表 × 宽度」有关,跟 61 层一点关系都没有, 所以模型越大这笔占比越小:两张表加起来只占 671 B 的 0.28%。 真正拉住词表不让它继续变大的,是另外两条。

停一下。下面这句是这一讲的第一个反直觉点。

从编号变成向量,用的不是乘法,是查表。

模型里存着一张 129,280 × 7,168 的大表。 你的 token 编号是多少,就直接把第几行抽出来。 没有矩阵乘,没有加法,就是按下标取一行

这是整个模型里最朴素的一步,也是唯一一步几乎不消耗算力的。 但它一点都不便宜 —— 因为那张表本身, 129,280 乘 7,168 等于 9 亿 2668 万个参数, bf16 下占 1.73 GiB

指着「残差流张量」那一行。

接下来是第二笔账。查完表,128K 个 token 每个都变成了一个 7,168 维的向量, 摞在一起就是一个 131,072 × 7,168 的张量。 bf16 两个字节,算下来 1.75 GiB

这里把两笔加起来,慢一点说。

一张表 1.73,一个张量 1.75,加起来 3.48 GiB。 而我们还没有进第一层。

这句话是这一讲的落点。说完停两秒,让它落地。

指着页面上那条贯穿全文的主干道示意(残差流那张图)。

最后交代一件事,它决定了后面 61 层怎么理解。

刚才那个 1.75 GiB 的张量不是用完就扔的中间结果, 它就是残差流 —— 一条从第 1 层一直通到第 61 层的主干道。 每一层都从它上面读一次,算完再写回去一次。

所以 7,168 这个宽度不是某一层的内部尺寸,是全程的主干道宽度。 这也是为什么上一讲说「只记一个数就记它」 —— 后面每一步要算激活占多少显存,公式都是 token 数 × 7,168 × 字节数

如果台下问「7,168 这个数怎么定的」, 展开那个「哪一半是硬约束」折叠块,对着里面讲。不问就别展开。

那一块要讲的就一句话:7,168 能被 128 整除,也能被 256 整除 —— TPU 上矩阵单元是 128 或 256 见方,NVIDIA 那边 FP16 要求维度对齐到 8、 A100 上对齐到 64。它两边都不用补齐。

但紧接着要补一句这只解释了「为什么不会是 7,000 或 7,200」, 没有解释为什么不是 6,144 或 8,192 —— 那两个也一样整除。 宽还是深,DeepSeek 从没公开说过怎么定的。

可能被问到

查表和「one-hot 乘一个矩阵」不是一回事吗?

数学上完全等价,工程上差着天。 一个 one-hot 向量长度是 129,280, 128K 个 token 摞起来是 131,072 × 129,280 的矩阵 —— 光这个中间量就 31.56 GiB,而且里面 99.999% 是 0。

所以实现上一律是按下标取行。这是个很好的例子: 同一个数学式子,换一种算法,代价差几个数量级。 后面 FlashAttention 也是同一个道理。

那位置信息呢?第几个 token 是在这里编进去的吗?

不是。V3 这一步只有内容,没有位置。 位置是在第 2 步的注意力里、用 RoPE 现场注入的 —— config 里 qk_rope_head_dim = 64 那一行就是它。

早期的 Transformer 确实是把位置编码加在嵌入上的, 现在主流模型基本都挪进注意力了。 为什么挪,属于专题三的内容,这里点到为止。

1.73 GiB 那张表,会一直占着显存吗?

会。 它是权重,从模型加载到卸载全程常驻。

而且要提醒一句:出口那张表是另外一份,不是同一块显存 —— 上一讲说的 tie_word_embeddings = false 就是这个意思。 进出口两张 926.68 M 的表,加起来 3.45 GiB。 这笔账到第 6 步会再翻出来算一次。

词表做大有那么多好处,为什么不干脆做到 100 万?

先给能站住的部分。 有一篇专门做词表缩放律的工作 (Scaling Laws with Vocabulary, NeurIPS 2024, arXiv 2407.13623) 的结论确实是「大多数 LLM 的词表都偏小」 —— 按它拟合的公式,Llama2-70B 的最优词表应该在 21.6 万 以上, 而它实际只有 3.2 万;同样算力预算下把词表从 32K 提到 43K, ARC-Challenge 从 29.1 提到 32.0

⚠️ 但答的时候必须带上边界:那篇是在 33 M 到 3 B 的 dense 模型上拟合的, 外推到 671 B 的 MoE 上没有依据它能说明方向,不能用来论证 129,280 就是 V3 的最优解。

至于「为什么不到 100 万」:罕见词会学成死词 —— 这类现象有专门的名字叫 under-trained / glitch token (arXiv 2405.05417,就是著名的 SolidGoldMagikarp 那一类, 一碰就让模型输出乱码)。再加上出口那层 softmax 越来越贵。

1.75 GiB 是 128K 全塞进去才有的吧?平时哪有这么长?

问得对,而且这正是我们要台下自己发现的规律。 当场把控制台的序列长度拨到 4K,让他们看着这个数字掉下来 —— 4K 时只有 56 MiB,128K 时 1.75 GiB,整整 32 倍,因为它是线性的。

线性还算是好消息。 第 2 步会看到一个平方增长的东西, 那才是真正的麻烦。这里可以埋这个钩子。

别讲什么

这一讲最容易跑偏的三个方向 ① 别讲分词算法。 BPE 怎么合并、词表怎么训出来 —— 跟这门课的主线(账怎么算)没有关系,一讲就是十分钟。 被问到答「分词器是单独训的,专题里不展开」。
② 别讲「嵌入向量为什么能表达语义」。 国王减男人加女人那套很好听,但它属于「模型为什么work」,不属于「资源账怎么算」。 这门课不碰前者。
③ 别说「7,168 要能被头数整除」。 上一讲已经埋过这个雷 —— V3 是 128 个头、每头输出 128 维,注意力内部 16,384 维,根本不等于 7,168。 MLA 把这条老约束解除了。
④ 别把「对齐硬件」讲成「所以他们选了 7168」。 对齐是硬约束,宽深比是取舍,这是两件事。 6,144 和 8,192 同样能整除 128 和 256 —— 一旦把约束说成理由,台下懂行的人一句话就能把你问停。
节奏提示 八分钟,但真正的重量只在最后两分钟。 前一分钟对着词表那张横轴图讲「129,280 不特别」, 中间五分钟讲清「查表」和两笔账, 最后两分钟讲「残差流是一条贯穿全程的主干道」—— 后者是给整个专题搭骨架的,讲慢一点。
这一讲结束时台下应该有一种感觉:「这才第一步,就已经 3.48 GiB 了?」 有这个感觉,第 2 步就好讲了。
关于页面上那些「展开」块 默认全是收起的,讲的时候一个都不用点开。 它们装的是推导过程、论文出处、以及「这句话要说多准」那类边界 —— 属于台下课后自己看的部分
只有被追问时才当场展开,对着里面讲。 (导出 PDF 时会自动全部展开,存档不会漏内容。)
03

KV cache 为什么会失控

⏱ 4 分钟
这一讲要留下什么 两个线性相乘,就是灾难。 这一讲不要给答案 —— 目的是让台下自己得出「这东西非改不可」。 你只负责把账摆出来,结论他们自己会走到。

讲稿

滚到「先看死穴:KV cache 为什么会失控」。

先说清楚这东西为什么必须存在。生成文字是一个字一个字往外蹦的, 每蹦一个新字,模型都得回头看前面已有的所有字。

问题是,如果什么都不存,每蹦一个字,前面几万个字的中间结果就得从头重算一遍。 第二个字重算一遍,第三个字再重算一遍 —— 越往后越亏

所以做法很自然:把前面每个字算出来的那份中间结果存下来,下次直接拿。 存的这两样东西叫 K 和 V,存它们的地方就叫 KV cache。 这一步本身没毛病,是标准操作,所有模型都这么干。

停。下面开始讲「麻烦出在哪」。

麻烦出在它有多大,而且这个账有两个方向同时在长。

第一个方向:序列越长存得越多。 聊了十万个字,就得存十万份。
第二个方向:模型有多少层,每层都得存自己那一份。 61 层就是 61 倍。

单看哪一个都不吓人。但它俩是相乘的关系。 一个东西同时按两个方向线性长,乘起来就是灾难。

指着那张对数阶梯图 —— 从左往右一根柱子一根柱子地走。

但「灾难」两个字太虚,我们把账一步一步堆出来。 先假设 V3 用的是最传统的注意力,也就是不做任何优化的版本。

第一根柱子:一个 token、一层。 要存 32,768 个数 —— K 和 V 两份、128 个头、每头 128 维,乘起来就是它。bf16 下 64 KiB微不足道。

第二根:乘 61 层。 一个 token 走完全程,3.81 MiB还是不痛不痒。

第三根:乘 131,072 个 token。 488 GiB。

说完这个数停两秒。这是这一讲的第一个爆点。

提醒台下看纵轴:这张图是对数刻度,每一格 10 倍 —— 必须这样,否则第一根柱子细到看不见。 从最左到最右跨了将近 800 万倍, 而中间只做了两件极普通的事:乘层数,乘 token 数

指红色虚线,然后指下面那个红框里的数字。

把它换算成能感觉到的东西。TPU v7 一张卡的显存是 94.74 GB, 这堆东西要五张多卡才装得下。而且这是一个用户、一段对话。 你要同时服务一百个人,就得再乘一百。

更刺眼的说法是这一句,可以直接背下来

照这个存法,一段 23,699 个 token 的对话,光缓存就吃满一整张卡 —— 模型权重还一个字节都没放进去。

滚到喇叭口那张图。

最后看它随长度怎么变。横轴是序列长度,纵轴是 KV cache,两条线 —— 红的是传统存法,蓝的是 MLA。

两条都是直线,因为都是线性的。但斜率差 57 倍, 于是它们从同一个原点出发,越走越张开中间那个红色的口子,就是换一种存法能省下来的。

这里有一句话值得说出口:它不是一个固定的数,是一个随长度张开的口子。 上下文越长,换存法的收益越大 —— 这就是为什么长上下文一出现, 所有人都在改注意力。

这一讲到此为止。不要往下说 MLA 怎么做的。

收尾一句就够:「到这儿,该怎么办已经不用我说了。」 停一下,让他们自己接。

可能被问到

不缓存、每次重算不行吗?拿算力换显存。

技术上可以,代价大到没人这么干。 不缓存的话, 生成第 n 个 token 要重算前面 n 个,整段生成下来总计算量从线性变成平方

而且方向反了:生成阶段本来就是被显存带宽卡着的,不是被算力卡着的。 拿算力换显存,是拿更缺的换没那么缺的。

不过「部分重算」确实是真实存在的手段,在训练里叫重计算, 那是另一个话题,这里不展开。

放不下能不能挪到 CPU 内存或者硬盘?

可以,这叫 offload,工程上是成熟做法。 但它把问题从「装不下」换成了「搬得慢」—— 每生成一个 token 都要把这堆东西搬一趟,带宽立刻变成新瓶颈。

这属于推理系统的范畴,本专题只算账不讲方案。 答一句「这是另一条路,代价是带宽」就走。

用 fp8 存 KV,不就直接减半了吗?

问得非常对,而且这是真实在用的手段。 当场把控制台的精度切到 fp8,让他们看着 488 GiB 变成 244 GiB。

但要说清楚它和 MLA 不是一回事:降精度是把每个数存小一点, MLA 是少存一些数两者可以叠加 —— 而且实际系统里经常一起用。

降精度有质量代价,能降到什么程度是个实验问题,这门课不下结论。

batch 变大呢?同时服务很多人?

再乘一层,而且是完全线性的。 把 batch 滑块拉到 8,让他们看着数字翻 8 倍。

这里可以点一句真实的工程后果:KV cache 是「每个用户一份」的, 权重是所有人共享一份的。所以服务的人越多,KV cache 在显存里的占比越大 —— 到某个点上,它会超过权重本身。

别讲什么

这一讲最容易跑偏的三个方向 ① 千万别提前说 MLA 怎么做的。 这一讲的全部价值在于 把人逼到「非改不可」那个点上,然后停住提前给答案,下一讲就没劲了。
② 别讲 PagedAttention / 显存碎片管理。 那是推理系统的工程, 跟「账为什么这么大」是两件事,一讲就收不住。
③ 别把 KV cache 和权重混着说。 反复强调: 权重是所有人共享的一份,KV cache 是每个用户一份的。 这个区分后面第 7 步合账时还要用。
节奏提示 四分钟,但它是整个专题的转折点。 前三讲都在「介绍」,这一讲第一次出现「出问题了」
两张图各给一分钟,阶梯图讲「账怎么堆起来的」,喇叭口讲「它随长度怎么张开」最后那句「该怎么办已经不用我说了」说完一定要停 —— 那两秒钟的沉默,比你再讲三句都管用。
04

三个旋钮,四代方案

⏱ 4 分钟
这一讲要留下什么 前三代拿质量换显存,MLA 拿算力换显存。 上一讲把人逼到「非改不可」,这一讲告诉他「能改的地方一共就那么几个」 —— 先收窄搜索空间,下一讲的细节才听得进去。

讲稿

滚到「三个旋钮,四代方案」。

上一讲停在「该怎么办已经不用我说了」。这一讲就把「怎么办」摊开 —— 但先说件让人安心的事:能改的地方一共就那么几个,数得过来。

把上一讲那个公式中间那一项拆开:每 token 每层存多少个数 = 2 × KV 头数 × 每头维度2 是 K 和 V 各一份,动不了。剩下两个,再加上层数,就是全部旋钮。

指那张三行的表,一行一行走。

① 存几份 —— KV 头数,V3 是 128。这是唯一被反复拧的那个,因为它最猛: 拧到 8 就是除以 16,拧到 1 就是除以 128。代价也最直白 —— 多个 Q 头被迫共用同一份 K 和 V,丢掉的表达力找不回来

② 每份多宽 —— 每头维度,业界基本钉在 128 没人动。两个原因: 它决定单个头能装多少信息,砍窄了每个头都变笨; 而且 kernel 只对几个固定宽度优化过,你省了显存,但跑不快

③ 存几层 —— 让相邻几层共用一份,或者只让部分层看全长。 这个旋钮跟前两个不是一回事:它改的是「存不存」,不是「存多少」, 所以它不在四代那张表里。台下要是问起,一句话带过就行。

停。三个旋钮说完了,再进四代。 滚到四代那张图,从左往右一块一块指。

四代的历史,其实就是第一个旋钮从 128 拧到 1 的历史。

MHA,2017,Transformer 原版,每个头一份 KV。质量最好,cache 最大。

MQA,2019,Shazeer 一篇很短的论文,标题就叫「一个写头就够了」。 所有头共用一份,直接除以头数

这里有个细节值得说出来,因为它解释了后面这十年 —— Shazeer 当年要解决的不是「装不下」,是「搬不动」: 生成每个字都要把 K 和 V 从显存里重新读一遍,卡的是带宽「装不下」是后来长上下文时代才有的问题。

GQA,2023,Ainslie 他们。不共用一份、也不各存一份,分组共用, 是前两者之间的插值。它能成为业界默认还有一个很现实的原因: 论文给了一个配方,用原始预训练 5% 的算力,就能把已有的 MHA 模型改造成 GQA —— 不用从头训。

停一下。下面是这一讲的爆点。 指最右边那块蓝的。

现在看 MLA。它一个旋钮都没拧。

128 个头还是 128 个头,每头还是 128 维,61 层一层不少。 它换的是存什么 —— 不存展开后的 K 和 V,改存一个 512 维的压缩向量, 外加 64 维单独走的位置信息,一共 576 个数

让他们回头看四代对照表里那一列数字。

576 卡在 GQA 的 2,048 和 MQA 的 256 中间 —— 但质量比两个都好。

这句话说完要停,让他们自己想一下哪里不对。 在同一根轴上,这是不可能的 —— 省得比 GQA 多、质量还比 GQA 好?没这种好事。 它做得到,正是因为它根本不在那根轴上。

停两秒。然后给落点。

所以这一讲真正的落点,不是那四个名字,是一句话:

前三代拿质量换显存,MLA 拿算力换显存。

前三代是把信息删掉,删掉的回不来。 MLA 是把信息压起来,要用的时候现场展开成 128 个头 —— 信息没丢,代价是每次用都要多算一遍。

这个形状,做过训练的人应该觉得眼熟:不存中间结果、要用时重算一遍, 那就是重计算。某种意义上,MLA 就是对 KV cache 做重计算。

指黄色那个提醒框。

最后必须补一句边界,这句不补会误导人,但补错了更误导人「MLA 省显存」是一句推理侧的话 —— 因为训练里根本没有 KV cache。 缓存是解码才有的东西:一次吐一个字,前面的 K/V 要留着给下一步复用。 训练整条序列一次前向,没有下一步要复用,无从缓存。

训练侧对应的是激活显存,而到了这个量级重计算是开到顶的 —— 一层只留层入口那条残差流,展开成 128 个头的 K/V 是重算出来、用完就扔的。 所以 MHA 和 MLA 存下来的是同一条残差流,这笔账上打平,不是 MLA 赢。

不要说成「MLA 对训练没用」,那是另一个方向的错。 它在训练侧吃得到的是两处,都留到专题四:重计算松一档时可以只存 512 维隐向量; 上下文并行传 K/V 时传的是隐向量,通信量按同一个倍数掉。

收尾直接抛给下一讲:「压成 512、再展开回 128 个头 —— 听着像变魔术。 下一讲就拆开看它到底怎么做到的,以及为什么位置信息那 64 维必须单独走一条路。」

可能被问到

MQA 2019 年就有了,为什么等到 2023 才大规模用?

可以答得有底气,但要分清哪句是论文说的、哪句是解读。

论文里的:Shazeer 那篇的动机写得很清楚 —— 生成时反复加载 K/V 张量的内存带宽开销,不是显存装不下

我的解读:2019 年序列长度是几百到两千,KV cache 在总显存里根本不显眼, 旋钮在那儿,但不值得拧。长上下文和大 batch 推理起来之后, 同一个旋钮的收益翻了几个数量级,这才有人真去拧。

一句话收:旋钮一直在,值不值得拧,看当时什么最贵。

既然 MLA 更好,为什么 Llama、Qwen 这些还在用 GQA?

先声明这是工程判断,不是实验结论。三个现实原因:

GQA 有 uptrain 配方,5% 算力就能从已有 MHA 模型改过来,MLA 得从头训; MLA 实现复杂得多,尤其推理侧要做矩阵吸收,生态支持来得晚; GQA 基本是一行配置,MLA 是一整套结构改动。

「更好」和「更容易被采用」是两件事 —— 这在基础设施里到处都是。 别在这个问题上预测谁将来赢。

每头维度真的不能动吗?

能动,只是很少动。有模型用 64,也有用 256 的。

不动的原因不是理论禁止,是两头都不划算:砍窄了每个头能装的信息变少, 加宽了 cache 又涨回去。而且注意力 kernel 通常只对几个固定宽度优化过, 挑一个奇怪的宽度,显存省了却跑得慢

三个旋钮能不能一起拧?

能,而且现实系统就是一起拧的。 GQA + KV 量化 + 滑动窗口,三样叠着上是常见配置;MLA 也能再叠 fp8。

但要提醒一句:显存收益是相乘的,质量损失却不一定是相加的 —— 叠到什么程度是实验问题,这门课不给数。

线性注意力、滑动窗口,算第几代?

它们不在这条线上。这张表比的是「每个 token 每层存多少个数」, 而它们改的是「存不存、存多久」 —— 一个把历史压成固定大小的状态,一个干脆丢掉远处的。

是另一条支线,一句话带过,别在这儿展开。

别讲什么

这一讲最容易跑偏的三个方向 ① 别在这里讲 MLA 怎么实现。 这一讲只回答「它换了哪根轴」, 怎么压、RoPE 怎么走,是下一讲的全部内容提前讲,下一讲就空了。
② 别把「MQA 质量很差」说死。 Shazeer 自己的论文写的是 only minor quality degradation,是 GQA 那篇才强调它会掉质量。 图上写「掉得明显」是为了对比清楚,嘴上要留余地 —— 说「够用,但明显不如 MHA,所以才有了 GQA」。
③ 别把「MLA 省显存」说成通用结论。 一定要补推理侧那句边界。 台下真做训练的人会当场抓这一点 —— 你自己先说出来,比被问出来好。
节奏提示 四分钟,三段:旋钮 1 分钟,四代 1 分半,MLA 换轴 1 分半。 前两段是铺垫,语速可以快;「它一个旋钮都没拧」那句要慢,说完停两秒。
这一讲跟上一讲是一对:上一讲把人逼到「非改不可」,这一讲告诉他「能改的就这几处」 —— 收窄了搜索空间,人才听得进下一讲的细节。
台下如果有人已经知道 MLA,这一讲最容易被抢答。 抢答了就顺势请他讲,然后你补那句推理侧的边界 —— 那句他大概率不会说。
05

MLA 怎么做的 · RoPE 为什么必须单独走

⏱ 4 分钟
这一讲要留下什么 存 576 个数,用的时候现场展开成 128 个头 —— 拿算力换显存。 而那 64 维位置之所以必须单独走,是因为位置一旦掺进被压缩的那部分, 推理时一个很值钱的省法就没了
这一讲的定位 只讲思路,不讲 shape。 每一步张量长什么样、几条路怎么分岔, 是下一讲(05⁺⁺)的全部内容 —— 这里讲了,那里就空了。
你在这一讲欠的债,下一讲会还。所以放心地说「这个留到下一讲」。

讲稿

滚到「MLA 到底怎么做的」。左边是文字,右边有张小图,两边对着讲。

上一讲说 MLA 换了根轴。这一讲就把那根轴具体是什么说清楚,一句话: 存的时候压,用的时候升。

具体做法是 —— 一个 token 的 KV 信息,不按 128 个头分别存, 先压成一个 512 维的隐向量存起来;真要算注意力了, 再用一个矩阵把它升回 128 个头

这里要强调一下:这个「升」是每次用都要做一遍的。 这就是上一讲那句「拿算力换显存」的落地方式 —— 显存里躺着的是 512,算的时候现场变出 128 个头。

停。压缩这半讲完了。下面是这一讲更难的那一半。

为什么位置信息不能跟着一起压? 这是整个 MLA 里最绕的一处。

指蓝色那个「先说清楚 RoPE 是什么」的框。

先花二十秒说清 RoPE 是什么,台下多半只是听过这个词

注意力本身看不见顺序 —— 把句子里的词打乱,算出来一模一样。 所以必须额外告诉它谁在前、谁在后。

RoPE 的做法很巧:不给向量加任何东西,而是按位置把它转一个角度 —— 第 1 个 token 转 1 度,第 100 个转 100 度。 两个向量做点积的时候,结果自然就带上了它们的角度差,也就是相对距离

这里补一句好记的:它没往向量里塞任何新数字,只是把向量转了个方向。

停一下。下面这段是全课最容易讲糊的地方,慢下来。

麻烦就出在这个「转」上:转多少度,取决于这个 token 排在第几位。

如果先转角度、再压进隐向量,那这个隐向量里就掺进了位置。 而这个旋转是夹在两个矩阵中间的 —— 矩阵乘法不满足交换律,夹在中间的东西挪不走。

下面这句必须说准,说错了台下懂行的人会当场纠正你:

坏掉的不是正确性。 那样做模型照样能训、照样能跑,答案也是对的。 坏掉的是推理时一个很值钱的省法 —— 代价是每生成一个字,都得把前面所有 token 的 K 重新算一遍

具体是哪个省法,别在这儿讲,留到下一讲。 这里只要台下接受「位置一旦掺进去,后面有笔账会变得很贵」,就够了。

指右边那张小图上金色那条支路。

所以 V3 的做法是把每个头劈成两半,各管一件事128 维不带位置,负责内容,可以压64 维带位置,负责顺序,不压,直接存

指「576 = 512 + 64」那个框。

576 = 512 + 64。这就是 MLA 每 token 每层存的全部东西。

停两秒。下面这句是这一讲真正的落点。

这个 576,跟头数没有关系。 128 个头是它,256 个头还是它 —— 头数从这笔账里彻底消失了。

这才是 MLA 和前三代最根本的区别: 前三代是在头数上做除法,MLA 直接把头数从公式里拿掉了。 做除法总有除尽的那一天,拿掉就没有这个上限。

收尾再占一个便宜:那 64 维是 128 个头共用同一份的。 要是每个头存一份,就得多存 128 × 64 = 8,192, cache 从 576 涨到 8,704 —— MLA 就白做了。

可能被问到

升回来的 K 和 V,跟原来直接算出来的一样吗?

不一样,这是有损压缩。 512 维装不下 32,768 维的全部信息。

但这恰恰是它成立的原因 —— 后面有一整讲专门回答 「那 32,768 个数里,本来就没有 32,768 个自由度」。

这里先给一句就走:它是有损的,而且损失不但可接受,实测质量反而更好。 为什么,下面会讲。 别在这儿展开。

既然每次用都要现场升维,算力是不是涨了?

涨了,但换的方向是对的。 生成阶段卡的是显存带宽不是算力, 拿没那么缺的换更缺的,划算。

但要顺手把上一讲那条边界再点一次: 这说的是推理。训练前向里,升开的那份 K 和 V 还得留成激活给反向用 —— 训练侧不省显存。

为什么是 512?那个 64 又是怎么定的?

公开资料没有给这两个数的推导。 论文给的是实验结果,不是推导过程。

能说的只有量级上的感觉:512 大约是 7,168 的十四分之一; 64 是常见的 RoPE 维度。

这是一个可以大方说「我不确定」的地方 —— 比编一个听起来合理的理由强得多。

RoPE 那 64 维所有头共用,不会打架吗?

论文里它就是一个 shared key,不是每头一份。

直觉上说得通:位置是「这个 token」的属性,不是「某个头」的属性 —— 「它排第几」这件事,对 128 个头来说都一样。

边界:这是从设计上讲得通的理由,不要说成论文给的论证

台下十有八九会追一句:那 Q 那 64 维为什么不也共用? —— 因为它不是纯位置,它是先从内容投出一个向量再去转。 角度每个头一样,被转的东西每个头不一样于是每个头拿到一条不同的「距离 → 分数」曲线

但真正的理由是账:Q 不进 cache,共用省不到东西,只白丢表达力; K 进 cache,共用能把 8,192 压成 64,那笔损失才值得认。 这个不对称不是数学要求的,是成本决定的。 详细推导在课件那个蓝框里,这里点到为止。

Q 也走了低秩(1,536),那又是为什么?

跟 cache 无关 —— Q 一个字节都不进 cache。

论文自己写的理由是:降低训练时的激活显存, 并且特意补了一句「即便它并不能减少 KV cache」。

这门课另外算出它还顺带省了权重(那张代价表里的第三笔)。 两个都成立,但要分清哪个是论文说的、哪个是我们算的。

别讲什么

这一讲最容易跑偏的三个方向 ① 别讲 shape 链。 每一步张量什么形状、三条路怎么分岔, 是下一讲的全部内容这里讲了,下一讲就空了。
② 别把 RoPE 本身展开。 复数旋转、频率怎么分配、YaRN 怎么外推 —— 一开口就是十分钟,而这门课不需要。 「按位置把向量转一个角度」这一句就够用。
③ 别把「压缩和旋转不能交换次序」说成正确性问题。 它是性能问题。 说成「算出来会错」,懂行的人会当场纠正你 —— 而且你后面「所以要隔离」那句的逻辑会跟着断掉。
节奏提示 四分钟,对半开:压缩 2 分钟,RoPE 2 分钟。
压缩那半很好讲,一句「存的时候压,用的时候升」就立住了,别拖
RoPE 那半慢下来,而且只讲到「后面有笔账会变得很贵」为止 —— 忍住不往下说,那是下一讲的钩子。
最后「头数从这笔账里消失了」那句要重一点 —— 它是第 2 步这一整段的收口,也是台下最可能带走的一句话。
05+

7,168 和 16,384:一条其实不存在的约束

⏱ 7 分钟
这一讲要留下什么 每个头都读入完整的 7,168,输入从来没有被切开。 「7,168 ÷ 128」说的是128 个头拼起来正好等于输入宽度那是拼接的结果,不是输入的分配 —— 而且它只是个惯例,不是定律。 MLA 出现之后,它连惯例都不再是了。
先声明:这一讲是从一次真实的讲错里长出来的 这门课在这个点上连着讲错过两次,而且第二次比第一次更根本。
第一次:把「7,168 = 128 个头 × 56 维」当成架构约束。 128 × 56 数学上确实等于 7,168,但 V3 每个头是 128 维
第二次:改对了数字,却仍然说成「7,168 切成 128 个头,每个头拿 7,168 ÷ 128」—— 这个说法把「切」放在了输入侧,是错的。 每个头的投影矩阵是 7,168 × d每个头都要看完整的输入128 份是拼出来的,不是分出来的。
两个错都可以在课上讲出来 —— 它们比任何抽象的提醒都有说服力, 而且台下多半人心里正在做同一个除法、脑补同一张切片图。

讲稿

滚到「⭐ 一个容易想当然的地方:7,168 和 16,384」。

先做一件事:请台下当场做个心算 —— 7,168 除以 128 个头,等于多少。

等两秒,会有人说出 56。别急着否定,先说下面这句。

这个除法背后藏着两个误解,而第二个比第一个更要命。

第一个误解简单:56 不是 V3 的每头维度。 V3 每个头是 128 维,128 个头拼起来是 16,384,是残差流的 2.29 倍。

停。下面这段是整节的地基,讲慢一点。

第二个误解是:以为输入被切开了。 很多人脑子里的画面是 —— 7,168 维排成一排,咔咔咔切成 128 段, 每段 56 维,一个头分一段

不是这样。输入从来没有被切开。

指图里左边那把扇子。

128 个头,每一个都读入完整的 7,168 维。 每个头有自己的一个投影矩阵,形状是「7,168 乘以它自己的输出维度」, 用这个矩阵把完整的 7,168 压到自己的工作空间里去。

所以在教科书那种取值下,每个头的矩阵是 7,168 × 56; 在 V3 上,每个头的矩阵是 7,168 × 128左边那个 7,168 是不变的 —— 每个头都要看全部。

那「切」在哪儿呢?切在输出侧。 128 个头各自输出一小段,拼起来才有那个总宽度。

所以那个除法真正在说的是:「128 个头拼起来,正好等于输入的宽度」 —— 它说的是拼接的结果,不是输入被分掉了。

这里可以停一下问:那这个「正好等于」是必须的吗?

看懂了上面这段,这个问题根本不用回答 —— 显然不必须。 既然每个头的矩阵是「7,168 乘以 d」,那个 d 从来就是自由的爱设多少设多少,128 个头拼出来是多宽就是多宽。

指上下两条的对比,尤其是每个格子里写的矩阵形状。

上面那条是教科书取值:每头 7,168 × 56,拼起来正好 7,168。
下面那条是 V3:每头 7,168 × 128,拼起来 16,384。 然后靠 o_proj 收回残差流。

两条的输入完全一样 —— 都是每个头读完整的 7,168。差别只在每个头输出多宽。

转折:那为什么八年来大家都选「正好等于」?

因为那是 2017 年那篇论文里的一个取值,然后被抄成了直觉。

原论文写多头注意力的时候,输出投影矩阵的形状定义是 「头数乘每头维度」乘「隐藏层宽度」 —— 从定义的第一天起,这两个数就允许不相等。

紧接着那句是:「在这项工作中,我们用 8 个头,每个头取 d_model 除以 h」「在这项工作中」——这六个字说明它是个选择。 论文还把理由写出来了:这样取值,总计算量才跟「单头、全宽度」差不多。 理由是算力预算,不是数学必要。

下一个问题才是重点:既然一直可以,为什么以前没人做?

因为把头做宽,是要付 KV cache 的钱的。

标准存法下,每个头的 K 和 V 都要各存一份 —— 头一宽,每一份就变大,cache 跟着线性涨。 宽 2.29 倍,cache 就涨 2.29 倍。

所以在老框架下,「注意力做多宽」和「缓存多贵」是同一个旋钮。 你想要表达力,就得掏显存。大多数人选择不掏。

指右边那条从实线变成打叉的链。

MLA 把这条链剪断了。 上一讲说过它只缓存那个压缩向量, cache 大小是「(压缩维度 + 位置那 64 维)乘层数」 —— 请注意这个式子里没有头数,也没有每头维度。

这不是我的推断,是论文里写的公式 —— V2 论文 Table 1 那一行写的是 (d_c + d_h^R) · l, 在 V3 上就是 (512 + 64) × 61。 口头讲不用报字母,说「576 乘 61 层」就行。 但如果台下有人拿着论文对,你要知道那 64 是算在里面的 —— 只说「压缩维度乘层数」会漏掉它。)

于是一个旋钮变成了两个。表达力归表达力,缓存成本归缓存成本。 V3 做的事,就是把「表达力」那个旋钮往上拧了 2.29 倍。

旁证:MLA 第一次出现的上一代 V2,残差流只有 5,120, 注意力内部同样是 128 × 128 = 16,384 —— 3.2 倍,比 V3 还激进。 官方没明说这是刻意的因果,但两代都这么配,很难当成巧合。

最后是代价。这一段千万别省,省了这一讲就成了单方面吹。 滚到「代价:撑宽要付三笔钱」那张图。先别说话,让台下看那条红色的竖虚线。

这张图上所有的条,都是对着中间那条红虚线量的。 虚线是「如果不撑宽、老老实实等宽做」的成本,定义为 1.00 倍。 条比虚线长,就是多付了钱。

这句说完停一下。整张图的读法就是这一句,读法不立住,后面三笔都白讲。

撑宽账面上要多付三笔,而 V3 只真的付了两笔。

指第一根红条。

第一笔,算力,2.29 倍。 工作维度从 7,168 撑到 16,384,注意力的计算量跟着涨。 这笔跑不掉 —— 而原论文当年设成「正好等于」,图的正是省这笔钱。 V3 是明确地拿算力换了表达力。

指第二根红条 —— 长度跟第一根一模一样,这不是巧合。

第二笔,o_proj 的权重,也是 2.29 倍。 它要跨 16,384 到 7,168 这道口子,一头宽了 2.29 倍,矩阵就大 2.29 倍。 51.38 M 变成 117.44 M,占了一层 MLA 权重的 63%。这笔也跑不掉。

两根一样长,是因为它们涨的是同一件事:那个 16,384。

指第三根 —— 黄色、虚线边框、最长的那根

第三笔本来最吓人。 128 个头,每个头一个 7,168 × 192 的 Q 投影,直接投是 176.16 M —— 3.43 倍,屏幕上这根条冲得最远。

但请注意它是虚线画的。虚线的意思是「本该这样,而 V3 没这么干」。

手往下移一格,指绿色那根短条。

V3 没有直接投。它先把 7,168 压到 1,536,再从 1,536 升到 128 个头 —— 中间掐了一个窄腰。两段加起来 48.76 M。

这个数字说完停一下,它有点反直觉。

0.95 倍。绿条比红虚线还短 —— 比等宽做法还小 5%。

也就是说:V3 把注意力撑宽了 2.29 倍, 而 Q 投影的参数量反而比不撑宽的做法还少一点。

屏幕上从那根 3.43 倍的虚线条掉到 0.95 倍的绿条,这一跌就是 q_lora_rank = 1536 那个字段干的事。 而且它顺便讲清楚了两个 LoRA 的分工完全不同:

kv_lora 那个 512,省的是 KV cache,因为存的就是它。
q_lora 这个 1536,省的是权重 —— Q 根本不进 cache它压缩纯粹是为了让「把头做宽」这件事在参数上付得起。

图最底下那行绿字写的就是这句话 —— 如果时间紧,把那行念出来也行。

可能被问到

每个头都读完整的 7,168,那 128 个头不就在做重复劳动吗?

输入一样,但每个头的投影矩阵是各自独立学出来的 —— 同样一份输入,128 个矩阵会把它投到 128 个不同的子空间里去。

打个比方:同一段话,128 个人各带一副不同的滤镜去看。 看的是同一段话,各自注意到的东西完全不同。 如果真把输入切开,每个头就只能看到一段话的 1/128,那才叫做残废。

原论文那句「多头让模型能同时关注不同表示子空间的信息」,说的就是这个。

那 q_lora_rank 1536、kv_lora_rank 512 这些数又是什么?跟 16,384 什么关系?

它们是「路上的窄腰」,不是终点。 一句话理清三个数:

7,168 是进出口 —— 残差流。
512 / 1536 是中途压缩到的最窄处 —— 存的就是这个
16,384 是算注意力时展开到的最宽处 —— 算完就扔,不存

整条路是「宽 → 窄 → 宽 → 收回宽」。 省显存省在窄的地方,要表达力要在宽的地方 —— MLA 的全部聪明就在这个错位上。

头做宽真的能换来质量吗?有证据吗?

老实说:我没有拿到「固定其他条件、只改头宽」的对照实验。 这种消融实验各家一般不公开。

能说的是可推的部分:更宽的头意味着每个头能表达更细的关系模式, 这是模型容量层面的常识;而 V2、V3 两代都在有了这个自由之后立刻用满了它, 说明在他们自己的实验里这笔交易是划算的。

不要替 DeepSeek 说他们没说过的话。 问到就答「我没看到公开的对照实验,这一点我不确定」。

既然可以随便撑,为什么只撑到 2.29 倍,不撑到 10 倍?

因为另外两笔代价是真的。 撑得越宽, 注意力那部分的算力越贵,o_proj 那块权重越大 —— o_proj 已经占一层的 63% 了,再撑就更失衡。

所以准确的说法不是「随便撑」,而是 「解除了一条约束,换来一个可以调的旋钮」2.29 是他们权衡出来的一个点,不是上限。

推理的时候真的要把 512 展开成 16,384 吗?那不是很费?

问得很专业,而且答案是「不一定要」。 MLA 论文里提到一个做法:升维矩阵可以被数学上吸收进相邻的矩阵里 —— 升 K 的那个可以并进 Q 的投影,升 V 的那个可以并进 o_proj。

吸收之后,推理时甚至不需要把 K 和 V 显式算出来。

要引原话就引这句,V2 论文附录 C 末尾: “due to the associative law of matrix multiplication, we can absorb WUK into WUQ, and WUV into WO. Therefore, we do not need to compute keys and values out for each query.” 页面上那个展开块里有。)

这属于实现层面的优化,本课不展开 —— 答一句「可以吸收进相邻矩阵,不用真展开」就够,别往下讲。

别讲什么

这一讲最容易跑偏的三个方向 ① 别把「惯例」说成「错的」。 等宽是个合理且经过验证的默认值, 绝大多数模型这么做没有任何问题。我们要拆的是「它是定律」这个误解, 不是这个做法本身。
② 别替 DeepSeek 声称因果。 「MLA 解除了约束」是能从公式推出来的; 「所以他们才选 128 × 128」是解读说的时候要分清这两句话的份量。
③ 别展开矩阵吸收那套推导。 它很漂亮,但它是实现优化, 一讲就是十分钟,而且跟这门课的主线(账怎么算)无关。
④ 千万别用「切开」「分给每个头」这种词。 哪怕只是顺口一说,台下立刻就会脑补出一张切片图 —— 而那张图一旦形成,后面「为什么能撑宽」怎么讲都讲不通。 统一说「每个头各自投影」「拼起来」。
节奏提示 七分钟,五段:制造疑惑 → 拆掉「切开」这个画面 → 讲清约束的出身 → 说明 MLA 解绑 → 补上代价。顺序不能换。
第二段(每个头都读全宽)要给到两分钟,它是整节的地基 —— 这一段讲透了,后面「为什么能撑宽」几乎不用解释。
开头那个心算是钩子,一定要真的让台下算,别自己说出 56 —— 他们自己算出来的错,印象比你讲的深十倍。
结尾落到 q_lora 那笔账,正好把后面那张五根条的权重图接上
关于页面上那些「展开」块 从这一讲开始,第 2 步一共埋了 7 个,默认全收起,讲的时候一个都不用点开。 里面是三笔账的完整算式、消融表的完整数据、论文原话、以及五个矩阵的形状表 —— 属于台下课后自己看的部分
只有被追问时才当场展开,对着里面讲。 后面每一讲的板书里会写清楚:哪个块是「跳过」,哪个块是「被追问才开」。 (导出 PDF 时会自动全部展开,存档不会漏内容。)
05⁺⁺

Shape 变换链:Q、K、V 各走了几步

⏱ 8 分钟
这一讲要留下什么 一层里只有 576 个数会被存下来,其余全是现算的。 Q、K、V 三样东西各走各的路,而 RoPE 只走其中两条路的一小段 —— 这不是省事,是不得不这么设计:位置编码一旦沾上主干, 前面省下来的 cache 会当场还回去。
这一讲的定位 前一讲讲的是「为什么可以这么设计」,这一讲讲「实际上是怎么走的」。 顺序不能倒 —— 先有「输入没被切开、宽度是自由的」这个地基, 这条链子上的每一步才讲得通。
这是全课唯一一处需要跟着图逐块走的地方。 别口述形状,一定要指着图说:台下记不住九个 shape,但记得住「三条路、金框那两块」。

讲稿

滚到「⭐ 完整的 shape 变换链」那张大图。先别急着讲,让台下看五秒。

这张图有点吓人,但它其实只说了三件事。 最上面进来一个矩阵,最下面出去一个同样形状的矩阵, 中间它被拆成了 Q、K、V 三样东西。

我们一条一条走。每一个方块就是一次变换 —— 方块里三行分别是:做什么、权重多大、出来是什么形状。

先定住起点,这个必须说清楚,否则后面全是悬空的。

起点:一批 token 进来,比如 128K 个,每个 7,168 维。 所以是一个 128K 乘 7,168 的矩阵。 这就是上一讲说的残差流。

指 Q 路那一列,从上往下。

① Q 路:先压下去,再升回来

第一步,q_a_proj,把 7,168 压到 1,536。 这个矩阵是 7,168 乘 1,536。 压完出来的东西,就是 Q 的潜空间表示。

第二步是个归一化,形状不变,跳过不讲

第三步,q_b_proj,把 1,536 升到 24,576。 然后把这 24,576 一 reshape,就是 128 个头,每个头 192 维。

这里停一下,把 192 这个数点破 —— 它是后面所有事情的钥匙。

为什么每个头是 192,不是 128? 因为 192 等于 128 加 64。 前面 128 维是「内容」,后面 64 维是专门留给位置编码的。

所以第四步是把每个头的 192 维劈成两段前 128 维叫 q_nope,一动不动;后 64 维叫 q_pe,拿去转 RoPE。

转完再拼回去,还是 192。Q 就定型了。 128K,128 个头,每头 192 维。

补一句,这句话决定了台下会不会把 Q 和 KV 搞混。

注意:Q 这一整条路,一个字节都不进 KV cache。 它压缩到 1,536,省的是权重和训练时的激活值,不是缓存。 上一讲那笔账就是这么来的。

切到 KV 路那一大列。

② KV 路:只存 576,其余全是现算的

K 和 V 共用一次下投影。 一个 7,168 乘 576 的矩阵,一把把残差流压成 576。

然后当场切开:前 512 叫 c_KV,后 64 叫 k_pe。

指金色虚线框。这是全图最重要的一句话。

整个模型推理的时候,要存起来的就只有这两块。512 加 64,等于 576。 就这么多。

停两秒。让这句话落地。

下面所有的方块 —— K、V、拼出来的那些 —— 全部是用的时候现算的,一个都不存。

接着往下:c_KV 过一层归一化,然后 kv_b_proj 把 512 升到 32,768。 reshape 一下是 128 个头,每个头 256 维。

再劈一刀:前 128 是 K 的内容部分,后 128 直接就是 V。 V 到这儿就定型了 —— 128K,128 个头,每头 128 维。 V 全程不碰 RoPE,一次都不碰。

回到 k_pe 那一小列。

那条只有 64 维的小路呢?它去转 RoPE 了。 转完之后,同一份广播给全部 128 个头 —— 注意图上它的形状是「128K,1,64」,那个 1 是关键,它只有一个头。

然后跟每个头自己的 K 内容部分拼在一起,K 定型:128 个头,每头 192 维。 跟 Q 正好对齐,可以打分了。

现在进最难的一段。先抛问题,别直接讲答案。

③ 为什么 RoPE 非得单开一条 64 维的小路

问台下:位置编码为什么不直接作用在整个向量上?为什么要单切出 64 维?

等一等。这个问题多数人答不上来,但会好奇 —— 这正是我们要的状态。

因为如果不这么做,前面省下来的 cache 会当场还回去。

推理的时候本来有一个很漂亮的省法: 把「K 的升维矩阵」提前乘进「Q 的矩阵」里,合成一个。 这样连 K 都不用算出来,直接拿 Q 去和缓存里那个 512 维打分就行。

但 RoPE 是一个跟位置有关的旋转 —— 它会夹在这两个矩阵中间。

矩阵乘法不能交换顺序,夹在中间的东西挪不走,这个合并就做不成了。

后果很具体:每生成一个字,都得把前面所有 token 的 K 重新算一遍。 省下来的显存,变成了重复计算。

指那个「转 / 不转」的小表。

所以他们做了一件很干脆的事:把位置信息隔离到一条小路上。

每个头的向量劈成两半,各管一件事。 前 128 维不转,负责内容,也负责保住「矩阵能合并」这个性质; 后 64 维转,位置信息全塞在这里面。

为什么能这么拆?因为点积天然可加。 两个拼起来的向量做点积,等于前半段的点积加上后半段的点积。

所以拼成 192 维算一次,其实就是「内容相似度」和「位置相关性」分别算完再相加 —— 互不干扰,各自那一半的数学性质都保住了。

最后一击:代价有多小。这句说完这一讲就到顶了。

这条位置小路只有 64 维,而且 128 个头共用同一份。

也就是说:多存 64 个数,换回「K 永远不用重算」。 576 里面那个 64,全部来历就是这个。

反过来算一下就知道有多划算:如果这条路不共享、每个头一份, 就要多存 128 乘 64,等于 8,192。 cache 从 576 直接涨到 8,704 —— MLA 就白做了。

下面有个收起的块,标题就是「那个自然的追问 —— Q 那 64 维为什么不也共用一份」。 不用点开,口头讲;真被追到细节了再展开。

这里台下十有八九会追一句:位置是 token 的属性、跟哪个头无关, 那 Q 那 64 维为什么不也共用一份?

因为它不是纯位置 —— 它是先从内容投出一个向量,再去转。 角度每个头一样,被转的东西每个头不一样。 于是每个头拿到一条不同的「距离 → 分数」曲线,共用就全绑死成同一条了。

但真正拍板的是账:Q 不进 cache,共用省不到东西,没理由做; K 进 cache,共用能把 8,192 压成 64,那笔损失才值得认。 这个不对称不是数学要求的,是成本决定的 —— 哪一侧花钱,哪一侧才做妥协。

指图最下面那一排。

最后收尾:Q 和 K 打分,除以根号 192,softmax,乘上 V, 128 个头拼成 16,384,o_proj 收回 7,168。 回到残差流,交给 MoE。

顺带看一眼那个红框 —— 打分矩阵是 128 个头乘以序列长度的平方。 它不进 cache,但它要占显存,而且是平方地占。 这就是下一讲要炸的第二个点。

最容易记错的三个数

讲之前自己先对一遍,讲错了台下真的会算 ① q_b_proj 是 1,536 × 24,576,不是 12,288。 24,576 = 128 头 × 192,而 192 = 128 不转 + 64 转过
128 × 128 = 16,384 是「输出侧」的宽度, 它出现在 o_proj 的入口v_head_dim × 头数), 不是 q_b_proj 的出口这两个数很容易串。
③ k_pe 只有 1 个头,不是 128 个。 所以缓存是 512 + 64 = 576,不是 512 + 128×64 = 8,704。 这是整套设计能成立的关键,说错了整节就垮了。

可能被问到

Q 也压缩了,为什么它省不到 cache?

因为 cache 里存的是「历史」,而 Q 只属于当前这个字。

生成第 N 个字的时候,要拿它的 Q 去跟前面 N-1 个字的 K 和 V 算注意力。 K 和 V 必须留着,Q 用完就扔。

所以压缩 Q 省的是权重(矩阵变小了)和训练时的激活值跟推理时的 KV cache 完全是两笔账。 MLA 论文自己也是这么说的 —— 压 Q「即使它不能减少 KV cache」。

k_pe 只有一个头,128 个头共用,这样表达力不会不够吗?

会有损失,但损失的只是「位置」那部分,而且这部分本来就不需要分头。

内容那 128 维仍然是每个头各自独立的 —— 128 副不同的滤镜一副没少。 共享的只是「第 5 个字和第 300 个字隔多远」这类信息, 而这件事对所有头来说本来就是同一个事实。

这也正是 MQA 的思路被局部借用了一次 —— 图里那个函数名 kv_a_proj_with_mqa,mqa 三个字母就是从这儿来的。

既然可以把矩阵合并,为什么图上还画着 K 和 V 被算出来?

因为图画的是模型的数学定义,公开的参考实现也确实是这么算的。

「把升维矩阵吸收进 Q、吸收进 o_proj」是推理引擎做的一步等价变形 —— 它不改变结果,只改变算的顺序。

但要强调一句:正因为要给这一步留出可能性, RoPE 才必须被隔离出去。 架构在设计阶段就为一个还没发生的优化让了路 —— 这是这一节最值得玩味的地方。

权重都是几个大矩阵,那这 128 个头是不是根本不存在,只是逻辑上的?

一半对。在「投影」这一段确实只是记账;但在「打分」那一段,头是物理的,而且合并不了。

对的那一半q_b_projkv_b_projo_proj 都是一整块大矩阵,不是 128 个小的。view 零数据移动、零计算。 「第 i 个头的矩阵」就是那块大矩阵里的第 i 段列。

不对的那一半,用反事实说最清楚: 假如真的不分头,拿完整的 24,576 维 Q 和 K 做一次点积 —— 每一对 token 得到一个分数。 分成 128 个头 —— 每一对 token 得到 128 个分数,各自 softmax、各自加权自己那份 V。

结果完全不同,而且后者写不成一次大矩阵乘 —— 因为 softmax 是非线性的。 线性的东西怎么摆都能合并,非线性一旦按组切开,就再也拼不回去。

所以「头」有生命周期view 那一刻只是拿到名字, 打分和 softmax 那一段真正活着o_proj 那一刻死掉 —— 那块大矩阵是 128 个头唯一一次互相说话的地方。

加一句硬件视角,做基础设施的听众会很吃这套: 投影是又大又规整的矩阵乘,加速器最喜欢;打分是一堆小矩阵批在一起, 算术强度天然低 —— 这就是注意力长期是瓶颈的原因之一。

为什么除以根号 192,不是根号 128?

因为点积是在 192 维上做的。 缩放因子跟着实际参与点积的维度走,不跟「内容部分」走。

代码里就是 q_head_dim ** -0.5,而 q_head_dim = 128 + 64 = 192

(V3 还会按 YaRN 的长上下文外推再调一个系数,这个别展开, 问到就说「长上下文外推另有一个修正,不影响这里的主线」。)

拆成两个矩阵,是不是就等价于一个 7,168 × 24,576 的大矩阵?

不等价,是降配 —— 这个必须说清楚,否则后面「代价」那一段就没意义了。

一个 7,168 乘 24,576 的大矩阵,能表达的变换更多。 中间掐一个 1,536 的窄腰之后,等于强行规定:这个变换最多只有 1,536 个独立方向。 数学上叫秩被限制住了。

打个比方:一个配电箱,7,168 根线进、24,576 根线出, 中间你只留 1,536 根。什么都能过,但过不了那么多花样。

还有一点更严格:中间还夹了一层 RMSNorm。 所以它连「两个矩阵相乘」都不是,中间那一下是非线性的。

省下来的是真的:直投要 1.76 亿参数,拆开只要 4,876 万,省 3.61 倍DeepSeek 赌的是这个变换本来就用不满那么高的秩。

这九个 shape 我记不住,有没有一句话能概括?

有,而且这才是这一讲真正要带走的:

「宽进 → 压到 576 存起来 → 用的时候现场展开成 128 个头 → 收回宽」。

Q 走的是同一个套路,只是它不用存。 RoPE 是这条路上唯一一个被单独隔开的乘客。

别讲什么

这一讲最容易失控的三个方向 ① 别逐个念 shape。 图上有十几个方块,照着念一定超时而且没人跟得上。 只口述四个数:7,168 进、576 存、192 打分、7,168 出。其余全靠指图。
② 别展开 RoPE 本身是怎么转的。 复数旋转、频率、外推 —— 那是另一门课。这里只需要「它跟位置有关、它是个旋转、它挪不走」。
③ 别把「矩阵吸收」讲成模型的一部分。 它是推理优化。说成架构的一部分,台下再去读参考代码会发现对不上。
④ 别为了完整去讲 kv_a_proj 里两个矩阵其实是拼在一起的。 (压 c_KV 的和产生 k_pe 的,在实现上合成了一个 7,168 × 576。) 这是实现细节,讲了只会让「576 = 512 + 64」这句话变模糊。
节奏提示 八分钟,三段:Q 路两分钟 → KV 路三分钟 → RoPE 三分钟。 前两段可以走快,第三段必须慢。
整节的高点在「多存 64 个数,换回 K 永远不用重算」那一句 —— 前面所有的形状都是为了让这句话能被听懂。讲到这里要停。
如果时间紧,Q 路可以压到一分钟(「压下去、升回来、切两段、转一段」四句带过), 但 KV 路和 RoPE 一个字都不能省
结尾指一下红框那个平方矩阵,直接把下一讲的第二个爆点接上。
05⁺³

凭什么能压这么狠:那个 32,768 从来就不存在

⏱ 7 分钟
这一讲要留下什么 56.9 倍里,有 4.57 倍根本不是压缩,是「别犯傻」。 真正的压缩只有 12.44 倍,而且它压的不是数,是秩
更重要的一条元认知:论文在这件事上没有给任何理论证明,它只给了消融实验。 能证明的、能推的、和只能靠实验说话的,是三种不同份量的话 —— 这一讲要把它们分开讲。
这一讲的定位 前面三讲讲的是「怎么做的」,这一讲讲「凭什么敢这么做」。 这是全课唯一一处真正回答「为什么能行」的地方, 也是整个专题一里最能体现「不确定就去查、绝不编」这个态度的一节
台下如果只记得住一句话,让他记住「那个 32,768 从来就不存在」。

讲稿

滚到「⭐ 凭什么能压这么狠」,先停在标题上。

前面我们算出来 MLA 省了 56.9 倍。这个数听起来像魔术。 而魔术总有障眼法。

这一节我把它拆开。拆完你会发现,其中一大半根本不是压缩。

指两刀那张图。

第一刀:那 32,768 里只有 7,168 个自由度

标准做法每个 token 每层要存 32,768 个数。 128 个头,每个头 K 和 V 各 128 维,乘起来就是这个数。

这里问一句,让台下自己想三秒。

我问一个问题:这 32,768 个数,是从哪儿来的?

全都是那 7,168 维的输入乘出来的。 K 是一个 7,168 乘 16,384 的矩阵作用在输入上,V 也一样。

而一个 7,168 乘 16,384 的矩阵,秩最多只能是 7,168。

停。下面这句是这一节的核心,讲慢。

意思是:那个 32,768 维的向量,永远只在一个不超过 7,168 维的子空间里打转。

所以你只要把输入本身那 7,168 个数存下来,就能把 K 和 V 一个 bit 不差地还原出来。

这一步是可以证明的,不是我在感觉。 标准存法里有 4.57 倍是纯冗余 —— 砍掉它不叫压缩,叫别犯傻。

马上会有人问:那为什么以前没人这么干?先自问自答,别等他问。

那为什么八年来没人只存输入呢?因为要重算。 只存输入的话,每生成一个字,都得把前面所有 token 的 K 和 V 重新乘一遍。 省了显存,赔了算力。

MLA 真正的贡献,是让这个「重算」几乎不用真的发生 —— 就是上一讲说的矩阵吸收。 于是这 4.57 倍才第一次变成可以拿走的。

指第二刀。

第二刀:把「秩」限制到 576

剩下的 7,168 到 576,是 12.44 倍。这一刀是真的,是有损的。 但它压的不是数,是秩。

切到信息总线那张图。

你可以把那个 512 想成一条信息总线。 128 个头都挂在这条总线上取数据。

每个头用自己的矩阵去取,取出来的东西各不相同 —— 头之间的差异性一点没丢。 但是,总线上没有的东西,谁也拿不到。

所以 MLA 押的赌注可以写成一句话: 128 个头真正需要的信息通道,加起来不超过 512 种。

指那条金色虚线。

顺便解决一个上一讲留下的疑问:那 64 维的位置小路,为什么不在总线里。 图上那条虚线画得很清楚 —— 它是从输入直接旁路出去的,根本不经过总线。 因为位置信息一旦被压进这条共享总线,矩阵吸收就做不成了。

现在进最重要的一段。这一段决定台下觉得你可不可信。

论文凭什么这么说 —— 它没说

我得把话说清楚:上面那套「两刀」和「总线」的解释,是我推的,论文里没有。

DeepSeek 的论文在这一节从头到尾没有给任何理论证明。 没有秩分析,没有信息论论证,就是一句「我们这么做」。

这句话说完要停一下。诚实本身是有分量的。

但它给了硬碰硬的消融实验,在附录 D。那份数据才是真正的答案。

指绿框「⭐ 论文凭什么这么说」。课上只讲这个绿框, 下面那个「展开:那两张消融表的完整数据」不要点开。

大号 MoE,2,500 亿参数,同样的架构,只换注意力机制。 用标准注意力的,每个 token 缓存 86 万个数。用 MLA 的,3 万 4 千 6 百个 —— 是它的百分之四。

而质量,BBH、MMLU、C-Eval、CMMLU,四项全赢。

停一下。这一句是整个第 2 步的地基,不要赶。

而且它打败的不是软柿子 —— 它越过的是「不省 cache、质量最好」的那个基线。

如果台下有人露出不信的表情,这时候才点开那个展开块, 对着里面的四列表讲下面三句。

四项的具体分数:BBH 46.6 到 50.7,MMLU 57.5 到 59.0, C-Eval 57.9 到 59.2,CMMLU 60.7 到 62.5。

小号那组三胜一负 —— C-Eval 从 51.6 掉到 50.9。 这一格要老实指出来,不要跳过。展开块里那一格是红的,本来就是给这一刻准备的。

同一篇论文还先做了另一组对照:标准注意力、GQA、MQA 三选一。 MMLU 上,标准注意力 45.2,GQA 41.2,MQA 37.9。 传统那些省 cache 的办法,是真的会掉质量的。

最后一个问题,也是最难受的一个。

压了 57 倍,质量为什么反而更好

论文没有解释。下面两条是我的推测,请当推测听。

推测一:低秩瓶颈本身可能是一种正则化。 强迫所有头把信息挤过一个窄口,反而逼出更干净的表示。 但这里没有对照实验支持,我只能说「可能」。

推测二,我觉得更可能:省下的预算被花在了别处。 MLA 省出来的 cache,让他们敢把注意力内部撑到 16,384,是残差流的 2.29 倍。

所以真实的对比,可能不是「压缩 vs 不压缩」, 而是「窄瓶颈加超宽头」对「无瓶颈加常规头」。

收尾。把 512 这个数字的三重身份点出来。

最后收一句。512 这个数字不是随便定的,它同时决定三件事,而且是同一个旋钮。

一,cache 多大 —— 存的就是它。 二,模型能表达多少 —— 秩上限就是它。 三,「不用真算 K」那个优化划不划算 —— 总线越窄,合并出来的矩阵越小。

而 RoPE 那 64 维之所以必须待在总线外面,正是为了保住第三条。 一个数字,三件事,全都锁在一起。

三句话的份量不一样,讲的时候要分开

这一讲最容易翻车的地方,就是把三种话混着说 ① 可以证明的:「32,768 个数里只有 ≤ 7,168 个自由度」—— 这是线性代数,秩不会超过输入维度。可以斩钉截铁地说。
② 可以推的:「所以 56.9 倍里有 4.57 倍是纯冗余」「512 是一条共享信息总线」—— 这是从①推出来的,逻辑成立,但要说明「这是本课的拆法,论文没这么写」
③ 只能靠实验说话的:「秩 512 够用」「MLA 比 MHA 好」—— 这两句只有消融实验支持,没有任何理论。 说的时候必须带上「论文的实验显示」这个前缀。
把 ③ 说成 ①,是这一讲唯一致命的错误。

可能被问到

既然存输入 7,168 就够了,为什么 MLA 不干脆存 7,168,还要再压到 512?

因为存 7,168 只解决了显存,没解决算力。

存输入的话,每一步都要为所有历史 token 重算 K 和 V要想不重算,就得把升维矩阵吸收进 Q —— 而吸收出来的那个合并矩阵,大小是跟瓶颈宽度走的

瓶颈 7,168 的话,合并矩阵大到没法用;瓶颈 512,它就小得多。 所以 512 不只是为了省显存,也是为了让「不重算」这件事在算力上划算。

(这一条是推导,论文没有明说。回答时带上这句。

秩限制到 512,信息就是丢了。凭什么说丢的那部分不重要?

不能凭什么。这就是个赌注,而且论文没有为它辩护。

唯一的证据是消融实验的结果:在两个规模上, 同架构只换注意力,MLA 的质量不比 MHA 差,大号那组还全面更好。

这是「事后验证」,不是「事前证明」。 这两者的区别值得当场点出来 —— 深度学习里大量的设计都是这样立住的。

不要为了讲得圆而编一个理由。说「这一点论文没给理论,只给了实验」, 比给一个像样的解释更有说服力。

那 512 是怎么定出来的?调参调出来的吗?

论文给的是一个比值,不是一个来历。 它写的是「d_c 设为 4 倍的每头维度,位置那条设为一半」 —— V3 每头 128,所以是 512 和 64。V2 也是同样的比例。

怎么选出来的,论文没写。 我没有看到任何公开的扫参记录,这一点我不确定。

能说的是它落在什么位置:论文自己算过, MLA 的 cache 相当于「2.25 组的 GQA」 —— 比三组 GQA 还省,但质量超过 MHA。

这个「秩」的说法,跟 LoRA 是一回事吗?

思路同源,用途完全不同 —— 这个要说清楚,否则容易混。

共同点:都是「一个大矩阵其实用不满它的秩,掐个窄腰就够」。

不同点:LoRA 压的是权重更新,为的是微调时少存优化器状态; MLA 压的是激活 —— 压的是每个 token 的中间结果,为的是少存 KV cache。

一个作用在训练,一个作用在推理。别混。

4.57 那一刀既然是免费的,为什么别的模型不也这么干?

因为它不是「免费的」,是「需要配套」的。 单独拿走它会赔算力(要重算),必须配上矩阵吸收才划算。

而矩阵吸收又要求 RoPE 被隔离出去 —— 也就是说, 这三件事是捆在一起的一整套设计,拆开任何一件都不成立。

这也是为什么 MLA 是一个「架构」,不是一个「技巧」。

别讲什么

四条红线 ① 别把「两刀拆法」说成论文的说法。 它是本课的推导。 每次提到都带一句「论文没这么写」。
② 别展开秩、子空间、线性映射的严格定义。 台下不是数学系。「一个 7,168 进的矩阵,出来的东西最多也只有 7,168 种花样」就够了。
③ 别跳过小号那组 C-Eval 掉分的那一格。 四个格子里报三个,是最坏的一种不诚实 —— 而且台下真的会看表。
④ 别去比较 MLA 和别家的方案。 (GQA 的变体、各种 KV 量化、sliding window……) 那是另一门课,而且很容易讲出没有实验支撑的话。
节奏提示 七分钟,四段:第一刀两分钟 → 第二刀两分钟 → 论文实证两分钟 → 为什么更好一分钟。
整节的高点在「那个 32,768 从来就不存在」 —— 这句话要说两遍, 开头说一遍当悬念,第一刀讲完再说一遍当结论。
「论文没有给任何理论证明」这句说完一定要停两秒。 这一停,台下会开始相信你前面说的每一个数字。
如果时间紧,砍掉「为什么反而更好」那两条推测 —— 它们是加分项,前三段才是骨头
05⁺⁴

打分那一仗:从点积走回残差流

⏱ 7 分钟
这一讲要留下什么 前面几讲全在讲「怎么把 Q、K、V 准备出来」。这一讲讲它们凑齐之后干了什么。
一句话落点:注意力做的事就是「给每个位置算一个概率分布, 决定它从前面各个位置各取多少信息」。
顺带把最后一块拼图放上:128 个头到 o_proj 才第一次、也是唯一一次互相交换信息。
最后一分钟收权重账:一层五块,最大的那块跟显存无关,最小的那块决定整张显存账。
这一讲的定位 这是第 2 步唯一一段「机制」讲解,前面几讲讲的都是「省」。 台下如果一路听下来只记住了压缩,会有个空洞:那压完之后到底怎么用?这一讲补上。
它同时是下一讲那个 4 TiB 的引子 —— 讲完这五步,「为什么这一段跟序列长度是平方关系」就是自明的,不用再解释一遍。

讲稿

滚到「⭐ Q、K、V 都到手了:这一仗到底怎么打」,停在那张五步图上。

先给个直觉,不要一上来就讲矩阵。

你读一句话,读到第五个字。你要理解它,得回头看看前面那四个字, 而且得判断哪几个字更重要。 打分做的就是这件事。

区别只有一个:模型有 128 个头,等于有 128 套不同的判断标准,同时在看。

这个比喻讲完停一秒,让它落地,再进流程。

① 点积:谁该看谁

拿当前位置的 Q,去跟每一个位置的 K 做点积。 方向接近,点积就大,意思是「这个位置我该多看一眼」; 不相干,甚至相反,就是小的或者负的。

每个头独立做一遍。所以这一步的产出是 128 张方阵,每张 13 万乘 13 万。

② 除以根号 192:不是玄学

192 个维度累加,点积的数值会跟着维度一起长大。

直接送进 softmax 会饱和 —— 几乎全部权重压给一个位置,其他的被压成 0,梯度也跟着没了。 除以根号 d,就是把幅度拉回能用的区间。

下面有个收起的块「关于『除以 √192』的两个细节」。 正常讲的时候跳过它;有人较真常数,当场展开,里面有代码和算式。

这里有两件事值得说准,但都不必展开。 第一,原始论文这一段的原话是「我们怀疑」,不是「我们证明」—— 它给的是直觉论证,不是定理。 第二,V3 代码里这个系数其实不等于 1 除以根号 192。 因为开了长上下文外推,它还要再乘一个系数,真实值是 0.1352 不是 0.0722。 原理一模一样,只是常数被外推方案改过。

③ 遮住未来:这是正确性,不是优化

第五个位置不许看第六个。 做法是把方阵的上三角全部设成负无穷,softmax 之后自然就是 0。

这句要重一点。台下经常把它当成一个工程细节。

这一步不是为了省什么,是为了对。 不遮的话,模型训练的时候就是在抄答案 —— 曲线会很漂亮, 但推理时后面的字还不存在,立刻垮掉。

④ softmax:把分数变成「取多少」

指图下半部分那张 8 乘 8 的缩略方阵,再指右边那几根柱子。

按行做归一化。做完之后,每一行加起来正好是 1。

这一行就是一个概率分布。 翻译成人话:「我这个位置的信息,百分之二十九从第一个位置取, 百分之四十七从第五个位置取。」

顺带把上一讲那个悬念收掉:为什么 128 个头到这里就合并不回去了。 因为 softmax 是逐行、逐头做的非线性运算。 非线性的东西,没法折叠回一个矩阵乘。

⑤⑥ 加权求和,然后拼起来出去

拿这套权重去对 V 加权求和,每个头吐出一个 128 维的向量。

128 个头拼成 16,384 维,过一次 o_proj 压回 7,168,汇回残差流。

最后这句是这一讲的收尾,说完停两秒。

o_proj 是一层里最大的一块矩阵,一亿一千七百万参数,占了 MLA 一层的六成。 而它之所以那么大、那么重要,是因为它是 128 个头之间唯一交换信息的地方。 在那之前,它们各算各的,谁也不看谁。

指那个「不是一类东西」的橙框。

这里插一句,它会在后面反复用到:这一段跟它前后的计算,根本不是一类东西。 投影是又大又规整的矩阵乘,形状固定,加速器最喜欢。 打分是 128 个小矩阵乘批在一起,中间还夹着一个逐行归约。

而且它跟序列长度是平方关系。序列翻倍,这一段翻四倍。 后面那个把人吓一跳的数,就是从这儿长出来的。

收尾:把一层 MLA 的权重账摆齐

往下滚一屏,停在「五个投影矩阵,一层多大」那张横条图。 不要念数字,先让台下看条长。

MLA 讲了这么久,其实一层只有五块权重。屏幕上就这五根条。

停两秒,让最上面那根蓝的自己说话。

最上面那根,o_proj,一根顶下面四根加起来还多 —— 六成三。 这跟刚才那句「它是 128 个头唯一交换信息的地方」是同一件事的两面: 它要一头连着 16,384、一头连着 7,168,两头都宽,所以它最大。

手指移到最底下那根黄色的短条 —— 短到几乎看不见。

而最下面这根,kv_a_proj,只有 4.13 M,两个百分点。 最大的那根和它差 28 倍。

下面这句是这张图存在的全部理由,说慢一点。

可是整个 KV cache 的账,从头到尾只由这根最短的条决定。 因为存进 cache 的就是它写出来的那 576 个数,别的四块一个字节都不进 cache。

权重最大的那块跟显存账没关系,权重最小的那块决定了整张显存账 —— 这两件事在这张图上是一眼能看出来的,用讲的讲不出来。

五块加起来 187 M,乘 61 层,11.41 B。 六千七百亿参数的模型,注意力全部加起来只占这么多 —— 约 1.7%。 剩下的都在 MLP 和 MoE 里,那是第 3、第 4 步的事。

可能被问到

为什么是除以根号 d,不是除以 d,也不是别的?

原始论文脚注给了一个直觉论证: 假设 Q 和 K 的各个分量独立、均值 0、方差 1, 那么它们的点积是 d 项之和,均值 0、方差正好是 d。

方差是 d,标准差就是根号 d。除以根号 d,标准差回到 1。 除以 d 就过头了,会把分数压得太平,注意力变成近似平均。

注意措辞:论文原文用的是 “We suspect”。 这是一个非常好用的直觉,但它不是定理,别说成「可以证明」。)

为什么分母是 192?每个头不是 128 维吗?

128 是 V 的每头维度,不是 Q/K 的。

Q 和 K 每个头是拼出来的:128 维不转 RoPE,加 64 维转 RoPE,一共 192。 打分是拿这 192 维整体做点积,所以分母跟着 192 走。

代码里这个变量就叫 q_head_dim,等于 qk_nope_head_dim + qk_rope_head_dim

那 128 张 13 万乘 13 万的方阵,显存装得下吗?

装不下 —— 这正是下一节要讲的那个爆点。

所以现实中没有人真的把这张矩阵完整存下来。 做法是分块算、边算边把结果累加进输出、算完就扔。

这里点到为止,具体怎么分块是专题二的内容, 这门课的听众不需要。)

掩码是训练时才有,还是推理时也有?

两边都有,但形态不一样,这个区别值得说一句。

训练、以及推理的第一步(把整段 prompt 一次吃进去), 是完整的上三角掩码 —— 因为这时候所有位置是同时算的。

而一个字一个字往外吐的时候,不需要显式掩码 —— 那时候只有一个新的 Q,它面前的 KV cache 里本来就只有历史,未来还不存在。

128 个头之间,真的一路上都不说话吗?

在一层里面,是的。从投影一直到加权求和,128 条路完全平行。

唯一的交汇点是 o_proj。 它输入 16,384 维、输出 7,168 维,每一个输出分量都是全部 128 个头的线性组合。

再加上残差流 —— 下一层的 Q、K、V 都从合并之后的结果重新长出来。 所以「头之间不说话」只在一层的内部成立。

注意力全部加起来才 11.41 B,占 671 B 的 1.7%。那它是不是没那么重要?

参数少,不等于代价小。这一问值得当场纠正,它是全课最容易走偏的一个直觉。

注意力的三笔账彼此完全脱钩: 权重 11.41 B(很小)、KV cache 8.58 GiB(跟序列长度成正比)、 算力在 128K 下占前向的 81.8%(跟序列长度成平方)。

MoE 那边正相反:参数几百 B,但每个 token 只激活 37 B, 而且跟序列多长完全没关系。

所以「参数占比」这把尺子,只能量权重那一栏。 下一讲那三个数就是专门用来打掉这个直觉的。

o_proj 那么大,能不能也像 Q 那样掐一个窄腰压下去?

结构上完全可以 —— 低秩分解对哪个矩阵都成立。但 V3 没有这么做, 而论文没有解释为什么,所以这里只能给推测,要说明是推测。

一个说得通的理由:o_proj 是 128 个头唯一一次交换信息的地方。 在那里掐一个窄腰,等于强行规定「128 个头的信息只能通过 r 维汇总」, 损失的正好是这一层里最不该省的那部分表达力。

被追问就到此为止。「论文没写,这是我的推测」比编一个理由强得多。)

别讲什么

三条红线 ① 别把「除以根号 d」讲成有严格证明的东西。 论文自己写的是「我们怀疑」。 照着讲就行,不要替它升级。
② 别在这里展开 FlashAttention 怎么分块。 台下不是 kernel 工程师知道「不整个存下来」就够了,细节是专题二。
③ 别把 softmax 讲成「归一化的一种」就带过。 「每一行是一个概率分布」这句话是本讲的核心 —— 它同时解释了注意力在干什么、以及为什么头合并不回去。
节奏提示 七分钟,四段:直觉加点积两分钟 → 缩放和掩码两分钟 → softmax 到出口两分钟 → 最后一分钟对着五根条收权重账。
整节有两个高点,都是图,都不要用话填满。 第一个是那张 8 乘 8 缩略方阵 —— 讲 softmax 时手指着高亮那一行,再指右边的柱子,「加起来是 100%」看一眼胜过讲三句
第二个是最后那张五根条的图 —— 「最长的那根跟显存无关,最短的那根决定整张显存账」,说完停两秒再进下一讲。
时间紧就砍掉「两个值得较真的细节」那一段但如果台下有人问到常数,一定要按框里的说,不要含糊过去。 五根条那一段不要砍 —— 它是第 3、第 4 步「参数都在 MoE 里」那句话的伏笔。
这一讲的两个「展开」块 「除以 √192 的两个细节」和「五个矩阵各自的形状和分工」,都默认收起、都不用点开。 前者只在有人较真常数时展开,后者只在有人要具体数字时展开 —— 五根条的图本身已经把该讲的都讲完了。
06

三个爆点

⏱ 3 分钟
这一讲要留下什么 前面五讲都在算细账。这一讲是收口 —— 把第 2 步的账浓缩成三个记得住的数。
一句话落点:省下来的那 57 倍很漂亮,但真正决定长上下文能不能做的, 是那个从来没被存下来过的 4 TiB。
三个数按「越来越吓人」排,最后一个当场拖滑块演示。
这一讲的定位 这是第 2 步的落幕,也是全课第一个「情绪节点」。 前面五讲信息密度很高,台下需要一个能带走的东西。
三分钟,三个数,不展开任何机制。 所有机制前面都讲完了,这里只负责把它们摆到一起,让量级自己说话。

讲稿

滚到 KV cache 那张四方案对比图,红色虚线要在屏幕里。

第 2 步讲完了。我用三个数收个尾,一个比一个大。

爆点一 · 57 倍

同样 128K 上下文、batch 1、bf16。标准 MHA 的 KV cache 是 488 GiB, MLA 是 8.58 GiB。 差 56.9 倍。

手指红色虚线。

这条红线是一张 TPU v7 device 的 94.74 GB。 MHA 那根柱子要五张多卡才放得下 —— 而模型权重还一个字节都没算进去。 MLA 那根,一张卡还剩九成。

这里停一下。这是台下第一次直观看到「装不下」长什么样。

爆点二 · 4 TiB

滚到「真正的爆点」那一节,红色小方块阵列。

上面那个是存下来的。还有一个东西比它吓人得多, 但它从来不出现在任何显存报表里,因为它压根就没被存过。

注意力要给每个 token 对每个 token 打分。这是一个 13 万乘 13 万的方阵, 而且每个头一份,128 份。

算出来是 4.00 TiB。一层,一次。

让这个数停两秒,然后再给对照。

给两个对照,让它落地。 第一,整个模型的权重是 1.22 TiB —— 这张一层一次的中间矩阵,是全部权重的三倍多。 第二,按一张卡 94.74 GB 算,它等于 47 张卡。

指那片红方块。「屏幕上这一片,每一格是一张卡。」

所以 FlashAttention 不是一个「让它更快」的优化。 它是让这件事根本能做的前提。 做法就一句话:永远不把这张矩阵整个物化出来,分块算、边算边累加、算完就扔。

爆点三 · 81.8%,拖滑块

滚到那条曲线,手放在顶上的序列长度滑块上。

第三个数不用我说,我拖给你们看。

注意力里有两处的计算量是序列长度的平方 —— 打分和加权求和。 其他所有部分,投影、MLP、MoE,全都只是线性的。

滑块拖到 4K,停。再一档一档往右推,边推边念数。

4K 的时候,平方项只占 12.3% 的前向算力。这时候没人会觉得注意力是问题。

32K,52.9%,过半了。 128K,81.8%。 推到 1M —— 97.3%。

拖到头之后手离开滑块,停三秒再说下一句。这是全课最强的一个瞬间。

请注意:模型一个字都没有改。 参数量、层数、专家数,全都一样。 变的只是输入有多长,而瓶颈整个换了地方。

在这个规模上,「模型有多少参数」已经不是算力的主要矛盾了 —— 序列有多长才是。

切到最后那个绿框,然后过第 3 步。

这条曲线一被算出来,接下来该干什么就很明显了: 如果注意力吃掉九成算力,那就别让每个 query 都去看全部 token。

怎么不看、少看哪些、赔掉什么,那是专题三整整一课的事。 这一课只负责把「为什么非改不可」算出来 —— 就是屏幕上这条线。

可能被问到

4 TiB 比全部权重还大,这可能吗?是不是算错了?

没算错,而且这正是它值得讲的原因。 权重的大小只跟模型有关,跟输入多长完全无关; 这张分数矩阵跟序列长度是平方关系。

序列一长,一个「中间结果」超过整个模型,是必然会发生的事。 128K 只是那个交叉点已经被远远越过的位置。

(算式当场可以写:128 × 131,072 × 131,072 × 2 B建议真的写出来 —— 台下会自己算一遍,比你保证十句都管用。)

既然从来不物化,那说它「4 TiB」有什么意义?

意义在于:它解释了为什么必须有 FlashAttention 这类东西。

「如果按教科书写法直接实现,需要 4 TiB」—— 这是一个反事实,但它是设计约束的来源。 不讲这个数,FlashAttention 听起来就只是个性能技巧, 台下会以为不用它也能跑,只是慢一点。

不是慢一点,是根本跑不起来。

那条 12% 到 97% 的曲线,是实测还是推算?

是按 FLOP 公式推算的,不是打点实测。要如实说。

分子是注意力里那两处平方项,分母是同一个前向的总 FLOPs, 参数全部来自 V3 的公开 config。 页面上那条线就是这个比值随序列长度扫出来的。

它跟实测的差别:真实运行时还有访存、kernel 效率、 算子融合的影响,所以实际耗时占比不会跟 FLOP 占比完全重合。 但方向和量级是这条线说了算的,这也是它有用的地方。)

batch 变大,这三个数怎么变?

前两个数跟 batch 成正比,第三个基本不动。

KV cache 和分数矩阵都是每个样本一份 —— batch 8 就是八倍,4 TiB 变成 32 TiB。

而那条占比曲线是一个比值,分子分母同时乘 batch,约掉了。 所以「注意力吃掉多少算力」只跟序列长度有关,跟 batch 无关。 这一点值得顺口说一句,它经常被误以为能靠调 batch 绕开。

那后来 DeepSeek 是怎么解决的?V3.2、V4 做了什么?

这是必被问的一条。答案页面上有,但课上只给三句,说完就停。

第一句:V3.2 的做法是让每个 query 只看最相关的 2,048 个 token, 不看全部。 128K 下这是 1.6%,平方项当场变成线性。 那个 2,048 就写在 V3.2 自己的 config 里,字段叫 index_topk

第二句:再往后的 V4 是混合架构,两种注意力配着用 —— 一种压缩之后还挑,一种压得更狠但不挑、看全局。 是两种,不是三种,这一点别说错。

第三句:结果是 100 万 token 上下文下,V4-Pro 的单 token 推理算力 只要 V3.2 的 27%,KV cache 只要 10%。 出处是 V4 论文的摘要。

然后就停。「怎么挑的、挑错了怎么办、训练时怎么学会挑」 —— 这三个才是专题三的正文,在这里开一句就要二十分钟。 标准收尾话术:「这一课只负责把『为什么非改不可』算出来, 怎么改是下一课整整一节。」)

别讲什么

三条红线 ① 别在这里讲 FlashAttention 怎么分块。 这一讲只要「不整个存下来」这一句分块细节是专题二,讲了会把三分钟拖成十分钟。
② 别把爆点三讲成静态的一个数。 81.8% 单独拿出来说没有力量,力量在「一个字没改,只是输入变长」这个对比上。 滑块必须真的拖,从 4K 拖到 1M。
③ 别在这里展开专题三的具体方案。 这一讲的职责是把问题立住,不是给答案。给了答案,专题三就没有开场了。
注意这条跟以前不一样了:页面最后那个绿框里现在真的写了 DSA 的 2,048、V4 的两种注意力、以及 27% 和 10% 这几个数。 那是留给台下课后自己看的,不是讲稿。 课上最多念绿框的第一句「别让每个 query 都看全部 token」,然后手从屏幕上拿开。 有人追问就按下面 Q&A 里那条答,三句话,不要延伸。
节奏提示 三分钟,三段各一分钟,一段比一段慢。 57 倍讲得最快,它只是复述;4 TiB 中速,要留出停顿;滑块那段最慢。
整讲的高点是滑块推到 1M 之后那三秒沉默。 不要用话把它填满。
时间紧就砍爆点一(前面刚讲过),但爆点二和三一个都不能省 —— 它们是整门课通往后面所有内容的门。
07

Dense MLP 的四条好性质

⏱ 7 分钟
这一讲要留下什么 一句话落点:MoE 省下来的算力,是拿这四条性质换的。
不先把这四条讲清楚,MoE 在台下听来就是白捡的便宜 —— 那样下一讲讲的所有麻烦,都会显得像是工程师自找的。
这一讲的真正身份是对照组。Dense MLP 本身没什么难的, 难的是让台下记住「原来这些是要付钱才有的」。
第二个落点在结尾那条链「为什么前三层不做 MoE」这个问题, 流行答案和论文答案不是一回事 —— 这一段是全课关于「怎么对待听起来很顺的解释」的样板。
这一讲的定位 V3 的前 3 层是普通 MLP(first_k_dense_replace = 3), 所以这不是一个假想的对照组,是模型里真实存在的三层。
结构上还有一层便宜可占:MoE 的每个专家用的是一模一样的三矩阵 SwiGLU。 这一讲把结构讲透,下一讲就只需要盯「宽度和数量怎么变」,省下三分钟。

讲稿

滚到第 3 步开头,SwiGLU 那张三矩阵图要完整在屏幕里。

第 3 步,Dense MLP。这一节看着最简单,但它是后面整个 MoE 的对照组, 请大家跟着我把它记住。

结构:两个撑宽,一个压回

一个 7,168 维的向量进来,被撑宽到 18,432,再压回 7,168。 就这么一件事。

指三根灰色的张量条。「这三根条的高度是按真实宽度画的。」

撑宽这一步做了两份 —— 这是唯一需要解释的地方。 上面那条过一个 SiLU,变成阀门;下面那条原样不动,是水流。 两者逐元素相乘,然后压回去。

这就是 SwiGLU。三个矩阵,每个 7,168 乘 18,432。 一层 396 M 参数,三层加起来 1.19 B。

这里会有人举手。这个问题几乎每次都来,而且问得对。

那个必来的问题:为什么要三个矩阵

如果没人问,我自己问:经典 MLP 明明只有两个 —— 撑宽、过非线性、压回。 第三个矩阵图什么?

图的是让那个非线性变成活的。

经典写法里,非线性是死的。 不管什么输入、哪个通道,都按同一个规则压一遍,这个规则写死在函数里, 训练也改不了它。

SwiGLU 把「哪些通道该放行、放多少」变成了学出来的、而且跟着输入变的东西。 同一个通道,这个 token 来的时候阀门全开,下一个 token 来可能就关掉了。

下面这句是本讲最容易讲偏的一句,先停一下再说。

真正买到表达力的不是 SiLU,是那个乘号。

滚到那张八根条的消融图。先别念数字,让台下看它分成了哪两堆。

一张图把这件事钉死

这是提出 SwiGLU 那篇论文里的原始实验。八个写法,参数量和算力全部对齐, 训一样多的步数,比 perplexity —— 条越长越好。

先指上面那两根红的。

上面这一组是「只换激活函数、还是两个矩阵」。GELU、Swish。 两根都在基线左边 —— 也就是说,比原来的 ReLU 还差一点。

手往下移到那五根蓝的和黄的。

下面这一组是「加一个乘号、变成三个矩阵」。五个变体,全都赢。

这里停一下,让对比自己说话。

两组之间的差距,比组内任何两个的差距都大。 所以分界线在哪很清楚:在有没有那个乘号,不在用了哪个激活函数。

手指黄色那根,Bilinear。这根是整张图的题眼。

最能说明问题的是这一根 —— Bilinear。它两条路都不过任何激活函数, 就是两个线性变换直接相乘。 结果它照样赢过上面那两个「精心挑过激活函数」的写法。

连激活函数都不要,光靠一个乘号,就赢了。

顺手指最下面那根 GEGLU。这一句能防住一个常见误解。

还有一件值得说的:SwiGLU 1.636,GEGLU 1.633,基本打平, GEGLU 甚至还略好一点点。 所以 V3 选 SwiGLU,不是因为它是最好的那个。 这一组里挑哪个都差不多 —— 重要的是进了这一组。

指下面那个黄框。这一句一定要讲,它是本课诚实标准的一个样板。

最后必须补一句:这篇论文的作者自己说,他解释不了为什么门控管用。 原话是「我们不提供任何解释,我们把它的成功归于老天赏饭」。

所以刚才那句「乘法带来交互」是一个说得通的直觉,不是被证明的因果。 这张表能撑住的只有一件事:门控这一组确实赢,而且赢的不是激活函数。

而且它并不更贵

滚到下面那张三行的图,红色的「参数持平线」要在屏幕里。

这是最容易被漏掉的一点。多一个矩阵,听起来像多 50% 的参数,其实没有。

从上往下指三行。

第一行,经典写法,两个矩阵,惯例撑到 4 倍也就是 28,672 —— 411 M。

第二行,论文给的规则:三矩阵写法把中间宽度乘三分之二。 这样一来三个格子的总长,跟上面两个格子一样长 —— 还是 411 M。 参数和算力完全持平,这是那篇论文里写死的一条规则,不是我们凑的。

第三行是 V3 实际的:18,432,比规则再低一点,396 M。

这句说慢一点。

多了一个矩阵,参数反而少了 3.57%。门控那点表达力,在参数账上是白送的。

如果台下有人问「所以 18,432 就是这么反推出来的?」—— 这句要小心,见下面 Q&A 那条

能说的是:18,432 落在那条规则附近,而且比它还省。 不能说的是「所以他们是照着这条规则定的」—— 论文没写。 而且今天多数模型其实没在守这条规则:Llama 3 和 Mistral 是 3.5 倍, Gemma 2 干脆 4 倍,都比规则宽得多。反倒是 V3 最接近原始规则。

四条好性质 —— 这一讲真正要留下的东西

滚到右边那张四条性质的表,一条一条指。

下面这四条,请大家记一下。三分钟之后我讲 MoE 的时候,会一条一条把它们拿走。

第一,每个 token 走完全相同的一条路。 形状是静态的、可预测的,编译期就知道。

第二,它就是一个大矩阵乘。 算力利用率天然就高,这是 MXU 最喜欢的形状。

第三,不需要任何通信来决定谁算什么。 没有调度,没有商量。

第四,编译器能提前把一切排好,运行时没有不确定性。

停一下,然后把这一讲的落点说出来。

这四条,MoE 一条都没有。

所以千万别把 MoE 理解成白捡的便宜。 MoE 确实省算力,省得很多 —— 但省下来的那部分,就是拿这四条换的。 下一讲讲的每一个麻烦,都能对回这里的某一条。

滚到最后那张三个方框的图。这是本讲的收尾,也是一个方法示范。

最后一个必来的问题:为什么偏偏是前三层

这个问题有一个流传很广的答案,我先说出来,然后告诉大家它为什么不能当答案讲。

那个流行说法是:靠近输入的层做的是通用活儿,基本的词法、局部句法, 每个 token 都要用,没什么可分工的。

停一下,让这个说法在空气里待两秒 —— 因为它听起来太顺了。

这个说法听起来非常合理。问题是,它不是 DeepSeek 给的理由。

从左往右指三个框。

去翻论文会看到一条链。最早提出这个做法的是 DeepSeekMoE,2024 年初。 它保留第一层 dense,而且给了理由 —— 原话是:他们观察到第一层的负载均衡收敛得特别慢。

这是一个训练稳定性的理由,跟「通用还是专用」一点关系都没有。

接下来 V2,同样保留第一层,但它只写了四个字:「沿用上一篇」。

再到 V3,改成前三层 —— 而这一次,论文只陈述了事实,没有给任何理由。 为什么从 1 变成 3,公开资料里查不到。

这里停两秒。下面这句是这一讲真正想留下的东西之一。

所以正确的讲法是:能说的是「最初的公开理由是训练稳定性」, 不能说的是「V3 选 3 层是因为前三层最通用」。 后面这句是我们的猜测,论文里没有任何依据。

把「负载均衡收敛慢」讲成「前几层比较通用」,是把一个训练现象换成了一个语义故事。 两者都可能对,但只有前者是能查证的。

指图最下面那行蓝字,一句话带过就走。

顺带一个小彩蛋:V3 的部署方案里,浅层这几个 dense MLP 是单独按 1-way 张量并行摆的,为的就是省掉那部分通信。 这反过来印证了刚才那四条好性质 —— dense 的好,是能在部署上兑现成真钱的。

可能被问到

为什么是三个矩阵?两个不是就够了吗

这是本讲最常被打断的地方,讲稿里已经正面回答了。这里是给追问准备的深一层。

短答:第三个矩阵买的是「阀门开多大,由这个 token 自己决定」。

如果对方追问「那为什么不干脆换一个更好的激活函数」: 因为再好的激活函数也是一个固定的一元函数, 它对每个通道的处理方式在训练结束后就不再变了。 门控给的是一个跟输入走的、逐通道的乘性系数,这是另一类东西。

再追问「多一个矩阵不是多 50% 参数吗」: 那是在中间宽度不变的前提下才成立。 V3 把宽度从惯例的 4 倍收到 2.57 倍,参数总量反而比经典写法少 3.57%。 3 × 7,168 × 18,432 = 396.36 M vs 2 × 7,168 × 28,672 = 411.04 M

「乘法带来二次项」这个说法,严格吗?

不严格 —— 而且这一点值得当场讲清楚,因为它是本讲最容易滑过去的地方。

SwiGLU 的两条路里,只有一条是纯线性的。 另一条过了 SiLU,所以乘出来的东西不是严格的二次型。

严格的二次型是同一篇论文里的另一个变体,叫 Bilinear —— 两条路都不过激活函数,直接相乘那个才是精确的 (xW) ⊙ (xV)

而 Bilinear 照样赢过所有「只换激活函数」的写法(1.648 vs 1.679 / 1.683) —— 这才是「关键在乘号不在激活函数」的真正证据。

本课的措辞标准:说「乘法带来了输入分量之间的交互」可以, 说「所以输出是 x 的二次项」只在 Bilinear 上成立, 对 SwiGLU 要说「保留了乘性交互」。)

SiLU 和 Swish、GLU、SwiGLU,这几个名字什么关系?

三层,一层套一层,说清楚只要三句:

SiLU 是一个激活函数(跟 Swish 是同一个东西的两个名字)。 GLU 是「两条路相乘」这个结构,跟用哪个激活函数无关。 SwiGLU 就是用 SiLU 当门的那种 GLU。

台下如果不追问,这一层不必讲 —— 对这门课的听众,「两条路 + 一个乘法」这个画面比名词谱系有用得多。)

那三层 Dense 占的参数多吗?值得单独讲一节?

参数上完全不值一提:1.19 B,占 671 B 的 0.177%。

但这一节讲的不是那 0.177%,是那四条性质。 它是免费的教具 —— 结构简单到可以一次讲透, 而且它跟 MoE 的专家用的是一模一样的三矩阵 SwiGLU。

在这儿花四分钟,下一讲能省下三分钟,还换来一个对照组。

18,432 这个数,除了「参数持平」还有别的讲究吗?

它同时卡在三条线上,三条都能当场验算,但没有一条被论文认领过。

① 最值得说的一条:9 × 2,048 = 18,432 2,048 是一个 MoE 专家的宽度,而 V3 每个 token 激活 8 个路由专家加 1 个共享专家 —— 正好 9 个。
也就是说:前 3 层 dense 每个 token 走的 MLP, 跟后面 58 层里每个 token 激活的那 9 个专家,逐个矩阵地一样宽。 整个模型从头到尾,每个 token 走的 MLP 算力几乎是一条平线。

「几乎」这两个字别省。 MoE 层还多一个路由器,7,168 × 256 = 1.84 M, 相对 396.36 M 是 0.46%。 台上要说「专家那部分严格相等,路由多千分之五」—— 这类小尾巴主动交代,比被人问出来强。

② 对齐加速器:18,432 ÷ 256 = 72,整除。 而论文那条 2/3 规则算出来是 19,114.67,连整数都不是。

③ 接近 2/3 规则但没踩上:V3 是 0.643 倍,规则是 0.667 倍。

措辞:「相等」是事实,「所以他们是照着这个定的」是解读。 V3 论文只写了 intermediate_sizemoe_intermediate_size 两个数,没有写它们之间的关系是有意的。 这是一个可以大方说「我不确定」的地方 —— 能分清「算得出来的」和「猜的」,比给一个圆满答案更值钱。)

为什么是前三层?不是前一层、前五层?

这一条的正确答法在讲稿里已经走过一遍,这里是压缩版,方便临场用。

「保留前几层 dense」这个做法最早出现在 DeepSeekMoE, 给的理由是「第一层的负载均衡收敛得特别慢」—— 训练稳定性问题。

V2 沿用,保留 1 层。V3 改成 3 层,但没有给理由。 为什么是 3 不是 1 或 5,公开资料里查不到。

不要用「前几层比较通用」去补这个洞。 那个说法很顺,但它是一个语义故事,替换掉了论文里真实给出的训练现象。 照实说「这一步他们没解释」就好。

MoE 的专家,结构上跟这个一样吗?

一模一样,只是窄很多。

同样是三矩阵 SwiGLU,中间宽度从 18,432 缩到 2,048 (config 里叫 moe_intermediate_size)。

所以下一讲不用重新学结构 —— 只要盯着两件事:宽度怎么变、数量怎么变。

别讲什么

四条红线 ① 别把这一讲讲成「顺便过一下 dense」。 那 0.177% 的参数确实不重要但四条性质是下一讲全部内容的对照组,讲塌了 MoE 就没有张力了。
② 别在这里比较各种激活函数谁好谁坏。 那张消融图的读法恰恰相反 —— 它说的是「换激活函数没用」,讲成「SiLU 比 GELU 好」就把结论讲反了。 本讲的重点是「门控」这个结构,不是那个 S 形曲线长什么样。
③ 别把 18,432 的选择说成确定的设计意图。 「9 × 2,048 正好相等」是能验算的事实,「所以他们照着这个定的」是解读。 照实说,别把推断讲成事实。
④ 别用「前几层比较通用」来解释为什么前三层不做 MoE。 这一条以前是这么讲的,现在改了 —— 论文给的理由是「第一层的负载均衡收敛得特别慢」,一个训练现象; 而 V3 为什么从 1 层变成 3 层,根本没解释。 把「没解释」讲成「因为通用」,是这一讲唯一会真的骗到人的地方。
节奏提示 七分钟,四段:结构一分钟 → 「为什么三个矩阵」两分半(含两张图) → 四条性质一分半 → 前三层那条链两分钟。
「为什么三个矩阵」那段几乎一定会被打断,索性主动提出来 —— 自己抛比被问着答从容,而且能控制它占多久。
整讲有两个落点,都要留白。 第一个是「这四条,MoE 一条都没有」 —— 它是听众从「dense 很无聊」切到「原来这是伏笔」的那个开关。
第二个是最后那条链:能说的、不能说的、和一个论文没回答的问题。 这一段不只是讲 dense,它是在示范这门课怎么对待「听起来很顺的解释」。
时间紧就砍消融图里 Bilinear 之外的四根条(念两组的结论就够), 四条性质和最后那条链一条都不能砍。
这一讲的三个「展开」块 「四行公式」「那张表的完整数据」「18,432 卡在三条线上」,默认全收起。 正常讲一个都不用点开 —— 有人要公式就开第一个,有人不信那张图就开第二个, 有人问 18,432 怎么来的就开第三个。
第二个块里还有一张今天主流模型的宽度对照表 (Llama 3 / Mistral / Qwen3 / Gemma 2),都是从各自 config 里读的 —— 被质疑「这是不是只有 DeepSeek 这么干」时直接打开它。
08

MoE 原理 ·「专家」不是那种专家

⏱ 6 分钟
这一讲要留下什么 一句话落点:MoE 把「模型有多大」和「每个字要算多少」拆开了。
而且这句话在 V3 上不是比喻,是一个恒等式 —— 那一层每个 token 走过的 FLOPs, 跟上一讲那层普通 dense MLP 一个都不多。
外加一条必须当场纠正的误解:专家不按领域分工。 这条不纠正,后面讲路由和负载均衡台下会一路想歪。
这一讲的定位 上一讲立了四条好性质,这一讲把它们一条一条拿走。 顺序要对:先给「省了多少」,台下才愿意听后面要付什么。
结构不用重讲 —— 专家就是上一讲那套三矩阵 SwiGLU, 只是中间宽度从 18,432 缩到 2,048。这一句带过就行。

讲稿

滚到第 4 步开头,dense 和 MoE 并排那张图。

MoE 只有一个核心思想,我先把它说完,剩下的都是它的推论。

核心:把参数量和计算量解耦

刚才那三层 dense,每个 token 都要把整层的权重完整过一遍。 参数越多,每个字就越贵 —— 这两件事是死死绑在一起的。

MoE 就是想把这个绑定拆开。

做法是把一个宽 MLP 拆成很多个窄的。V3 是 256 个路由专家,加 1 个共享专家。 每个专家还是刚才那套三矩阵 SwiGLU,只是中间宽度从 18,432 缩到 2,048。

指那张「一个 token 的路径是它自己的」的图。

每个 token 进来,一个门控网络只挑 8 个路由专家给它, 再加上那个所有 token 都要过的共享专家,一共 9 个。 剩下 248 个,这个 token 碰都不碰。

下面这段是整讲的高点,慢下来。

⭐ 那个恰好相等

滚到那张九段拼一根的图,让两条红色虚线在屏幕里。

现在算笔账。一层里 257 个专家,参数是 11.32 B。 但每个 token 只激活 9 个 —— 折算下来是 396,361,728 个参数。

这个数,我上一讲讲 dense 的时候说过一模一样的。

停两秒。让台下自己反应过来。

不是接近,是完全相等。因为 9 乘以 2,048,正好等于 18,432。

指图上那两条红色虚线,从左端划到右端。

屏幕上这张图,九段拼起来跟上面那根 dense 条一样长。 不是画得像,是相等。

所以这句话可以说得很硬:MoE 那一层,每个 token 在专家上做的计算, 跟一层普通 dense MLP 严格相等。 但那一层里躺着的参数,是 dense 的 28.6 倍。

下面这句半秒钟带过,但要说 —— 它是这门课的信誉。

严格说还多一点点:MoE 得先跑一遍门控矩阵,7,168 乘 256, 相对那 396 M 是千分之五。 所以准确的说法是「专家那部分一个都不多,外加千分之五的分诊费」。

这就是落点。说完停一下再往下走。

这就是「参数量和计算量解耦」这句话的字面意思。 模型可以一直变大,而每个字的账单不动。

放到整个模型上

61 层里有 58 层是 MoE,权重加起来 656 B,占了 671 B 的九成八。

而每个 token 只真的用到其中 3.5%。 「671B 的模型,每个字只算 37B」——这句话的全部来源就在这儿。

切到那个「细粒度」的三张卡片。

顺带说一句为什么专家要做小做多。 同样的激活预算,8 个大专家里选 1 个,只有 8 种可能; 256 个小专家里选 8 个,组合数是十的十四次方量级。 专家越小越多,能表达的分工组合就越多。这是细粒度 MoE 的全部道理。

必须当场纠正的一条

语气换一下,这是一个纠正,不是一个补充。

最后一条,很重要,我需要当场纠正一下大家脑子里刚刚长出来的东西。

「专家」不是那种专家。

听到 expert 这个词,多数人想到的是医生、律师、翻译 —— 觉得是按领域分工的,这个专家管代码,那个管中文。 不是。

它们就是训练出来的 256 个窄网络,编号 0 到 255,没有任何人类可读的含义。

这里别停在「我说不是」,直接把证据摆出来。这一段以前是空口说的,现在有数了。 滚到那张红绿两半的图。先只讲左边那半。

这件事有人真的量过。做 Mixtral 那篇论文,专门拆开数了路由。

他们把不同领域的文本喂进去 —— 数学论文、生物摘要、哲学论文 —— 然后看每个领域各自把 token 送给了哪些专家。 如果真按学科分工,这三条分布应该长得很不一样。

结果三条分布几乎重合。 论文原话是「我们没有观察到专家分配跟主题之间有任何明显的规律」, 而且前面还加了一个词:surprisingly。 写论文的人自己也觉得意外。

手移到右边那半,指那三根绿条。

那它到底跟着什么走?他们换了个问法:相邻两个 token, 会不会被送到同一个专家?

八个专家随机选,这个比例应该是八分之一,12.5%。 第 0 层量出来 14.1%,基本就是随机。 但到了中间层,26.4% —— 是随机的两倍还多。

所以它跟的是局部结构,不是学科。 论文举的例子很具体:Python 里的 self、英文里的 Question, 总是走同一个专家;代码的缩进 token「总是」被分给同一批。

下面这句是这一段真正要留下的东西。

而这件事马上就是一个系统问题 —— 这才是我们关心它的原因。

局部性高,意味着做专家并行的时候更容易把某几个专家挤爆。 但反过来,局部性也意味着可以做缓存。 同一个性质,既是负载均衡的敌人,又是缓存的朋友。

指图下面那行适用范围,一句话带过。

要交代一句范围:Mixtral 是 8 专家 top-2,V3 是 256 专家 top-8, 这两组数不能直接换算。 它能证明的是那个否定结论 ——「按学科分工」这个直觉在实测里没出现。 V3 那 256 个到底怎么分,我没有查到同等力度的公开分析。

用分诊台那个比喻收尾,然后过 4b。

比喻是分诊台:它不懂医,它只是学会了「这种症状归那扇门」。 门后面具体是什么,它不关心,我们也不需要知道。

为什么要专门纠正这一条 —— 因为如果你以为专家是按领域分的, 那接下来讲的负载不均、辅助损失、分组限制,全都会显得莫名其妙。 「不就按领域派活吗,能有什么难的」。 难就难在没有领域这回事。

可能被问到

9 × 2,048 = 18,432,是作者特意设计成相等的吗?

这个问题一定要答得干净:相等是事实,意图是猜测。

可以确定的:四个数全部来自 V3 公开的 config (intermediate_sizemoe_intermediate_sizenum_experts_per_tokn_shared_experts), 谁都能自己对一遍。

不能确定的:公开资料里没有说他们是为了让它相等才这样挑参数。 所以说「这两个数相等」,不要说「他们特意设计成相等」。

但相等本身很有用 —— 它给了一把现成的尺子: 凡是 MoE 比 dense 多付出的,都不在 FLOPs 上。 路由、通信、负载不均、显存,后面每一条麻烦都能对回这句话。)

共享专家为什么要单独设一个?路由专家里挑一个不行吗?

因为它要解决的是冗余,不是分工。

语法、常识这类东西每个 token 都要用。 如果不设共享专家,那 256 个路由专家里每一个都得把这些基本功再学一遍 —— 同一份知识存了 256 份。

拎出 1 个专门装这些、所有 token 都过它, 路由专家就可以专心做分工。

那 248 个没被激活的专家,是不是就不占资源了?

不占算力,但全额占显存。这是 MoE 最大的误解之一。

权重必须全程躺在显存里 —— 下一个 token 可能就要用到它,没法按需搬进来。

所以 MoE 省的是算力,不省显存。 671 B 的权重,一个字节都少不了,这是第 11 讲要算的账。

为什么被稀疏化的是 MLP,不是 attention?

两个原因,都值得说。

一,参数大头在 MLP。 MLA 一层 187 M,dense MLP 一层 396 M,MoE 一层 11.32 B。

二,MLP 天然就是「一堆独立的问题」 —— 它对每个 token 单独作用,token 之间不交互, 所以可以放心让不同 token 走不同的路。

attention 的全部意义恰恰是让 token 互相看,切不开。

真的没人研究过专家学到了什么吗?

有人研究过,而且就是我刚才引的那篇 —— Mixtral,arXiv 2401.04088 第 5 节。

它给了一个否定结论和一个肯定结论。 否定的:专家分配跟主题之间看不出明显规律。 肯定的:跟局部语法结构相关,相邻 token 走同一个专家的比例在中间层是随机的两倍。

但要注意这里的措辞边界。 「没有观察到明显的主题规律」不等于「已经证明专家没有任何可解释性」 —— 后者是一个强得多的断言,我没有把握,别替论文说。

安全的说法:「实测下来,路由跟的是局部语法结构,不是学科; 编号本身没有含义,别指望能读懂第 137 号在干什么。」)

那 Mixtral 的结论能套到 V3 上吗?

结构上差得不小,所以要分两半答。

能套的是那个否定结论。 「专家按学科分工」这个直觉,是听众自己从 expert 这个词脑补出来的, 而目前唯一一份公开的实测数据反驳了它。 在没有相反证据之前,不该让这个脑补留在台下脑子里。

不能套的是具体数字。 Mixtral 8 个专家选 2 个,V3 256 个选 8 个 —— 稀疏程度差一个量级, 那个 26.4% 换到 V3 上是多少,没人量过。

如果台下追问「V3 自己有没有做这个分析」我没有查到。 直接这么说,别猜。)

别讲什么

五条红线 ① 别把那个「恰好相等」讲成作者的设计意图。 算式是事实,动机没有公开。 这一条越是漂亮,越要守住边界 —— 讲砸了整段的可信度一起赔进去。
② 别在这里展开路由怎么算、负载怎么均。 那是下一讲,而且是整个 MoE 最麻烦的地方,需要完整的四分钟。
③ 别顺口说「MoE 省显存」。 它省算力,不省显存 —— 这是台下最容易自己脑补错的一条, 宁可主动澄清一句,也别让它带着这个印象走到第 11 讲。
④ 别把「一个 FLOP 都不多」讲成绝对的。 这一条以前是这么讲的,现在改了 —— 专家那部分确实严格相等,但 MoE 还要多跑一遍门控,多出千分之五。 这个尾巴很小,可它是「我们真的自己算过」的证据,主动交代比被问出来强。
⑤ 别把「专家不按学科分」讲成一句断言。 它现在有实测撑着 —— 台上要把那张图指出来, 而不是说「我告诉你不是这样的」。
节奏提示 六分钟,五段:核心思想一分钟 → 恰好相等一分半 → 整模型账半分钟 → 纠正「专家」并走那张实测图两分钟 → 分诊台收尾一分钟。
整讲有两个高点,中间隔了三分钟,别让它们互相抢。 第一个是「不是接近,是完全相等」那两秒沉默 —— 前面那句「这个数我上一讲说过一模一样的」要故意说得平, 让台下自己反应过来,台下自己想到的比你告诉他的记得牢。
第二个是那张红绿图右半边的「26.4%,是随机的两倍」 —— 这一下要打在「所以它跟的是局部结构」上,不要停在数字本身。
那张实测图不能砍。 时间紧就砍「细粒度为什么小而多」那段,再不够就砍左半边的领域分布 (只留右半边那三根条也成立)但「专家不按领域分工」如果不当场说掉,后面两讲会一路错下去。
09

路由:怎么选那 8 个

⏱ 5 分钟
这一讲要留下什么 主落点:n_group=8topk_group=4 这两个数字跟模型质量毫无关系, 是通信逼出来的。 这是全课主线「硬件反过来决定模型长什么样」最锋利的一个例证。
副落点:偏置只参与「挑谁」,不参与「按多大权重算」—— 调度归调度,不污染输出。
两个落点都指向同一句收尾:这一节里几乎每个设计, 都不是为了让模型更聪明,是为了让它能跑。
这一讲的定位 上一讲讲了 MoE 有多划算,这一讲开始还债 —— 而且这是四条好性质里被拿走得最狠的一条: 「不需要任何通信来决定谁算什么」。
五分钟,靠三张图撑,自己少说。 路由数据流那张图上蓝绿两条路完全不相交,「偏置只挑不算」看图一秒就懂,讲要三句; 四根条那张让「谁在等」变成看得见的;两个框那张把「free 不代表没有」摆平。
这一讲有一处是主动认错的地方 —— 「auxiliary-loss-free」这个名字比实情干净,台上要自己点破。 在一门讲边界的课里,这种地方讲得越自然,整门课越可信。

讲稿

滚到 4b,路由数据流那张图,整张要在屏幕里。

门控网络长什么样,先给个数,可能跟你们想的不一样。

它是这一层里最小的东西

它就是一个矩阵,7,168 乘 256。参数量 184 万。

而它所在的那一层有 11.32 B 参数 —— 它占万分之一点六。

这句停一下,它是个很好的反差。

整个 MoE 最麻烦的东西,是这一层里最小的东西。

手指黄色那个 sigmoid 框。

打分这一步有个细节跟直觉不一样:用的是 sigmoid,不是 softmax。 softmax 是让 256 个分数加起来等于 1,专家之间互相抢。 sigmoid 是每个专家独立给一个 0 到 1 的分,谁也不影响谁。

真正的难点:负载均衡

门控是学出来的,它会自己形成偏好。 某几个专家早期表现好,就会被越挑越多,然后训练得更好,然后被挑得更多。 赢家通吃。

最后可能两百多个专家基本闲着,算力全堆在几个上面。

滚到那张四根条的图 —— 上下两半,上面红下面绿。

先看这件事到底有多贵,不然后面那套机制看起来会像小题大做。

专家是摊在不同卡上的。上面这半张图,四张卡分到的 token 是 40、20、25、15。 斜线那些格子是在干等。

这一层什么时候结束?等最慢那张卡。 于是买的四张卡里,有 37.5% 的算力是花了钱没用上的。

下面这句是这一段真正的分量。

而且它每一层都重来一次。58 层,每层都要等齐。

所以这不是「效率损失几个百分点」,是一个木桶问题 —— 整个集群按最慢那张卡的速度走。

那四个数字是编的只是为了让「谁在等」看得见。 真实失衡程度取决于数据和路由,不是一个固定的数 —— 台上要说这一句。

老办法是在损失函数里加一项「分得匀不匀」的惩罚。 问题是你在让模型同时优化两件不相干的事 —— 答得对,和分得匀。 这两个目标会互相拉扯,最后两头都打折。

指左边那个蓝色的「偏置 b」小框,再顺着蓝箭头走一遍。

V3 的做法是给每个专家挂一个偏置。 谁最近吃太饱,就把它的偏置往下调,让它在竞争里稍微吃亏; 谁一直没人选,就调高。

而且它不是梯度学出来的,是按实际负载直接调的 —— 完全不进主目标的损失函数。

下面这段是副落点,慢下来,让图自己说话。

⭐ 偏置只挑不算

手沿着红色虚线框比一圈,然后指那条被剪断的地方。

请看这张图上的两条路。

蓝色这条带着偏置,它往下走,分组、选人,最后吐出 8 个编号 —— 然后就被剪断了。

手改指绿色那条,从 sigmoid 一路划到最底下的绿框。

绿色这条是原始分数,从 sigmoid 那一步直接旁路到最后,一路没碰过偏置。

最后加权用的,是绿色这条。

停两秒,然后给这句话。

所以偏置能把负载掰匀,却一个字节都没改变模型的输出。 调度归调度,不污染输出。

这带来三个很实在的好处:偏置可以任意大力地掰,掰得再狠输出也不失真; 它不参与反向传播,不跟主目标抢梯度;它是训练循环外面的一个动作, 想改策略不用动模型。

下面这段是补一个诚实。三十秒,但要讲。

⭐ 但「完全不用辅助损失」这句话,说过头了

滚到下面那张蓝黄两个框的图。

这套方法的名字叫 auxiliary-loss-free,直译就是「无辅助损失」。 名字很干净,实情没那么干净。

翻回论文原文,它写的是:虽然主要依赖这个无辅助损失的策略, 但为了防止任何单条序列内部出现极端失衡,还是同时用了一个补充的序列级均衡损失。 它接着说,这个损失的系数会被赋一个极小的值。

左框、右框,各指一下。

所以其实是两套机制,管的是两个不同尺度的问题。

左边的偏置管的是「跨步」的事 —— 每一步结束看一遍全局负载再调。 它管得了「某几个专家长期吃太饱」。

右边那个极小的损失管的是「一步之内」的事 —— 某一条序列碰巧把 token 全挤到少数几个专家上。 这件事发生在一步内部,偏置根本够不着,因为它一步才调一次。

这句是这一段的落点。

左边是策略,右边是保险丝。名字里的 free 说的是左边那件事是主力, 不是右边不存在。

要交代一句边界「两套机制、各管一个尺度」是论文直接写的; 「所以那个系数才能取极小值」是我们的读法,论文没这么解释过。

最后一块:分组限制 —— 本讲的主落点

指那个「256 个分成 8 组,只准挑 4 组」的蓝框。

256 个专家被分成 8 组,每组 32 个。一个 token 最多只允许碰 4 组。

我问大家一个问题:这么限,是为了让模型学得更好吗?

真的停一下等反应。这个停顿是这一讲的支点。

一点关系都没有。

因为组就是机器。

一个 token 挑中 8 个专家,如果这 8 个散落在 8 台不同的机器上, 它的激活值就得发到 8 个地方去,算完再收回来。 这就是 all-to-all 通信。

限制到 4 组,就是直接把「一个 token 最多打扰几台机器」这个数按住了。

这是全课主线最锋利的一句,说完停三秒。

换句话说:一个通信约束,被直接写进了模型结构里。

这不是工程师在模型外面做的优化。这是模型定义本身的一部分 —— 你去读 V3 的 config,那两个数字就明明白白写在那儿。

如果时间还有三十秒,加这一段。它是「硬件先划线」最直白的一个证据。

而且论文里有一句,我第一次读到的时候愣了一下。

它说:虽然 V3 实际只选 8 个路由专家, 但在通信代价完全不变的前提下,这个数可以一路加到 13 个。

停一秒,让这句话落地。

读法是这样的:他们先按机器的形状定下「最多跨 4 个节点」这个盒子, 然后在盒子里看能塞多少专家。盒子能装 13,最后填了 8。

所以顺序是反的 —— 不是模型说要 8 个、系统去满足; 是系统先划了一条线,模型在线里挑了个数。

收尾,然后过 4c。

所以这一节的落点是:路由里几乎每一个设计, 都不是为了让模型更聪明,是为了让它能跑。 sigmoid、偏置、分组限制、最后再乘一个 2.5 的缩放 —— 没有一条是在提升表达能力。

可能被问到

为什么用 sigmoid 不用 softmax?softmax 不是更标准吗?

能说清的部分:softmax 强行让 256 个分数加起来等于 1, 这是一个零和约束 —— 一个专家分数高,别的必然被压低,即使它们本来都合适。 sigmoid 让每个专家独立判断「这个 token 适不适合我」,不必零和。

另外它跟偏置那套配合得更自然 —— 独立分数上加一个偏置,含义是清楚的(「让它更容易被选中」); 在一个归一化过的分布上加偏置,含义就乱了。

边界:这是从设计上讲得通的理由, 不要说成「已经证明 sigmoid 更好」 —— 我没有看到这样的对照实验。)

偏置既然不学,那它靠什么更新?

靠统计实际负载,直接调。 某个专家这一批接的 token 明显偏多,就把它的偏置往下调一点;偏少就调高。

它是一个反馈控制器,不是一个被优化的参数。 所以它既不需要梯度,也不出现在损失函数里。

具体的调节步长和频率属于训练细节, 这门课的听众不需要,问到就说「按负载反馈调,具体步长看实现」。)

分组限制会不会让模型选不到最合适的专家?

会。这就是它的代价,要老实承认。

如果这个 token 最适合的 8 个专家恰好散在 5 个组里,那它就选不全。 被牺牲掉的那部分表达能力,换的是通信量可控。

这恰恰是这一讲想让大家看到的东西 —— 模型结构里存在一些设计,它们唯一的理由是硬件。 不是所有超参数都在为质量服务。

负载不均到底会有多严重?不就是慢一点吗?

不是慢一点,是整个集群按最慢的那张卡走。

回到那张图四张卡分到 40/20/25/15,整层的耗时由那个 40 决定。 算下来买的算力有 37.5% 是空转的,掰匀之后同样的工作量能快 1.6 倍。

而且它每层都重来一次 —— 58 层,每层都要等齐。 所以它不是「效率损失几个百分点」,是一个木桶问题, 而且这个木桶要连着装 58 回。

那四个数是示意真实失衡程度取决于数据和路由。 被追问「那实际是多少」就说不确定 —— 这个数没有一个通用答案。

组分为什么用「组内前 2 名之和」?那个 2 是哪来的?

这个 2 不用猜,它是算出来的 —— 论文给的是一个公式,不是一个数。

原文写的是:每个 token 最多送到 M 个节点, 节点按「该节点上专家的前 Kr/M 高亲和分之和」来挑。 Kr 是激活的路由专家数 = 8,M 是最多能跨的节点数 = 4。

写在白板上:8 ÷ 4 = 2。

所以「前 2 名」只是 8÷4 的结果。 换个配置,比如允许跨 2 个节点,那就变成「前 4 名之和」。

而这条规则的形状是讲得通的最多去 4 个节点、总共要选 8 个专家,平均每个节点得贡献 2 个。 用「前 2 名之和」打分,等于在问「假如我选了这个节点,它最好的那 2 个能给我多少」—— 正好对上后面要从它那儿拿走的份额。

边界:公式是原文的,「所以它对上了份额」这个解读是我们的, 论文没有这样解释。这门课以前在这里答的是「公开资料没解释,不确定」—— 那是查得不够,现在改了。

V3 为什么要在 V2 的基础上把 softmax 换成 sigmoid?

先说能确定的:这处改动论文明写了。 原文说「与 DeepSeek-V2 略有不同,DeepSeek-V3 用 sigmoid 计算亲和分, 然后在所有被选中的分数之间做归一化,得到门控值」。

这里有个容易被抓住的点,值得主动说清楚: 既然最后还要归一化到和为 1,那 sigmoid 和 softmax 不是一回事了吗?

不是,因为它们作用在两个不同的时刻。 softmax 的分母是全部 256 个 —— 竞争被烤进了打分函数, 抬高一个就必然压低其余 255 个。 sigmoid 没有这个分母,每个专家可以独立地说「这个 token 我在行」。 竞争被推迟到了「排序取前 8」那一步。

边界不要说「已经证明 sigmoid 更好」 —— 我没有看到 V3 公开过这两者的对照实验。)

别讲什么

五条红线 ① 别把分组限制讲成「一个优化技巧」。 它写在 config 里,是模型定义的一部分 —— 这个区别就是这一讲存在的全部理由,讲软了整节就白讲了。
② 别在这里展开 all-to-all 怎么实现。 「一个 token 要发给几台机器」这个画面就够了通信量的账在 4c 算。
③ 别把偏置说成「学出来的参数」。 它不进损失函数、不走反向传播 —— 说错这一点,前面「不跟主目标抢梯度」那句话就自相矛盾了。
④ 别说「V3 完全不用辅助损失」。 这一条以前是这么讲的,现在改了 —— 论文自己写着「同时还使用了一个补充的序列级均衡损失」,只是系数极小。 名字叫 auxiliary-loss-free,实情是「主力不靠它」,不是「一点都没有」。 这是全讲最容易被懂行的人当场纠正的一句。
⑤ 别再说「那个 2 公开资料没解释」。 它是 Kr/M = 8÷4 算出来的,论文给的就是这个公式。 以前答不确定,是查得不够。
节奏提示 五分钟,六段:门控有多小半分钟 → 负载均衡的病半分钟 → 偏置只挑不算一分钟 → 两套机制的诚实半分钟 → 分组限制一分半 → 收尾一分钟。
整讲的支点是那个提问:「这么限,是为了让模型学得更好吗?」 一定要真的停下来等反应 —— 台下先在心里答了「应该是吧」,你再说「一点关系都没有」,那一下才有力量。
三样东西不能砍:「偏置只挑不算」、「组就是机器」、「free 不代表没有」。 时间实在不够就砍 sigmoid 那段(它是细节), 再不够就砍「盒子能装 13」那一小段 —— 它很漂亮,但它是加分项不是主线。
这一讲的两个「展开」块 正常讲一个都不用点开。 第一个是「node-limited routing 在论文里长什么样」 —— 有人问那个 2 怎么来的就点开它,里面有 Kr/M = 8÷4 的算式, 还有「盒子能装 13」那段原文。
第二个是「sigmoid 加起来不等于 1,最后又归一化到 1,矛盾吗」 —— 这是懂行的人一定会抓的一个点,点开里面有分时刻的写法和那个 2.5 是怎么回事。
10

挪 token 还是挪权重

⏱ 2 分钟
这一讲要留下什么 主落点:前面每一笔账都有唯一答案,这一笔没有 —— 它是真正的二选一。 而「取决于场景」不是一句和稀泥,是从两条线的斜率里看出来的: 一条斜率是 1,一条是 0,它们必然相交。
副落点:拐点两边分别住着推理 decode 和训练 —— 这就是为什么同一个模型, 这两件事在这道题上给出相反的答案。
这一讲也是专题六「prefill 和 decode 为什么瓶颈相反」的第一颗种子, 但只埋,不展开。
这一讲的定位 4b 讲的是「怎么选专家」,4c 讲的是「选完之后,数据和权重谁去找谁」。 这是 MoE 从 dense 那里拿走的第四条好性质 —— dense 时代不存在这个问题,因为每个 token 走的都是同一份权重。
两分钟,全靠那张双对数图。图讲清了,话可以很少。

讲稿

滚到 4c,那张两条线交叉的图,整张要在屏幕里。

专家权重放不下在一张卡上 —— 一层就 21 GiB,58 层加起来 1.19 TiB。 所以专家一定是散在很多张卡上的。

于是就有了这道题:token 在这张卡上,它要用的专家在那张卡上。谁去找谁?

两条路,都能走通

手指绿色那条水平线,再指蓝色那条斜线。

第一条:把 token 送过去。 它选中了 8 个专家,就把它的激活值发到那 8 张卡上,算完再收回来。 发出去一次、收回来一次,每个 token 每层 224 KiB。

第二条:把权重搬过来。 各张卡先把自己手上那份专家权重广播给大家,凑齐之后本地算,token 一步都不用动。 一层 21.08 GiB。

下面这句是这一讲的支点,慢一点。

⭐ 关键不在两个数谁大,在两条线的形状

第一条的量,跟 token 数成正比 —— 图上是那条斜线。

第二条的量,跟 token 数完全无关 —— 你搬一次权重,这一批有 10 个 token 还是 1000 万个 token,搬的都是同样这 21.08 GiB。图上是那条水平线。

停一下,让大家自己看出下一句。

一条斜率是 1,一条斜率是 0。所以它们一定相交。

这就是为什么「要看场景」这句话在这里不是和稀泥 —— 它是从几何上看出来的。

手指红圈那个拐点。

相交的地方,大概是 9 万 8 千个 token。

拐点两边住着谁

依次指三个蓝点:左下、中间、右上。

推理 decode,一步就出几十个 token —— 远在拐点左边。挪 token 划算, 而且不是划算一点点,是差着好几个数量级。

训练一个 step,动辄上百万 token —— 远在右边。挪权重划算。

而一次 128K 的 prefill,13 万 1 千个 token —— 刚刚过拐点。

这句停两秒,它是给专题六埋的种子。

所以你会看到一个很有意思的现象:同一个模型、同一次推理请求, prefill 那一段和 decode 那一段,落在拐点的两边。 这两段为什么在很多事情上都是相反的,专题六会专门讲。这里先记住有这么回事。

把边界说清楚,这很重要

指图下面那行灰字,和右边那个黄箭头。

这个 9 万 8 千,不是一个工程门槛,不要拿去用。

它是个粗口径 —— 我只比了「每层要搬多少字节」, 没算 all-to-all 和 all-gather 的效率差异,那两种通信的代价本来就不一样。

而且拐点自己会动。 专家摊在越多张卡上,挪 token 那条线就被分摊得越薄,拐点就越往右移。

那它的价值在哪?在于它告诉你:这两个方案在同一个数量级上。

这是本讲最后一句,说完停三秒再翻页。

如果差着一百倍,那就没什么好选的了。正因为它们咬得这么近, 「二选一」才真的成立 —— 前面所有的账都有唯一答案,这一笔没有。

如果时间还够,指一下下面那行小字,只念名字。

这个方向上有一批专门的工作,名字听过就行:DeepEP、EPLB、dropless。 都是在这道题上做文章的,这门课不展开。

可能被问到

为什么不干脆把所有专家都放到每张卡上?就不用搬了

放不下。 一层的专家权重 21.08 GiB,58 层加起来 1.19 TiB —— 那几乎就是整个模型的全部重量(1.22 TiB)。

而单张卡的显存是几十 GiB 这个量级。 差着一个多数量级,不是省一点就能塞下的。

这也是为什么这道题必然存在 —— 只要一张卡装不下全部专家,就一定有东西要跨卡搬。

挪权重那条线为什么真的跟 token 数一点关系都没有?

因为你搬的是权重,而权重不认识 token。

这一层有多少专家、每个专家多大,是模型定死的 —— 你这一批喂进来 10 个 token 还是 1000 万个,要凑齐的还是那 257 份专家权重。

所以它是一条水平线,这是这张图能成立的全部原因。 如果它也随 token 数增长,两条线就可能永远不相交,也就没有「二选一」了。

两个方案能不能混着用?

能,而且真实系统里经常是混的 —— 这点要说,别把图讲成非黑即白。

比如按维度混专家在一部分卡之间挪权重、在另一部分卡之间挪 token。 比如按阶段混prefill 走一种、decode 走另一种。

具体怎么混属于系统实现,这门课不展开。 问到就说「有很多种组合,取决于并行策略怎么切」, 不要现编一个具体方案。)

那 9 万 8 千到底能不能拿去做判断?

不能当阈值用,但能当量级感用。

它是「每层搬多少字节」的粗比 —— 没算两种通信的效率差异,也没算专家摊在几张卡上。 摊得越开,拐点越往右。

能拿走的结论只有一句:这两个方案在同一个数量级上。 要落到具体系统上,得按你自己的并行切法和网络实测重算。

这跟前面讲的分组限制(只准挑 4 组)是同一件事吗?

是同一条链上的两环,但不是同一件事。

分组限制管的是「一个 token 最多打扰几台机器」 —— 它把挪 token 那条线的斜率按住了,让它不至于更陡。

这一节管的是「要不要挪 token」这个更上层的选择。

合起来看很清楚4b 是先把这条路的代价压到可接受, 4c 是再问一句「这条路本身该不该走」。

别讲什么

三条红线 ① 别把 98,688 说成一个工程门槛。 粗口径、且会随专家摊几张卡移动 —— 它唯一能支撑的结论是「两个方案在同一个数量级」, 多说一个字就是在给听众一个会害到他们的数。
② 别讲成「训练一定挪权重、推理一定挪 token」。 图上是一条连续的线,不是两个格子真实系统经常混用。
③ 别在这里展开 DeepEP / EPLB / dropless。 只念名字,让听过的人对上号就行 —— 展开任何一个都会把两分钟撑成十分钟,而且这门课的听众用不上。
节奏提示 两分钟,三段:两条路各二十秒 → 斜率必相交四十秒 → 三个落点加边界四十秒。
支点是「一条斜率 1、一条斜率 0,所以一定相交」这句。 说完停一下 —— 台下如果自己先想到了「那就一定有个交点」,这一讲就成了。
时间不够就砍最后那三个名字,以及 prefill 那颗种子。 但「边界」那段不能砍 —— 给出一个漂亮的数却不说它不能用,比不给这个数更糟。
10⁺

dropless:静态形状是怎么抢回来的

⏱ 3 分钟
这一讲要留下什么 主落点:MoE 拿走的第一条好性质是「形状静态」, 而老办法把它抢回来的方式是 —— 硬来。 给每个专家一个固定大小的盒子,装不下的扔掉,没装满的拿零去填。 形状确实又静态了,代价是同一层里同时在两头赔钱。
副落点:这一节给 4b 的负载均衡补上了真正的理由。 第 9 讲说负载不均是个木桶问题,只是慢; 这一讲要让他们看到,在容量盒子的世界里,负载不均会让 token 直接消失。
这一讲的定位 这是全课「四条好性质」那条暗线的最后一次回收。 第 7 讲立了四条,第 9 讲还了「不需要通信决定谁算」, 第 10 讲还了通信量本身,这一讲还的是「形状静态」和「编译器提前排好」。
它也是整门课少有的、能把一个抽象约束讲成一张具体图的地方 —— 「静态形状」四个字听着很虚,那张容量盒子图一放上去就实了。
时间紧的时候这一讲可以整节砍掉,主线不断。 但它是性价比很高的两分钟,能砍就尽量别砍。

讲稿

滚到 4c 最后,容量盒子那张对比图。

刚才提了一个词叫 dropless,我想花两分钟讲一下 —— 因为它背后是这门课的一条暗线。

先回到第七讲那四条好性质

第一条是:形状是静态的。 编译器在开跑之前就知道每个矩阵多大、每块内存放在哪,才能把整张图排好。

MoE 一来,这条断得最彻底。

这一批里有多少个 token 落到第 37 号专家头上?路由跑完才知道。而且下一批就变了。

这句停一下,这是整讲的病根。

可硬件要的是开跑之前就定死。

老办法:给每个专家发一个固定大小的盒子

手指左边那张图,从左到右三个专家依次点过去。

这个盒子多大,是你自己设的一个超参数,叫容量系数。

设完之后形状就又静态了 —— 每个专家永远算固定这么多个 token,编译器满意了。

但你看这张图里同一层同时在赔两笔钱。

指专家 A 上面那两个红色虚线框。

左边这个专家来了 6 个 token,盒子只装得下 4 个。多出来的 2 个 —— 直接丢掉。

丢掉的意思是:这两个 token 在这一层的输出被置零,只剩残差穿过去。 这一层对它们来说等于白过。

手改指右边那个画满斜线的专家 C。

而右边这个专家只来了 1 个 token,剩下 3 个格子拿零去填 —— 照样占算力、占显存, 算完是零。

下面这句是主落点,慢一点说。

所以这个容量系数是一个非常难受的旋钮:开小了丢真数据,开大了白烧算力。 两个方向都赔。

三个数字,说明这不是「调个参数」那么简单

滚到下面那个蓝框,三条一条一条念。

第一,丢 token 是真的伤质量。 同一组对照实验里,会丢的那版验证损失只降了 0.15,完全不丢的那版降了 0.26 —— 是前者的 1.73 倍。

第二,靠把盒子开大来不丢,很贵。那个例子里 MoE 层的算量涨了两倍多。

第三,而且你根本不知道该开多大。 有的模型这个系数要开到 11 才不丢;还有的模型,这个值会在训练中途毫无征兆地飙上去。

停两秒。

一个你不知道该设多少、设错了两个方向都赔、还会中途自己变的超参数。

如果台下有人较真,这三个数的适用范围要给那组实验是 Transformer-Small 规模、64 个专家、top-1 路由, 跟 V3 的 256 专家 top-8 差着量级。 它们能证明的是「容量系数这个旋钮本身是个麻烦」, 不能换算成「V3 如果丢 token 会掉多少分」。

dropless 干的事

手指右半张图。

它的思路很直接:不要盒子了。

把整个 MoE 层重写成一种叫 block-sparse 的矩阵乘法 —— 这种算子本来就能处理大小不一的块,所以来 6 个就算 6 个,来 1 个就算 1 个。

不丢,也不白算。

但真正的收获不是省了那点算力。 是那个旋钮直接不存在了 —— 你不用再调它,也不用担心它中途变。

那 V3 呢

滚到绿框。

V3 的技术报告写得很直白:因为负载在整个训练过程中一直保持得不错, 所以它训练时不丢弃任何 token。

这里停一下,然后回接第 9 讲。

请注意它给的理由 —— 是负载被掰得足够匀。

所以第九讲那个负载均衡的偏置,作用比我当时说的还要大。 我当时说负载不均是个木桶问题,大家一起等最慢那张卡。 现在你看到了另一半:在有容量盒子的世界里,负载不均会让 token 直接消失。

它不只是在防止变慢,它是在防止数据凭空不见。

还有半句话,很多人漏掉。三十秒。

那上线以后呢

报告那一小段其实是两句话,第二句一样重要: 他们还实现了专门的部署策略来保证推理时的负载均衡,所以推理时也不丢 token。

但训练和推理靠的不是同一套东西。 训练时有训练循环,可以每步调偏置。上线之后没有这个循环了。

推理靠的是搬专家:把线上观测到的高负载专家复制一份,额外部署。 prefill 阶段设了 32 个冗余专家,每张卡除了原本的 8 个再多带 1 个; 哪些是热点根据线上统计来定,每隔十分钟左右重新调一次。

下面这句是这一段值得留下的东西。

注意这个动作的边界:重排只在节点内部做,明确不增加跨节点的 all-to-all。 又一次 —— 先认拓扑,再谈优化。

收尾,过 4d。

可能被问到

丢掉的 token 是彻底没了吗?那模型不就学坏了?

不是整个 token 没了,是它在这一层的专家输出没了。 残差连接还在,所以它的信息会原样穿过这一层继续往下走。

可以这么说:它跳过了这一层的 MLP,只保留了注意力那部分的结果。

所以模型不会崩,但质量确实会掉 —— 那组对照实验里 1.73 倍的差距就是在量这个。

为什么不干脆把盒子开得非常大,反正显存够?

两个原因。

一是不划算盒子里没装满的部分是要真算的,你在拿算力去乘一堆零。 那个例子里为了不丢,算量涨了两倍多。

二是你不知道该开多大有的模型要开到 11 倍, 而且这个需求会在训练中途突然跳上去 —— 你按今天的情况调好了,明天照样丢。

这第二条才是真正难受的地方 —— 它不是「贵一点」,是「你无法提前知道」。

既然 dropless 这么好,为什么还有人用容量盒子?

因为 dropless 要求你有一套能处理不规整形状的算子,这不是白来的。

容量盒子那套的好处是:它只用最普通的稠密矩阵乘法。 形状是静态的,编译器和硬件都最舒服,任何框架都能跑。

dropless 是拿「算子实现的复杂度」去换「不用调那个旋钮」。 这笔交易在大模型上很划算,在小规模上未必值得。

别展开 block-sparse 具体怎么实现, 这门课的听众用不上,问深了就说「那是算子层的工作,有专门的库在做」。)

V3 用的是 dropless 吗?

这个要小心答,别顺口说是。

能确定的是结果报告说它训练时不丢弃任何 token。 能确定的是它给的原因负载均衡做得好。

不能确定的是机制它自己的训练算子内部到底还留不留「容量」这个概念, 我没有在公开资料里查到。

所以「V3 训练不丢 token」可以讲,「V3 用的是 dropless 算子」不要讲。 顺带说一句,别人在别的硬件上复现 V3 时用 dropless 算子, 跟 V3 自己当初怎么训的,是两件事。

推理的时候也会丢 token 吗?

就 V3 而言,报告明说了不丢 —— 而且靠的是另一套办法。 训练时靠偏置每步调;上线后没有训练循环,改成复制热点专家额外部署 (prefill 阶段 32 个冗余专家,每十分钟按线上统计重排一次,且只在节点内重排)。

换成别人的实现,那就取决于它有没有设容量,不是模型本身决定的。

另有个结构上的差别值得说推理 decode 一步只有几十个 token, 它们分到 256 个专家上本来就极度稀疏 —— 「某个专家被挤爆」这件事在 decode 上的形态跟训练完全不同。

推理侧真正头疼的是反过来那一半:绝大多数专家一个 token 都没接到, 权重白搬。 这跟上一讲那张交叉图是同一件事的两个侧面。

别讲什么

三条红线 ① 别说「V3 用的是 dropless」。 报告只说了不丢 token 和归因,没说算子内部长什么样 —— 这是这一讲唯一一个容易顺口说错的地方,而且一旦说错, 整节课「查过的和推出来的分开讲」这个基调就破了。
② 别展开 block-sparse 怎么实现。 「这种算子本来就能处理大小不一的块」这一句就够了 —— 再往下就是算子工程,这门课的听众用不上。
③ 别把「丢 token」讲成「这个 token 消失了」。 残差还在,它只是跳过了这一层的 MLP —— 讲成彻底消失,台下会觉得这套系统荒谬到不可能work,反而不信了。
节奏提示 三分钟,五段:形状为什么静不了二十秒 → 盒子和两头赔四十秒 → 三个数字三十秒 → dropless 加回接第 9 讲三十秒 → 上线之后靠冗余专家三十秒。
整讲的重量在那句回接「它不只是在防止变慢, 它是在防止数据凭空不见。」 说完再翻页,别赶。
时间不够可以整节砍主线不断只剩一分钟就留「盒子两头赔」那张图加那句回接,三个数字和冗余专家都砍掉。
11

671B 与 37B:山和溪

⏱ 3 分钟
这一讲要留下什么 主落点:MoE 省的是算力,不省显存。 这是全课被误解最多的一条 —— 很多人听到「激活 37B」就以为它跟一个 37B 的 稠密模型一样好养。完全不是:你要的显存跟 671B 稠密模型一模一样。
副落点:参数、算力、显存是三本账,同一个部件在三本账上的排名完全不同。 注意力占参数 1.7%,却占 128K 算力的 81.8%。 这句话是全课想让人带走的思维方式,也是通向第 7 步合账的桥。
还有一件事这一讲顺带完成了:把账合上。 671.03 B 对上官方 671B —— 这不是对上一个数字, 是把两个小时里用过的每一条公式一次性验了。
这一讲的定位 这是第 4 步的收尾,也是全课第一个「结账时刻」。 前面每一节都在往表里填数,这一节把表加起来跟外部口径对齐。
三分钟,三段:合账 → 61 : 1 那个对照 → 山和溪。 前两段是理性的,最后一段是感性的,节奏上正好收束。
那张山和溪的插图值得多停两秒。 整节课数字太密了,这里给耳朵一个休息点。

讲稿

滚到 4d,那张参数表,整张要在屏幕里。

先把账合上

这张表里每一个数,前面都推过了,现在把它们加起来。

单个专家 44.04 M,一层 257 个专家加一个门控 11.32 B,58 层 656.57 B。

一层注意力 187.11 M,61 层 11.41 B。三层 dense 1.19 B。进出口两张大表 1.85 B。

下面这个数字念慢一点,一位一位念。

加起来:671.03 B。官方口径 671B。

停两秒。

我想说清楚这一步的意义 —— 它不是对上了一个数字。

是把我们这两个小时用过的每一条公式,一次性验了。 隐藏维 7168、专家宽度 2048、SwiGLU 要三个矩阵、MLA 那几个秩 —— 任何一条记错了,这个总数都对不上。

这也是我推荐大家验算的方式:不要孤立地检查某个数对不对, 去找一个能把一整套公式串起来的外部锚点。

⭐ 表里最扎眼的一行

滚到那张蓝色 61 片 / 绿色一整块的对照图。

请看这两条。上面那条是全部 61 层注意力加起来,11.41 B。 下面那条是随便哪一层 MoE,11.32 B。

两条一样长。

这句停三秒,让它自己发酵。

六十一层的注意力,等于一层 MoE。

而我们刚才在第六讲算过:在 128K 长度上,注意力吃掉 81.8% 的算力。

所以注意力占参数 1.7%,占算力 81.8%。

参数量和计算量,是两本完全不同的账 —— 连排序都不一样。

如果时间够,指一下下面那个 MLA 拆解的蓝框。

顺带一个小发现,同样是「不同的账不同的答案」。 MLA 那 187 M 参数里,最大的一块不是压缩 KV 用的那几个矩阵, 是把 128 个头拼回残差流的输出投影,一个矩阵就占 62.8%。

压了半天缓存,参数大头在出口那一侧。 这不矛盾:MLA 压的是「每个 token 要存多少」,那是显存账; 这里数的是「这一层有多少参数」,那是参数账。

山和溪 —— 本讲主落点

滚到那张蓝条 / 绿条的图,再往下就是那张山的插图。

上面那条蓝的是 671.03 B,全部参数。下面那一小截绿的是 36.62 B, 一个 token 真正用到的。

差不多 18 倍。

下面这句是全课最容易被听错的地方,一定要重。

这 18 倍,全都是算力。显存,一个字节都没省。

中间那 634 B 参数,这一步确实闲着 —— 但它们必须待在显存里, 因为下一个 token 可能就要用其中的某几个。

指那张山和溪的图。

那座山是显存要扛的,那条溪才是算力要付的。

MoE 做的事,就是把山和溪分开。在它之前,你想要多大的山,就得付多大的水。

停一下,然后给出那句纠正。

所以「激活 37B」不等于「跟 37B 的稠密模型一样好养」。 你需要的显存,跟一个 671B 的稠密模型一模一样。

MoE 换来的是「同样算力下能装更多知识」,不是「同样显存下能跑更大的模型」。

收尾,过第 5 步。

最后把这一讲拎成一句:参数、算力、显存,是三本账。 一个部件在一本账上微不足道,在另一本账上可能就是主角。 第 7 步我们会把三本账并排放在一起看。

可能被问到

37B 到底是 36.62 还是 37?为什么两个数不一样

照实说,这个差在「嵌入表算不算激活」。

逐项加是 36.62 B58 层各 9 个专家、全部 61 层注意力、 3 层 dense、加上出口那个大矩阵。 把嵌入表也算进去就是 37.55 B。

官方那个 37 落在这两个数中间,报告没有细说怎么归的,我也没有查到。 所以这门课统一用 36.62,需要跟官方对齐时说「约 37B」。

差的这不到 1 B 不影响任何结论 —— 18 倍还是 18 倍。

那把不常用的专家换到 CPU 内存或者硬盘上,不就省显存了吗?

可以做,而且真的有人做,但它换的是另一笔账。

问题在于「不常用」这件事没法提前知道 —— 下一个 token 挑哪 8 个专家,要等路由跑完; 等你知道的时候,再去外面把权重搬进来就来不及了。

所以这条路是拿延迟换显存。 对吞吐要求不高的场景划算,对 decode 这种一步一步来的场景很难受。

别展开具体的 offload 策略, 这门课的听众不需要;说清「它是拿延迟换显存」这个性质就够了。)

既然显存一点没省,MoE 到底图什么?

图的是:在你能买得起的算力上,装下更多的知识。

换个问法就清楚了如果你想要一个 671B 的稠密模型, 它每个 token 都要算满 671B —— 那个算力账没人付得起。

MoE 让你用 37B 的算力预算,拿到 671B 的容量。 代价是显存要按 671B 准备。

所以它是一笔明确的交易:用显存换算力。 值不值,取决于你手上这两样哪个更紧张。

为什么 61 层注意力才等于一层 MoE?是注意力太小了吗

不是注意力小,是 MoE 那一层实在太大。

一层 MoE 有 257 个专家,每个 44 M —— 它本质上是把 257 份 MLP 摞在一起放着,只是每次只用 9 份。

而注意力一层 187 M,跟一个 dense MLP 层(396 M)比也就一半。 注意力从来就不是参数大户,它是算力大户。

这恰恰是这一讲想说的 —— 「大」这个字,在参数账和算力账上指的不是同一件事。

671.03 跟官方 671 差的那 0.03 B 是什么?

就是四舍五入。官方那个 671 本来就是取整报出来的。

值得注意的反而是另一件事我这张表里没有算 MTP(多 token 预测)那个模块。 没算它,总数就已经对上了 671 —— 说明官方那个 671B 的口径本来也不含它。

MTP 是什么这门课不讲,问到就说「训练时的一个辅助模块, 不在这个 671B 里」,别展开。)

别讲什么

三条红线 ① 绝对不能把「激活 37B」讲成「像 37B 模型一样好部署」。 这是全课被误解最多的一条,也是这一讲存在的全部理由 —— 讲软了,台下会带着一个错误的成本模型回去。
② 别把 671.03 对上 671 讲成「你看我算得多准」。 重点不是准,是「找一个外部锚点一次性验整套公式」这个方法 —— 方法能被带走,一个漂亮的数字带不走。
③ 别在这里展开 offload、量化、专家并行怎么切。 这一讲只负责立起「三本账」这个框架具体怎么省是后面专题的事,在这儿开口会收不住。
节奏提示 三分钟,三段各一分钟:合账 → 61 : 1 → 山和溪。
两个必须留的停顿念完 671.03 停两秒说完「两条一样长」停三秒这两处不停,冲击力就没了。
时间不够砍 MLA 拆解那一小段(它是加分项不是主线), 山和溪那段一秒都不能砍 —— 它是第 4 步的落幕,也是全课复述率最高的一句。
12

残差流是总线不是管道

⏱ 4 分钟
这一讲要留下什么 主落点:网络不是一根管道,是一条总线。差别就在「加」这一个字 —— 管道是替换,总线是累加。 这是全课唯一一次直接去扳一个听众已经有的错误图像, 所以要先把那个错的画出来,再推翻它。
副落点:顺手立住「61 层的激活为什么不能算完就扔」这个问题 —— 但不回答。 那是专题四的事。带着疑问离开,比当场给答案更有价值。
还有一件事这一讲免费拿到:为什么层数能是 61 这种随意的数字。 因为形状全程不变,插一层删一层都不影响别人。
这一讲的定位 前面四步讲的全是「一层里面」,这一步第一次讲「层与层之间」。 它是全课结构上的一个换挡点,节奏可以放慢一点。
四分钟,靠那张左右对照图撑。 左边错的、右边对的,两张并排 —— 这种图比任何说明都管用, 因为听众脑子里那张错的图是「看」出来的,不是「想」出来的, 只能用另一张图去替换它。
这一讲不难,但很容易讲平。 要有一个明确的「翻转时刻」,就是你说出「注意是加,不是替换」那一下。

讲稿

滚到第 5 步,那张左右对照图,整张要在屏幕里。

我先问大家一个问题:你脑子里的 Transformer 长什么样?

真的停一下,让他们在心里画。 手指左半张图。

大部分人会画成左边这样:一根管道。数据进第 1 层,出来进第 2 层,再进第 3 层, 一路传到底。

这个图像是错的。

手改指右半张。

它其实是一条总线

有一个宽度 7,168 的向量,从头到尾一路贯穿。

每一层做的事是三步:从它读一份,算出点东西,再加回去。

滚到下面那个公式框。

写成公式就一行:x 等于 x,加上 MLA 处理过的 x。 下一行,x 等于 x,加上 MoE 处理过的 x。

这两行重复 61 遍,整个模型就没了。

这是翻转时刻,重读「加」这个字,然后停两秒。

请注意是「加」,不是「替换」。这一个字是这一讲的全部重量。

为什么必须是加

两个理由,一个关于梯度,一个关于语义。

梯度上:加法给反向传播留了一条直达的高速路。 不然梯度要穿过 61 层的乘法,会衰减到什么都不剩。

语义上更有意思 —— 它让「什么都不做」成为默认状态。

某一层如果暂时没学到有用的东西,它输出接近 0,加上去等于没动, 不会破坏已经有的信息。

停一下,下面三条是这一讲的价值密度所在。

⭐ 这个区别带来三个能观察到的后果

第一,每一层看到的是「到目前为止的全部」,不是「上一层的输出」。

所以第 40 层可以直接读到嵌入层当初写进去的东西 —— 中间那 39 层没有把它冲掉,只是往上叠了东西。

第二,形状全程不变。7,168 进,7,168 出,61 层都一样。

所以你能随便插一层、删一层。 61 这个数字不是算出来的,是试出来的。

这一条在硬件上也很关键:因为整条流的宽度不变, 你可以按这个维度一路切下去做并行,不用每层重新规划。

手指左右两张图的底部注释,对着念。

第三,管道模型里第 30 层坏了,后面 31 层全废。 总线模型里第 30 层输出 0,只是少了一份贡献,别人照跑。

归一化摆在哪,一句带过

V3 用的是 pre-norm:先 norm,再进子层,子层的输出直接加回主干,主干本身不动。

另一种摆法叫 post-norm,是先加、再对加完的结果做 norm —— 那样这条总线上每隔一段就横着一个 norm,刚才说的那条「直达的高速路」就断了。

现代模型基本都站到了 pre-norm 这一边。 这里可以加一句给懂行的人听:2020 年 Xiong 那篇 Layer Normalization 的论文证过, post-norm 在初始化时输出层附近梯度很大,所以学习率 warm-up 是必需的; pre-norm 则可以把 warm-up 整个去掉。 「训不稳」这个说法,落到能验证的形式,就是这一条。

最后一个数字,它只立问题

滚到那个「1.75 GiB × 61」的框。

残差流在 128K 长度上,每层要留一份:131,072 乘 7,168 乘 2 字节, 正好 1.75 GiB。

61 层全留着,106.75 GiB。而一张卡是 88 GiB 出头。

停一下,然后补这句 —— 它比上面那个数更有冲击力。

请注意这个数字里一个权重都没有。全是中间结果。 权重那 1.22 TiB 是另外一笔账。

于是一个问题自己冒出来了:凭什么要留着?算完不能扔吗?

这里不要接着讲,让问题悬着。

「为什么反向非要这些中间量」,那是专题四的事。这一讲不答。

但屏幕上这段有半个答案,念一下就好,别展开 —— V3 论文里明写着:所有 RMSNorm 和 MLA 的上投影,反向的时候重算,不存。

所以真实系统面对「装不下」的第一反应,不是加卡,是问一句:这份非存不可吗? 存不下就别存,反向时拿算力换回来。这是拿时间换空间,不是免费的。

这一句就够了,别顺势讲重算策略 —— 那是专题四。 如果时间还够,滚到最后那个 mHC 的框和它上面那张图,30 秒带过。

题外一眼。V3 只有一条残差流;V4 换成了 mHC,四条并行的残差流。

对着图说一句就行:变宽的是总线,不是车间。 层的输入输出还是 7,168 维,MLA 和 MoE 内部一个字节都不用改。

加宽残差流不是 DeepSeek 起的头,前人做过,问题是堆深了训不稳。 mHC 的那一下是把混合矩阵约束成双随机矩阵 —— 行和列和都是 1,信号既不放大也不消失。 所以它不是「更宽」,是「让更宽这件事变得能训」。

代价那句要小心讲 —— 不要说「4 倍宽所以显存 4 倍」。V4 论文用 fused kernel 加选择性重算把这笔账吃掉了大半, 他们给出的唯一绝对数是墙钟开销 6.7%。 细节在展开框里,课上不讲。

可能被问到

「总线」这个说法是比喻,还是真的?

基本是字面成立的,这也是我喜欢这个比喻的原因。

那个 7,168 维的向量在整个前向过程里一直活在显存里 —— 每一层读它、往它加,物理上就是同一块内存被反复读写。

它不像总线的地方只有一点:层是严格按顺序访问的,不存在并发抢占。 所以说它是「一条被顺序访问的总线」最准确。

既然每层都是「可选的增量」,那是不是可以随便剪掉几层?

结构上确实剪得掉 —— 这正是这一讲想说的性质。 形状不变,剪掉一层,后面的层照样能跑,不会因为形状对不上而崩。

但质量会掉,而且掉多少不好预测。 训练时那一层学到的东西没有备份在别处。

所以「能剪」和「剪了没事」是两回事 —— 剪层是一个真实存在的压缩手段,但它属于模型压缩,不在这门课里。

pre-norm 和 post-norm 到底差在哪?

差在主干上有没有被 norm 动过。

pre-norm先 norm 再进子层,子层输出直接加回主干 —— 主干那条路上是纯粹的加法,一路没有被缩放过。

post-norm加完之后再 norm 整条主干 —— 于是那条「梯度高速路」上每一层都被动了一次。

层数一多,post-norm 就训不稳。 这跟前面说的第一个理由是同一件事:你不想在高速路上设收费站。

如果对方想要更硬的东西,给他这条: Xiong 等 2020(On Layer Normalization in the Transformer Architecture) 分析了初始化时刻的梯度,结论是 post-norm 输出层附近梯度大、warm-up 是必需的; pre-norm 梯度良态,warm-up 可以整个删掉

注意这个结论的形状:它说的不是「post-norm 训不出来」, 是「post-norm 多了一个必须调对、越深越难调的东西」。 在几十上百层的规模上这跟「训不稳」是一回事,但转述时说准一点更好。

别展开各种 norm 的变体, 这门课只需要「主干要保持干净」这一条直觉。)

106.75 GiB 是怎么算的?为什么每层只算一份

131,072 个 token,每个 7,168 维,bf16 两个字节 —— 一份就是 1.75 GiB。 乘 61 层,106.75 GiB。

这里按「每层留一份」算,是个下限口径。 真实实现里一层内部还有别的中间结果要留,所以实际只会更多,不会更少。

用下限反而更有说服力 —— 连最省的算法都已经装不下了。

如果有人追问「更多是多少」,手上有个数备着:约 4.15 TiB,四十倍左右。 大头是 MoE 把每个 token 的激活复制成九份,和 MLA 把 K、V 解压回 三万多维 —— 那个压缩省的是推理的 KV cache,训练前向省不到。 但这些属于专题四,这里报个量级就停,不要展开。

V4 那个四条残差流,是不是等于把模型变宽了四倍?

不是,这个要说清楚,不然容易误解成参数涨四倍。

变宽的是残差流本身,不是每层的计算宽度。 子层读进去和写出来的还是 7,168 那个尺度, 变的是「层与层之间用几条通道来传」。 V4 论文原话是「the expanded residual width does not influence the design of the inner layers」。

数字可以直接给:两个型号的 nhc 都是 4,Sinkhorn-Knopp 都迭 20 次。 V4-Pro 是 61 层、hidden 7,168 —— 跟 V3 一模一样,所以这一页的账在它上面直接成立; V4-Flash 是 43 层、4,096。

那 4 倍宽,激活显存是不是就涨 4 倍?

这是这一讲最容易答错的一个问题,而且我们自己以前就答错过。

「1.75 GiB × 4 = 7.00 GiB 每层」是个上界,不是实际付出的代价。 那个算法假设四条流每层的激活全部原样留着。

V4 论文对这一点写得很清楚:他们上了 fused kernel、选择性重算、 改了 DualPipe 的重叠方式。 原文说「重算层间大部分 hidden state 和全部归一化后的层输入」—— 也就是说,很大一部分根本不存。

所以正确的答法是:显存的绝对增量论文没给,因为它取决于重算档位; 论文给了绝对数的是时间 —— mHC 的墙钟开销是重叠后 1F1B 流水线级的 6.7%。

引用 6.7% 一定要连分母一起说 —— 它不是「整个训练慢 6.7%」,是「重叠后的那一级流水线慢 6.7%」。)

别讲什么

四条红线 ① 别跳过「先画那张错的图」这一步。 直接讲总线,听众会点头,然后回去继续用管道那张图想问题 —— 错误图像只能被另一张图替换,不能被一段说明说服。
② 立完「凭什么要留着」这个问题,不要顺口答一句「因为反向要用」。 答了就没有悬念了而这个悬念是专题四的入场券。 屏幕上那句「V3 自己选择不存、反向重算」可以念,因为它答的是「他们怎么办的」, 不是「反向为什么需要」—— 悬念还在。念完就停。
③ 别把 mHC 讲成「V4 把模型变宽了四倍」。 宽的是残差流,不是计算宽度;层的输入输出仍然是 7,168 维。
④ 别再说「4 倍宽所以每层从 1.75 GiB 变 7.00 GiB」。 这一条以前是这么讲的,现在改了 —— 那是个被工程手段大幅抵消掉的上界, 被当成了实际代价。 论文给出绝对数的只有墙钟开销 6.7%,而且分母是「重叠后的 1F1B 流水线级」。 这是全讲最容易被读过 V4 报告的人当场纠正的一句。
节奏提示 四分钟,四段:错图与对图一分钟 → 为什么必须是加一分钟 → 三个后果一分半 → 数字与立问题半分钟。 mHC 那 30 秒是溢出项,超时就砍。
开场那个提问「你脑子里的 Transformer 长什么样」一定要真的停 —— 他们心里先画出那根管道,你再说「这是错的」,冲击力才在。
三个后果里如果只能留一个,留第三个坏一层的后果)—— 它最具体,也最能让人记住「加」和「替换」的区别。
13

出口:最吓人的临时张量

⏱ 5 分钟
这一讲要留下什么 主落点:算显存只算权重,一定会算错。 这句话前面已经暗示过两次,这一讲是第三次,也是最狠的一次 —— 所以这里要把它明说出来,当成整个前向部分的收口。
撑住这句话的证据:一个在参数表上根本不存在的张量, 体积超过了 61 层注意力的全部权重。 31.56 GiB vs 21.26 GiB,1.48 倍。
副落点:两个「同一个东西,两种用途」—— 进出口两份权重,以及 MTP。 都只用一句话,不展开。
这一讲的定位 这是旅程的最后一站,动作最简单,数字最吓人 —— 这个反差本身就是内容。 前面每一步都是「越算越复杂」,这一步是「一个矩阵乘就完了, 然后爆出全程最大的张量」。节奏上要用得上这个落差。
五分钟,前一分钟讲机制,后四分钟全在那个数上。 机制真的很简单,别恋战。
这一讲讲完,第 7 步合账就水到渠成了。 因为「权重之外还有东西」这件事,到这里已经被证明三遍。

讲稿

滚到第 6 步开头。

走完 61 层,手里还是那个 7,168 维的向量。现在要把它变回「下一个字是什么」。

动作只有两步:最后一次 RMSNorm,然后乘一个矩阵, 从 7,168 维摊开成词表的 129,280 维 —— 给每一个词打一个分。

softmax 一下,就是概率分布。温度就是在这一步起作用的。

顺带一个容易漏的配置

手指那个 tie_word_embeddings 的红框。

这个矩阵跟第 1 步那张嵌入表,形状一模一样,7,168 乘 129,280。

很多模型会让它们共享同一份权重,叫权重绑定 —— 毕竟「表示一个词」和「预测一个词」直觉上是一回事。

但 V3 的配置里这一项是 false。它认为读和写是两个不同的任务,各给一份。 代价是多 926 M 参数。

这也解释了第 7 步那张表里的一个数:嵌入相关是 1.85 B,不是 0.93 B —— 因为它数了两份。

这里换气。下面是这一讲的全部重量。

⭐ 现在看这个矩阵的输出

滚到那张红色的 logits 图,整张要在屏幕里。

131,072 个位置,每个位置 129,280 个分数,两个字节。

乘出来 31.56 GiB。

停两秒,让这个数落地,再给对照。

我给三个对照。它比 61 层 MLA 的全部权重加起来还大 —— 那个是 21.26 GiB。 它比一层 MoE 里 257 个专家的全部权重还大。它是残差流一份的 18 倍。

手指图下面那行「三个让它更难堪的细节」。

三个让它更难堪的细节

第一,这一步的算力只占整个前向的千分之五。

242.92 TFLOP,而整个前向是 52.5 PFLOP。 全程算力最少的一步,产出了全程最大的张量。

第二,算 cross-entropy 的时候,logits 通常要提到 fp32,不然数值不稳。

那就是 63.13 GiB —— 占单卡 88 GiB 的 72%。 一个纯临时的东西,吃掉四分之三张卡。

第三个对照我觉得最有意思。这 31.56 是训练的账, 因为训练每个位置都要算 loss。

推理的 prefill 只需要最后一个位置的 logits —— 252 KiB。

滚到那张左右对照图:左边一整块红板,右边一条绿细线。

同一个算子,训练和推理差 131,072 倍,正好就是序列长度。

右边那条绿线其实是骗你们的 —— 按真实比例它只有那个虚线框的十三万分之一高, 合零点零零一三个像素,画不出来。为了让你们看见,它被放大了大概一千倍。

这句话本身就是个记忆点,说完停一下。

所以「lm_head 这个算子贵不贵」这个问题, 脱离了「训练还是推理」,根本没法回答。

所以真实现是怎么做的

没人会把它整个物化出来。标准做法是分块: 一次只算几千个位置,当场把 loss 算掉、梯度攒下,扔掉这块再算下一块。

也就是把 loss 融进去,让那个大张量从头到尾都没有完整存在过。

这跟 FlashAttention 是同一类手法 —— 不要把中间结果落地。 这门课里第二次出现这个手法了。

下面是收口,慢下来。

⭐ 落点

滚到最后那个绿框。

请注意,这已经是这门课第三次撞上同一件事了。

第 2 步是 KV cache。第 5 步是激活。现在是 logits。

三次的形状一模一样:一个在参数表上根本不存在的东西, 体积超过了参数本身。

停,然后一字一句说下面这句。

所以这一步真正的收获不是 31.56 这个数,是这句话 —— 算显存只算权重,一定会算错。

如果时间还够,滚到 MTP 那个表,30 秒带过。

题外一眼:MTP

配置里还有一项,除了预测下一个字,还额外预测下下一个。 注意是「一个」,深度就是 1。

它是为训练造的 —— 论文自己写的目的就是「用 MTP 来改善训练」。 只预测下一个字,模型容易学成顺嘴溜;逼它同时看远一点,等于要求它有规划。

而且推理的时候可以直接把它丢掉,主模型照常工作。 论文里的消融实验就是这么做的,所以对比双方的推理成本完全一样。

但丢掉太可惜,所以它被顺手拿来做投机解码的草稿。 V3 实测第二个字的接受率 85 到 90%,端到端吐字速度一点八倍。

同一个东西,为训练造的,被推理捡了便宜。 为什么这笔买卖划算,是专题六的事,这里点到为止。

可能被问到

logits 怎么会有三十多个 GB?出口那个矩阵不就是 7168 × 129280 吗?

这是全场最高频的一个卡点,几乎一定有人问。 根子在于「位置」这个词被用在了两个地方,听的人把它们叠一起了。

先肯定他:那个 7168 × 129280 的矩阵确实存在,也确实只有一份。 它是权重,1.73 GiB,早就算在 671B 里了。他说的完全没错 —— 但那只是一个字的事。

然后把维度掰开:你喂进去的是十二万八千个字。 每一个字都要拿自己那 7168 维去乘同一个矩阵,各自拿到自己的 129280 个分数。 所以结果不是一行,是十二万八千行 × 十二万九千列的一张表。 一行 252 KB,乘十二万八千行,31.56 GiB。

在白板上画两条边,横的写「词表 129,280」,竖的写「字数 131,072」, 指着说「这两个数差不多大,是巧合」。 这句一定要说 —— 不说的话,一半人会以为它们是同一个东西

收尾给一个反直觉的角度:前面每一层,一个位置只占 7168 个数; 到出口这一下变成 129,280,胖了 18 倍。 整个模型最宽的地方不在中间,在出门那一下。

那为什么预训练一般用 4K?是不是为了让 logits 小一点?

结论对,理由错 —— 这个要当场纠,不然后面会一直错下去。

logits 只看 token 总数,不看序列多长。 总数就是序列长度乘 batch。128K × batch 1 和 4K × batch 32, 都是 131,072 个 token,logits 两边一模一样,都是 31.56 GiB。 你把序列缩短、batch 拉高,等于什么都没省。

短序列真正省的是注意力。 注意力的代价是 batch 乘序列长度的平方, 所以在 token 总数相同的前提下,它跟序列长度成正比 —— 128K 那种切法要付 4K 切法的 32 倍。

如果对方是懂行的,可以再给一个能查证的锚点: V3 的配置文件里 original_max_position_embeddings 是 4096, YaRN 的 factor 是 40,4096 × 40 = 163840 —— 它本体就是按 4K 训的,128K 是后面扩出来的。

再深就打住:训练时 logits 还要转 fp32、于是普遍用分块 cross entropy 这件事,属于专题四的内容,这里提一句名字就够了。

31.56 GiB 真的会占出来吗?还是只是理论值

正常实现里不会 —— 而这恰恰是重点,不是免责声明。

它是「照数学定义直写会得到的东西」。 之所以没人这么写,正是因为它这么大。

所以这个数的价值不在「显存里真有这么一块」, 而在「它解释了为什么所有框架都要在这里做分块和 fuse」。 你不知道这个数,就不知道那些代码在躲什么。

提醒自己别把它写进容量规划 —— 真实峰值取决于你切多大块,不是这个数。)

为什么 V3 不共享进出口那两份权重?共享不是更省吗

省是真省 —— 926 M 参数,1.73 GiB。 小模型上这个比例很可观,所以小模型几乎都绑。

但在 671 B 上,这一份只占千分之一点四,省下来意义不大。

而不绑的理由是:读和写其实不是同一个任务。 「这个词的含义是什么」和「下一个位置该不该是这个词」, 要的表示不一样。绑在一起等于强行让两个任务共用一组参数。

别断言 DeepSeek 是出于这个理由不绑的 —— 报告里没写。这是通行的解释,不是它的原话。)

分块算 logits,结果会和一次算完不一样吗

数学上一样,浮点上会有极小的差异。

为什么一样cross-entropy 对每个位置是独立的, 位置之间不耦合。所以按位置切开算,再把 loss 加起来, 跟一次算完是同一个式子。

差异来自求和顺序 —— 浮点加法不满足结合律, 分块换了累加顺序,最后几位可能不同。这个量级不影响训练。

真正要小心的不是这里,是 softmax 本身的数值稳定 —— 那是为什么要提到 fp32、要先减最大值。

算力只占 0.46%,是不是说明这一步不重要

正好相反 —— 这个反差本身就是这一讲要教的东西。

算力和显存是两本账,这一步是全课最极端的例子。 它在算力账上几乎不存在,在显存账上是第一名。

如果你按算力去分配资源,会完全漏掉它; 等真跑起来就在这一步 OOM。

这条可以顺手回指第 4 步那个「参数量和计算量是两本账」 —— 同一个道理,换了一对账本。)

「tie = false 不共享」,可是我听说 V3 里 output head 是共享的?

两件事,都是真的,共享的对象不一样 —— 这是这一讲最容易被绕进去的一个点。

不共享的是「嵌入表 ↔ output head」。 这就是 tie_word_embeddings = false,所以要数两份 926 M。

共享的是「主模型的 output head ↔ MTP 模块的 output head」。 论文把最浅的几层(含嵌入)和最深的几层(含 output head) 摆在同一个流水线 rank 上,让 MTP 和主模型物理上共用这两份参数和梯度。

所以结论是:MTP 不会让总账再多一份 926 M。 如果对方是拿这个来质疑那张总表,这就是答案。

63.13 GiB 除以 94.74 我算出来是 67%,怎么会是 72%

因为那两个数不是同一个进制,这是个非常常见的坑。

GiB 是 1024 的三次方,GB 是 1000 的三次方,差 7.4%。 63.13 GiB 换成十进制是 67.78 GB,除以 94.74 GB 才是 72%。

或者换个方向:94.74 GB 换成 GiB 是 88.23 GiB,63.13 ÷ 88.23 也是 72%。 怎么换都行,不能一头 GiB 一头 GB。

页面上所有百分比都是按字节算的,不是按显示出来的那两个数算的。 这一条值得当场说 —— 硬件厂商标 GB、框架报 GiB,这个坑几乎每个人都踩过一次。

词表为什么要 129,280 这么大?做小一点不就省了

词表大小是一个权衡,两头都有代价。

做大一个词占一个 token,序列变短、算得快, 但嵌入表和 logits 都跟着涨。

做小省了这两项,但同样一段文字要更多 token 才装得下, 而算力是随 token 数涨的 —— 而且注意力那一项是平方涨。

所以缩词表通常是亏的 —— 省下的是线性项,付出的是平方项。

别去比各家词表大小, 这门课不做这个对比,说清「是个权衡、而且方向不对称」就够。)

别讲什么

四条红线 ① 别把 31.56 GiB 说成「跑起来会占的显存」。 它是照定义直写才会出现的东西,真实现从不物化它 —— 说错这一点,听众会拿它去做容量规划,而那是错的。 正确的说法是「正因为这么大,所以工程上必须绕开」。
② fp32 那 63.13 GiB 不要说成 V3 的官方数字。 「cross-entropy 提到 fp32」是通行做法,不是 V3 报告里的规定 —— 这一整讲里,只有 129,280、7,168 和 tie=false 是配置里读出来的, 其余都是按口径算的。
③ MTP 给两个数就停,不要滑进投机解码的原理。 那个话题一开就是十分钟,而且它属于专题六 —— 这里要的只是「为训练造的东西,被推理捡了便宜」这个观感, 加上「接受率 85–90%、1.8× TPS」这两个可查的数。 被追问「为什么接受率能这么高」,就答一句「因为草稿读的是主模型自己的隐状态, 不是另一个小模型」,然后停。
④ 别说 MTP 是「多预测两个字」。 深度是 1,多预测的是一个字;主模型一次验证的是两个位置 —— 这两句听起来像,但前一句是错的。
节奏提示 五分钟,一头轻三头重:机制一分钟 → 那个数一分钟 → 三个细节一分半 → 训练 vs 推理那张对照图半分钟 → 收口半分钟。 MTP 那 30 秒是溢出项,超时就砍。
「31.56」说出来之后一定要停两秒再给对照 —— 不停的话它只是又一个数字;停了,对照才砸得下去。
收口那句「算显存只算权重,一定会算错」要一字一句说。 这是整个前向部分的总结论,也是第 7 步合账的入口 —— 如果全场只记住一句话,希望是这句。
14

合账 · 算出来 vs 量出来 · 抛钩子

⏱ 13 分钟
这一讲要留下什么 主落点:「为什么需要并行」这句话,从今天起是个除法的结果,不是一句口号。 1.23 TiB ÷ 94.74 GB = 14.3。整堂课攒了六站零件,就是为了让这一除有意义。
第二落点:静态分析靠得住,但它最不准的地方,恰好是你最需要它的地方。 AOT 那三条是我们自己踩出来的,比任何一条公开结论都值钱。
第三落点(钩子):全课拿 94.74 当了一整天分母,却从没说过它从哪来。 这是专题二的开场白,今天只负责让人痒。
这一讲的定位 十三分钟,全课最长的一讲,也是唯一一讲「不引入新概念」。 所有材料前面都出现过,这里只做三件事:合、验、抛。 所以节奏可以慢,可以留白,可以让人喘口气。
前面六站是「攒」,这一站是「结」。语气要有明显变化。 不要再用讲新东西的那种推进感,要用「我们来看看攒出了什么」的语气。
这一讲最大的风险不是讲不完,是讲成念表。 那张总表不要逐行念 —— 让它在屏幕上待着,你只说三个结论。

讲稿

滚到第 7 步,那张总表整张在屏幕里。

我们走完了七步。这张表就是一路攒下来的全部。

给三秒让他们扫一眼,不要念。

我不打算逐行念它。它在屏幕上,你们随时能看。 我只用它回答三个问题。

问题一:一次 128K 前向,多少算力

52.5 PFLOP。

而其中 81.8% 是 attention 那个平方项。 不是那 671 B 参数,是那个跟序列长度平方成正比的东西。

问题二:常驻显存多少

权重 1.22 TiB,加 KV cache 8.58 GiB,一共 1.23 TiB。

问题三:要多少张卡

手指第三张橙色的卡片。

1.23 TiB 除以单卡的 94.74 GB。

停一拍,再报数。这是全课的收口。

14.3。

你不能有零点三张卡,所以是 15 张。

这里停久一点,然后说下面这句 —— 这才是重点。

这个数字本身不重要。重要的是它是除出来的

今天开场我没有说「大模型需要并行」这句话,一次都没说过。 我们只是一步一步算,然后它自己出现了。

⭐ 但请不要把 15 记成「答案」

滚到下面那个黄色的警告框。

15 张卡是最乐观的可能值。它建立在五个假设上, 而每一个在真实训练里都不成立。

只算前向、batch 等于一、不含激活、不含临时缓冲, 还假设权重能完美打包。

滚到下面那张三条横杠的图,整张在屏幕里。

把峰值激活那 41.89 GiB 加进去,14.3 变成 14.7。 就是图上第二条 —— 你们看,跟第一条几乎一样长。这一步还好。

铺垫完了,下面这个数是这一段的落点。

但换成训练就不是一个量级了。看第三条。

每个参数要 16 个字节 —— 权重两个、梯度两个、 fp32 主权重四个、再加两个动量各四个。

手指第三条上那五个色块。

请注意这五段的比例:模型本身只占最左边那两个字节, 右边那 12 个字节全是优化器的。

671 B 乘 16,10.74 TB。除以 94.74,大概 113 个 device。

比这里多将近八倍。 那笔账是专题四的,这里只报个数 —— 但那三段橙紫红怎么砍, 基本就是专题四的全部内容。

这张图真正的信息不是 113,是那个形状,值得点一句。

这张图想说的其实不是 113 这个数,是这个形状: 前两条几乎一样长,第三条长了八倍。

因为①②③问的根本不是同一个问题。①问「权重放得下吗」, ②问「跑一次前向放得下吗」,③问「能不能训」。

而这三个问题,平时经常被同一句「这个模型要几张卡」混在一起问。 下次听到这句话,先反问一句是哪个口径。

所以这一页正确的读法是:连最省的口径都要 15 张, 而真实训练要八倍于此。

换个感觉:这些算力是多长时间

滚到下面那三张时间卡片。

PFLOP 是个没有体感的单位。除以硬件的算力,它就变成秒了。

单个 TPU v7 device 的 bf16 峰值大约 1153 TFLOP 每秒。 官方标的是每颗芯片 2307,而一颗芯片里有两个 device,除以二得来 —— 这个 2 比 1 后面还会再遇到。

理论极限,百分之百利用率:45.5 秒。

现实一点,按 35% 利用率:130 秒。

这句要慢,让它沉一下。

两分多钟,就为了把一段 128K 的输入过一遍。 而且这只是前向 —— 没有反向,没有参数更新。

摊到那 15 张卡上:8.7 秒。

手指第三张卡片下面那行小字。

但这个 8.7 后面跟着七个字:「假设并行完美无损」。

停两秒。这是整门课的入口,值得停。

那七个字,就是这门课剩下的全部内容。

把活分给 15 张卡,绝不等于快 15 倍。它们要互相传数据、 要等最慢的那一个、要为了省显存把一部分重算一遍。

那道差距有多大、从哪来、怎么缩小 —— 是专题五和专题七。

这一段结束了,换气,语气从「算」切到「验」。

⭐ 第 7.5 步:这些推导,能不能信

滚到第 7.5 步。

前面这些全是推导。所以有一个很自然的问题:能信吗?

能,而且不用占一张卡。

编译器本身就是一个静态分析器。把模型和并行配置喂给它提前编译一次 —— 这条路子一般叫 AOT —— 它就能报出这份配置要多少显存,全程不申请任何加速卡。

排队等卡之前就知道答案 —— 这是整个流程里最省钱的一步。

这句话要说,不然听众会以为下面在教工具。

但先说清楚:下面三条不是 AOT 的使用说明,这门课不讲怎么跑它。

下面三条讲的是:一个「不用跑就能告诉你答案」的工具,该信到什么程度。 这个问题跟工具无关 —— 换成任何一个性能模型、任何一份容量估算表,结论都一样。

铺垫完。下面三条是这一讲的价值密度所在。

这三条是我们自己踩出来的,不是书上的。

① 编译过了,不等于装得下

编译成功只说明这张图合法 我们见过编译一路绿灯、但显存需求远超单卡上限的配置。

下面这句才是这一条真正的内容。

更麻烦的是:报错会误导人。

真正的编译失败,常常被报成「装不下」。 于是人就去调小 batch —— 方向完全错了。

这类「错误的错误信息」比没有信息更危险。 因为它会让你很有信心地朝错的方向使劲。

② 报出来的那个数,不是全部

它只是临时缓冲。参数和优化器状态是另外一笔账。

而且它不随并行度缩小 —— 你切得再宽,临时缓冲该多大还多大。 这一点最容易算错:以为加卡就能压下去,结果它纹丝不动。

③ ⭐ 贴着上限的那一带,它会失灵

远离上限的时候,AOT 非常可靠。

但逼近上限的那个窄带里,它判不准。我们判错过, 而且错的全落在这一带

滚到那张三色横条的图。

看这张图:左边一大片绿的,右边一小片绿的,中间紧贴着上限有一条红的。 绿的地方判得准,红的地方判不准。

下面这句是这一条的钥匙,说慢一点。

现在请注意红带下面那行字 —— 真正值得一试的配置,几乎全挤在这一带。

想想也是:谁会花时间去试一个明显装不下、或者明显浪费的配置?

停,然后一字一句说下面这句。

也就是说:它最不准的地方,恰好就是你最需要它的地方。

而且这不是工具的 bug,是它的性质 —— 换个工具也一样。

所以 —— 不要把静态分析当神谕。它帮你排除掉大部分明显不行的配置, 但不替你做最后那个决定。

还有一个反直觉的

同一个模型、同样的并行配置,只是换了某个算子的实现, 显存需求就会变。而且方向不固定,有的变大,有的变小。

滚到那个深色的收口框。

所以整个 7.5 步就落在这一句上:显存不是模型的属性, 是「模型 × 实现 × 编译器」三者的属性。

全课最后一段,语气放松下来。

最后,留个钩子

我们今天从头到尾都在拿 94.74 这个数当分母。

算 KV cache 撞墙点用它,算激活装不装得下用它, 刚才算 15 张卡也用它。

停一拍。

但我一次都没解释过它从哪来。

为什么是 94.74,不是 96、不是 128?

为什么单个 device 的峰值是 1153,而官方标的是 2307?

为什么一颗芯片里会有两个 device?

不要回答。停两秒,然后收。

那是下一课。

今天这一课的任务,只是把「装不下」这个事实出来 —— 而不是出来。

可能被问到

峰值那张表里,「残差流 ×2」为什么是 2?

先提醒一句:那一行里「×2」出现了三次,是三件不同的事,别混着答。 Q/K 各一份、V/输出各一份 —— 这两个纯粹是「两个一样大的张量」, 没什么可讲的。只有残差流那个 ×2 需要解释。

一层的算法是这样的:先把进来的 x 原样存一份, 然后对 x 做 layer norm,norm 出来的是另一块内存, 子层拿它去算,算完再加回到最开始存的那份上

关键就在最后那个「加回去」—— 因为它,最开始那份 x 扔不得。 于是从 norm 那一刻到加法做完,两份 1.75 GiB 同时活着。这就是 ×2。

MoE 那层是同一个结构再走一遍,所以也是 ×2。 出口那一下没有「加回去」这一步,所以只有一份 —— 表里也是这么写的, 眼尖的人会发现这个不一致,正好顺势讲。

14.3 张卡,实际是不是就用 16 张?

实际几乎不会是 15,但原因不只是「凑整」。

第一,切片拓扑有形状要求 —— 不是任意张数都能组成一个合法的互连拓扑, 所以实际规模往往落在 2 的幂或者特定的长宽高组合上。

第二,也是更重要的:15 本来就是个不该被当真的下限。 前面那个黄框说了,训练态要 113 张。真实规划是从那个数起步的, 不是从 15。

别去讲具体拓扑规则 —— 那是专题二和专题五的内容, 这里说「有形状要求」就够。)

8.7 秒那个数,实际能跑到多少?

诚实的回答是:这门课没有测过这个数,所以我不给。

能说的是差距从哪来卡之间要传数据、要同步、 要等最慢的那一个,还要为了省显存把一部分计算重做一遍。 这几项加起来不是几个百分点的量级。

而这恰恰是「假设并行完美无损」那七个字的价值 —— 它把一个未知量明确地标了出来,而不是假装它不存在。

被追问就把它交给专题七,那一课专门讲怎么量、怎么归因。 不要随口给一个百分比,这一课里所有数都是算出来或查出来的, 这里破了例,前面的可信度就一起没了。)

attention 占 81.8% 的算力,可参数只占 1.7%,这不矛盾吗

不矛盾,而且这是这门课最想让人记住的对照之一。

参数量决定的是「要存多少」,跟输入多长无关。 MLA 那 11.41 B 权重,喂 4K 还是 128K 都是这么多。

算力里 attention 那一项是随序列长度平方涨的。 所以序列一长,它就把整张算力表吃掉,而参数表纹丝不动。

一句话:参数量是静态的,算力是随输入变的。 拿一本账去推另一本,一定出错 —— 这一课已经撞见三次了。

113 张卡、15 张卡 —— 这里的「张」到底是芯片还是 device

是 device,不是芯片。这个必须答准,因为差两倍。

原因很简单:94.74 GB 是每个 device 的容量, 所以拿它做分母除出来的一切,单位都是 device。

TPU v7 一颗芯片里有两个 device。 所以 113 个 device 换算成芯片是 57 颗;15 个 device 是 8 颗芯片。 两个数都对,但说的不是一回事。

刚才讲 1153 和 2307 的时候已经埋过这个 2 比 1 了,可以回指一下。 这个 1:2 在后面几门课里会反复咬人 —— 现在讲清楚一次很值。

课件上写的「个」「张」一律指 device, 图的说明里也钉了这一句。)

那条红带到底有多宽?多少百分比以内算「贴着上限」

这个我给不出可复现的数,所以我不给。

能负责任说的只有形状:它确实贴着上限、确实很窄, 两侧可信、中间不可信。 图上那条红带的宽度是示意的,位置不是。

宽多少取决于模型、算子实现和编译器版本 —— 而下一条「换个算子实现显存就变」讲的正是这个。 既然实现一换数就变,那「几个百分点」这种说法本来就没有一个稳定的答案。

这里千万别顺口编一个数 —— 这一整课所有数字都是算出来或查出来的,破一次例,前面的可信度就一起没了。)

AOT 到底是什么?需要什么前提

就是「先编译,不执行」。 把模型和并行配置交给编译器,让它把整张计算图排好、 把显存需求算出来,但不真的去申请硬件。

它的价值是时间不是精度 —— 拿卡要排队,编译不用。你可以在等卡的同时把十几种配置先筛一遍。

不要在这一课讲怎么跑 AOT。 「从 0 跑起来」不在这门课的范围里, 这里只需要听众知道「有这么一个东西,而且它有已知的三个坑」。)

既然它在关键区间不准,那还有什么用

用处很大,只是要用对地方。

它擅长的是「排除」,不是「确认」。 十几个候选配置里,它能非常可靠地告诉你哪八个明显不行 —— 这一步就已经省掉了大量排队和试错。

它不擅长的是最后那个二选一 —— 而那两个候选,恰恰都贴着上限。

所以正确的用法是:拿它砍掉大头,剩下的两三个老老实实上机器跑。 把它当筛子,不要当法官。

别讲什么

三条红线 ① 绝对不要把「14.3」说成「所以 15 张卡就能跑 V3」。 它是前向、batch=1、不含激活的下限 —— 听众里一定有人会拿这个数回去做规划,而那会错八倍。 每次报这个数,后面必须紧跟「这是最乐观的可能值」。
② AOT 那三条要说成「我们踩过」,不要包装成通用定理。 它们是实测经验,样本是我们自己的模型和配置 —— 说成定理,一旦有人在别的场景没复现出来,整堂课的可信度一起赔进去。
③ 抛完 94.74 那个钩子,不要顺口解释。 哪怕只补一句「因为要留一部分给系统」,钩子就废了 —— 那是专题二的开场白,今天的任务是让人带着这个问题走出门。
节奏提示 十三分钟,五段:三个问题 3 分 → 三种口径那张图 3 分 → 时间换算 2 分半 → 静态分析三条 3 分 → 抛钩子 1 分半。
这一讲有三个「必须停」的点,一个都不能省报「14.3」之前停一拍;说完「假设并行完美无损」停两秒; 抛完三个「为什么」停两秒再收。 这一讲的力量全在这三个停顿里,把它们讲连贯了反而弱。
那张总表不要逐行念。 它有六行五列,念完要三分钟,而且听众一个数都记不住 —— 让它在屏幕上待着,你只说三个结论。
如果全场超时,砍「换个感觉」那一段时间换算)—— 但「假设并行完美无损」那句话要单独留下来, 它是通往整个课程后半程的门。