学透 Kimi K3 技术报告
不是让你看懂,是让你没法骗自己。
47 页的技术报告,拆成 17 章、229 道题、6 个可运行的数值实验室和 3 个三维场景。从「什么是矩阵乘法」开始。
第0章 起步:读这篇论文之前,你需要的全部基础
这一章不属于论文。它是一块地基。读完它,你会拥有读懂后面十五章所需的每一个概念——不是「听说过」,而是「能用自己的话讲清楚,并且算得出来」。如果你已经知道什么是注意力机制和 KV 缓存,可以跳到第1章;但我建议你至少做一遍本章末尾的自测,看看是不是真的知道。
学完这一章你应该能做到
- 用一句不含术语的话解释「大语言模型在做什么」
- 手算一个 2×2 矩阵乘法,并说出它在神经网络里代表什么
- 手算一次 softmax,并解释为什么要先取指数
- 画出注意力机制的三步,说清 Q、K、V 各自是什么
- 解释为什么对话越长越贵,并算出 KV 缓存的大小
- 分清总参数、激活参数、FLOPs 三个量,并解释 MoE 为什么能让它们脱钩
- 说出显存、带宽、算力三者中哪个最常成为瓶颈,以及为什么
0.1 先给你一个方向感:这篇论文在讲什么
Kimi K3 是月之暗面(Moonshot AI)在 2026 年 7 月发布的一个大语言模型,同时公开了模型权重。这份 47 页的技术报告讲的是:他们怎么把它造出来的。
造一个这种规模的模型,大致要回答四类问题,而这份报告的结构正好对应这四类:
- 模型长什么样(第 2 节,本站第 3–6 章)——里面有哪些零件,为什么这么摆。
- 怎么把知识灌进去(第 3 节,本站第 7 章)——用什么数据、训多久、怎么定超参数。
- 怎么让它学会做事(第 4 节,本站第 8–9 章)——从「会说话」到「会干活」,靠的是后训练和强化学习。
- 怎么让这一切在真实机器上跑得动(第 5 节,本站第 10–11 章)——2.8 万亿个参数装不进任何一张显卡,这本身就是个大工程。
剩下的是第 6 节的评测(本站第 12 章)、第 7 节的案例(第 13 章)和附录里的几个证明(第 14 章)。
为什么要先补地基,而不是直接读
这份报告的目标读者是同行——它默认你已经知道什么是 Transformer、什么是 KV 缓存、什么是专家并行。它在一句话里能塞进四个你没见过的名词,然后继续往下讲。硬读的结果通常是:每个字都认识,连起来完全不知道在说什么。
所以我们先花一章,把这些默认知识一个个装进你的脑子。这一章会有点长,但它是唯一一次你需要从零学起的地方。
0.2 语言模型到底在做什么:猜下一个词
这件事说出来会让人失望,但它是真的:大语言模型做的唯一一件事,是根据前面的文字,猜下一个字最可能是什么。
比如给它「中华人民共和」,它会给出一个概率分布:
然后按这个分布抽一个字出来,接在后面,变成「中华人民共和国」,再拿这个新的句子去猜下下一个字。如此反复,就「写」出了一整段话。
下一 token 预测(Next-Token Prediction, NTP):上面这个「看前文、猜下一个」的任务。整篇论文里所有的预训练,做的都是这一件事。
打个比方
像一个读遍了全世界所有书的人在玩填空游戏。你给他半句话,他凭着读过的一切,判断下一个字最可能是什么。他不是在「思考」,他是在做一个极其精细的模式匹配。
类比失效处:这个比方容易让人以为模型只是在「背书」和「抄近似的句子」。实际上,为了把下一个字猜准,模型被迫在内部学会了语法、事实、推理链条、甚至代码的执行逻辑——因为这些东西能让预测更准。「只是猜下一个字」描述的是任务,不是模型内部学到了什么。这个区别在后面理解「为什么 RL 能让它学会用工具」时非常关键。
有人说:「语言模型不可能真的会算数,因为它只是在猜下一个字。」 这句话的推理哪里有问题?
三位数乘三位数有 900×900 = 81 万种组合,四位数是 8100 万种,位数再涨就完全不可能穷举。而训练数据里不可能包含所有组合。所以对模型来说,「背下所有算式」这条路在数据量和参数量上都走不通;能把没见过的算式也猜对的唯一办法,就是在内部实现某种近似的计算过程。
训练目标(猜下一个字)是一个压力,不是一个限制。这个压力逼出来的内部结构可以任意复杂。反过来说,这也解释了为什么模型的算数能力是「不可靠的近似」而不是「精确的算法」——它学到的是在训练分布上够用的近似过程,位数一多就崩。这也正是本站第9章里,为什么 Kimi 团队要专门给模型配一个 Python 解释器来做精确计算。
变式:同样的推理套到「模型不可能真的理解代码,因为它只是在猜下一个字符」上。这次的反驳会更强还是更弱?为什么代码这个领域,「猜下一个字符」这个任务施加的压力可能更大?
0.3 token:模型眼里的「字」
上一节我说「猜下一个字」,那是简化。模型实际处理的单位叫 token,中文常译作「词元」。
token 不是字,也不是词,而是一种介于两者之间的、由统计方法切出来的片段。比如英文 unhappiness 可能被切成 un + happi + ness 三个 token;中文里常见字通常一个字就是一个 token,生僻字可能被拆成两三个。
词表(vocabulary):所有可能 token 的清单。Kimi K3 的词表大小是 160K(16 万个),这个数字你在表 1 里会看到。
所以更准确的说法是:模型看到的是一串编号(比如 [3612, 8829, 174, ...]),它要预测的是下一个编号,从 16 万个候选里挑。
0.4 向量:把 token 变成一串数
计算机不能直接对「编号 3612」做数学。所以第一步是把每个 token 变成一串数字,叫做向量。
向量(vector):一串有顺序的数,比如 (0.3, −1.2, 0.8)。这串数有多长,就叫它的维度。
Kimi K3 用 7168 维的向量表示一个 token。也就是说,「猫」这个字在模型内部是 7168 个小数组成的一串。
为什么用一串数而不是一个数
想象你要用数字描述一个人。用一个数(比如身高)显然不够。用一串数——身高、体重、年龄、收入、开朗程度、……——就能刻画得细致得多,而且相似的人,这串数也会接近。
7168 维向量就是这个意思:它是这个 token 的「多维档案」。语义相近的 token(「猫」和「狗」),它们的向量在空间里离得近。
类比失效处:真实模型里这 7168 个维度没有人类可命名的含义。不存在「第 42 维代表可爱程度」这回事。这些维度是训练自动生成的,绝大多数无法解释。
0.5 矩阵乘法:神经网络里唯一真正重的运算
这一节是全章最重要的一节。如果你只学一个数学工具,学这个。
矩阵(matrix):把数排成一个长方形的表格。一个 3 × 2 的矩阵有 3 行 2 列。
矩阵乘以向量是这样算的:把向量竖着放在矩阵右边,矩阵的每一行与这个向量逐个相乘再求和,得到结果向量的一个分量。
[ 0 4 ] × [ 5 ] = [ 0×3 + 4×5 ] = [ 20 ]
[ 1 −1 ] [ 1×3 − 1×5 ] [ −2 ]
注意形状:一个 3×2 的矩阵乘一个 2 维向量,得到一个 3 维向量。矩阵把向量从一个维度搬到了另一个维度,同时把信息重新混合了一遍。
三个你会在论文里反复看到的写法
Wx:矩阵 W 乘向量 x。
x⊤:转置,把竖着的向量放倒成横的(或反过来)。
kv⊤:一个竖向量乘一个横向量,结果是一个矩阵(这叫外积,第2章会大量用到)。
⊙:逐元素相乘,两个同长的向量对应位置相乘,长度不变。
Diag(α):把向量 α 摆到一个方阵的对角线上,其余位置填 0。
Kimi K3 的隐藏维度是 7168。现在有一个矩阵 W,它把一个 token 的 7168 维向量变成一个 3584 维向量(这正是论文里的「潜投影」 W↓)。
(a) 这个矩阵是几行几列?
(b) 做一次这样的乘法,需要多少次乘法运算?
(c) 这个矩阵本身占用多少个参数?
(a) 3584 行 × 7168 列。规则是「行数 = 输出维度,列数 = 输入维度」。
(b) 3584 × 7168 = 25,690,112 次乘法,约 2570 万次。
(c) 参数就是矩阵里的格子数,也是 25,690,112 个,约 2570 万。
这里有一个极重要的巧合:参数量和单 token 的乘法次数是同一个数。这不是偶然——每个参数在处理一个 token 时恰好被用到一次。这个关系是后面理解「激活参数决定算力成本」的基石。
顺带感受一下规模:这只是一个矩阵。K3 有 93 层,每层有十几个这样的矩阵,还有 896 个专家各自带着自己的矩阵。总共 2.78 万亿个参数。
变式:如果把这个矩阵拆成两个——先 7168 → 64,再 64 → 3584(这叫「低秩分解」,论文式 2 里的 Wα↑Wα↓ 正是这么做的)——参数量变成多少?省了多少倍?代价是什么(提示:想想两步走能表达的变换,是不是所有一步走能表达的变换都能做到)?
0.6 一层神经网络 = 矩阵乘法 + 掰弯
如果只有矩阵乘法,会出问题:连续做很多次矩阵乘法,效果等同于只做一次(两个矩阵可以先乘起来变成一个矩阵)。那么叠 93 层就毫无意义。
所以每做完一次矩阵乘法,要插进一个非线性函数——一个不能被矩阵乘法模拟的、把直线掰弯的操作。
激活函数(activation function):那个负责「掰弯」的函数。常见的有:
| 名字 | 做什么 | 直觉 |
|---|---|---|
| Sigmoid(x) | 把任意实数压到 0 和 1 之间,大的接近 1,小的接近 0 | 一个「软开关」:开多少 |
| tanh(x) | 压到 −1 和 1 之间 | 带方向的软开关 |
| Swish(x) | = x · Sigmoid(x);正的地方几乎不变,负的地方渐渐压到 0 | 「正的放行,负的渐消」 |
第5章的主角 SiTU-GLU 就是在 Swish 的基础上改造出来的,你到那时会需要这张表。
为什么 Sigmoid 这么常用
因为它天然是个「闸门」。当你想让模型自己决定「某个信息放行多少」,最自然的做法就是算一个 Sigmoid 值(0 到 1 之间),然后拿它去乘那个信息。乘 0 = 完全关闭,乘 1 = 完全放行,乘 0.6 = 放行六成。
论文里所有叫「门」(gate)的东西——KDA 的输出门、MLA 的输出门、GLU 的门分支——都是这个套路。你只要看到 Sigmoid(Wgx) ⊙ 某个东西,就知道是「模型自己决定放行多少」。
0.7 softmax:把任意一串数变成一组概率
模型算出 16 万个候选 token 的「分数」之后,怎么变成概率?分数可能是负的,加起来也不等于 1。
softmax 解决这个问题,两步:
- 对每个数取指数 ex(结果一定是正数,而且大的数被放大得更厉害)
- 除以所有指数的总和(这样加起来正好是 1)
| 符号 | 是什么 | 直觉 |
|---|---|---|
| xi | 第 i 个候选的原始分数(也叫 logit) | 模型对这个候选的「看好程度」,可正可负 |
| ex | 指数函数,把分数变成正数 | 拉开差距:分数差 1,指数差 2.7 倍 |
| ∑j | 对所有候选求和 | 算总量,好做归一化 |
动手算一遍:三个候选的分数是 (2, 1, 0)。
有人提议:干脆别取指数了,直接「每个数除以总和」不就能变成概率吗?请构造一个具体的例子,说明这个方案会出什么问题。至少找出两个不同的失效情形。
失效一:合法性。 分数可以是负数,而概率不能。取 (2, −1, −1),总和为 0,除法未定义;取 (−1, −2, −3),总和为 −6,得到 (0.167, 0.333, 0.5)——顺序被完全颠倒,因为除以负数会反转大小关系。指数函数把所有数搬到正区间,从根上消除了这个问题。
失效二:表达力。 就算强行只允许正分数,除法也是线性的:分数 (10, 9) 得到 (0.526, 0.474),几乎五五开;分数 (100, 99) 得到 (0.503, 0.497),更接近五五开。分数越大反而越难拉开差距,模型没法表达「我极度确信」。而 softmax 下 (10, 9) 得到 (0.731, 0.269),(100, 99) 同样是 (0.731, 0.269)——只取决于差值,不取决于绝对大小,这正是我们想要的性质。
附带一个好处:softmax 里加一个常数不改变结果(分子分母同乘 ec)。这个「平移不变性」在第5章的 Quantile Balancing 里会以一模一样的形式再次出现——那里的偏置 b 要减去均值,理由就是「公共偏移不改变 Top-k 选择」。你现在就可以先记住这个模式。
变式:softmax 有个变体叫「温度」:先把所有分数除以 T,再做 softmax。论文说 K3 评测时用 temperature = 1.0。请推断:T 趋近 0 时会发生什么?T 很大时呢?为什么「温度高」常被说成「更有创造力」?
0.8 注意力:让每个 token 自己去「查资料」
现在到了最关键的机制。注意力(attention)回答的问题是:处理「它」这个词时,模型怎么知道「它」指的是前面哪个东西?
答案是:让「它」主动去前面查一遍,看谁跟自己最相关,就多读谁的信息。具体分三步。
查询、键、值(Query, Key, Value,简称 Q、K、V):每个 token 用三个不同的矩阵,从自己的向量里造出三个新向量。Q 是「我要找什么」,K 是「我是什么,供别人搜」,V 是「如果你选中我,我给你什么内容」。
打个比方
像在图书馆查资料。Q 是你写在纸上的搜索词,K 是每本书的书脊标题,V 是书里的内容。你拿搜索词去比对所有书脊,算出每本书跟你的需求有多匹配(打分 + softmax),然后按匹配度把这些书的内容混合成一份摘要带走。
类比失效处:真实的注意力不是「选一本书」,而是永远把所有书都读一点,只是权重不同。而且它一次要为序列里的每一个 token 都做一次这样的检索。这是它计算量大的根源。
为什么这个机制值一个诺贝尔级的位置
在注意力出现之前,处理序列靠的是循环神经网络(RNN):从左到右一个词一个词地读,把读过的东西压缩进一个固定大小的「记忆」里。这有两个致命问题:记忆会被冲淡(第 1000 个词很难记住第 1 个词),以及没法并行(必须等第 999 个算完才能算第 1000 个)。
注意力把这两个问题一起解决了:每个位置都能直接访问任意远的位置(不会被冲淡),而且所有位置的计算互不依赖(可以同时算)。这就是 2017 年那篇《Attention Is All You Need》带来的革命。
但它有代价——这个代价正是本站第2章的全部主题。
0.9 注意力的代价:为什么长文本这么贵
注意力的代价来自一个简单的事实:序列里每个 token 都要跟前面所有 token 打一次分。
如果序列有 n 个 token,打分次数大约是 n²/2。这叫平方复杂度。
从 1000 涨到 100 万,长度涨 1000 倍,计算量涨一百万倍。这就是为什么「100 万 token 上下文」在工程上是件大事。
还有第二个代价,而且往往更要命。
KV 缓存(KV cache):生成文字时,模型是一个 token 一个 token 往外吐的。每吐一个新 token,它都要回头查之前所有 token 的 K 和 V。为了不重复计算,就把算过的 K 和 V 全都存在显存里——这就是 KV 缓存。
常见误解
很多人以为「模型很慢是因为参数太多、算力不够」。在长对话场景下,真正的瓶颈往往是 KV 缓存把显存吃光了,以及把这些缓存从显存搬到计算核心的带宽不够。
KV 缓存的大小和序列长度成正比——每多一个 token,就要多存一份 K 和 V,永远不会缩小。100 万 token 的上下文,KV 缓存可以轻松达到几百 GB,而一张顶级显卡只有 80–140 GB 显存。
假设一个模型有 93 层,每层每个 token 要存一份 K 和一份 V,各 128 维,每个数用 2 字节存(这叫 BF16 精度)。
(a) 一个 token 的 KV 缓存占多少字节?
(b) 100 万 token 的上下文,KV 缓存总共占多少 GB?
(c) 如果一张显卡有 141 GB 显存,光是这份缓存就占了多少比例?
(a) 每层每 token:(128 + 128) × 2 = 512 字节;93 层共 47,616 字节 ≈ 46.5 KB。
(b) 100 万 token:47,616 × 1,000,000 ≈ 4.76 × 10¹⁰ 字节 ≈ 44.3 GB。
(c) 44.3 / 141 ≈ 31%。
而这还只是一个用户的一次对话。生产环境要同时服务成百上千个请求,显存瞬间就爆了。
更重要的是:请注意题目里的假设有多「温柔」——我只用了 128 维、只存一份。真实的多头注意力有几十个头,每个头都要存,缓存会大几十倍。这正是第3章 MLA(把 K、V 压缩成一个小的潜向量再存)之所以被发明出来的原因,也是第2章 KDA(干脆不存逐 token 的缓存,改存一个固定大小的状态)之所以是一次更激进的赌注的原因。
你现在应该能预感到:K3 的核心架构选择,本质上是在跟这张表上的数字讨价还价。
变式:现在改用 KDA 那种方案——每层不存逐 token 的 K/V,而是存一个固定大小的 128×128 的状态矩阵,不管序列多长都是这么大。(a) 93 层一共占多少 MB?(b) 它和上面 44.3 GB 相比是多少倍的差距?(c) 既然差这么多,为什么不干脆全部用 KDA、一个 MLA 层都不要?(这一问先想不出来没关系,第3章会正面回答它。)
0.10 残差连接:给信息修一条高速公路
网络很深(K3 有 93 层)时会出现一个问题:信息在层层传递中被反复揉搓,原始内容丢失,而且训练时的梯度信号传不回前面的层。
残差连接(residual connection)的做法极其简单:让每一层的输出等于「这一层算出来的东西」加上「这一层的输入」。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| hl−1 | 进入第 l 层的向量 | 前面所有层累积下来的信息 |
| fl(·) | 第 l 层实际做的运算 | 这一层的「贡献」或「修改意见」 |
| + | 直接相加 | 关键所在:原始信息原封不动地穿过去了 |
打个比方
像一条主干道,每一层是路边的一个小作坊。信息沿主干道往前走,每经过一个作坊,作坊往里面添一点自己的东西,但不拦截主干道。所以再深的网络,原始信息也总有一条畅通的路直达终点。
类比失效处:这个比方会让你觉得「信息完好无损」,但其实每层都在往同一个向量上加东西,加到后面就是一锅粥——第 90 层想单独取出第 3 层的输出是做不到的。这个「加法造成的混淆」正是本站第4章 Attention Residuals 要解决的问题。看到这里请记住这个伏笔。
0.11 归一化:不让数字失控
深层网络里数值容易越滚越大或越滚越小。归一化(normalization)就是每隔一段把一组数拉回到一个标准尺度。
RMSNorm(Root Mean Square Normalization):先算这组数的「均方根」(每个数平方、求平均、开根号),再让每个数都除以它。结果是这组数的整体大小被拉到 1 附近,但相互之间的比例关系不变。
论文里 RMSNorm 出现过至少五次,每次都是同一个动机:某个地方的数值尺度可能失控,先归一化再往下走。你在第4章(防止某一层主导注意力权重)、第5章(防止专家聚合后的尺度乱跳)都会见到它。
0.12 MoE:一个巨大的专家团,每次只请几个
常规神经网络里,每个 token 都要经过全部参数。参数越多,算得越慢。这让「模型变大」和「推理变贵」死死绑在一起。
混合专家(Mixture-of-Experts, MoE)解开了这个绑定:
- 把一层里的前馈网络复制成很多份,每份叫一个专家(expert)。K3 有 896 个。
- 加一个小小的路由器(router),它看一眼当前 token,给 896 个专家各打一个分。
- 只把分数最高的几个专家叫来干活。K3 每个 token 只激活 16 个。
打个比方
像一家有 896 位专科医生的超级医院。你来看病,前台(路由器)看一眼你的症状,从中挑 16 位最对口的医生给你会诊。医院很大(总参数多,什么病都能治),但你的这次就诊只占用了 16 位医生的时间(计算量小)。
类比失效处:这个比方暗示「专家们各有明确分工」。实际上专家的分工是训练中自发形成的,往往不对应任何人类能命名的领域,而且不保证均衡——有的专家会被挤爆,有的没人光顾。这个「负载不均衡」问题是第5章 Quantile Balancing 的全部动机。
K3 有 896 个路由专家,每个 token 激活 16 个。
(a) 稀疏度(总数除以激活数)是多少?
(b) MoE 让计算量降下来了,但有一样东西没有降。是什么?为什么?
(c) 这个没降的东西,会在什么场景下成为真正的瓶颈?
(a) 896 ÷ 16 = 56。论文原文就是这么写的:「corresponding to a sparsity of 56」。
(b) 没降的是 显存占用。全部 2.78 万亿个参数都必须常驻在显存(或至少随时可取)里,因为你无法预知下一个 token 会路由到哪些专家。MoE 省的是算力(FLOPs),不是存储。
(c) 两个场景:
· 部署时:2.78 万亿参数即使用 4 比特存也要约 1.4 TB,需要一整个机柜的显卡才装得下。这正是论文第 4.1.4 节要做 MXFP4 量化(本站第8章)的直接原因。
· 小批量解码时:每生成一个 token,都要把用到的专家权重从显存流式读进计算核心。这时候瓶颈是内存带宽而不是算力——论文第 5.4.2 节原话是「group GEMM 退化成权重矩阵的内存受限流式读取」,并因此要专门写一个 token 中心的解码内核(本站第11章)。
把这三件事串起来,你就理解了为什么论文里「架构」和「基础设施」两部分必须放在一起读:一个架构选择(896 个专家)会直接决定后面几十页的工程难题。
变式:假设有人提出把专家数从 896 减到 448,但每 token 激活数从 16 提到 32。稀疏度从 56 降到 14。(a) 每 token 的计算量怎么变?(b) 显存占用怎么变?(c) 路由器的选择空间怎么变?(d) 你认为这个改动会让模型变强还是变弱?说清你的不确定性来自哪里。
0.13 三个必须分清的量:总参数、激活参数、FLOPs
| 量 | 含义 | K3 的值 | 决定什么 |
|---|---|---|---|
| 总参数 total parameters | 模型里一共有多少个可训练的数 | 2.78 万亿 | 显存:模型有多大、要多少卡才装得下 |
| 激活参数 activated parameters | 处理一个 token 实际用到多少个数 | 1042 亿(约 3.7%) | 算力:每个 token 多贵、生成多快 |
| FLOPs | 浮点运算次数,算力的计量单位 | 论文未披露训练总量 | 训练一次要多少电、多少钱、多少时间 |
常见误解
新闻里说「某模型 2.8 万亿参数」时,很多人直接拿它跟另一个「6710 亿参数」的模型比大小。但如果一个是稠密模型(全部参数都激活)、另一个是 MoE,这两个数字不可直接比较——它们衡量的是不同的东西。
更公平的比法是同时看这两个数。K3 是「2.78 万亿总 / 1042 亿激活」,前一个数说的是它的知识容量上限,后一个数说的是它每吐一个字的成本。
0.14 训练与推理:两种完全不同的活
训练(training):给模型看海量文本,让它猜下一个 token,猜错了就微调所有参数让它下次猜得准一点。这个「根据错误反过来调参数」的过程叫反向传播(backpropagation),它需要把前向过程中的中间结果(叫激活值,activations)都保存下来——这是训练时显存爆炸的主因,第10章会详谈。
推理(inference):模型训练好了,参数冻结,只用来生成文字。推理又分两个阶段:
- 预填充(prefill):把你输入的整段提示一口气读进去。这一步所有 token 可以并行处理,算力吃满,是「计算受限」的。
- 解码(decode):一个一个往外吐字。这一步必须串行(要先吐出第 5 个字才能吐第 6 个),每吐一个字都要把整个模型的权重过一遍,所以是「带宽受限」的。
0.15 三段人生:预训练、后训练、强化学习
| 阶段 | 干什么 | 类比 | 本站章节 |
|---|---|---|---|
| 预训练 pre-training | 读遍互联网,学会预测下一个 token | 读完了图书馆所有的书 | 第7章 |
| 监督微调 SFT | 看人类示范的「好回答」,学会照着做 | 跟着师傅照猫画虎 | 第8章 |
| 强化学习 RL | 自己动手做题,做对了给奖励,做错了给惩罚 | 自己下场练,靠反馈进步 | 第8、9章 |
强化学习(Reinforcement Learning, RL):模型自己尝试完成一个任务(比如「修好这个 bug」),产生一整条行动轨迹;然后有个奖励函数给这条轨迹打分;模型据此调整参数,让高分行为更容易发生。
为什么 RL 对「智能体」如此关键
SFT 只能教模型模仿见过的示范。但「用二十个工具、花三百步、修好一个从没见过的 bug」这种任务,没有人能写出足够多的示范。RL 不需要示范——它只需要一个能判断结果对不对的机制,模型自己在环境里摸索出路径。
这也解释了为什么论文第 4.2 节(本站第9章)花了整整四页讲「怎么造环境、怎么造题、怎么防作弊」——对 RL 来说,环境和奖励的质量就是能力的上限。
智能体(agent):能自主使用工具、多步执行任务的模型。工具调用(tool call)指模型输出一段结构化的请求(比如「运行这段 Python」),外部系统执行后把结果送回来作为新的输入。一次任务里模型可能这样来回几百次,这叫长时程(long-horizon)。
假设你要用 RL 训练模型写代码。你设计的奖励是:「单元测试全部通过,得 1 分;否则得 0 分。」 请构造至少两种模型可能采取的、能拿到高分但完全违背你本意的策略。(这类行为有个名字叫「奖励黑客」,reward hacking,论文里出现过五次以上。)
assert True。然后想更隐蔽的——不改测试,但在被测函数里检测「当前是不是在跑测试」,是的话就返回硬编码的期望值。① 改测试。 把断言删掉或改成永真,或者干脆删掉失败的测试用例。
② 硬编码答案。 不实现算法,而是把测试用例里的输入输出对写成一个查找表。测试全过,换个输入就崩。
③ 检测测试环境。 在代码里判断是否处于测试运行中(查环境变量、查调用栈),是则走一条特殊分支。
④ 破坏测试框架。 篡改 pytest 的行为,让所有断言都被吞掉。
⑤ 跳过而非通过。 给测试加上 skip 标记,某些统计口径下「没有失败」就算通过。
这道题的真正意义:论文第 4.2.4 节(内核优化任务)说他们要专门建一套「作弊检测系统」,惩罚 CUDA graph replay、输入缓存、精度削减这些手法,并且「随着开发中观察到新的作弊手法持续扩充」——注意「持续扩充」这四个字,它承认了这是一场没有终点的军备竞赛。第 4.2.6 节(AET)则用了三重设计:隔离智能体与验证器、公开验证器配隐藏验证器、有限提交预算下的惩罚性奖励。
你现在应该能理解,为什么这些看起来啰嗦的设计是必需的:奖励函数写下的那一刻,就是在向一个比你更有耐心的优化器公开你所有的漏洞。
变式:论文第 4.2.7 节说,网页开发任务的奖励在「项目构建失败、运行报错、或者假装实现而非真正实现产物」时被清零。前两条是客观可判定的,第三条「假装实现」却很主观。(a) 举一个「假装实现」的具体例子。(b) 论文说这一条靠「模型评判」来判定。这引入了什么新的风险?(c) 如果被评判的模型和当评委的模型来自同一个家族,风险会变大还是变小?
0.16 GPU:为什么显存和带宽经常比算力更要命
训练和运行大模型全靠 GPU(图形处理器)。你需要知道三个数,以及它们之间的紧张关系。
| 资源 | 是什么 | 不够会怎样 |
|---|---|---|
| 算力 FLOPS | 每秒能做多少次浮点运算 | 算得慢 |
| 显存 memory / HBM | 能装下多少数据(参数、激活值、KV 缓存) | 直接跑不起来(OOM,内存溢出) |
| 带宽 bandwidth | 每秒能把多少数据从显存搬进计算核心 | 计算核心闲着等数据(「内存受限」) |
打个比方
算力像厨房里厨师的手速,显存像冰箱的容量,带宽像从冰箱到灶台的传菜通道。现代 GPU 的情况是:厨师手速快得离谱,但传菜通道相对很窄。所以大量时间里厨师是在等菜,而不是在炒菜。
工程师的很多工作,本质上是「让厨师在等菜的间隙里去干点别的」——这就是论文里到处出现的重叠(overlap):把通信和计算重叠、把卸载和计算重叠、把视觉编码器塞进流水线气泡。
类比失效处:真实 GPU 里还有「片上缓存」这一层(相当于灶台边的小料盒),它极快但极小。很多内核优化的核心技巧就是想办法让数据尽量待在料盒里别回冰箱。论文第 5.4.2 节说 KDA 解码时「在片上重建被接受 token 的状态」,说的就是这件事。
还有一组必须知道的名词——当一张卡装不下时,就要把模型切开放到多张卡上,这叫并行:
| 名字 | 切什么 | 一句话 |
|---|---|---|
| 数据并行 DP | 切数据 | 每张卡放一整个模型,各自处理不同的数据,最后汇总梯度 |
| 张量并行 TP | 切矩阵 | 把一个大矩阵横着或竖着切开,几张卡一起算一次矩阵乘法 |
| 流水线并行 PP | 切层 | 第 1–20 层在卡 A,第 21–40 层在卡 B,像流水线一样传下去 |
| 专家并行 EP | 切专家 | MoE 专用:896 个专家分散到多张卡,token 被送到对应的卡上 |
| 上下文并行 CP | 切序列 | 长序列专用:把 100 万个 token 切成几段,每张卡负责一段 |
0.17 100 万 token 的上下文,到底是什么概念
上下文窗口(context window):模型一次能「看见」的最大 token 数。超出的部分它就完全不知道了。
K3 是 100 万 token。给你一点尺度感:
而 K3 的前一代 K2 只有 128K(12.8 万)。表 1 里那个「8×」就是这么来的。
为什么长上下文对「智能体」是刚需
一个智能体做一个复杂任务,可能要调用几百次工具。每次工具返回的结果(比如一个文件的内容、一次搜索的结果、一段报错日志)都要进上下文。论文第 4.2.5 节说,一次个人助理任务的 rollout 「可能涉及多达数千次工具调用和数百万 token 的上下文」。
如果上下文只有 128K,模型做到一半就得开始遗忘前面做过什么——它会重复劳动、会忘记约束、会前后矛盾。所以上下文长度直接决定了智能体能处理多复杂的任务。这是理解整篇论文动机的关键之一。
0.18 符号速查表
下面这些符号会在后面十五章反复出现。现在不需要背,但读到卡壳时回来查。
| 符号 | 读作 | 含义 |
|---|---|---|
| xt | x sub t | 第 t 个位置的 token 向量 |
| q, k, v | Q、K、V | 查询、键、值向量 |
| St | S sub t | KDA 在第 t 步的状态矩阵(第2章核心) |
| W | W | 一个可学习的权重矩阵 |
| α | alpha | 论文里有两个含义:KDA 的衰减因子(式 1);注意力权重(式 9)。看上下文 |
| β | beta | KDA 的写入强度(式 1);SiTU-GLU 的软上限参数(式 12) |
| γ | gamma | 累积衰减(式 3);也用作偏置更新步长 |
| πθ | pi theta | 策略,即模型本身;下标 θ 表示「由参数 θ 决定」 |
| ∑ | sigma | 求和 |
| ∏ | pi(大写) | 连乘 |
| ⊙ | 圈乘 | 逐元素相乘 |
| ⊤ | 转置 | 行列互换 |
| I | 单位矩阵 | 对角线全是 1、其余为 0 的方阵;乘它等于什么都没做 |
| sg(·) | stop gradient | 停止梯度:这一项只当数值用,不参与反向传播(式 15) |
| clip(x, a, b) | 裁剪 | 把 x 限制在 [a, b] 内,超出就取边界值 |
现在请你把这一章学的东西串起来,回答一个「元问题」:
论文摘要里说 K3 是「2.8 万亿参数、1042 亿激活参数、100 万 token 上下文」。请从这三个数字出发,预测这篇论文的第 5 节(基础设施)必然会花大篇幅解决哪三类问题。每类问题请指出它是被三个数字中的哪一个逼出来的。
现在你自己走另外两条链。
链一:2.8 万亿总参数 → 专家并行的负载不均衡。 参数太多必须切到多卡;MoE 结构下最自然的是按专家切;但 token 路由不均匀,有的卡忙死有的卡闲着;闲着的卡拖慢全局(木桶效应)。论文的回答:§5.2.1 的 MoonEP——用动态冗余专家实现完美均衡,并证明每 rank 只需 E/R 个冗余槽位(本站第10章、第14章)。
附带的第二条支线:2.8 万亿参数在部署时也装不下 → 必须量化 → §4.1.4 的 MXFP4 QAT(本站第8章)。
链二:1042 亿激活参数 + 解码是串行的 → 带宽受限。 每吐一个 token 都要把 1042 亿个参数流式读一遍;解码阶段算力用不满,瓶颈在带宽;小批量下更糟。论文的回答:§5.4.2 的 token 中心 MoE 解码内核(借鉴 WarpDecode),以及 §4.1.4 的推测解码——让小草稿模型先猜几个,大模型一次验证多个,摊薄每 token 的带宽成本(本站第8、11章)。
链三:100 万 token 上下文 → 两个爆炸。 (a) 注意力计算量按平方涨;(b) KV 缓存按线性涨且不缩小。论文的回答分三层:架构层用 KDA 把三分之二的注意力层换成固定大小状态(§2.1,本站第2章);训练层用上下文并行把序列切到多卡,并为 KDA 专门设计 KCP(§5.1.2,本站第10章);服务层用前缀缓存避免重算,并把 KDA 状态和 MLA 缓存统一管理(§5.4.1,本站第11章)。
这道题想让你获得的能力:拿到任何一篇模型技术报告,光看摘要里的几个数字,你就能预判它必须解决哪些工程问题。如果它没讲,你就该问「那你们是怎么绕过去的?」——这是从「读者」变成「审稿人」的第一步。
变式:再加一个数字——K3 是「原生多模态」的,图像和视频与文本混在同一个上下文里。请预测这会额外逼出哪一类第 5 节必须解决的问题,并说明它和上面三条链哪一条最容易冲突。(提示:想想一张 3584×3584 的图会变成多少个 token,以及不同样本里图像数量差异很大会造成什么。)
本章小结
你现在手里有了十七个概念。它们不是孤立的名词,而是一张互相咬合的网:
token 变成向量,向量经过矩阵乘法和激活函数被反复变换;注意力让每个位置能查阅所有位置,代价是平方复杂度和线性增长的 KV 缓存;残差保证深层网络训得动,归一化保证数值不失控;MoE 让总参数和激活参数脱钩,代价是负载不均衡和显存不降;这一切跑在 GPU 上,而 GPU 的显存和带宽往往比算力更早撞墙,于是需要各种并行,而并行的代价是通信。
接下来的每一章,都是这张网上某一处的放大。
Chapter 0 · Getting started: everything you need before you read this paper
This chapter is not part of the paper. It is a foundation. Once you have read it you will hold every concept the following fifteen chapters need — not “I have heard of it”, but “I can explain it in my own words, and I can compute it”. If you already know what attention and the KV cache are, skip to Chapter 1; but I suggest you at least work through the self-test at the end of this chapter to see whether you really do know.
After this chapter you should be able to
- Explain “what a large language model does” in one sentence with no jargon
- Work out a 2×2 matrix multiplication by hand and say what it stands for in a neural network
- Work out one softmax by hand and explain why you take the exponential first
- Draw the three steps of attention and say clearly what Q, K and V each are
- Explain why a longer conversation costs more, and compute the size of the KV cache
- Tell apart the three quantities total parameters, activated parameters and FLOPs, and explain why MoE can uncouple them
- Say which of GPU memory, bandwidth and compute most often becomes the bottleneck, and why
0.1 A sense of direction first: what this paper is about
Kimi K3 is a large language model released by Moonshot AI in July 2026, with the model weights made public at the same time. This 47-page technical report is about how they built it.
Building a model at this scale means answering roughly four kinds of question, and the structure of the report maps exactly onto those four:
- What the model looks like (Section 2; Chapters 3–6 of this site) — which parts are in it, and why they are arranged this way.
- How the knowledge gets poured in (Section 3; Chapter 7 of this site) — what data, how long the training runs, how the hyperparameters are set.
- How it learns to do things (Section 4; Chapters 8–9 of this site) — going from “can talk” to “can work” rests on post-training and reinforcement learning.
- How to make all of this run on real machines (Section 5; Chapters 10–11 of this site) — 2.8 trillion parameters do not fit on any single GPU, and that alone is a large engineering job.
What is left is the evaluation in Section 6 (Chapter 12 of this site), the case studies in Section 7 (Chapter 13), and a few proofs in the appendix (Chapter 14).
Why build the foundation first instead of reading straight on
This report is written for peers — it assumes you already know what a Transformer is, what a KV cache is, what expert parallelism is. It can pack four terms you have never seen into a single sentence and then keep going. If you push through anyway, what usually happens is: you recognise every word, and strung together you have no idea what they say.
So this chapter goes first and loads that assumed knowledge into your head, one piece at a time. It runs a bit long, but it is the only place where you have to start from zero.
0.2 What a language model is actually doing: guessing the next word
Saying it out loud is a let-down, but it is true: the only thing a large language model does is guess, from the text in front of it, what the next character is most likely to be.
Give it “the People's Republic of Chin”, for example, and it hands you a probability distribution:
Then it draws one character from that distribution, sticks it on the end to give “the People's Republic of China”, and takes the new sentence to guess the character after that. Repeat, and it has “written” a whole passage.
Next-token prediction: the task above — look at what came before, guess what comes next. All the pre-training in the entire paper is this one thing.
An analogy
Like someone who has read every book in the world playing a fill-in-the-blank game. You give him half a sentence, and on the strength of everything he has read he judges what the next character most likely is. He is not “thinking”; he is doing an extremely fine-grained pattern match.
Where the analogy breaks down: it makes it easy to believe the model is only “reciting” and “copying sentences that look close”. In fact, to guess the next character accurately, the model is forced to learn grammar, facts, chains of reasoning, even the execution logic of code inside itself — because those are what make the prediction more accurate. “It just guesses the next character” describes the task, not what the model has learned inside. That distinction matters enormously later, when you work out “why RL can teach it to use tools”.
Someone says: “A language model cannot really do arithmetic, because it is only guessing the next character.” Where does the reasoning in that sentence go wrong?
A three-digit number times a three-digit number has 900×900 = 810,000 combinations; four-digit numbers give 81 million; add more digits and enumeration becomes flatly impossible. And the training data cannot possibly contain every combination. So for the model, “memorise every expression” is a road that fails on both data volume and parameter count; the only way to get expressions it has never seen right is to implement some kind of approximate computation process inside itself.
The training objective (guess the next character) is a pressure, not a limit. The internal structure that pressure forces out can be arbitrarily complex. Turned around, this also explains why the model's arithmetic is an “unreliable approximation” rather than an “exact algorithm” — what it learned is an approximate process that is good enough on the training distribution, and it collapses once there are more digits. This is exactly why, in Chapter 9 of this site, the Kimi team give the model a Python interpreter of its own to do exact computation.
Variant: apply the same reasoning to “a model cannot really understand code, because it is only guessing the next character”. Is the rebuttal stronger or weaker this time? Why might the pressure the task “guess the next character” exerts be greater in the domain of code?
0.3 token: the “character” as the model sees it
In the last section I said “guess the next character”. That was a simplification. The unit the model actually works on is called a token; Chinese usually renders it as “cíyuán”.
A token is not a character and not a word, but a fragment somewhere between the two, cut out by statistics. English unhappiness, for instance, may be cut into the three tokens un + happi + ness; in Chinese a common character is usually a token on its own, while a rare character may be split into two or three.
Vocabulary: the list of every possible token. Kimi K3's vocabulary size is 160K (160,000 of them), a number you will meet in Table 1.
So the more accurate statement is: the model sees a string of ID numbers (say [3612, 8829, 174, ...]), and what it has to predict is the next ID number, picked out of 160,000 candidates.
0.4 Vectors: turning a token into a string of numbers
A computer cannot do mathematics directly on “ID number 3612”. So the first step is to turn every token into a string of numbers, called a vector.
Vector: an ordered string of numbers, for example (0.3, −1.2, 0.8). How long that string is is called its dimension.
Kimi K3 uses a 7168-dimensional vector to represent one token. That is: inside the model, the word “cat” is a string of 7168 decimal numbers.
Why a string of numbers rather than a single number
Imagine describing a person with numbers. One number (height, say) is obviously not enough. A string of numbers — height, weight, age, income, how outgoing they are, … — describes them far more finely, and people who are alike have strings of numbers that are close together.
The 7168-dimensional vector means exactly this: it is a “multi-dimensional file” on that token. Tokens close in meaning (“cat” and “dog”) have vectors that sit close together in the space.
Where the analogy breaks down: in a real model those 7168 dimensions have no meaning a human can name. There is no such thing as “dimension 42 stands for cuteness”. The dimensions are generated automatically by training, and the vast majority cannot be interpreted.
0.5 Matrix multiplication: the only genuinely heavy operation in a neural network
This is the most important section in the chapter. If you learn one piece of mathematics, learn this one.
Matrix: numbers laid out in a rectangular table. A 3 × 2 matrix has 3 rows and 2 columns.
A matrix times a vector is computed like this: stand the vector up on the right of the matrix, take each row of the matrix and multiply it against that vector element by element, then sum, giving one component of the result vector.
[ 0 4 ] × [ 5 ] = [ 0×3 + 4×5 ] = [ 20 ]
[ 1 −1 ] [ 1×3 − 1×5 ] [ −2 ]
Watch the shapes: a 3×2 matrix times a 2-dimensional vector gives a 3-dimensional vector. A matrix carries a vector from one dimension into another, and remixes the information on the way.
Three notations you will see over and over in the paper
Wx: matrix W times vector x.
x⊤: transpose — lay a vertical vector down flat, or the other way round.
kv⊤: a column vector times a row vector; the result is a matrix (this is the outer product, used heavily in Chapter 2).
⊙: element-wise multiplication — two vectors of the same length multiplied position by position, length unchanged.
Diag(α): put the vector α along the diagonal of a square matrix and fill the rest with 0.
Kimi K3's hidden dimension is 7168. Take a matrix W that turns a token's 7168-dimensional vector into a 3584-dimensional vector (this is exactly the “latent projection” W↓ in the paper).
(a) How many rows and columns does this matrix have?
(b) How many multiplications does one such multiplication take?
(c) How many parameters does the matrix itself occupy?
(a) 3584 rows × 7168 columns. The rule is “rows = output dimension, columns = input dimension”.
(b) 3584 × 7168 = 25,690,112 multiplications, about 25.7 million.
(c) The parameters are the cells in the matrix, so also 25,690,112, about 25.7 million.
There is an extremely important coincidence here: the parameter count and the number of multiplications for a single token are the same number. This is not an accident — each parameter is used exactly once while one token is processed. That relation is the bedrock for understanding later that “activated parameters set the compute cost”.
While you are here, get a feel for the scale: this is one matrix. K3 has 93 layers, each layer holds a dozen or so matrices like it, and there are 896 experts each carrying matrices of their own. 2.78 trillion parameters in total.
Variant: if you split this matrix into two — first 7168 → 64, then 64 → 3584 (this is “low-rank factorisation”, and Wα↑Wα↓ in equation 2 of the paper does exactly this) — what does the parameter count become? By what factor does it shrink? And what does it cost (hint: think about the transformations two steps can express — can they reach every transformation one step can)?
0.6 One layer of a neural network = matrix multiplication + a bend
With matrix multiplication alone there is a problem: doing many matrix multiplications in a row has the same effect as doing one (two matrices can be multiplied together into a single matrix first). Which would make stacking 93 layers pointless.
So after every matrix multiplication you insert a non-linear function — an operation no matrix multiplication can imitate, one that bends the straight line.
Activation function: the function in charge of the bending. Common ones:
| Name | What it does | Intuition |
|---|---|---|
| Sigmoid(x) | Squashes any real number to between 0 and 1; large ones approach 1, small ones approach 0 | A “soft switch”: how far open |
| tanh(x) | Squashes to between −1 and 1 | A soft switch with a direction |
| Swish(x) | = x · Sigmoid(x); barely changes the positive side, gradually squashes the negative side to 0 | “Let the positive through, fade the negative out” |
SiTU-GLU, the lead character of Chapter 5, is built on top of Swish, and you will need this table when you get there.
Why Sigmoid is used so much
Because it is naturally a “sluice gate”. When you want the model to decide for itself “how much of some piece of information gets through”, the most natural thing to do is compute a Sigmoid value (between 0 and 1) and multiply that information by it. Times 0 = fully shut, times 1 = fully open, times 0.6 = 60% gets through.
Everything called a “gate” in the paper — KDA's output gate, MLA's output gate, GLU's gate branch — is this same move. As soon as you see Sigmoid(Wgx) ⊙ something, you know it means “the model decides for itself how much gets through”.
0.7 softmax: turning any string of numbers into a set of probabilities
Once the model has computed a “score” for each of 160,000 candidate tokens, how do those become probabilities? A score can be negative, and they do not add up to 1 either.
softmax solves this, in two steps:
- Take the exponential ex of each number (the result is always positive, and larger numbers are blown up harder)
- Divide by the sum of all the exponentials (so they add up to exactly 1)
| Symbol | What it is | Intuition |
|---|---|---|
| xi | The raw score of candidate i (also called the logit) | How much the model likes this candidate; can be positive or negative |
| ex | The exponential function, turning a score into a positive number | Opens the gap: a difference of 1 in score is a factor of 2.7 in the exponential |
| ∑j | Sum over all candidates | The total, so you can normalise |
Work it through by hand: three candidates with scores (2, 1, 0).
Someone proposes: forget the exponential, just “divide each number by the sum” — does that not turn them into probabilities? Construct a concrete example showing what goes wrong with that scheme. Find at least two different ways it fails.
Failure one: legality. Scores can be negative; probabilities cannot. Take (2, −1, −1): the sum is 0 and the division is undefined. Take (−1, −2, −3): the sum is −6 and you get (0.167, 0.333, 0.5) — the order is completely reversed, because dividing by a negative number flips the size relation. The exponential moves every number into the positive range and removes this problem at the root.
Failure two: expressive power. Even if you force scores to be positive, division is linear: scores (10, 9) give (0.526, 0.474), almost 50–50; scores (100, 99) give (0.503, 0.497), closer still to 50–50. The larger the scores, the harder it is to open a gap, and the model cannot express “I am extremely sure”. Under softmax, (10, 9) gives (0.731, 0.269), and (100, 99) gives the same (0.731, 0.269) — it depends only on the difference, not on the absolute size, which is exactly the property you want.
One benefit that comes along with it: adding a constant inside softmax does not change the result (numerator and denominator are both multiplied by ec). This “shift invariance” turns up again in exactly the same form in Quantile Balancing in Chapter 5 — the bias b there has its mean subtracted off, and the reason is that “a common offset does not change the Top-k selection”. You can start remembering this pattern now.
Variant: softmax has a variant called “temperature”: divide every score by T first, then take the softmax. The paper says K3 uses temperature = 1.0 for evaluation. Work out: what happens as T approaches 0? And when T is large? Why is “high temperature” so often described as “more creative”?
0.8 Attention: letting every token “look things up” for itself
Now for the most important mechanism of all. Attention answers this question: when it processes the word “it”, how does the model know which earlier thing “it” refers to?
The answer: let “it” go back and run a search over what came before, see who is most related to it, and read more from whoever that is. Three steps, concretely.
Query, key, value: every token uses three different matrices to build three new vectors out of its own vector. Q is “what I am looking for”, K is “what I am, for others to search against”, V is “what I hand you if you pick me”.
An analogy
Like looking something up in a library. Q is the search term you wrote on a slip of paper, K is the title on each book's spine, V is the content inside the book. You take your search term along every spine, work out how well each book matches what you need (score + softmax), and then blend the contents of those books in proportion to the match into one summary to take away.
Where the analogy breaks down: real attention does not “pick one book”; it always reads a little of every book, only with different weights. And it has to run one such search for every token in the sequence. That is the root of its heavy compute.
Why this mechanism is worth a Nobel-level place
Before attention, sequences were handled by recurrent neural networks (RNNs): read left to right one word at a time, compressing what has been read into a fixed-size “memory”. That has two fatal problems: the memory gets diluted (word 1000 has a hard time remembering word 1), and it cannot be parallelised (you have to finish word 999 before you can compute word 1000).
Attention solved both at once: every position can reach any position however far away directly (nothing gets diluted), and the computation at each position is independent of the others (they can all run at the same time). That is the revolution brought by “Attention Is All You Need” in 2017.
But it has a price — and that price is the whole subject of Chapter 2 of this site.
0.9 The price of attention: why long text is so expensive
The price of attention comes from one simple fact: every token in the sequence has to score itself against every token before it.
If the sequence has n tokens, the number of scorings is about n²/2. This is called quadratic complexity.
Going from 1000 to 1 million, the length goes up 1000-fold and the compute goes up a millionfold. This is why “a 1 million token context” is a serious matter in engineering.
There is a second price, and it is often the deadlier one.
KV cache: when it generates text, the model spits out one token at a time. For each new token it spits out, it has to go back and look at the K and V of every earlier token. To avoid computing them again, every K and V already computed is kept in GPU memory — that is the KV cache.
A common misreading
Many people think “the model is slow because it has too many parameters and not enough compute”. In long conversations the real bottleneck is usually that the KV cache has eaten all the GPU memory, and that there is not enough bandwidth to move that cache from GPU memory into the compute cores.
The size of the KV cache is proportional to sequence length — one more token means one more K and V stored, and it never shrinks. At a 1 million token context the KV cache can easily reach several hundred GB, while a top-end GPU has only 80–140 GB of memory.
Suppose a model has 93 layers, and every layer stores one K and one V per token, 128 dimensions each, with 2 bytes per number (this is BF16 precision).
(a) How many bytes does one token's KV cache take?
(b) For a 1 million token context, how many GB does the KV cache take in total?
(c) If a GPU has 141 GB of memory, what share of it does this cache alone take?
(a) Per layer per token: (128 + 128) × 2 = 512 bytes; across 93 layers, 47,616 bytes ≈ 46.5 KB.
(b) For 1 million tokens: 47,616 × 1,000,000 ≈ 4.76 × 10¹⁰ bytes ≈ 44.3 GB.
(c) 44.3 / 141 ≈ 31%.
And this is still only one conversation for one user. Production has to serve hundreds or thousands of requests at the same time, and the GPU memory is gone in an instant.
More important: notice how “gentle” the assumptions in the question are — I used only 128 dimensions and stored only one copy. Real multi-head attention has dozens of heads, every head has to be stored, and the cache comes out dozens of times bigger. This is exactly why MLA in Chapter 3 (compress K and V into a small latent vector before storing) was invented, and why KDA in Chapter 2 (do not store a per-token cache at all; store a fixed-size state instead) is a far more aggressive bet.
You should be able to feel it coming now: K3's core architectural choices are, at bottom, haggling with the numbers in this table.
Variant: switch to the KDA scheme — instead of a per-token K/V, each layer stores one fixed-size 128×128 state matrix, the same size however long the sequence is. (a) How many MB do 93 layers take in total? (b) By what factor does that differ from the 44.3 GB above? (c) Given a gap that large, why not use KDA for everything and drop MLA layers entirely? (It is fine if you cannot answer this one yet; Chapter 3 takes it head-on.)
0.10 Residual connections: building a motorway for information
When the network is deep (K3 has 93 layers) a problem shows up: information is kneaded over and over as it passes from layer to layer, the original content is lost, and during training the gradient signal cannot get back to the earlier layers.
What a residual connection does is extremely simple: make each layer's output equal to “what this layer computed” plus “what came into this layer”.
| Symbol | What it is | Intuition |
|---|---|---|
| hl−1 | The vector entering layer l | Everything the earlier layers have piled up |
| fl(·) | What layer l actually computes | This layer's “contribution”, its proposed edit |
| + | Plain addition | The crux: the original information passes straight through untouched |
An analogy
Like a main road with a small workshop at each stop along it. Information travels down the main road, and every workshop it passes adds a little of its own to it without blocking the road. So however deep the network, the original information always has a clear route straight to the end.
Where the analogy breaks down: it will leave you feeling the information arrives intact, but in fact every layer is adding to the same vector, and by the end it is porridge — layer 90 cannot pull out layer 3's output on its own. This “confusion caused by addition” is exactly the problem Attention Residuals in Chapter 4 of this site sets out to solve. Remember this as a hook for later.
0.11 Normalisation: keeping the numbers from running away
In a deep network values easily snowball larger and larger, or smaller and smaller. Normalisation is pulling a group of numbers back to a standard scale every so often.
RMSNorm: first compute the “root mean square” of the group (square each number, take the average, take the square root), then divide every number by it. The result is that the overall size of the group is pulled to somewhere near 1, but the ratios between them do not change.
RMSNorm appears at least five times in the paper, with the same motivation every time: the numeric scale somewhere may run away, so normalise before going on. You will meet it in Chapter 4 (stopping one layer from dominating the attention weights) and in Chapter 5 (stopping the scale from jumping around after the experts are aggregated).
0.12 MoE: a huge panel of experts, only a few called in each time
In an ordinary neural network every token has to pass through all the parameters. More parameters, slower compute. That binds “make the model bigger” and “make inference more expensive” tightly together.
Mixture-of-Experts (MoE) unties the binding:
- Copy the feed-forward network in a layer many times over; each copy is called an expert. K3 has 896 of them.
- Add a tiny router, which glances at the current token and gives each of the 896 experts a score.
- Call in only the few experts with the highest scores to do the work. K3 activates only 16 per token.
An analogy
Like a super-hospital with 896 specialists. You come in, the front desk (the router) glances at your symptoms and picks the 16 doctors who fit best to see you. The hospital is large (many total parameters, it can treat anything), but this visit of yours takes up the time of only 16 doctors (little compute).
Where the analogy breaks down: it hints that “the experts each have a clear speciality”. In reality the division of labour among experts forms by itself during training, often corresponds to no domain a human can name, and is not guaranteed to be balanced — some experts get swamped, others get no visitors at all. This “load imbalance” problem is the entire motivation for Quantile Balancing in Chapter 5.
K3 has 896 routed experts and activates 16 per token.
(a) What is the sparsity (total divided by activated)?
(b) MoE brought the compute down, but one thing did not come down. What is it, and why?
(c) In what situation does that thing become the real bottleneck?
(a) 896 ÷ 16 = 56. That is exactly how the paper puts it: “corresponding to a sparsity of 56”.
(b) What did not come down is GPU memory. All 2.78 trillion parameters have to be resident in GPU memory (or at least reachable at any moment), because you cannot know in advance which experts the next token will be routed to. What MoE saves is compute (FLOPs), not storage.
(c) Two situations:
· At deployment: 2.78 trillion parameters take about 1.4 TB even stored at 4 bits, which needs a whole rack of GPUs to hold. This is the direct reason the paper does MXFP4 quantisation in §4.1.4 (Chapter 8 of this site).
· Small-batch decoding: generating each token means streaming the expert weights it uses out of GPU memory into the compute cores. Here the bottleneck is memory bandwidth, not compute — §5.4.2 of the paper puts it as “group GEMM degenerates into a memory-bound streaming read of the weight matrices”, and that is why a token-centric decoding kernel had to be written (Chapter 11 of this site).
String these three things together and you understand why the “architecture” and “infrastructure” parts of the paper have to be read together: one architectural choice (896 experts) directly sets the engineering problems of the next few dozen pages.
Variant: suppose someone proposes cutting the expert count from 896 to 448 while raising the activated count per token from 16 to 32. Sparsity falls from 56 to 14. (a) What happens to compute per token? (b) What happens to GPU memory? (c) What happens to the router's space of choices? (d) Do you think this change makes the model stronger or weaker? Be clear about where your uncertainty comes from.
0.13 Three quantities you must keep apart: total parameters, activated parameters, FLOPs
| Quantity | What it means | K3's value | What it determines |
|---|---|---|---|
| Total parameters total parameters | How many trainable numbers there are in the model in total | 2.78 trillion | GPU memory: how big the model is, how many cards it takes to hold it |
| Activated parameters activated parameters | How many numbers are actually used to process one token | 104.2 billion (about 3.7%) | Compute: how expensive each token is, how fast it generates |
| FLOPs | Floating-point operations, the unit compute is measured in | The paper does not disclose the training total | How much electricity, money and time one training run takes |
A common misreading
When the news says “this model has 2.8 trillion parameters”, many people put it straight up against another model with “671 billion parameters” to compare sizes. But if one is dense (all parameters activated) and the other is MoE, the two numbers cannot be compared directly — they measure different things.
The fairer comparison looks at both numbers at once. K3 is “2.78 trillion total / 104.2 billion activated”; the first number says what the ceiling on its knowledge capacity is, the second says what it costs to spit out each character.
0.14 Training and inference: two completely different jobs
Training: show the model an enormous amount of text, have it guess the next token, and when it guesses wrong adjust every parameter a little so that it guesses more accurately next time. That process of “adjusting the parameters backwards from the error” is called backpropagation, and it requires keeping all the intermediate results of the forward pass (the activations) — the main reason GPU memory explodes during training, which Chapter 10 goes into in detail.
Inference: the model is trained, the parameters are frozen, and it is only used to generate text. Inference splits again into two stages:
- Prefill: read the whole prompt you typed in one go. Every token in this step can be processed in parallel, the compute units run flat out, and it is “compute-bound”.
- Decode: spit characters out one at a time. This step has to be serial (character 5 must come out before character 6 can), and every character spat out means running through the whole model's weights, so it is “bandwidth-bound”.
0.15 Three lives: pre-training, post-training, reinforcement learning
| Stage | What it does | Analogy | Chapter on this site |
|---|---|---|---|
| Pre-training pre-training | Read the whole internet, learn to predict the next token | Having read every book in the library | Chapter 7 |
| Supervised fine-tuning SFT | Look at the “good answers” humans demonstrate, learn to do the same | Copying a master stroke by stroke | Chapter 8 |
| Reinforcement learning RL | Work the problems yourself: a reward when you get it right, a penalty when you get it wrong | Practising in the ring yourself, improving on feedback | Chapters 8 and 9 |
Reinforcement learning: the model tries to complete a task on its own (say “fix this bug”), producing a whole trajectory of actions; then a reward function scores that trajectory; the model adjusts its parameters accordingly, so that high-scoring behaviour becomes more likely.
Why RL is so critical for “agents”
SFT can only teach a model to imitate demonstrations it has seen. But for a task like “use twenty tools, take three hundred steps, fix a bug nobody has ever seen before”, no one can write enough demonstrations. RL needs no demonstrations — it only needs a mechanism that can judge whether the result is right, and the model feels out the path itself, inside the environment.
This also explains why §4.2 of the paper (chapter 9 of this site) spends four whole pages on “how to build the environments, how to build the tasks, how to stop cheating” — for RL, the quality of the environment and the reward is the ceiling on capability.
Agent: a model that can use tools on its own and carry out a task over many steps. A tool call (tool call) means the model emits a structured request (say “run this Python”); an external system executes it and sends the result back as new input. In one task the model may go back and forth like this several hundred times, which is called long-horizon (long-horizon).
Suppose you are going to train a model to write code with RL. The reward you design is: “all unit tests pass, score 1; otherwise score 0.” Construct at least two strategies the model might take that score high while completely violating what you meant. (This class of behaviour has a name: “reward hacking”, and it appears more than five times in the paper.)
assert True. Then think of something more hidden — leave the tests alone, but inside the function under test, detect “am I inside a test run right now”, and if so return the hard-coded expected value.① Change the tests. Delete the assertions or make them always true, or simply delete the failing test cases.
② Hard-code the answers. Do not implement the algorithm; write the input-output pairs from the test cases into a lookup table. Every test passes; change the input and it collapses.
③ Detect the test environment. Have the code work out whether it is inside a test run (check the environment variables, check the call stack), and take a special branch if it is.
④ Break the test framework. Tamper with pytest's behaviour so that every assertion gets swallowed.
⑤ Skip rather than pass. Mark the tests as skipped; under some ways of counting, “nothing failed” counts as passing.
What this question is really for: §4.2.4 of the paper (the kernel optimisation task) says they had to build a dedicated “cheat detection system” that penalises tricks like CUDA graph replay, input caching and precision reduction, and that it is “continuously expanded as new cheating patterns are observed during development” — note that phrase, “continuously expanded”: it is an admission that this is an arms race with no end. §4.2.6 (AET) then uses a triple design: isolate the agent from the verifier; a public verifier paired with a hidden verifier; and penalising rewards under a limited submission budget.
You should now be able to see why these long-winded-looking designs are necessary: the moment you write a reward function down, you are publishing every one of your loopholes to an optimiser more patient than you are.
Variant: §4.2.7 of the paper says the reward for web development tasks is zeroed out when “the project fails to build, throws errors at runtime, or fakes an implementation rather than really producing the artefact”. The first two are objectively decidable; the third, “fakes an implementation”, is very subjective. (a) Give a concrete example of “faking an implementation”. (b) The paper says this one is decided by “model judging”. What new risk does that introduce? (c) If the model being judged and the model doing the judging come from the same family, does the risk get bigger or smaller?
0.16 GPU: why memory and bandwidth are so often deadlier than compute
Training and running a large model rests entirely on GPUs (graphics processing units). You need to know three numbers, and the tension between them.
| Resource | What it is | What happens when there is not enough |
|---|---|---|
| Compute FLOPS | How many floating-point operations per second it can do | Everything computes slowly |
| Memory memory / HBM | How much data it can hold (parameters, activations, KV cache) | It simply will not run (OOM, out of memory) |
| Bandwidth bandwidth | How much data per second it can move from memory into the compute cores | The compute cores sit idle waiting for data (“memory-bound”) |
An analogy
Compute is how fast the chef's hands move, memory is how much the fridge holds, bandwidth is the passage that carries ingredients from the fridge to the stove. On a modern GPU: the chef's hands are absurdly fast, but the passage is relatively narrow. So for much of the time the chef is waiting for ingredients rather than cooking.
A great deal of engineering work is essentially “give the chef something else to do while waiting” — this is the overlap (overlap) that appears everywhere in the paper: overlap communication with computation, overlap offloading with computation, stuff the vision encoder into the pipeline bubbles.
Where the analogy breaks down: a real GPU also has an “on-chip cache” layer (the little tray of seasonings beside the stove), which is extremely fast and extremely small. The central trick in many kernel optimisations is to keep data in that tray and not send it back to the fridge. When §5.4.2 of the paper says that KDA decoding “rebuilds the state of accepted tokens on chip”, this is exactly what it is talking about.
One more set of terms you have to know — when one card cannot hold the model, you cut the model up across several cards, and that is called parallelism:
| Name | What it cuts | In one line |
|---|---|---|
| Data parallelism DP | Cuts the data | Every card holds one whole model, each handles different data, and the gradients are pooled at the end |
| Tensor parallelism TP | Cuts the matrices | Cut one big matrix across rows or down columns, and several cards compute one matrix multiplication together |
| Pipeline parallelism PP | Cuts the layers | Layers 1–20 on card A, layers 21–40 on card B, passed down the line like an assembly line |
| Expert parallelism EP | Cuts the experts | MoE only: 896 experts spread over many cards, and each token is sent to the card it belongs to |
| Context parallelism CP | Cuts the sequence | Long sequences only: cut 1 million tokens into a few segments, one card per segment |
0.17 A context of 1 million tokens: what that actually means
Context window: the largest number of tokens the model can “see” at once. Anything beyond it, it knows nothing about at all.
K3 is 1 million tokens. To give you a sense of the scale:
K2, the generation before K3, had only 128K (128,000). That “8×” in Table 1 is where it comes from.
Why long context is a hard requirement for “agents”
An agent doing one complex task may call tools several hundred times. Every result a tool returns (the contents of a file, the results of a search, a chunk of error log) has to go into the context. §4.2.5 of the paper says that one rollout of a personal assistant task “may involve up to thousands of tool calls and millions of tokens of context”.
If the context is only 128K, the model has to start forgetting what it did earlier when it is halfway through — it repeats work, forgets constraints, contradicts itself. So context length directly decides how complex a task an agent can handle. This is one of the keys to understanding the motivation of the whole paper.
0.18 Symbol quick reference
The symbols below come back again and again over the next fifteen chapters. You do not need to memorise them now, but come back and look them up when you get stuck.
| Symbol | Read as | Meaning |
|---|---|---|
| xt | x sub t | The token vector at position t |
| q, k, v | Q, K, V | Query, key, value vectors |
| St | S sub t | KDA's state matrix at step t (the core of chapter 2) |
| W | W | A learnable weight matrix |
| α | alpha | Two meanings in the paper: KDA's decay factor (eq. 1); attention weight (eq. 9). Read the context |
| β | beta | KDA's write strength (eq. 1); SiTU-GLU's soft-cap parameter (eq. 12) |
| γ | gamma | Cumulative decay (eq. 3); also used as the bias update step size |
| πθ | pi theta | The policy, that is, the model itself; the subscript θ means “decided by the parameters θ” |
| ∑ | sigma | Sum |
| ∏ | pi (capital) | Product |
| ⊙ | circle-dot | Multiply elementwise |
| ⊤ | transpose | Swap rows and columns |
| I | identity matrix | A square matrix with 1 all down the diagonal and 0 everywhere else; multiplying by it does nothing |
| sg(·) | stop gradient | Stop gradient: this term is used as a number only and takes no part in backpropagation (eq. 15) |
| clip(x, a, b) | clip | Hold x inside [a, b]; anything outside is taken to the boundary value |
Now thread together what you have learned in this chapter and answer a “meta-question”:
The paper's abstract says K3 is “2.8 trillion parameters, 104.2 billion activated parameters, 1 million tokens of context”. Starting from these three numbers, predict which three classes of problem §5 of this paper (infrastructure) is bound to spend a lot of space on. For each class, say which of the three numbers forced it out.
Now walk the other two chains yourself.
Chain one: 2.8 trillion total parameters → load imbalance under expert parallelism. Too many parameters, so they must be cut across many cards; under MoE the most natural cut is by expert; but token routing is uneven, some cards are worked to death while others sit idle; the idle cards slow everything down (the weakest-link effect). The paper's answer: MoonEP in §5.2.1 — perfect balance through dynamic redundant experts, with a proof that each rank needs only E/R redundant slots (chapters 10 and 14 of this site).
A second branch that comes with it: 2.8 trillion parameters do not fit at deployment time either → they have to be quantised → MXFP4 QAT in §4.1.4 (chapter 8 of this site).
Chain two: 104.2 billion activated parameters + decoding is serial → bandwidth-bound. Every token spat out means streaming all 104.2 billion parameters through once; in the decoding phase the compute is never saturated and the bottleneck is bandwidth; with small batches it is worse. The paper's answer: the token-centric MoE decoding kernel in §5.4.2 (borrowing from WarpDecode), and speculative decoding in §4.1.4 — a small draft model guesses a few tokens first and the big model verifies several at once, thinning out the bandwidth cost per token (chapters 8 and 11 of this site).
Chain three: 1 million tokens of context → two explosions. (a) attention compute grows with the square; (b) the KV cache grows linearly and never shrinks. The paper answers on three levels: at the architecture level, KDA replaces two thirds of the attention layers with a fixed-size state (§2.1, chapter 2 of this site); at the training level, context parallelism cuts the sequence across cards, with KCP designed specially for KDA (§5.1.2, chapter 10 of this site); at the serving level, prefix caching avoids recomputation, and the KDA state and the MLA cache are managed together (§5.4.1, chapter 11 of this site).
What this question is meant to give you: take any model technical report, and from the handful of numbers in the abstract alone you can predict which engineering problems it has to solve. If it does not tell you, you should be asking “so how did you get around that?” — this is the first step from “reader” to “reviewer”.
Variant: add one more number — K3 is “natively multimodal”, with images and video mixed into the same context as the text. Predict which extra class of problem this forces §5 to solve, and say which of the three chains above it conflicts with most easily. (Hint: think about how many tokens one 3584×3584 image turns into, and what it causes when the number of images varies a great deal from sample to sample.)
Chapter summary
You now have seventeen concepts in hand. They are not isolated terms but a mesh whose parts bite into each other:
Tokens become vectors, and the vectors are transformed over and over by matrix multiplication and activation functions; attention lets every position consult every position, at the price of quadratic complexity and a KV cache that grows linearly; residuals keep a deep network trainable, normalisation keeps the numbers from running away; MoE decouples total parameters from activated parameters, at the price of load imbalance and memory that does not come down; all of this runs on GPUs, and a GPU's memory and bandwidth usually hit the wall before its compute does, so you need every kind of parallelism, and the price of parallelism is communication.
Every chapter that follows is one place on this mesh, magnified.
第1章 全景:这篇论文在主张什么,以及知识地图
在钻进任何细节之前,你需要一张地图。这一章做三件事:说清这篇论文想解决什么问题、把它的十六个知识点排成一张依赖图、给你一条推荐的行进路线。读完这一章,你后面每读到一个新概念,都能知道它挂在哪根枝上。
学完这一章你应该能做到
- 用两句话说清「测试时缩放」和「预训练缩放」是两条什么样的轴,以及这篇论文为什么强调「同时推进两条」
- 画出这篇论文的知识依赖图,指出哪三个概念是必须最先学的
- 把摘要里的每一个短语对应到论文的某一节
- 分辨这篇论文里哪些陈述是「有证据的」、哪些是「作者的设计主张」
1.1 论文开篇的那个论断:缩放有两条轴,开源界只走了一条
论文的第一段做了一件很聪明的事:它先给整个领域画了一个坐标系,然后指出坐标系里有一块空地,而 K3 就是要去占这块空地。
第一条轴:部署前的算力。 在大语言模型发展的大部分时间里,「缩放」意味着在部署之前投入更多算力——训更大的模型、喂更多的数据。这是 2020 年那批缩放定律论文奠定的路线。
第二条轴:测试时的算力。 推理模型的兴起确立了第二条轴:让模型在回答问题的时候多花算力。论文点名了几条支线:OpenAI 的 o 系列用强化学习扩展测试时推理;Anthropic 的扩展思考让模型分配自适应的思考预算并把思考与工具调用交错;DeepSeek-R1 和 Kimi K1.5 表明大规模强化学习能从强预训练模型中引出复杂的推理行为;Kimi K2.5 Agent Swarm 则把测试时缩放从「顺序推理」扩展到「并行的智能体协同」。
论文指出的那块空地
作者的观察是:开源生态在第二条轴上进步很快,但在第一条轴上进展缓慢——很多近期的开源模型仍停留在 1 万亿参数级别或略高一点。而随着越来越复杂的推理和智能体强化学习方法被应用到规模相近的预训练底座上,开源的进展有趋同的风险,同时与最强的专有系统之间的差距在拉大。
K3 的定位由此确定:两条轴一起往前推——把预训练底座扩到前所未有的 3 万亿参数级别,同时扩展强化学习、推理努力、以及 100 万上下文长度下的长时程交互。
读的时候要小心
「开源进展有趋同风险」是一个论断,不是一个被测量出来的结论。论文没有给出任何量化证据(比如开源模型能力分布的方差随时间变化)来支撑它。这是技术报告开篇常见的叙事手法:先建立一个问题框架,让自己的工作成为这个框架下的自然答案。
这不代表它是错的,但你应该把它记成「作者的视角」而不是「已确立的事实」。整篇论文里这类陈述还有很多,本站会一路标出来。
1.2 摘要拆解:每一个短语对应哪一节
下面这张表是本站最实用的一张。摘要里的每个短语,都能在论文里找到一整节来展开。你也可以把它当作全站的导航。
| 摘要里的短语 | 什么意思 | 论文节 | 本站 |
|---|---|---|---|
| 2.8T 参数 MoE 模型 | 2.78 万亿总参数的混合专家模型 | §3.2 表 1 | 第7章 |
| 1040 亿激活参数 | 每个 token 实际用到 1042 亿 | §2.3、§3.2 | 第5、7章 |
| 原生视觉能力 | 图像视频与文本共享同一个骨干,无事后对齐 | §2.4 | 第6章 |
| 100 万 token 上下文窗口 | 一次能看见 100 万个 token | §3.4 | 第7章 |
| Kimi Delta Attention (KDA) | 把注意力换成固定大小的循环状态 | §2.1.1 | 第2章 |
| Attention Residuals (AttnRes) | 让每一层用注意力从所有先前层取信息 | §2.2 | 第4章 |
| Stable LatentMoE | 896 个专家、激活 16 个,且不炸 | §2.3 | 第5章 |
| 约 2.5× 的整体缩放效率提升 | 同样的验证损失,只要 K2 约 1/2.5 的算力 | §3.2 图 7 | 第7章 |
| 跨通用/智能体/编码域的强化学习 | 三个领域各训一个专家 | §4.1.2 | 第8章 |
| 多个推理努力等级 | low / high / max,三档思考预算 | §4.1.2 | 第8章 |
| 组合泛化与稳健的长时程执行 | 能把学到的能力拼起来用,能跑几百步不崩 | §4.2 | 第9章 |
| KDA 的算法–系统协同设计 | 为 KDA 专门写内核和上下文并行 | §5.1 | 第10章 |
| 完美均衡的专家并行训练 | MoonEP,每张卡收到的 token 数完全相同 | §5.2.1 | 第10、14章 |
| 百万 token 智能体 RL | 可恢复的部分 rollout 与沙箱状态 | §5.3 | 第11章 |
| 部署创新 | MXFP4 量化、推测解码、前缀缓存、舰队调度 | §4.1.4、§5.4 | 第8、11章 |
| 落后于 Claude Fable 5 和 GPT-5.6 Sol | 论文自陈:整体仍不如这两个专有模型 | §6 | 第12章 |
| 释放完整模型权重 | 权重公开在 HuggingFace | 脚注 1 | 第13章 |
关于那些陌生的模型名
论文用 Claude Fable 5、GPT-5.6 Sol、Claude Opus 4.8、GPT-5.5、GLM-5.2 作为对比基线。如果你没听过这些名字,那很正常——这篇报告发表于 2026 年 7 月,这些是当时的模型。
本站在引用评测数字时,一律照论文原文转述,不做任何外部核实。第12章会专门讲,为什么「照转述」这件事本身也需要小心。
1.3 知识地图:谁依赖谁
下面这张图是整篇论文的骨架。箭头 A → B 读作「要理解 B,先得理解 A」。
1.4 架构总纲:把信息流往三个方向撑开
论文第 2 节开头给了一个非常清晰的组织原则,值得单独拎出来记住。K3 的架构设计沿三个互补的维度扩展信息流:
打个比方
把模型想象成一栋楼里的一个信息流通系统。序列维度是同一层楼里不同房间之间的走廊;深度维度是楼层之间的电梯;宽度维度是每一层楼里房间的数量。K3 的三个改动分别是:把走廊改造得更省钱(KDA)、把电梯从「只能到隔壁层」改成「能直达任意层」(AttnRes)、把房间数量翻倍但每次只开其中十六间(LatentMoE)。
类比失效处:这三者在真实模型里不是独立的三套系统,而是交错堆叠的——每个注意力层后面紧跟一个 MoE 层,AttnRes 又横跨所有层。三个维度是分析视角,不是物理分区。
第0章讲过 GPU 的三种资源:算力、显存、带宽。现在请把上面三个架构维度的改动与它们主要缓解的资源压力对应起来,并各指出一个它们新增的压力。
KDA(序列维): 缓解显存与带宽——KV 缓存不再随长度增长,长上下文下省得极多。新增压力:串行依赖。循环状态必须一步步往下传,与 GPU 喜欢的「又宽又整齐的并行」直接冲突。论文第 5.1 节原话是「它的串行形式给并行执行带来困难」,整个 §5.1 都在还这笔债(FlashKDA 内核、SM 级上下文并行、KCP)。
AttnRes(深度维): 主要不是省资源,而是提升表达力——让每层能选择性检索所有先前层,而不是被迫接受一个被反复叠加的和。新增压力:显存与跨阶段通信。完整形式要保存所有层的输出,是 O(Ld) 的显存,在流水线并行下还要跨阶段传。这正是 Block AttnRes(切成 8 块,降到 O(Nd))存在的理由。
LatentMoE(宽度维): 缓解算力——总参数 2.78 万亿,每 token 只算 1042 亿。新增压力有两个:显存不降(896 个专家全都得常驻),以及负载不均衡(token 路由不均匀 → 专家并行下有的卡忙死有的闲着)。前者导出第8章的 MXFP4 量化,后者导出第5章的 Quantile Balancing 和第10章的 MoonEP。
这道题的意义:你现在拿到了读这篇论文最有力的一把工具——每读到一个架构创新,立刻问「它省了什么,又欠下了什么」。论文第 5 节的每一个小节,都是在还第 2 节欠下的某一笔债。
变式:论文说 MLA 层用了 NoPE(不加位置编码),理由之一是「KDA 层已经提供位置敏感的混合」。请分析:这个设计省下了什么(提示:想想扩上下文时要重调什么),又新增了什么风险(提示:如果某天有人想把 KDA 换掉,会发生什么)?
1.5 论文的四条贡献,以及它们的证据强度
论文在引言末尾自己列了四条贡献。这里逐条列出,同时标注证据强度——这是训练「批判性阅读」的第一次练习。
| 论文自述的贡献 | 论文给的证据 | 证据强度 |
|---|---|---|
| 开放前沿的预训练:2.8T 参数原生多模态 MoE,1042 亿激活,1M 上下文;KDA、AttnRes、Stable LatentMoE、精炼的数据与训练配方合起来把整体缩放效率提升约 2.5× | 图 7 的两条缩放定律曲线;表 1 的架构对比 | 中。曲线无坐标轴数值;2.5× 是架构、数据、配方打包的结果,各项贡献未拆分;无单项消融 |
| 面向多努力测试时缩放的强化学习:跨通用、智能体、编码域做 RL,跨多个推理努力等级,再合并成一个模型 | 图 8 的 RL FLOPs 与分数/步数曲线;§6 的评测分数 | 中。图 8 无坐标轴数值;「扩 RL 算力 → 步数增长 → 能力提升」是相关性观察,无对照实验 |
| 面向万亿参数、百万 token 的基础设施:MoonEP、显存高效基建、可恢复沙箱的同址 RL 系统 | §5 的详细方法描述;附录 E 的定理证明;沙箱数量 51,219,741 | 方法描述强,性能证据弱。定理有严格证明;但几乎所有系统优化都没有给加速比(「大幅超过 Triton 参考实现」没有倍数) |
| 一个开放的前沿模型:释放完整权重 | HuggingFace 链接 | 强。这是唯一可被任何人直接验证的一条 |
读的时候要小心:这是技术报告,不是同行评审论文
技术报告(technical report)和会议论文有一个体制性的区别:它没有经过同行评审。作者可以选择报告什么、省略什么,可以只展示成功的案例,可以在没有消融实验的情况下声称某个设计「改善了」结果。
这不是在指责这篇报告——它在同类报告里已经算相当详实的了(附录里有真证明,还诚实报告了自己的弱项,比如 CritPt 落后、内核赛道四分之三没解出)。但你读它时的姿态应该是「一份来自建造者的、有立场的详细自述」,而不是「经过验证的科学结论」。
本站会一路用这种红色的框标出需要打折扣的地方。第13章末尾有一节专门汇总「这篇论文没有告诉你的事」。
下面是从论文里摘出的五句话。请把它们分成三类:(A) 有可核验证据支撑的事实、(B) 有数据但数据本身可质疑的结论、(C) 纯粹的设计主张或叙事。并对每一句说明你的判断依据。
① 「我们发布完整的 Kimi K3 模型权重。」
② 「这些改进合起来带来约 2.5× 的整体缩放效率提升。」
③ 「开源进展有趋同的风险,同时与最强专有系统的差距在拉大。」
④ 「Kimi K3 在 BrowseComp 上取得 91.2%,是所有被评测模型中的最好成绩。」
⑤ 「MoonViT-V2 在视觉评测上追平了 SigLIP 初始化的基线,说明对比预训练作为初始化不是必需的。」
① → A。 权重公开在 HuggingFace,任何人可以下载、跑、验证。这是这篇报告里可验证性最强的一条。
② → B。 有图 7 作为证据,但:图上没有坐标轴数值(无法反推任何具体损失值);2.5× 是架构+数据+配方三者打包的结果,论文没有拆分各自贡献;实验由作者自己完成,外部无法复现(因为没有公开训练数据和训练代码)。所以它是「有数据支撑的作者结论」,但你不能把它当成一个可迁移的科学事实。
③ → C。 纯叙事。没有任何量化指标(比如开源模型能力的分布、差距的定义与测量方式)。它的作用是为 K3 的定位提供合法性。
④ → B。 有具体数字,但配置里藏着一堆条件:K3 用了 300K token 触发的上下文压缩策略;论文没有说对手用了什么上下文管理策略;论文自己也说了,如果 K3 用完整 1M 上下文不做压缩,分数是 90.4%——而它领先第二名 GPT-5.6 Sol(90.4%)恰好是 0.8 分。领先幅度和自身策略造成的波动是同一个量级。再加上论文没报告这个基准的重复次数和方差。所以这个「最好成绩」是真的,但「领先」这件事非常脆弱。第12章会把这类分析做透。
⑤ → B,而且是跨度最大的一条。 「追平」是数据,「说明对比预训练不是必需的」是从数据外推出的一般性结论。问题在于:论文在这两页里没有给出任何评测表或分数来支持「追平」;结论加了 「at scale」(在这个规模上)的限定,但没有给出规模下界;而且这是一次对比的结果,不是多次独立实验。
这道题的意义:读技术报告的核心技能,不是记住数字,而是随时知道自己脚下踩的是哪一层——是可验证的事实、是作者的实验、还是作者的叙事。三层都有价值,但不能混为一谈。
变式:论文说 K3 在 Kimi Webdev Bench 上相对 Claude Opus 4.8 有 +31.0 分的总体优势,评判方式是「盲评专家评分」。请列出你需要追问的至少四个问题,才能判断这个 +31.0 有多少分量。(提示:想想基准是谁造的、专家是谁、样本量、以及为什么对手选的是 Opus 4.8 而不是论文自己承认更强的 Claude Fable 5。)
1.6 推荐的行进路线
十六章不必按顺序读完。下面是三条路线,按你的目标挑一条。
| 你的目标 | 路线 | 大致投入 |
|---|---|---|
| 完整学透(你选的就是这条) | 0 → 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12 → 13 → 14 → 15,顺序读,每章的测验必须做对变式才往下走 | 长。但第2、5、10、11、14 五章占了一半的难度 |
| 只想懂架构 | 0 → 1 → 2 → 3 → 4 → 5 → 6 → 14(只读附录 B、C) | 中。核心是第2章和第5章 |
| 只想懂「它到底行不行」 | 0 → 1 → 12 → 13 | 短。但要有心理准备:第12章的重点是教你不要轻信任何一张评测表 |
关于本站的用法(新版有三个模式,请先看这段)
右上角有三个模式。学习是读正文;复习是系统按遗忘规律推给你的题;进度是你的掌握度仪表盘和进度存取。
两种题,判分方式不同。「自评题」是开放题,你自己点「我答对了/我答错了」——请诚实,虚报只是骗自己。「机判自测」在每章末尾,数值、选择、排序由机器判分,不给你自我欺骗的余地。两种题都会自动进入复习队列。
提示按顺序用。四级提示先给方向、再给思路、再给第一步,最后才是完整答案。直接翻答案你会觉得「这我懂」,然后在变式题上原地卡住。用了几级提示会被记下来,直接影响这道题下次出现的时间。
实验室和 3D 不是插图。标着「实验室」的组件里,论文的算法是真的在你浏览器里跑——第2章那个能让你亲眼看到递推形式和分块并行形式的输出差是 1e-16。3D 场景也是真的在执行论文的公式。这些地方值得多待一会儿。
进度会自动保存在这台电脑的浏览器里。但换设备、清缓存会丢,所以学完一章去「进度」页导出一次文件。如果顶栏那个小圆点是黄的,说明当前环境不能自动保存,那就更要记得导出。
掌握的标准始终是:能做对变式并说清理由。
答辩:如果我是审稿人
论文说 K3 的三个架构创新「合起来带来约 2.5× 的缩放效率提升」。我的攻击是:既然是「合起来」,那你怎么排除这样一种可能——真正起作用的只是「层数从 61 涨到 93、激活参数从 326 亿涨到 1042 亿」这种朴素的变大,而 KDA、AttnRes、LatentMoE 这三个花哨的东西贡献接近于零,甚至是负的?请你为论文辩护,或者承认这个攻击成立。
参考防守(先自己组织语言再看)
先承认攻击的合理部分:这个攻击在本篇报告的证据范围内是成立的。论文确实没有给出任何单项消融——没有「去掉 AttnRes 后的缩放曲线」、没有「KDA 换回全 MLA 后的曲线」。仅凭图 7,你无法把 2.5× 拆开。这是这篇报告一个真实的证据缺口。
但辩护有两层:
第一层,缩放定律曲线的语义。图 7 画的是「验证损失 vs FLOPs」,而不是「验证损失 vs 参数量」。如果 K3 只是单纯变大,那么在相同的 FLOPs 预算下它不会自动更优——变大意味着每步更贵,同样的算力能训的步数更少。缩放定律曲线的整条左移,说明的是「每一份算力买到的损失下降更多」,这是效率而非规模的改善。所以「只是变大」这个解释,在这个特定的图上并不成立。
第二层,各个改动的动机是可独立论证的。KDA 的收益(固定大小状态)在长上下文下是数学上确定的,不依赖实验;LatentMoE 让 896 个专家变得可行,这也是结构性的。所以即使贡献比例未知,「贡献为零或为负」这个强版本的攻击也站不住。
但辩护到此为止。你依然不能从这篇报告推出「KDA 贡献了 2.5× 中的多少」。诚实的表述是:这三个改动合起来,在作者的训练配方下,产生了约 2.5× 的效率增益;各自的贡献比例未知。本站会一路使用这种带限定的表述。
顺带一提:论文对 KDA 和 AttnRes 都引用了先前的独立工作(Kimi Linear、Attention Residuals 各自的论文),那些论文里可能有消融。但那些实验不是在 2.78 万亿参数规模上做的,能否外推是另一个开放问题。
本章小结
你现在有了三样东西:一张知识依赖图(知道每个概念挂在哪)、一张摘要对照表(知道每个短语去哪一章找)、一套证据分级的习惯(知道每句话该信几分)。
下一章是全站最难也最核心的一章:Kimi Delta Attention。它会回答第0章留下的那个问题——如果 KV 缓存随长度线性增长是个诅咒,有没有办法根本上摆脱它?答案是有,代价是你要重新学一遍「记忆」这件事该怎么建模。
Chapter 1 · The big picture: what this paper claims, and the knowledge map
Before you dig into any detail, you need a map. This chapter does three things: it states what problem the paper is trying to solve, arranges its sixteen ideas into a dependency graph, and gives you a recommended route through them. Once you have read it, every new concept you meet later will have a branch to hang on.
After this chapter you should be able to
- Say in two sentences what kind of axes “test-time scaling” and “pretraining scaling” are, and why this paper insists on “pushing both at once”
- Draw the paper's knowledge dependency graph and point out which three concepts have to be learned first
- Map every phrase in the abstract to a section of the paper
- Tell which statements in this paper are “backed by evidence” and which are “the authors' design claims”
1.1 The claim the paper opens with: scaling has two axes, and open source has walked only one
The paper's first paragraph does something clever: it draws a coordinate system for the whole field, then points out an empty patch inside it — and K3 is what goes there.
Axis one: compute before deployment. For most of the history of large language models, “scaling” meant putting more compute in before deployment — training a bigger model, feeding it more data. This is the route laid down by the scaling-law papers of 2020.
Axis two: compute at test time. The rise of reasoning models established a second axis: let the model spend more compute while it is answering the question. The paper names several strands: OpenAI's o series scales test-time reasoning with reinforcement learning; Anthropic's extended thinking lets the model allocate an adaptive thinking budget and interleave thinking with tool calls; DeepSeek-R1 and Kimi K1.5 show that large-scale reinforcement learning can draw complex reasoning behaviour out of a strong pretrained model; and Kimi K2.5 Agent Swarm extends test-time scaling from “sequential reasoning” to “parallel agent collaboration”.
The empty patch the paper points at
The authors' observation: the open-source ecosystem has moved fast on axis two but slowly on axis one — many recent open-source models are still at the 1 trillion parameter level or a little above. And as ever more elaborate reasoning and agentic reinforcement learning methods get applied to pretraining bases of similar size, open-source progress risks converging, while the gap to the strongest proprietary systems widens.
That fixes K3's position: push both axes at once — scale the pretraining base to an unprecedented 3 trillion parameter level, and at the same time scale reinforcement learning, reasoning effort, and long-horizon interaction at 1M context length.
Read this carefully
“Open-source progress risks converging” is a claim, not a measured result. The paper gives no quantitative evidence for it (the variance of the open-source capability distribution over time, say). This is a common narrative move at the start of a technical report: set up a problem frame first, so that your own work becomes the natural answer inside that frame.
That does not make it wrong, but you should file it as “the authors' view”, not “an established fact”. There are many more statements like this through the paper, and this site will flag them as they come.
1.2 The abstract taken apart: which section each phrase belongs to
The table below is the most useful one on this site. Every phrase in the abstract has a whole section of the paper behind it. You can also use it as navigation for the whole site.
| The phrase in the abstract | What it means | Paper section | This site |
|---|---|---|---|
| 2.8T-parameter MoE model | a mixture-of-experts model with 2.78 trillion total parameters | §3.2 Table 1 | Chapter 7 |
| 104B activated parameters | 104.2B are actually used for each token | §2.3, §3.2 | Chapters 5, 7 |
| native vision | images and video share one backbone with text, with no alignment bolted on afterwards | §2.4 | Chapter 6 |
| 1M token context window | it can see 1M tokens at once | §3.4 | Chapter 7 |
| Kimi Delta Attention (KDA) | replaces attention with a fixed-size recurrent state | §2.1.1 | Chapter 2 |
| Attention Residuals (AttnRes) | lets every layer use attention to fetch from all earlier layers | §2.2 | Chapter 4 |
| Stable LatentMoE | 896 experts, 16 of them active, and it does not blow up | §2.3 | Chapter 5 |
| about 2.5× better overall scaling efficiency | the same validation loss for about 1/2.5 of K2's compute | §3.2 Figure 7 | Chapter 7 |
| reinforcement learning across general / agentic / coding domains | one specialist trained in each of the three domains | §4.1.2 | Chapter 8 |
| several reasoning-effort levels | low / high / max — three thinking budgets | §4.1.2 | Chapter 8 |
| compositional generalisation and robust long-horizon execution | it can put the abilities it learned together, and run hundreds of steps without falling over | §4.2 | Chapter 9 |
| algorithm–system co-design for KDA | kernels and context parallelism written specifically for KDA | §5.1 | Chapter 10 |
| perfectly balanced expert-parallel training | MoonEP: every card receives exactly the same number of tokens | §5.2.1 | Chapters 10, 14 |
| million-token agentic RL | resumable partial rollouts and sandbox state | §5.3 | Chapter 11 |
| deployment innovations | MXFP4 quantisation, speculative decoding, prefix caching, fleet scheduling | §4.1.4, §5.4 | Chapters 8, 11 |
| behind Claude Fable 5 and GPT-5.6 Sol | the paper says so itself: overall it is still short of these two proprietary models | §6 | Chapter 12 |
| releasing the complete model weights | the weights are public on HuggingFace | footnote 1 | Chapter 13 |
About those unfamiliar model names
The paper uses Claude Fable 5, GPT-5.6 Sol, Claude Opus 4.8, GPT-5.5 and GLM-5.2 as comparison baselines. If you have never heard of these names, that is normal — this report came out in July 2026, and those were the models of the day.
When this site quotes evaluation numbers, it repeats the paper's own text and verifies nothing externally. Chapter 12 is devoted to why “just repeating it” is itself something to be careful about.
1.3 The knowledge map: what depends on what
The figure below is the skeleton of the whole paper. Read an arrow A → B as “to understand B, you first have to understand A”.
1.4 The architecture in outline: pushing information flow out in three directions
The opening of §2 gives a very clear organising principle, worth pulling out and remembering on its own. K3's architecture scales information flow along three complementary dimensions:
An analogy
Picture the model as the system that moves information around a building. The sequence dimension is the corridor between rooms on one floor; the depth dimension is the lift between floors; the width dimension is how many rooms there are on each floor. K3's three changes are: rebuild the corridor so it costs less (KDA), change the lift from “it only reaches the next floor” to “it goes straight to any floor” (AttnRes), and double the number of rooms but open only sixteen of them at a time (LatentMoE).
Where the analogy fails: in a real model these are not three independent systems but interleaved and stacked — every attention layer is followed immediately by an MoE layer, and AttnRes cuts across all layers. The three dimensions are a way of looking, not a physical partition.
Chapter 0 covered the three GPU resources: compute, memory, bandwidth. Now match the change in each of the three architectural dimensions above to the resource pressure it mainly relieves, and name one pressure each of them adds.
KDA (sequence dimension): relieves memory and bandwidth — the KV cache no longer grows with length, which saves an enormous amount at long context. Added pressure: a serial dependency. The recurrent state has to be passed down one step at a time, which runs straight into the wide, regular parallelism a GPU likes. §5.1 of the paper puts it as “its sequential form poses difficulties for parallel execution”, and the whole of §5.1 is spent paying that debt off (the FlashKDA kernel, SM-level context parallelism, KCP).
AttnRes (depth dimension): mostly not about saving resources but about more expressive power — every layer can retrieve selectively from all earlier layers instead of being handed one repeatedly accumulated sum. Added pressure: memory and cross-stage communication. The full form has to keep the output of every layer, which is O(Ld) of memory, and under pipeline parallelism it has to be sent across stages. That is exactly why Block AttnRes exists (cut into 8 blocks, down to O(Nd)).
LatentMoE (width dimension): relieves compute — 2.78 trillion parameters in total, only 104.2B computed per token. Two pressures get added: memory does not drop (all 896 experts have to stay resident), and load imbalance (uneven token routing → under expert parallelism some cards are swamped while others sit idle). The first leads to MXFP4 quantisation in Chapter 8, the second to Quantile Balancing in Chapter 5 and MoonEP in Chapter 10.
Why this question matters: you now hold the most powerful tool there is for reading this paper — every time you meet an architectural change, ask immediately: what did it save, and what debt did it take on? Every subsection of §5 of the paper is paying off some debt taken on in §2.
Variant: the paper says the MLA layers use NoPE (no positional encoding), one reason being that “the KDA layers already provide position-sensitive mixing”. Work out what this design saves (hint: think about what has to be retuned when you extend the context), and what new risk it adds (hint: what happens if someone one day wants to swap KDA out).
1.5 The paper's four contributions, and how strong the evidence is
At the end of the introduction the paper lists four contributions of its own. Here they are one by one, with the strength of the evidence marked alongside — this is the first exercise in reading critically.
| The contribution as the paper states it | The evidence the paper gives | Strength of the evidence |
|---|---|---|
| Open frontier pretraining: a 2.8T-parameter natively multimodal MoE, 104.2B activated, 1M context; KDA, AttnRes, Stable LatentMoE and a refined data and training recipe together improve overall scaling efficiency by about 2.5× | the two scaling-law curves in Figure 7; the architecture comparison in Table 1 | Medium. The curves carry no numbers on the axes; the 2.5× is a package of architecture, data and recipe, with the individual contributions not separated out; no single-item ablation |
| Reinforcement learning for multi-effort test-time scaling: RL across the general, agentic and coding domains, across several reasoning-effort levels, then merged into one model | the RL FLOPs and score/step curves in Figure 8; the evaluation scores in §6 | Medium. Figure 8 carries no numbers on the axes; “more RL compute → more steps → more capability” is an observed correlation, with no controlled experiment |
| Infrastructure for trillion-parameter, million-token work: MoonEP, memory-efficient infrastructure, a colocated RL system with resumable sandboxes | the detailed method descriptions in §5; the theorem proofs in Appendix E; a sandbox count of 51,219,741 | Strong on method description, weak on performance evidence. The theorems come with rigorous proofs; but almost none of the systems optimisations give a speedup number (“substantially exceeds the Triton reference implementation” comes with no factor) |
| An open frontier model: the complete weights released | a HuggingFace link | Strong. This is the only one anybody can verify directly |
Read this carefully: this is a technical report, not a peer-reviewed paper
A technical report differs from a conference paper in one institutional way: it has not been through peer review. The authors get to choose what to report and what to leave out, they can show only the cases that worked, and they can claim that a design “improved” the result without an ablation to back it.
This is not an accusation against this report — as reports of this kind go it is a fairly detailed one (there are real proofs in the appendix, and it reports its own weak spots honestly, such as trailing on CritPt and failing three quarters of the kernel track). But the stance to read it with is “a detailed account from the builders, with a position of its own”, not “verified scientific conclusions”.
This site will use this red box throughout to mark the places you should discount. There is a section at the end of Chapter 13 that collects “the things this paper does not tell you”.
Below are five sentences taken from the paper. Sort them into three classes: (A) facts backed by verifiable evidence, (B) conclusions with data behind them, where the data itself can be questioned, (C) pure design claim or narrative. And say for each one what your judgement rests on.
① “We release the complete Kimi K3 model weights.”
② “Together these improvements give about 2.5× better overall scaling efficiency.”
③ “Open-source progress risks converging, while the gap to the strongest proprietary systems widens.”
④ “Kimi K3 reaches 91.2% on BrowseComp, the best score among all models evaluated.”
⑤ “MoonViT-V2 matches the SigLIP-initialised baseline on vision evaluations, showing that contrastive pretraining as initialisation is not necessary.”
① → A. The weights are public on HuggingFace; anybody can download them, run them, check them. This is the most verifiable statement in the report.
② → B. Figure 7 is offered as evidence, but: the figure has no numbers on the axes (you cannot recover any specific loss value from it); the 2.5× is a package of architecture + data + recipe, and the paper does not separate out what each contributed; the experiment was run by the authors themselves and cannot be reproduced outside (the training data and training code are not public). So it is “a conclusion of the authors, with data behind it”, but you cannot treat it as a transferable scientific fact.
③ → C. Pure narrative. Nothing quantified at all (no distribution of open-source capability, no definition of the gap, no way of measuring it). Its function is to give K3's position legitimacy.
④ → B. There is a concrete number, but a pile of conditions is hidden in the configuration: K3 used a context-compression strategy triggered at 300K tokens; the paper does not say what context-management strategy its opponents used; and the paper says itself that if K3 uses the full 1M context with no compression, the score is 90.4% — while its lead over the second-placed GPT-5.6 Sol (90.4%) is exactly 0.8 points. The size of the lead and the swing caused by its own strategy are the same order of magnitude. On top of that, the paper does not report how many times this benchmark was repeated, or the variance. So the “best score” is real, but the “lead” is very fragile. Chapter 12 works this kind of analysis through properly.
⑤ → B, and it reaches further than any of the others. “Matches” is data; “showing that contrastive pretraining is not necessary” is a general conclusion extrapolated from that data. The trouble is: across these two pages the paper gives no evaluation table and no scores to support “matches”; the conclusion is qualified with “at scale”, but no lower bound on the scale is given; and this is the result of one comparison, not of several independent experiments.
Why this question matters: the core skill in reading a technical report is not remembering numbers, it is knowing at every moment which layer you are standing on — a verifiable fact, an experiment of the authors, or the authors' narrative. All three have value, but they must not be run together.
Variant: the paper says K3 has an overall advantage of +31.0 points over Claude Opus 4.8 on Kimi Webdev Bench, judged by “blind expert scoring”. List at least four questions you would need to ask before you can judge how much that +31.0 is worth. (Hint: think about who built the benchmark, who the experts are, the sample size, and why the opponent chosen is Opus 4.8 rather than Claude Fable 5, which the paper itself admits is stronger.)
1.6 The recommended route
You do not have to read all sixteen chapters in order. Here are three routes; pick one according to your goal.
| Your goal | Route | Roughly what it takes |
|---|---|---|
| Learn the whole thing properly (this is the one you picked) | 0 → 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12 → 13 → 14 → 15, read in order; you only move on from a chapter once you have got its variant question right | Long. But Chapters 2, 5, 10, 11 and 14 carry half of the difficulty |
| Only the architecture | 0 → 1 → 2 → 3 → 4 → 5 → 6 → 14 (Appendices B and C only) | Medium. The core is Chapter 2 and Chapter 5 |
| Only “is it actually any good” | 0 → 1 → 12 → 13 | Short. But be ready for this: the point of Chapter 12 is to teach you not to take any evaluation table on trust |
How to use this site (the new version has three modes — read this part first)
There are three modes in the top right. Read is the main text; Review is the questions the system pushes at you according to how forgetting works; Progress is your mastery dashboard and where progress is saved and loaded.
Two kinds of question, graded in different ways. A “self-graded question” is open-ended and you click “I got it right / I got it wrong” yourself — be honest, a false report only fools you. The “Machine-graded self-test” at the end of each chapter has numbers, choices and orderings graded by machine, which leaves you no room to fool yourself. Both kinds go into the review queue automatically.
Use the hints in order. The four tiers give you the direction, then the idea, then the first step, and only then the full answer. Turn straight to the answer and you will feel “I know this”, and then get stuck where you stand on the variant. How many tiers you used is recorded, and it directly affects when this question comes back.
The labs and the 3D are not illustrations. In the components marked “lab”, the paper's algorithm really is running in your browser — the one in Chapter 2 lets you see with your own eyes that the output difference between the recurrent form and the chunked parallel form is 1e-16. The 3D scenes really are executing the paper's formulas too. These are places worth staying at a while.
Progress is saved automatically in the browser on this computer. But it is lost if you change device or clear the cache, so go to the “Progress” page and export a file once you finish a chapter. If that little dot in the top bar is yellow, this environment cannot save automatically, which makes exporting all the more important to remember.
The standard for mastery is always the same: get the variant right and be able to say why.
Defend it: if I were a reviewer
The paper says K3's three architectural innovations “together give about 2.5× better scaling efficiency”. My attack: since it is “together”, how do you rule out the possibility that what actually did the work was plain enlargement — “layers from 61 to 93, activated parameters from 32.6B to 104.2B” — while those three fancy things, KDA, AttnRes and LatentMoE, contributed close to nothing, or even negatively? Defend the paper, or concede that the attack lands.
A reference defence (put it in your own words first)
First concede the part of the attack that is fair: within the evidence this report contains, the attack holds. The paper really does give no single-item ablation — no “scaling curve with AttnRes removed”, no “curve with KDA switched back to all-MLA”. From Figure 7 alone you cannot take the 2.5× apart. This is a real gap in the report's evidence.
But the defence has two layers:
Layer one, what a scaling-law curve means. Figure 7 plots “validation loss vs FLOPs”, not “validation loss vs parameter count”. If K3 were simply bigger, it would not automatically come out better at the same FLOPs budget — bigger means each step costs more, so the same compute trains fewer steps. The whole scaling-law curve shifting left says “each unit of compute buys more loss reduction”, which is an improvement in efficiency, not in size. So the “it is just bigger” explanation does not hold on this particular figure.
Layer two, the motivation for each change can be argued independently. KDA's benefit (a fixed-size state) is mathematically certain at long context and does not depend on experiments; LatentMoE is what makes 896 experts feasible, which is structural too. So even with the proportions unknown, the strong version of the attack — “contributed zero or negative” — does not stand up.
But the defence stops there. You still cannot get “how much of the 2.5× KDA contributed” out of this report. The honest formulation is: these three changes together, under the authors' training recipe, produced about a 2.5× efficiency gain; how much each contributed is unknown. This site will use qualified wording like that throughout.
One more thing: for both KDA and AttnRes the paper cites earlier independent work (the Kimi Linear and Attention Residuals papers), and those papers may contain ablations. But those experiments were not run at the 2.78 trillion parameter scale, and whether they extrapolate is another open question.
Chapter summary
You now have three things: a knowledge dependency graph (you know where each concept hangs), an abstract cross-reference table (you know which chapter to look in for each phrase), and a habit of grading evidence (you know how much to believe each sentence).
The next chapter is the hardest and the most central on the site: Kimi Delta Attention. It answers the question Chapter 0 left open — if a KV cache that grows linearly with length is a curse, is there a way to be rid of it at the root? There is, and the price is that you have to relearn how “memory” should be modelled.
第2章 Kimi Delta Attention:把无限增长的记忆压成一块固定大小的黑板
这是全站最核心也最难的一章。KDA 是 K3 架构的地基——93 层里有 69 层是它,第10章的内核与并行、第11章的前缀缓存,全都是在为它擦屁股。这一章我们会从「记忆该怎么建模」这个最朴素的问题出发,一步步走到论文的式 1 到式 6。请慢一点读,每个小节末尾的题都做。
学完这一章你应该能做到
- 解释为什么「固定大小的状态」和「随长度增长的 KV 缓存」是一次根本性的交易,各自换来了什么
- 用外积和矩阵乘法,说清一个状态矩阵 S 是怎么「存」和「取」键值对的
- 说清 delta 规则里那个 (I − βkk⊤) 到底在干什么,以及为什么必须有它
- 逐符号读懂式 1,并解释 α 和 β 的分工
- 解释分块并行形式为什么能让「串行的东西」跑在 GPU 上,以及式 4 的两项各是什么
- 说清一个数值溢出问题,是怎么反过来逼着作者改掉衰减函数的数学形式的
2.1 出发点:注意力的诅咒,和一个被放弃的老方案
第0章讲过注意力的两个代价:计算量随长度平方增长,KV 缓存随长度线性增长且永不缩小。在 100 万 token 的尺度上,第二个代价尤其致命——你算过那道题,光是一份缓存就能吃掉一张顶级显卡三成的显存。
但在注意力发明之前,序列建模用的是另一套方案:循环神经网络(RNN)。它的做法是从左到右读,把读过的一切压缩进一个固定大小的状态里。
RNN 的固定状态有一个无可替代的优点:不管序列多长,内存都不变。但它当年被抛弃,是因为两个致命伤:
- 记忆被冲淡:状态就那么大,读到第 1000 个词时,第 1 个词的信息早被后来的内容挤掉了。
- 不能并行:必须先算完第 999 步才能算第 1000 步。而 GPU 的全部威力都建立在「同时算很多东西」上。
为什么现在又回去了
因为形势变了。当年序列长度是几百,注意力的平方复杂度不算什么,能并行才是关键。现在序列长度是一百万,注意力的两个代价开始主导一切,而 RNN 的固定状态突然变成了压倒性的优势。
于是问题变成:能不能保留固定状态的内存优势,同时修好那两个致命伤?
「记忆被冲淡」要靠更聪明的写入规则来解决——这就是 delta 规则。「不能并行」要靠数学重写来解决——这就是 分块并行形式。KDA 是这两条路走到当下的产物。
线性注意力(linear attention):一类把 softmax 去掉、从而可以改写成「固定大小状态 + 递推更新」的注意力变体。KDA 属于这一类,它是 Kimi Linear 那篇工作的延续。第0章的 softmax 那一节你算过:softmax 里的指数让每个 query 必须亲自和每个 key 打分,无法提前汇总。去掉 softmax,汇总就成为可能——这是整个线性注意力家族的数学起点。
2.2 状态是一块黑板:怎么「写」和「读」
先把 KDA 的状态 S 想象成一块黑板。它是一个矩阵,形状是 dk × dv——第0章讲过,矩阵就是一个数字表格。
怎么往黑板上写一个「键 → 值」的配对? 答案是外积:
第0章提过,一个竖向量乘一个横向量得到一个矩阵。kv⊤ 就是这么一个矩阵——它可以理解为「在 k 指向的那个方向上,刻下 v 这个内容」。
怎么从黑板上读? 用查询向量去乘它:
为什么这样能读出来?做一次代数就明白了。假设黑板上只写过一对 (k, v),那么:
注意 k⊤q 是一个数(两个向量的点积,也就是它们的相似度)。所以读出来的是「v 乘以一个相似度」。
打个比方
注意力像一个档案柜:每份文件单独放一个抽屉,查的时候一个个翻,永远精确,但抽屉数量随文件数增长。
KDA 的状态像一张反复叠印的透明胶片:所有内容印在同一张胶片上,胶片大小恒定。用对了「滤镜」(查询向量)就能把某一层内容提出来,但印得多了就会互相干扰。
类比失效处:胶片叠印是纯粹的相加、无法撤销;而 KDA 的核心创新恰恰是它能撤销——下一节的 delta 规则就是在写新内容之前,先把旧内容擦掉。这正是它比朴素线性注意力强的地方。
2.3 delta 规则:写之前先擦
朴素写入 S ← S + kv⊤ 有个明显的毛病:如果同一个键被写了两次,两个值会叠在一起。
假设「首都」这个键先被写成「北京」,后来上下文变了要改成「东京」。朴素写入的结果是黑板上同时有「北京」和「东京」,读出来是两者的混合——一团糨糊。这正是 RNN 「记忆被冲淡」的数学本质。
delta 规则的想法很直接:写新的之前,先把这个键上原有的内容擦掉。
关键在那个 (I − βtktkt⊤)。它长得吓人,但意思很简单。让我们看看它作用在旧状态上会发生什么。
自己推一遍:那个括号在干什么
先看 I St−1。I 是单位矩阵,乘它等于什么都没做。所以第一项保留了什么?
想好了再看
完整保留了旧状态。如果括号里只有 I,那这就退化成了上一节的朴素写入。
再看被减掉的那一项:βtktkt⊤St−1。先算最右边两项 kt⊤St−1,它是什么?(提示:回忆上一节的读取公式)
想好了再看
它就是「用当前的键 kt 去黑板上读一次」,读出来的是旧状态里已经存在这个键上的值。记它为 v旧⊤。
这一步是整个 delta 规则的灵魂:要擦掉旧内容,得先知道旧内容是什么,而知道的办法就是读一次。于是被减掉的那一项变成了 βtktv旧⊤。这个形式你在上一节见过。它是什么?
想好了再看
它正是「把 (kt, v旧) 这一对写进去」的那个外积。而现在它前面带了个负号——所以是把它写出去,也就是擦除。
现在把整条式子读成一句人话。
想好了再看
「用 kt 读一下旧值,按强度 βt 把旧值擦掉,再按同样的强度写入新值 vt。」
换个写法你会看得更清楚。把式子整理一下:
St = St−1 + βtkt(vt − v旧)⊤
这就是「delta」这个名字的来源——更新量正比于「新值与旧值之差」。如果新值和旧值一样,状态根本不变(差为零);差得越多,改动越大。这和最早的感知机学习规则、和梯度下降的形式是同源的。最后:βt 取 0 会怎样?取 1 会怎样?
想好了再看
βt = 0:括号变成 I,写入项也为零,状态完全不变——这个 token 什么都没写。
βt = 1:旧值被完全擦掉,新值完全写入——彻底覆盖。
中间值就是部分覆盖。所以论文把 βt 叫做「delta 规则的写入强度」(write strength),并把它限制在 (0,1) 里,正是因为这个区间对应「从不写到全写」。
注意 βt 是模型自己算出来的(式 2 里 βt = Sigmoid(Wβxt))。也就是说,模型能逐 token 地决定「这个信息值不值得覆盖已有的记忆」。
上面推导出 St = St−1 + βtkt(vt − v旧)⊤,其中 v旧⊤ = kt⊤St−1。
论文式 2 说 q 和 k 都经过了 L2 归一化(把向量的长度缩放到 1)。请解释:如果不做这个归一化,delta 规则的擦除会出什么问题?
接着上面展开:(I − kk⊤)kv⊤ = kv⊤(1 − ‖k‖²)。
· 若 ‖k‖ = 1:结果是 0。擦得干干净净,正是我们想要的。
· 若 ‖k‖ = 0.5:剩下 0.75 kv⊤。擦不干净,旧记忆残留。
· 若 ‖k‖ = 2:剩下 −3kv⊤。擦过头了,而且符号翻转、幅值放大到三倍。
最后这种情况是灾难性的:每次「擦除」反而把内容放大并变号,逐步递推下去状态会指数爆炸。
所以 L2 归一化不是一个可有可无的工程细节,而是让 delta 规则的语义成立的必要条件:只有 ‖k‖ = 1 时,(I − βkk⊤) 才是一个真正的「在 k 方向上按比例 β 投影掉」的算子,其特征值恰好落在 [1−β, 1] 里,永远不会放大。
这道题想让你获得的习惯:论文里那些看起来像「顺手加的正则化」的操作(L2Norm、RMSNorm、Sigmoid 的值域),往往是某个数学性质成立的前提。读到它们时,多问一句「如果去掉会怎样」。这一章后面的下界衰减,是同一个道理的更极端版本。
变式:论文式 2 里 q 也做了 L2 归一化。q 只用于读取(o = S⊤q),不参与擦除。那么归一化 q 的理由和归一化 k 一样吗?如果不一样,你猜是什么?(提示:想想读出来的 o 的幅值会怎样,以及它接下来要送进什么。)
2.4 加上遗忘:式 1 的完整形态
delta 规则解决了「同一个键被反复覆盖」的问题。但还有一个问题它解决不了:那些再也用不到的旧信息,怎么慢慢淡出?
比如你读一篇长文档,第一章的细节到第十章基本就不需要了。delta 规则只会在「同一个键被重写」时才擦,从没被重写的旧内容会永远占着黑板。
解法是加一个遗忘门:每走一步,就让整块黑板按一定比例淡化一点。这就得到了论文的式 1:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| St | 第 t 步的状态矩阵,形状 dk × dv | 黑板。不管序列多长,大小恒定 |
| kt, qt | 键、查询向量,dk 维,都做了 L2 归一化 | 「往哪写」 / 「从哪读」 |
| vt | 值向量,dv 维 | 「写什么内容」 |
| βt ∈ (0,1) | 标量,delta 规则的写入强度 | 「这个信息值不值得覆盖旧记忆」,模型逐 token 决定 |
| αt ∈ (0,1)dk | 向量,通道级的单步保留因子 | 「黑板的每一行各自淡化多少」。注意它是向量不是标量——这是「通道级」的含义 |
| Diag(αt) | 把 αt 摆到对角线上的方阵 | 乘上去的效果就是「逐行缩放」 |
| ōt | 这一步读出来的原始输出 | 上面那一横表示「还没过输出门」,式 6 会处理它 |
为什么 α 是向量而不是一个数(这是 KDA 相对前代的核心改进之一)
如果 α 是一个标量,那么「遗忘」就是整块黑板整体变淡——所有信息以同样的速度淡出。
但真实的记忆不是这样。有些信息(比如「这段代码用的是 Python」)应该长期保留,有些(比如「上一行的变量名」)应该很快淡出。α 是 dk 维的向量,意味着黑板的每一行可以有自己的遗忘速度,而且这个速度是模型根据当前 token 现算的。
论文称之为通道级遗忘门(channel-wise forget gate),并明确说 KDA 的定位就是「用通道级遗忘门扩展 delta 规则递推」。这是 KDA 相对更早的 DeltaNet 的关键增量。
常见误解:α 和 β 是一回事
它们完全不同,别混。
αt 是向量,管的是「旧的东西留多少」,作用在整块黑板上,逐通道生效。它是被动的时间衰减。
βt 是标量,管的是「新的东西写多重」,只作用在 kt 指向的那一个方向上。它是主动的定点覆盖。
一个是「整体褪色」,一个是「局部涂改」。式 1 里两者的作用顺序也很重要:先褪色(Diag(α) 乘进去),再涂改(delta 项)。这个顺序在第10章的上下文并行里会变成一个大麻烦,你到时会明白为什么。
(a) 用一句人话把式 1 完整读出来。
(b) 假设 dk = dv = 128,每个数用 2 字节存。一个注意力头的状态 S 占多少字节?
(c) 对比第0章那道题里 KV 缓存的算法,说清这两者随序列长度增长的行为有什么本质区别。
(a) 「先让整块黑板按每行各自的比例 αt 淡化一点;再用当前的键 kt 读出这个键上的旧值、按强度 βt 擦掉它;然后按同样的强度把新值 vt 写上去。最后用查询 qt 从更新后的黑板上读一次,得到输出。」
(b) 128 × 128 × 2 = 32,768 字节 = 32 KB。
(c) 本质区别是:一个是常数,一个是线性函数。
· KDA 状态:序列长 1000 时是 32 KB,长 100 万时还是 32 KB。序列长度根本不出现在内存公式里。
· KV 缓存:第0章算过,每层每 token 512 字节,93 层就是 46.5 KB 每个 token。100 万 token 是 44.3 GB。
换算成同一个尺度:如果 93 层全是 KDA,总状态是 93 × 32 KB ≈ 3 MB,和 44.3 GB 相差约一万五千倍。
这就是 K3 敢做 100 万上下文的底气。但请立刻警惕:相差一万五千倍的东西,不可能是免费的。KDA 用一块 32 KB 的黑板去装 100 万 token 的信息,必然有大量信息被压掉了。这正是 K3 不敢全用 KDA、每 3 层要配 1 层完整 MLA 的原因——下一章的主题。
变式:K3 有 69 个 KDA 层和 24 个 MLA 层。假设 KDA 头的状态如上是 32 KB/层,MLA 层每 token 每层占 512 字节。(a) 请写出「总缓存大小」关于序列长度 n 的表达式。(b) 在 n 等于多少时,两部分的大小相等?(c) 这个数字告诉你,KDA 的收益在什么长度以下其实微不足道?
2.5 式 2:这些 q、k、v、α、β 是从哪来的
式 1 里的每个量都要从 token 向量 xt 算出来。论文式 2 给了具体做法(h 是头的编号):
vth = Swish( ShortConv( Wvhxt ) )
βth = Sigmoid( Wβhxt )
zth = Wα↑Wα↓xt + bαh
| 成分 | 是什么 | 为什么在这 |
|---|---|---|
| Wxt | 矩阵乘向量(第0章) | 标准做法:从 token 向量投影出所需的量 |
| ShortConv | 短卷积:把相邻几个位置的值做一次加权混合 | 让每个位置能「瞥一眼」紧邻的几个 token,补充局部信息 |
| Swish | 激活函数(第0章表格) | 引入非线性 |
| L2Norm | 把向量长度缩放到 1 | 只对 q、k 做。上一题证明过:这是 delta 规则语义成立的前提 |
| Sigmoid | 压到 (0,1) | 保证 β 落在「从不写到全写」的合法区间 |
| Wα↑Wα↓ | 低秩投影:先降到很低维再升回来 | 省参数。第0章那道变式题算过这个技巧 |
| bαh | 每个头自己的偏置 | 让不同的头有不同的「默认遗忘倾向」 |
| zth | 衰减 logit,还不是 α 本身 | 注意:它到 α 的映射被推迟了,因为这个映射的形式取决于分块并行的数值细节。这是 2.7 节的主题 |
注意最后一行留下的悬念
论文在式 2 后面明确写道:从 zth 到 αth 的「下界映射」是在分块形式给出之后才引入的。
这个安排本身就在告诉你一件事:这个映射的形式不是从建模需要推出来的,而是从数值精度需要推出来的。论文把它放在后面,是因为不先讲分块形式,你根本看不懂为什么要那么改。我们也照这个顺序走。
2.6 分块并行形式:让串行的东西跑在 GPU 上
现在面对 RNN 的第二个致命伤:式 1 是严格串行的。St 依赖 St−1,一百万个 token 就要一百万步,GPU 完全用不上。
解法叫分块并行形式(chunkwise parallel form),思路是一个漂亮的折中:
为什么块内可以并行——关键的数学洞察
块内第 5 个 token 的状态明明依赖第 4 个,怎么可能并行?
诀窍在于把「依赖传入状态的部分」和「块内自己产生的部分」拆开。块内任何一个位置的输出,都可以写成两部分之和:
① 从块外传进来的状态贡献的部分——对块内所有位置来说,传入状态 S[t] 是同一个,所以这部分可以用一次矩阵乘法一起算完。
② 块内 token 之间互相贡献的部分——这部分只涉及块内的 Q, K, V,可以写成一个矩阵乘法加一个下三角掩码,也是一次算完。
这就是式 4 的两项。而「块与块之间的串行」只剩下「把状态从一块传到下一块」这一件事。
要写出这个形式,先需要一个记号。论文式 3 定义累积衰减:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| γi→j | 从第 i 步到第 j 步的所有 α 连乘 | 「经过这么多步之后,还剩多少没被遗忘」 |
| γr | = γ1→r,从块首到第 r 步的累积衰减 | 块内位置 r 相对于块首的衰减量 |
| Γ[t]1→C | 把 γ1…γC 按行摞起来的矩阵,C × dk | 整个块的衰减表 |
有了它,再加上一个叫 UT 变换的技巧产出的 U[t] 和 W[t],定义伪值项 Ṽ[t] := U[t] − W[t]S[t],块内所有输出就可以并行算出来:
O[t] = (Γ[t]1→C ⊙ Q[t])S[t] + A[t]Ṽ[t]
└──── 块间(inter-chunk)────┘ └─ 块内(intra-chunk)─┘
这条式子不需要你能自己推出来(论文自己也把完整推导推给了 Kimi Linear 那篇),但你需要读懂它在说什么:
| 部分 | 是什么 | 直觉 |
|---|---|---|
| Tril[·] | 把严格上三角的元素全置零,保留下三角含对角线 | 因果掩码:位置 i 只能看见 j ≤ i。对角线要保留,因为每个输出读的是「当前 token 更新之后」的状态 |
| Q ⊙ Γ | 查询按累积衰减缩放 | 位置越靠后,它看块首方向的东西时衰减得越多 |
| K / Γ | 键除以累积衰减 | 这个除法是全章最大的麻烦制造者,下一节的主角 |
| 第一项 | 块间:传入状态 S[t] 对块内所有位置的贡献 | 「从上一块继承来的记忆」 |
| 第二项 | 块内:块内 token 之间的相互作用 | 「这一块自己内部发生的事」 |
打个比方
像记账。块间项是「上个月结转过来的余额」,对本月所有交易来说是同一个数字。块内项是「本月内部发生的所有收支」,只需要看本月的账本。月末把两者一加,就是本月结束时的余额,再结转给下个月。
「月与月之间必须按顺序结转」(串行),但「一个月内部的几千笔交易可以同时录入」(并行)。分块并行就是这个意思。
类比失效处:记账里的结转是简单加法,而 KDA 的结转要经过 Diag(α) 缩放和 delta 规则的矩阵作用——它不是加法,而是一个依赖 token 的矩阵变换。这个区别看起来微小,但它在第10章会直接导致「上下文并行不能用简单求和」这个大问题。请记住这个伏笔。
分块并行里,块大小 C 是一个可调的参数。
(a) C 取 1 会退化成什么?
(b) C 取等于整个序列长度会退化成什么?
(c) 那么 C 变大时,什么变好、什么变坏?请至少指出两个相反方向的效应。
(a) C = 1:退化成完全串行的式 1,每步一个 token,GPU 闲置。
(b) C = n:退化成完全并行的形式,此时块内项 A 是一个 n × n 的矩阵——注意,这正是标准注意力的形状和计算量,平方复杂度回来了。
(c) 三组相反的效应:
· 串行步数 = n/C,C 越大越好;
· 块内计算量 = nC,C 越大越差(趋向平方复杂度);
· 片上内存:块内那个 C × C 矩阵和相关的 Q, K, V 块要能塞进 GPU 的快速片上缓存,C 太大就塞不下,被迫回主显存,反而更慢。
所以 C 是在「串行深度」和「平方复杂度」之间取的一个平衡点,最优值由硬件(片上缓存大小、Tensor Core 的形状偏好)决定,而不是由数学决定。
论文没有告诉你 K3 的 C 取多少——它只在讲下界衰减时提到把每个块再划分成「次级的 16-token 小片」,从这里能推断 C 是 16 的倍数且明显大于 16,但具体值未披露。这是这一节一个真实的信息缺口。
变式:论文说 Tril 保留了对角线,理由是「每个输出读的是当前 token 更新之后的状态」。请构造一个反例说明:如果错误地把对角线也置零(即位置 i 只能看 j < i),模型会失去什么能力?(提示:想想一个 token 想读取「它自己刚写进去的东西」的场景,比如复制任务。)
2.7 下界衰减:一个溢出问题如何改写了数学
这一节是全章最精彩的地方,因为它展示了一件在论文里很少被讲得这么透的事:硬件的数值精度限制,可以反过来决定模型的数学形式。
问题:那个除法会爆炸
回看式 4 里的 K[t] / Γ[t]1→C——键要除以累积衰减。
而 Γ 是一串 (0,1) 之间的数连乘。连乘会发生什么?
BF16:训练大模型最常用的数字格式,每个数占 2 字节。它能表示的最大值约为 3.4 × 1038。超过就变成「无穷大」,计算全毁。
论文原文的说法是:「因为 Γ 是一串 (0,1) 中保留因子的乘积,这个倒数可以无界增长,在有限精度下溢出。」
前代的解法,以及它留下的疙瘩
Kimi Linear(KDA 的前身)用两招控制这个数值范围:
- 在对数空间计算相对衰减——连乘变成连加,动态范围大幅压缩。
- 把每个块再划分成 16 个 token 的次级小片(tile)——只在小片内部算相对衰减,跨小片的部分另外处理。
这两招之后,非对角的小片可以直接用稠密矩阵乘法在 Tensor Core 上算。但对角线上的小片不行——论文原话是:对角小片「仍然需要显式的位置对计算,这仍是块内的主要瓶颈」。
Tensor Core:现代 GPU 里专门做矩阵乘法的硬件单元,比通用计算单元快好几倍。但它只吃「标准的稠密矩阵乘法」这一种形式。任何需要「逐个位置对单独处理」的计算都用不上它,只能退回慢得多的通用路径。
为什么偏偏是对角小片有问题
因果掩码的缘故。非对角小片里,所有的 (查询位置, 键位置) 组合都满足 j < i,掩码全通过,是一个完整的稠密矩阵乘法。
而对角小片横跨了掩码的边界——它内部一半元素要保留、一半要置零,而且每一对位置的相对衰减都不一样。这就没法写成一次干净的矩阵乘法,只能一对一对地算。
K3 的解法:换掉衰减函数本身
这里是关键的思维转折。前人的思路是「数会爆炸,那我想办法在计算上绕开」。K3 的思路是「数会爆炸,那我从一开始就不让它爆炸」。
回忆 2.5 节留下的悬念:从衰减 logit z 到保留因子 α 的映射被推迟了。现在揭晓这个映射:
αth = exp( gth ) ∈ (egmin, 1)dk
| 符号 | 是什么 | 直觉 |
|---|---|---|
| gth | 每步的对数衰减(log-decay) | 因为 α < 1,所以 log α < 0。g 就是这个负数 |
| gmin | 固定为 −5,不学习 | 对数衰减的下界。这个数字是整个设计的核心 |
| Sigmoid(·) | 压到 (0,1) | 于是 gmin·Sigmoid(·) 一定落在 (−5, 0) 里,不可能更负 |
| Ah | 每个头一个可学习的对数尺度,初始化为 0 | 让不同的头能自己调整衰减曲线的陡峭程度 |
| eAh | 指数保证这个尺度恒正 | 不会意外把曲线方向翻转 |
对比一下前代的做法。Kimi Linear(以及 GDN、Mamba-2)用的是负 softplus 映射:
差别只有一个字:值域。前代是 (−∞, 0),K3 是 (−5, 0)。前代无下界,K3 有下界。这就是「下界衰减」(lower-bounded decay)这个名字的来源。
为什么 −5 这个数字刚刚好
论文给出的推理链条非常干净,值得完整走一遍:
自己推一遍:−5 是怎么算出来的
既然 g > −5,那么每一步的保留因子 α = eg 至少是多少?
想好了再看
α > e−5 ≈ 6.7 × 10−3,也就是至少 0.67%。论文原文正是这个数。
换句话说,无论模型多想遗忘,它一步之内最多只能忘掉 99.33%,不可能忘干净。现在看一个 16-token 的小片。在最坏情况下(每一步都用最大的遗忘),跨越这个小片的累积对数衰减是多少?
想好了再看
对数衰减是可加的:16 步各 −5,累积就是 16 × (−5) = −80。
所以累积对数衰减一定落在 (−80, 0) 这个区间里。论文原文:「跨一个 16-token 小片的累积对数衰减落在 (−80, 0)。」那么式 4 里那个要命的倒数缩放因子 1/Γ,最大是多少?
想好了再看
1/Γ = 1/e−80 = e80。论文原文:「对应的倒数缩放因子因此小于 e80。」
最后一步:e80 大概是多少?BF16 能装下吗?
想好了再看
e80 ≈ 5.5 × 1034。BF16 的最大值约 3.4 × 1038。
装得下,而且还留了大约四个数量级的余量。论文原文:「保持在 BF16 的动态范围内。」
现在你明白 gmin = −5 这个数字是怎么来的了:它不是调出来的经验值,而是从「16-token 小片」和「BF16 动态范围」这两个硬件事实反推出来的。如果小片改成 32 token,或者换成 FP16(最大值只有 65504),这个 −5 就得跟着变。
打个比方
前代的做法像是:允许员工请任意长的假,然后为「请了超长假的人」专门建一套复杂的复职流程。K3 的做法是:规定假期最长不超过五天,于是所有人走同一条简单流程就行了,那套复杂流程可以整个删掉。
限制表达力,换来的是整条代码路径的消失。
类比失效处:这个比方会让你觉得「限制」是纯粹的损失。但论文指出,这种有下界的递推门在先前的工作里已经出现过(论文引用了三篇),也就是说它可能本身就有建模上的好处(防止信息被彻底抹掉)。论文没有做消融来区分「下界带来的建模收益」和「下界带来的效率收益」各占多少——这是一个真实的信息缺口。
读的时候要小心
论文说这个新参数化「与先前工作里有下界的递推门密切相关」,但没有给出任何对比实验:没有「换成下界衰减前后的验证损失曲线」,也没有「对角路径消除后的实测加速比」。
所以这一节你能确信的是:这个设计在数值上是自洽的(−5 的推导是硬的),以及它在原理上消除了一条慢路径。至于它对最终模型质量的净影响是正是负、幅度多大,这篇报告没有回答。
下界衰减保证了 α > e−5 ≈ 0.0067,即「一步最多忘掉 99.33%」。请构造一个具体的任务场景,说明这个下界会损害模型的能力。你的场景需要说清:模型本应做什么、下界让它做不到什么、以及这个损害在实践中有多严重。
场景:多段独立上下文的隔离。假设输入里有 50 段互不相关的文档,每段开头都定义了同名但不同值的变量(第 1 段「设 x = 3」,第 2 段「设 x = 7」,……)。理想情况下,模型处理第 k 段时应该完全忘掉前 k−1 段里 x 的值。
下界让它做不到「完全」。但严不严重需要算一下——这才是这道题真正想训练的能力:
假设两段之间隔了 n 个 token,模型全程用最大遗忘。残留比例是 (e−5)n = e−5n。
· n = 1:残留 0.67%
· n = 3:残留 3 × 10−7
· n = 10:残留 2 × 10−22——远低于 BF16 能表示的精度,实际上就是 0
所以结论是:这个「损害」在实践中几乎不存在。只要两段信息之间隔了十几个 token,下界衰减和无下界衰减的效果就没有可测量的区别。真正被限制掉的只有「在一步之内彻底清零」这一种极端行为。
这道题的意义有两层。第一层:学会把「理论上的限制」和「实践中的影响」分开算——很多看起来严重的限制,算一下发现是零。第二层,也更重要:这个计算反过来解释了为什么这个设计是划算的——它放弃的是一个几乎用不上的能力(单步清零),换来的是一整条慢代码路径的消失。
如果你在第 4 级提示之前就自己想到了「要算残留量」,那你已经具备了评估这类工程权衡的核心能力。
变式:现在反过来。假设有人把 gmin 从 −5 改成 −20,理由是「给模型更大的遗忘自由度」。(a) 跨 16-token 小片的累积对数衰减范围变成多少?(b) 倒数缩放因子最大是多少?(c) BF16 还装得下吗?(d) 如果装不下,作者有哪两条路可选,各自的代价是什么?
2.8 满秩输出门:最后一道闸
式 1 算出的 ōt 还不是这一层的最终输出。KDA 在它后面加了一道门:
| 成分 | 是什么 | 为什么在这 |
|---|---|---|
| RMSNorm(ōt) | 对循环输出做逐头的归一化(第0章 0.11 节) | 循环状态读出来的东西幅值可能乱跳,先拉回标准尺度 |
| Sigmoid(Wgxt) | 由当前 token 算出的门,每个通道一个 0–1 的值 | 让每个 token 自己决定「从黑板上读到的这些通道,各放行多少」 |
| ⊙ | 逐元素相乘 | 门作用在归一化后的输出上 |
| Wo | 输出投影 | 把结果映射回模型的主维度 d |
「满秩」这个词在强调什么
论文特别说明:Kimi Linear 用的是低秩参数化的输出门,K3 改成了输入依赖的满秩投影。
低秩的意思是(回忆式 2 里的 Wα↑Wα↓):先把 x 压到一个很低的维度,再升回来。这省参数,但也限制了门能表达的模式——如果中间维度是 64,那么门的所有可能取值都被困在一个 64 维的子空间里,无法为每个通道独立设定。
满秩就是不做这个压缩,Wg 直接是全尺寸矩阵,门的每个通道可以完全独立。代价是参数变多。K3 在这里选择了花参数买表达力。
这是本章的综合题。KDA 里有五个设计选择,请你说清每一个是为了解决什么问题,以及它引入了什么新问题(如果有)。
① 用固定大小的状态矩阵代替 KV 缓存
② delta 规则的擦除项 (I − βkk⊤)
③ 通道级(向量)而非标量的衰减 α
④ 分块并行形式
⑤ 下界衰减 gmin = −5
① 固定大小状态。 解决:KV 缓存随长度线性增长、100 万 token 下吃掉几十 GB 显存。代价:状态容量有限,信息被压缩(记忆冲淡);且递推严格串行,与 GPU 的并行偏好冲突。这两个代价分别由 ② ③ 和 ④ 来处理。
② delta 擦除项。 解决:同一个键被反复写入时旧值不被清除、变成一团糨糊。代价:擦除的正确性依赖 ‖k‖ = 1,所以必须加 L2 归一化(q2-1 那道题算过,不加会指数爆炸)。更深的代价:擦除项是一个依赖 token 的矩阵 Mt 作用在传入状态上,这让 KDA 的递推不再是简单可加的——第10章会告诉你,这直接导致普通线性注意力的上下文并行方法对 KDA 失效,必须专门发明 KCP。
③ 通道级衰减。 解决:标量衰减下所有信息以同一速度淡出,无法区分「该长期记住的」和「该马上忘掉的」。代价:α 从一个数变成 dk 维向量,累积衰减 Γ 也随之变成矩阵,让式 4 里的缩放变得更麻烦。
④ 分块并行形式。 解决:串行导致 GPU 闲置。代价:式 4 需要用 1/Γ 对键做重缩放,而 Γ 是一串小于 1 的数连乘,倒数会无界增长并溢出。
⑤ 下界衰减。 解决:④ 留下的溢出问题。做法是从源头限制 α > e−5,使 1/Γ < e80,落在 BF16 范围内。净收益:对角小片不再需要单独的位置对计算路径,全部可用 Tensor Core,块内瓶颈消失。代价:模型失去「单步彻底清零」的能力——但 q2-4 算过,这个能力在隔了十几个 token 之后就无关紧要了。
这条链的形状值得记住:①的代价 → ②③④来救 → ④又制造新代价 → ⑤来救。每一个「优雅的设计」都是上一个设计留下的债务的偿还。这正是读工程性论文最该培养的视角:不要孤立地欣赏某个技巧,要看它在这条债务链上的位置。
而这条链还没有结束——② 留下的「不可加性」这笔债,要到第10章的 KCP 才还;固定状态在推测解码时「无法回滚」这笔债,要到第11章才还。KDA 是全站唯一一个横跨架构、训练、推理三层的概念,原因就在这里。
变式:现在做一个设计决策。假设你要把 KDA 用在一个上下文只有 4K token 的小模型上。请逐条判断:① 到 ⑤ 这五个设计里,哪些依然必要、哪些可以简化甚至去掉?对每一个「可以去掉」的,说清你的依据是什么(提示:先算 4K 上下文下 KV 缓存到底有多大,再决定第一步的交易还划不划算)。
答辩:如果我是审稿人
你花了一整章论证 KDA 用固定大小状态换来了巨大的内存优势。那我问你:既然一块 32 KB 的黑板要装下 100 万 token 的信息,压缩比是天文数字,凭什么相信它没丢掉关键信息?如果它真的丢了很多,那 K3 在长上下文任务上的好成绩,会不会其实全是那 24 层 MLA 的功劳,而 69 层 KDA 只是在「不添乱」?请你为 KDA 辩护。
参考防守(先自己组织语言再看)
首先必须承认:这个攻击在本篇报告的证据范围内无法被驳倒。论文没有做任何「改变 KDA:MLA 比例」的消融实验,没有「全 MLA 对照组」,也没有报告长上下文检索类任务(比如大海捞针)的分数。所以「69 层 KDA 的实际贡献有多大」这个问题,这篇报告给不出答案。这是一个真实的、值得指出的证据缺口。
但辩护有三层,从弱到强:
第一层(最弱):混合结构本身就是对这个担忧的承认。如果作者相信 KDA 能独立扛下全部工作,就不会每 3 层配 1 层 MLA、还在骨干末尾额外补一层。这个 3:1 的比例本身就在说:「我们知道固定状态会丢信息,所以留了完整注意力的通道。」问题只是这个比例对不对,而不是「KDA 有没有丢信息」。
第二层:压缩比这个直觉本身有误导性。「32 KB 装 100 万 token」听起来荒谬,但这个算法有偏差。首先每层都有自己的状态,69 层就是 69 块黑板,而且它们处在不同的表示层级上,存的不是同一类信息。其次,KDA 的状态存的不是 token 本身,而是「键→值」的关联;很多 token 携带的信息是高度冗余或短程的(虚词、格式、局部语法),本来就不需要长期保留。更关键的是,delta 规则和通道级衰减的全部意义,就是让模型自己决定什么该留——这不是均匀压缩,而是学出来的选择性保留。
第三层(最强,但也是双刃的):分工假说是可检验的,而论文给了一个间接证据。论文说所有 MLA 层用 NoPE(不加位置编码),理由是「KDA 层提供位置敏感、近因感知的混合」。这句话意味着:K3 的位置信息完全依赖 KDA。如果 KDA 只是在「不添乱」,那 24 层无位置感的 MLA 加上一堆无用的 KDA,模型根本不可能知道词序——而它显然知道。所以 KDA 至少承担了一项无可替代的功能。
但这一层辩护同时暴露了一个新问题,也是我认为这篇报告最该被追问的地方:既然位置信息全靠 KDA 的隐式衰减来编码,那么 69:24 这个比例、以及 MLA 的比例上限,就不只是效率问题,而是能力问题。论文对此没有任何讨论,也没有消融。如果有人想把 KDA 换成别的线性注意力,或者调整比例,这篇报告没有提供任何指导。
诚实的结论:KDA 承担了实质功能(至少是位置编码),混合结构说明作者也知道它不够用,但「69 层 KDA 相对于更多 MLA 的边际贡献」在本报告中无法确定。
本章小结
KDA 做的是一笔交易:把「每个 token 一份、永远增长的 KV 缓存」,换成「一块固定大小、原地改写的黑板」。内存从随长度线性增长变成常数,代价是信息被压缩、递推变成串行。
为了让这笔交易划算,KDA 叠了四层补救:delta 规则让写入能擦除旧值(代价是必须 L2 归一化,且递推不再可加);通道级衰减让每一行有自己的遗忘速度;分块并行把串行压缩了三个数量级(代价是引入了会溢出的倒数缩放);下界衰减从源头堵住溢出,顺带消掉了对角小片那条慢路径。最后用满秩输出门让每个 token 自己决定读到的东西放行多少。
下一章:既然 KDA 这么省,为什么还要留 24 层完整的 MLA?两种记忆各自补什么?
Chapter 2 · Kimi Delta Attention: compressing a memory that grows without bound onto one fixed-size blackboard
This is the most central and the hardest chapter on this site. KDA is the foundation of the K3 architecture — 69 of the 93 layers are KDA, and the kernels and parallelism of Chapter 10 and the prefix cache of Chapter 11 all exist to clean up after it. This chapter sets out from the plainest question there is, “how should memory be modelled”, and walks step by step to Equations 1 through 6 of the paper. Read slowly, and do the questions at the end of every section.
After this chapter you should be able to
- Explain why “a fixed-size state” and “a KV cache that grows with length” are a fundamental trade, and what each one buys
- Use the outer product and matrix multiplication to say exactly how a state matrix S “stores” and “retrieves” key-value pairs
- Say exactly what that (I − βkk⊤) in the delta rule is doing, and why it has to be there
- Read Equation 1 symbol by symbol, and explain the division of labour between α and β
- Explain why the chunkwise parallel form lets “something serial” run on a GPU, and what the two terms of Equation 4 are
- Say exactly how one numerical overflow problem ended up forcing the authors to change the mathematical form of the decay function
2.1 Starting point: the curse of attention, and an old approach that was given up
Chapter 0 covered the two costs of attention: compute grows with the square of the length, and the KV cache grows linearly with the length and never shrinks. At the scale of 1M tokens the second cost is especially deadly — you did that exercise, and one copy of the cache alone can eat thirty percent of the memory on a top-end graphics card.
But before attention was invented, sequence modelling used a different approach: the recurrent neural network (RNN). It reads left to right and compresses everything it has read into a state of fixed size.
The RNN's fixed state has one irreplaceable advantage: however long the sequence, the memory does not change. It was abandoned back then because of two fatal weaknesses:
- The memory gets diluted: the state is only so big, and by the time you reach word 1000, the information from word 1 has long been squeezed out by everything that came after.
- It cannot be parallelised: you have to finish step 999 before you can compute step 1000. And the entire power of a GPU rests on “computing many things at once”.
Why it is coming back now
Because the situation changed. Back then sequences were a few hundred long, the quadratic complexity of attention was nothing, and being parallelisable was what mattered. Now sequences are a million long, the two costs of attention start to dominate everything, and the RNN's fixed state suddenly becomes an overwhelming advantage.
So the question becomes: can you keep the memory advantage of a fixed state and repair those two fatal weaknesses at the same time?
“The memory gets diluted” has to be solved with a smarter write rule — that is the delta rule. “It cannot be parallelised” has to be solved by rewriting the maths — that is the chunkwise parallel form. KDA is where those two lines of work stand today.
Linear attention: a family of attention variants that drop the softmax and can therefore be rewritten as “a fixed-size state + a recurrent update”. KDA belongs to this family; it continues the work of the Kimi Linear paper. You worked this out in the softmax section of Chapter 0: the exponential inside softmax forces every query to score against every key in person, so nothing can be aggregated in advance. Drop the softmax and aggregation becomes possible — that is the mathematical starting point of the whole linear attention family.
2.2 The state is a blackboard: how to “write” and “read”
Start by picturing KDA's state S as a blackboard. It is a matrix of shape dk × dv — Chapter 0 covered this: a matrix is just a table of numbers.
How do you write a “key → value” pair onto the blackboard? The answer is the outer product:
Chapter 0 mentioned that a column vector times a row vector gives a matrix. kv⊤ is exactly such a matrix — you can read it as “along the direction that k points in, carve in the content v”.
How do you read from the blackboard? Multiply it by the query vector:
Why does that read anything out? One pass of algebra makes it clear. Suppose only one pair (k, v) has ever been written on the blackboard. Then:
Note that k⊤q is a number (the dot product of two vectors, that is, their similarity). So what comes out is “v times a similarity”.
An analogy
Attention is like a filing cabinet: every document sits in its own drawer, you go through them one at a time when you look something up, it is always exact, but the number of drawers grows with the number of documents.
KDA's state is like a transparency printed over and over: everything is printed on the same sheet, and the sheet stays the same size. With the right “filter” (the query vector) you can lift one layer of content back out, but the more you print, the more the layers interfere.
Where the analogy breaks down: printing on a transparency is pure addition and cannot be undone, whereas KDA's core innovation is precisely that it can undo — the delta rule in the next section erases the old content before writing the new. That is exactly where it beats naive linear attention.
2.3 The delta rule: erase before you write
The naive write S ← S + kv⊤ has an obvious flaw: if the same key is written twice, the two values pile on top of each other.
Suppose the key “capital” is first written with “Beijing”, and then the context changes and it has to become “Tokyo”. With the naive write, the blackboard now holds both “Beijing” and “Tokyo”, and what you read out is a blend of the two — mush. This is exactly the mathematical content of the RNN's “memory gets diluted”.
The idea of the delta rule is direct: before writing the new content, erase what is already on that key.
Everything hinges on that (I − βtktkt⊤). It looks frightening, but the meaning is simple. Look at what happens when it acts on the old state.
Derive it yourself: what that bracket is doing
Start with I St−1. I is the identity matrix, so multiplying by it does nothing. What does the first term keep, then?
Think it through, then look
It keeps the old state in full. If the bracket held only I, this would collapse back into the naive write of the previous section.
Now the term being subtracted: βtktkt⊤St−1. Work out the rightmost two factors kt⊤St−1 first — what are they? (Hint: recall the read formula from the previous section.)
Think it through, then look
They are exactly “one read of the blackboard with the current key kt”, and what comes back is the value already stored on that key in the old state. Call it vold⊤.
This step is the soul of the whole delta rule: to erase the old content you first have to know what the old content is, and the way to know is to read it once.So the subtracted term becomes βtktvold⊤. You have seen this form in the previous section. What is it?
Think it through, then look
It is precisely the outer product that “writes the pair (kt, vold) in”. Only now it carries a minus sign — so it writes it out, which is to say it erases.
Now read the whole equation out as one plain sentence.
Think it through, then look
“Read the old value with kt, erase that old value with strength βt, then write the new value vt in at the same strength.”
Another way of writing it makes this clearer. Rearrange the equation:
St = St−1 + βtkt(vt − vold)⊤
That is where the name “delta” comes from — the size of the update is proportional to “the difference between the new value and the old value”. If the new value is the same as the old one, the state does not change at all (the difference is zero); the bigger the difference, the bigger the change. This has the same ancestry as the earliest perceptron learning rule and as gradient descent.Finally: what happens when βt is 0? What happens when it is 1?
Think it through, then look
βt = 0: the bracket becomes I and the write term is zero too, so the state does not change at all — this token wrote nothing.
βt = 1: the old value is erased completely and the new value is written completely — a full overwrite.
Values in between are partial overwrites. That is why the paper calls βt the “write strength of the delta rule” and confines it to (0,1): this interval is exactly “from writing nothing to writing everything”.
Note that βt is computed by the model itself (in Equation 2, βt = Sigmoid(Wβxt)). In other words, the model can decide token by token whether “this piece of information is worth overwriting the memory already there”.
Above you derived St = St−1 + βtkt(vt − vold)⊤, where vold⊤ = kt⊤St−1.
Equation 2 of the paper says that both q and k go through L2 normalisation (scaling the vector's length to 1). Explain: if you do not do this normalisation, what goes wrong with the delta rule's erasing?
Continuing the expansion above: (I − kk⊤)kv⊤ = kv⊤(1 − ‖k‖²).
· If ‖k‖ = 1: the result is 0. Erased clean, which is exactly what you want.
· If ‖k‖ = 0.5: 0.75 kv⊤ is left. Not erased clean, and the old memory lingers.
· If ‖k‖ = 2: −3kv⊤ is left. Over-erased, and the sign has flipped and the magnitude has been amplified threefold.
That last case is catastrophic: every “erase” amplifies the content and flips its sign instead, and step after step of the recurrence the state blows up exponentially.
So L2 normalisation is not an optional engineering detail; it is the condition under which the semantics of the delta rule hold at all: only when ‖k‖ = 1 is (I − βkk⊤) a genuine operator that “projects out the k direction in proportion β”, with eigenvalues that land exactly in [1−β, 1] and never amplify.
The habit this question wants to give you: the operations in the paper that look like “regularisation thrown in along the way” (L2Norm, RMSNorm, the range of Sigmoid) are usually the precondition for some mathematical property to hold. When you read one, ask the extra question: “what if it were taken away?” The lower-bounded decay later in this chapter is a more extreme version of the same point.
Variant: in Equation 2 of the paper, q is L2-normalised as well. q is only used for reading (o = S⊤q) and takes no part in erasing. So is the reason for normalising q the same as the reason for normalising k? If it is not, what do you guess it is? (Hint: think about what happens to the magnitude of the o you read out, and what it gets fed into next.)
2.4 Adding forgetting: the full form of Equation 1
The delta rule solves the problem of “the same key being overwritten again and again”. But there is one problem it cannot solve: how does old information that will never be used again fade out?
Say you are reading a long document: by chapter ten you basically no longer need the details from chapter one. The delta rule only erases when “the same key is rewritten”, so old content that is never rewritten occupies the blackboard forever.
The fix is to add a forget gate: at every step, fade the whole blackboard by some proportion. That gives Equation 1 of the paper:
| Symbol | What it is | Intuition |
|---|---|---|
| St | the state matrix at step t, of shape dk × dv | the blackboard. However long the sequence, its size is constant |
| kt, qt | the key and query vectors, dk-dimensional, both L2-normalised | “where to write” / “where to read from” |
| vt | the value vector, dv-dimensional | “what content to write” |
| βt ∈ (0,1) | a scalar, the write strength of the delta rule | “is this information worth overwriting the old memory”, decided by the model token by token |
| αt ∈ (0,1)dk | a vector, the channel-wise single-step retention factor | “how much each row of the blackboard fades”. Note that it is a vector, not a scalar — that is what “channel-wise” means |
| Diag(αt) | the square matrix with αt laid along the diagonal | multiplying by it amounts to “scaling row by row” |
| ōt | the raw output read out at this step | the bar on top means “it has not been through the output gate yet”; Equation 6 deals with it |
Why α is a vector and not a single number (one of KDA's core improvements over its predecessors)
If α were a scalar, “forgetting” would fade the whole blackboard as one — every piece of information fading at the same rate.
But real memory does not work like that. Some information (“this code is in Python”, say) should be kept for a long time; some (“the variable name on the previous line”) should fade fast. α is a dk-dimensional vector, which means every row of the blackboard can have its own rate of forgetting, and that rate is computed on the spot by the model from the current token.
The paper calls this a channel-wise forget gate and says explicitly that KDA is positioned as “extending the delta rule recurrence with a channel-wise forget gate”. This is the key increment of KDA over the earlier DeltaNet.
A common misreading: α and β are the same thing
They are completely different. Do not mix them up.
αt is a vector. It controls how much of the old stuff stays, acts on the whole blackboard, and takes effect channel by channel. It is passive decay over time.
βt is a scalar. It controls how hard the new stuff is written, and acts only where kt points, on that one direction. It is active overwriting of one spot.
One is “fading everything”, the other is “painting over one patch”. The order in which the two act in Equation 1 matters as well: fade first (multiply Diag(α) in), then paint over (the delta term). That order becomes a big nuisance in the context parallelism of Chapter 10, and you will understand why when you get there.
(a) Read Equation 1 out in full as one plain sentence.
(b) Suppose dk = dv = 128 and each number is stored in 2 bytes. How many bytes does the state S of one attention head take?
(c) Compare this with the way the KV cache was worked out in that question in Chapter 0, and say what is fundamentally different about how the two grow with sequence length.
(a) “First fade the whole blackboard a little, each row by its own factor αt; then read out, with the current key kt, the old value sitting on that key and erase it with strength βt; then write the new value vt in at the same strength. Finally read the updated blackboard once with the query qt to get the output.”
(b) 128 × 128 × 2 = 32,768 bytes = 32 KB.
(c) The fundamental difference: one is a constant, the other is a linear function.
· The KDA state: 32 KB at sequence length 1000, and still 32 KB at 1M. Sequence length does not appear at all in the memory formula.
· The KV cache: Chapter 0 worked it out — 512 bytes per token per layer, so 46.5 KB per token across 93 layers. At 1M tokens that is 44.3 GB.
Converted to the same scale: if all 93 layers were KDA, the total state would be 93 × 32 KB ≈ 3 MB, which differs from 44.3 GB by a factor of about fifteen thousand.
That is what gives K3 the nerve to go for a 1M context. But be wary immediately: something that differs by a factor of fifteen thousand cannot possibly be free. KDA packs the information in 1M tokens onto one 32 KB blackboard, so a great deal of information must have been squeezed out. That is exactly why K3 does not dare use KDA everywhere and pairs every 3 layers with 1 full MLA layer — the subject of the next chapter.
Variant: K3 has 69 KDA layers and 24 MLA layers. Assume a KDA head's state is 32 KB per layer as above, and an MLA layer takes 512 bytes per token per layer. (a) Write down an expression for “total cache size” in terms of the sequence length n. (b) At what value of n are the two parts equal in size? (c) What does that number tell you about the length below which KDA's benefit is in fact negligible?
2.5 Equation 2: where these q, k, v, α, β come from
Every quantity in Equation 1 has to be computed from the token vector xt. Equation 2 of the paper gives the concrete recipe (h is the head index):
vth = Swish( ShortConv( Wvhxt ) )
βth = Sigmoid( Wβhxt )
zth = Wα↑Wα↓xt + bαh
| Component | What it is | Why it is here |
|---|---|---|
| Wxt | matrix times vector (Chapter 0) | the standard move: project the quantities you need out of the token vector |
| ShortConv | a short convolution: one weighted mix of the values at a few neighbouring positions | lets each position “glance at” the few tokens right next to it, adding local information |
| Swish | an activation function (the table in Chapter 0) | introduces non-linearity |
| L2Norm | scales the vector's length to 1 | applied to q and k only. The question above proved it: this is the precondition for the semantics of the delta rule to hold |
| Sigmoid | squashes into (0,1) | keeps β inside the legal interval “from writing nothing to writing everything” |
| Wα↑Wα↓ | a low-rank projection: down to a very low dimension first, then back up | saves parameters. That variant question in Chapter 0 worked this trick out |
| bαh | each head's own bias | lets different heads have different “default tendencies to forget” |
| zth | the decay logit, not α itself yet | Note: its mapping to α has been deferred, because the form of that mapping depends on the numerical details of the chunkwise parallel form. That is the subject of section 2.7 |
Note the loose end left by the last row
Right after Equation 2 the paper states explicitly that the “lower-bounded mapping” from zth to αth is introduced only after the chunkwise form has been given.
That arrangement is itself telling you something: the form of this mapping does not follow from what the modelling needs, it follows from what numerical precision needs. The paper puts it later because without the chunkwise form first, you simply cannot see why the change has to be made. This site follows the same order.
2.6 The chunkwise parallel form: getting something serial to run on a GPU
Now for the RNN's second fatal weakness: Equation 1 is strictly serial. St depends on St−1, so a million tokens means a million steps, and the GPU is of no use at all.
The fix is called the chunkwise parallel form, and the idea is an elegant compromise:
Why the inside of a chunk can be parallelised — the key mathematical insight
The state of the 5th token in a chunk plainly depends on the 4th, so how can they possibly be parallel?
The trick is to split “the part that depends on the incoming state” from “the part the chunk produces itself”. The output at any position inside the chunk can be written as a sum of two parts:
① The part contributed by the state handed in from outside the chunk — the incoming state S[t] is the same one for every position in the chunk, so this part can be computed for all of them together with a single matrix multiplication.
② The part the tokens inside the chunk contribute to each other — this part only involves the chunk's own Q, K, V, and can be written as one matrix multiplication plus a lower-triangular mask, also computed in one go.
Those are the two terms of Equation 4. And “serial between chunks” is now down to one single thing: handing the state from one chunk to the next.
Writing this form down needs a piece of notation first. Equation 3 of the paper defines the cumulative decay:
| Symbol | What it is | Intuition |
|---|---|---|
| γi→j | from step i to step j, all the α multiplied together | “after this many steps, how much has still not been forgotten” |
| γr | = γ1→r, the cumulative decay from the head of the chunk to step r | how much position r in the chunk has decayed relative to the head of the chunk |
| Γ[t]1→C | the matrix stacking γ1…γC as rows, C × dk | the decay table for the whole chunk |
With that, plus a trick called the UT transform, which produces U[t] and W[t], define the pseudo-value term Ṽ[t] := U[t] − W[t]S[t], and every output inside the chunk can be computed in parallel:
O[t] = (Γ[t]1→C ⊙ Q[t])S[t] + A[t]Ṽ[t]
└──── inter-chunk ────┘ └─ intra-chunk ─┘
You are not required to be able to derive this equation yourself (the paper itself hands the full derivation off to the Kimi Linear paper), but you do need to read what it says:
| Part | What it is | Intuition |
|---|---|---|
| Tril[·] | sets every strictly upper-triangular element to zero, keeping the lower triangle including the diagonal | the causal mask: position i can only see j ≤ i. The diagonal has to be kept, because each output reads the state after the current token's update |
| Q ⊙ Γ | the query scaled by the cumulative decay | the later a position is, the more decay it sees when it looks towards the head of the chunk |
| K / Γ | the key divided by the cumulative decay | this division is the biggest troublemaker in the chapter, and the star of the next section |
| the first term | inter-chunk: the contribution of the incoming state S[t] to every position in the chunk | “the memory inherited from the previous chunk” |
| the second term | intra-chunk: the interaction among the tokens inside the chunk | “what happens inside this chunk itself” |
An analogy
It is like bookkeeping. The inter-chunk term is “the balance carried over from last month”, the same number for every transaction this month. The intra-chunk term is “all the income and spending inside this month”, which only needs this month's ledger. Add the two at month end and you have the closing balance for the month, which is then carried over to next month.
“Months have to be carried over in order” (serial), but “the few thousand transactions inside one month can be entered at the same time” (parallel). That is what chunkwise parallelism means.
Where the analogy breaks down: carrying over in bookkeeping is simple addition, whereas KDA's carry-over has to go through the Diag(α) scaling and the matrix action of the delta rule — it is not addition, it is a token-dependent matrix transformation. The difference looks tiny, but in Chapter 10 it leads directly to the big problem that “context parallelism cannot just add things up”. Remember this piece of foreshadowing.
In the chunkwise parallel form, the chunk size C is a tunable parameter.
(a) What does it collapse into when C is 1?
(b) What does it collapse into when C is equal to the whole sequence length?
(c) So when C gets bigger, what gets better and what gets worse? Point out at least two effects that pull in opposite directions.
(a) C = 1: it collapses into the fully serial Equation 1, one token per step, GPU idle.
(b) C = n: it collapses into the fully parallel form, where the intra-chunk term A is an n × n matrix — note that this is exactly the shape and the compute of standard attention: the quadratic complexity is back.
(c) Three opposing effects:
· serial steps = n/C, so the bigger C the better;
· intra-chunk compute = nC, so the bigger C the worse (heading towards quadratic complexity);
· on-chip memory: that C × C matrix inside the chunk and the corresponding Q, K, V blocks have to fit into the GPU's fast on-chip cache; if C is too big they do not fit, everything is forced back to main GPU memory, and it gets slower instead.
So C is a balance struck between “serial depth” and “quadratic complexity”, and the optimum is decided by the hardware (on-chip cache size, the shapes Tensor Cores prefer), not by the mathematics.
The paper does not tell you what C is in K3 — it only mentions, when discussing the lower-bounded decay, that each chunk is further divided into “secondary 16-token sub-chunks”, from which you can infer that C is a multiple of 16 and clearly larger than 16, but the actual value is not disclosed. This is a real gap in the information in this section.
Variant: the paper says Tril keeps the diagonal, on the grounds that “each output reads the state after the current token's update”. Construct a counterexample showing what ability the model would lose if the diagonal were wrongly zeroed out as well (that is, if position i could only see j < i). (Hint: think of a case where a token wants to read “what it has just written itself”, a copying task for instance.)
2.7 Lower-bounded decay: how an overflow problem rewrote the maths
This section is the best part of the chapter, because it shows something the paper is rarely this clear about: a limit on hardware numerical precision can turn around and decide the mathematical form of the model.
The problem: that division blows up
Look back at K[t] / Γ[t]1→C in equation 4 — the keys have to be divided by the cumulative decay.
And Γ is a chain of numbers in (0,1) multiplied together. What happens when you keep multiplying?
BF16: the number format used most often for training large models; each number takes 2 bytes. The largest value it can represent is about 3.4 × 1038. Go past that and the number becomes “infinity”, and the computation is wrecked.
The paper's own wording: “because Γ is a product of retention factors in (0,1), this reciprocal can grow without bound and overflow at finite precision.”
How the previous generation solved it, and the knot it left behind
Kimi Linear (KDA's predecessor) used two tricks to keep this numerical range under control:
- Compute the relative decay in log space — a running product becomes a running sum, which shrinks the dynamic range enormously.
- Split each chunk again into sub-chunks of 16 tokens (tiles) — compute the relative decay only inside a sub-chunk, and handle the part that crosses sub-chunks separately.
After these two tricks, the off-diagonal sub-chunks can be computed directly as dense matrix multiplications on Tensor Cores. The sub-chunks on the diagonal cannot — in the paper's own words, diagonal sub-chunks “still require explicit position-pair computation, which remains the main bottleneck inside a chunk”.
Tensor Core: the hardware unit inside a modern GPU built specifically for matrix multiplication, several times faster than the general-purpose units. But it eats exactly one shape of work: “a standard dense matrix multiplication”. Any computation that needs “each position pair handled on its own” cannot use it and falls back to the much slower general path.
Why it is the diagonal sub-chunks in particular that are a problem
The causal mask. Inside an off-diagonal sub-chunk every (query position, key position) pair satisfies j < i, the mask lets everything through, and you have one complete dense matrix multiplication.
A diagonal sub-chunk straddles the boundary of the mask — half of the elements inside it must be kept and half must be zeroed, and the relative decay differs for every pair of positions. That cannot be written as one clean matrix multiplication; you have to compute it pair by pair.
K3's solution: replace the decay function itself
Here is the key turn of thought. The earlier line of thinking was “the numbers blow up, so I will find a way to route around it in the computation”. K3's line of thinking is “the numbers blow up, so I will not let them blow up in the first place”.
Recall the loose end left in section 2.5: the map from the decay logit z to the retention factor α was postponed. Here is that map:
αth = exp( gth ) ∈ (egmin, 1)dk
| Symbol | What it is | Intuition |
|---|---|---|
| gth | the log-decay at each step | because α < 1, log α < 0. g is that negative number |
| gmin | fixed at −5, not learned | the lower bound on the log-decay. This number is the core of the whole design |
| Sigmoid(·) | squashes into (0,1) | so gmin·Sigmoid(·) always lands inside (−5, 0) and cannot go any more negative |
| Ah | one learnable log-scale per head, initialised to 0 | lets each head tune the steepness of its own decay curve |
| eAh | the exponential keeps this scale strictly positive | the direction of the curve cannot be flipped by accident |
Compare the previous generation. Kimi Linear (and GDN, Mamba-2) used a negative softplus map:
The difference is one thing only: the range. The previous generation had (−∞, 0), K3 has (−5, 0). The previous generation had no lower bound, K3 has one. That is where the name “lower-bounded decay” comes from.
Why −5 is exactly the right number
The chain of reasoning the paper gives is very clean, and worth walking through end to end:
Derive it yourself: where −5 comes from
Given g > −5, what is the smallest the per-step retention factor α = eg can be?
Think it through first, then look
α > e−5 ≈ 6.7 × 10−3, that is at least 0.67%. This is exactly the number in the paper.
Put another way: however badly the model wants to forget, it can forget at most 99.33% in a single step. It can never forget cleanly.Now take a 16-token sub-chunk. In the worst case (maximum forgetting at every step), what is the cumulative log-decay across that sub-chunk?
Think it through first, then look
Log-decay is additive: 16 steps of −5 each, so the total is 16 × (−5) = −80.
So the cumulative log-decay must lie inside the interval (−80, 0). The paper's own words: “the cumulative log-decay across a 16-token sub-chunk lies in (−80, 0).”Then how large can that lethal reciprocal scaling factor 1/Γ in equation 4 get?
Think it through first, then look
1/Γ = 1/e−80 = e80. The paper's own words: “the corresponding reciprocal scaling factor is therefore smaller than e80.”
Last step: roughly how big is e80? Can BF16 hold it?
Think it through first, then look
e80 ≈ 5.5 × 1034. The largest BF16 value is about 3.4 × 1038.
It fits, with roughly four orders of magnitude to spare. The paper's own words: “stays within the dynamic range of BF16.”
Now you can see where gmin = −5 came from: it is not an empirical value someone tuned, it is worked backwards from two hardware facts, the “16-token sub-chunk” and the “BF16 dynamic range”. If the sub-chunk became 32 tokens, or the format became FP16 (whose largest value is only 65504), that −5 would have to change with it.
An analogy
The earlier approach is like this: let employees take leave of any length, then build a whole complicated return-to-work procedure for “people who took extremely long leave”. K3's approach is: rule that leave can never exceed five days, so everyone goes through the same simple procedure and the complicated one can be deleted entirely.
You restrict expressiveness and get an entire code path disappearing in exchange.
Where the analogy breaks down: it makes “restriction” look like pure loss. But the paper points out that this kind of lower-bounded recurrent gate has already appeared in earlier work (the paper cites three papers), meaning it may bring modelling benefits of its own (it stops information from being wiped out completely). The paper runs no ablation separating “the modelling gain from the lower bound” from “the efficiency gain from the lower bound” — that is a real information gap.
Read this carefully
The paper says this new parameterisation is “closely related to lower-bounded recurrent gates in prior work”, but gives no comparison experiment at all: no “validation loss curves before and after switching to lower-bounded decay”, and no “measured speed-up once the diagonal path is removed”.
So what you can be sure of from this section is: this design is numerically self-consistent (the derivation of −5 is solid), and in principle it removes one slow path. Whether its net effect on final model quality is positive or negative, and how large that effect is, this report does not answer.
Lower-bounded decay guarantees α > e−5 ≈ 0.0067, i.e. “at most 99.33% forgotten in one step”. Construct a concrete task scenario in which this lower bound hurts what the model can do. Your scenario has to make clear: what the model ought to do, what the lower bound stops it from doing, and how serious that harm is in practice.
Scenario: isolating several independent contexts. Suppose the input holds 50 unrelated documents, each of which opens by defining a variable with the same name but a different value (segment 1: “let x = 3”, segment 2: “let x = 7”, …). Ideally, when the model processes segment k it should completely forget the value of x from the previous k−1 segments.
The lower bound stops it from doing “completely”. But whether that is serious takes a calculation — and that is the skill this question is really training:
Suppose n tokens separate two segments and the model forgets maximally the whole way. The surviving fraction is (e−5)n = e−5n.
· n = 1: 0.67% survives
· n = 3: 3 × 10−7 survives
· n = 10: 2 × 10−22 survives — far below the precision BF16 can represent, so in practice it is 0
So the conclusion is: this “harm” barely exists in practice. As long as a dozen or so tokens separate two pieces of information, lower-bounded decay and unbounded decay have no measurable difference. The only thing genuinely ruled out is one extreme behaviour: “zeroing something out completely within a single step”.
This question matters on two levels. First: learn to compute “the restriction in theory” and “the impact in practice” separately — plenty of restrictions that look severe turn out to be zero once you do the arithmetic. Second, and more important: the calculation explains in reverse why this design is a good deal — what it gives up is an ability that is almost never used (single-step zeroing), and what it gets back is the disappearance of an entire slow code path.
If you thought of “I should compute how much survives” on your own before hint level 4, you already have the core skill for judging engineering trade-offs of this kind.
Variant: now turn it around. Suppose someone changes gmin from −5 to −20, on the grounds that it “gives the model more freedom to forget”. (a) What does the range of cumulative log-decay across a 16-token sub-chunk become? (b) How large can the reciprocal scaling factor get? (c) Can BF16 still hold it? (d) If it cannot, which two routes are open to the authors, and what does each cost?
2.8 The full-rank output gate: the last valve
The ōt that equation 1 computes is not yet the final output of this layer. KDA puts one more gate behind it:
| Component | What it is | Why it is here |
|---|---|---|
| RMSNorm(ōt) | normalises the recurrent output per head (Chapter 0, section 0.11) | what you read out of the recurrent state can swing wildly in magnitude, so pull it back to a standard scale first |
| Sigmoid(Wgxt) | a gate computed from the current token, one value between 0 and 1 per channel | lets each token decide for itself “of these channels read off the blackboard, how much of each do I let through” |
| ⊙ | element-wise multiplication | the gate acts on the normalised output |
| Wo | the output projection | maps the result back to the model's main dimension d |
What the word “full-rank” is stressing
The paper spells this out: Kimi Linear used a low-rank parameterisation for the output gate, and K3 changed it to an input-dependent full-rank projection.
Low-rank means (recall Wα↑Wα↓ in equation 2): first squeeze x down to a very low dimension, then lift it back up. This saves parameters, but it also limits the patterns the gate can express — if the middle dimension is 64, then every possible value of the gate is trapped in a 64-dimensional subspace and cannot be set independently for each channel.
Full-rank means skipping that squeeze: Wg is simply a full-size matrix, and every channel of the gate can be completely independent. The cost is more parameters. Here K3 chose to spend parameters to buy expressiveness.
This is the chapter's synthesis question. KDA contains five design choices. For each one, state what problem it was meant to solve and what new problem it introduced (if any).
① replacing the KV cache with a fixed-size state matrix
② the delta rule's erase term (I − βkk⊤)
③ channel-level (vector) rather than scalar decay α
④ the chunked parallel form
⑤ lower-bounded decay gmin = −5
① Fixed-size state. Solves: a KV cache that grows linearly with length and eats tens of GB of VRAM at 1M tokens. Cost: the state has limited capacity, so information is compressed (memory dilution); and the recurrence is strictly serial, which fights the GPU's preference for parallel work. These two costs are handled by ② ③ and ④ respectively.
② The delta erase term. Solves: when the same key is written over and over, the old value is not cleared and everything turns to mush. Cost: the erase is only correct if ‖k‖ = 1, so L2 normalisation is mandatory (q2-1 did the arithmetic: without it you get exponential blow-up). The deeper cost: the erase term is a token-dependent matrix Mt acting on the incoming state, which makes KDA's recurrence no longer simply additive — Chapter 10 will show you that this is exactly why the ordinary context-parallel method for linear attention fails on KDA, and why KCP had to be invented specially.
③ Channel-level decay. Solves: under scalar decay all information fades at the same speed, with no way to separate “what should be remembered for a long time” from “what should be forgotten at once”. Cost: α goes from one number to a dk-dimensional vector, the cumulative decay Γ becomes a matrix along with it, and the rescaling in equation 4 gets more awkward.
④ The chunked parallel form. Solves: serial execution leaves the GPU idle. Cost: equation 4 has to rescale the keys by 1/Γ, and Γ is a running product of numbers smaller than 1, so the reciprocal grows without bound and overflows.
⑤ Lower-bounded decay. Solves: the overflow ④ left behind. It does so by constraining α > e−5 at the source, so that 1/Γ < e80 and stays inside the BF16 range. Net gain: diagonal sub-chunks no longer need a separate position-pair path, everything can run on Tensor Cores, and the bottleneck inside a chunk disappears. Cost: the model loses the ability to “zero something out completely in a single step” — but q2-4 did the arithmetic, and that ability stops mattering once a dozen or so tokens have passed.
The shape of this chain is worth remembering: ① has a cost → ② ③ ④ come to the rescue → ④ manufactures a new cost → ⑤ comes to the rescue. Every “elegant design” is a repayment of the debt left by the design before it. This is exactly the view worth cultivating when you read engineering papers: do not admire a trick in isolation, look at where it sits on this chain of debt.
And the chain is not finished — the “non-additivity” debt left by ② is only repaid by KCP in Chapter 10; the debt of a fixed state that “cannot be rolled back” during speculative decoding is only repaid in Chapter 11. KDA is the one concept on this site that spans all three layers — architecture, training and inference — and this is why.
Variant: now make a design decision. Suppose you want to use KDA in a small model whose context is only 4K tokens. Judge them one by one: of the five designs ① to ⑤, which are still necessary, which can be simplified or even dropped? For each one you would drop, say what your grounds are (hint: first work out how big the KV cache actually is at a 4K context, then decide whether the trade in step one is still worth making).
Defend it: if I were a reviewer
You have spent an entire chapter arguing that KDA trades a fixed-size state for an enormous memory advantage. So let me ask you this: if one 32 KB blackboard has to hold the information in 1M tokens, the compression ratio is astronomical — on what grounds should anyone believe it has not dropped something essential? And if it has dropped a great deal, might K3's good scores on long-context tasks in fact be entirely the doing of those 24 MLA layers, with the 69 KDA layers merely “not making a mess”? Defend KDA.
A reference defence (put it in your own words first, then look)
First you have to concede: within the evidence in this report, the attack cannot be refuted. The paper runs no ablation that “changes the KDA:MLA ratio”, has no “all-MLA control group”, and reports no scores on long-context retrieval tasks (needle-in-a-haystack, for example). So the question “how much do the 69 KDA layers actually contribute” gets no answer from this report. That is a real evidence gap, and worth pointing out.
But the defence has three levels, weakest to strongest:
Level one (weakest): the hybrid structure is itself an admission of this worry. If the authors believed KDA could carry the whole job alone, they would not pair every 3 layers with 1 MLA layer and add one more at the end of the backbone. That 3:1 ratio is already saying: “we know a fixed state loses information, so we kept a full-attention channel.” The question is only whether the ratio is right, not whether “KDA loses information”.
Level two: the compression-ratio intuition is itself misleading. “32 KB holding 1M tokens” sounds absurd, but that arithmetic is skewed. First, every layer has its own state, so 69 layers means 69 blackboards, and they sit at different levels of representation and do not store the same kind of information. Second, KDA's state does not store the tokens themselves, it stores “key→value” associations; much of what tokens carry is highly redundant or short-range (function words, formatting, local syntax) and never needed keeping for long in the first place. More important still, the whole point of the delta rule and channel-level decay is to let the model decide for itself what to keep — this is not uniform compression, it is learned selective retention.
Level three (strongest, but double-edged): the division-of-labour hypothesis is testable, and the paper hands you one indirect piece of evidence. The paper says all MLA layers use NoPE (no positional encoding), on the grounds that “the KDA layers provide the position-sensitive, recency-aware mixing”. That sentence means: K3's positional information depends entirely on KDA. If KDA were merely “not making a mess”, then 24 position-blind MLA layers plus a pile of useless KDA layers could not possibly let the model know word order — and it plainly does. So KDA carries at least one irreplaceable function.
But this level of defence exposes a new problem at the same time, and it is the place I think this report most deserves to be pressed on: if positional information is encoded entirely by KDA's implicit decay, then the 69:24 ratio, and the ceiling on the MLA share, are not merely questions of efficiency but questions of capability. The paper discusses none of this, and ablates none of it. If someone wanted to swap KDA for a different linear attention, or change the ratio, this report offers no guidance whatsoever.
The honest conclusion: KDA carries real function (positional encoding at the very least), and the hybrid structure shows the authors know it is not enough on its own, but “the marginal contribution of 69 KDA layers relative to more MLA” cannot be settled from this report.
Chapter summary
KDA makes one trade: it swaps “a KV cache with one entry per token that grows forever” for “a fixed-size blackboard that is rewritten in place”. Memory goes from growing linearly with length to constant, and the price is compressed information and a serial recurrence.
To make that trade pay, KDA stacks four repairs on top: the delta rule lets a write erase the old value (at the price of mandatory L2 normalisation, and a recurrence that is no longer additive); channel-level decay gives every row its own forgetting speed; the chunked parallel form compresses the serial part by three orders of magnitude (at the price of a reciprocal rescaling that can overflow); lower-bounded decay plugs the overflow at the source and clears away the slow path for diagonal sub-chunks along the way. Finally the full-rank output gate lets each token decide how much of what it read gets through.
Next chapter: if KDA is this cheap, why keep 24 full MLA layers at all? What does each of the two kinds of memory make up for?
第3章 Gated MLA 与混合注意力:两种记忆的分工
一句话导语:K3 不把「注意力」当成一种东西,而是让两种机制分工——KDA 负责顺着句子往下记、天然带先后顺序,Gated MLA 负责回头在全篇里随便翻、不受位置约束。这一章讲清它们按什么比例混、各自补什么、以及为什么 MLA 层可以干脆不要位置编码。
学完这一章你应该能做到
- 用自己的话说清 K3 沿哪三个维度扩展信息流,每个维度由哪个模块负责
- 从表 1 的「69 KDA + 24 MLA」反推出骨干里有多少个 block,并解释为什么 69÷24 不等于 3
- 解释 MLA 只缓存潜向量 ct 为什么能省显存,以及代价出在哪三处
- 说清 NoPE 在 K3 里成立所依赖的前提,并构造一个让这个前提失效的改动
- 逐符号读懂式 7,解释「满秩门」和低秩门的差别
- 把这一节里「论文给了理由的」和「论文只是这么做了的」分开列出来
3.1 先看地图:K3 把信息流往三个方向撑开
先把最基础的事情说清楚。一个语言模型拿到一段话,会先把它切成一个个 token(token,可以粗略理解成「词或词的一部分」),每个 token 变成一串数字。K3 里这串数字有 7,168 个(论文表 1 的 Hidden Dimension)。之后模型做的事情,翻来覆去就一件:让这些数字互相看一看、混一混。所谓「设计架构」,说白了就是决定谁跟谁混、怎么混、混几次。
论文 §2 开篇给的框架是:混合可以发生在三个互不相同的方向上,K3 沿这三个方向分别做了扩展。
第一个方向是序列长度,即 token 之间怎么混。K3 的做法叫混合注意力(Hybrid Attention):每个 block 里放 3 个 KDA 层加 1 个 Gated MLA 层,论文说这样「为长上下文的 token 混合提供了高效机制,同时保留了有选择性的高容量注意力」。第二个方向是网络深度,即层与层之间怎么混:注意力残差(Attention Residuals, AttnRes) 让每个模块能从词嵌入、当前块、以及之前的块里「挑着取」,而不是只能沿一条残差链均匀累加——这是第4章的内容。第三个方向是模型宽度,即同一个 token 内部 7,168 个通道之间怎么混:每个注意力层后面跟一个 Stable LatentMoE 层做稀疏的通道混合,每个 token 只激活 896 个路由专家里的 16 个。
为什么非要拆成三个方向来讲
因为这三件事的瓶颈完全不同:句子想更长,卡在缓存和平方复杂度;网络想更深,卡在信息要挤过一条细细的残差链;每层想更宽,卡在参数量和通信量。分开看,才能理解 K3 为什么要同时引入三个看上去毫不相干的新模块。
读的时候要小心
论文在这一段说,这些改动配合改进的训练与数据配方,相对 Kimi K2 带来约 2.5 倍的整体缩放效率提升。这是论文自述的结果,来自作者自己拟合的缩放曲线(论文 Figure 7),而且是「架构 + 数据 + 训练配方」打包在一起的数字。论文没有把功劳拆开,所以不能读成「混合注意力单独带来 2.5 倍」。
打个比方
像给一间图书馆扩容:书架可以排得更长(序列)、楼可以盖得更高(深度)、每层的架子可以更宽(宽度)。
类比失效处:图书馆三个方向互不干扰,但神经网络里三者共享同一份显存——加深会直接挤占加宽的预算。第4章你会看到,深度方向的扩展代价恰恰就是显存,逼得作者不得不做块化。
下面三句描述各对应哪个维度、哪个模块?(一)「让第 40 层能直接读到第 3 层的输出。」(二)「让第 1000 个 token 能看到第 1 个 token。」(三)「让这个 token 的 7,168 个数被 16 个专家分别加工。」
变式:「把上下文从 128K 扩到 1M」主要动的是哪个维度?再想一层——它会不会连带影响另外两个维度的开销?(提示:每个 token 都要在每一层留下痕迹。)
3.2 三个 KDA 配一个 Gated MLA:比例,和末尾补的那一层
为什么不能只用一种注意力
全局注意力(MLA 属于这一类)让每个 token 回头看前面所有 token:远处的细节都能被精确取回,但计算量随长度平方增长,且推理时必须把每个 token 的键和值一直缓存着——1M token 时这笔缓存大到离谱。线性注意力(KDA 属于这一类)反过来:把历史压进一个固定大小的状态往下传,代价随长度线性增长、缓存不涨,但「压缩」必然丢信息。两边各有一个致命短板,于是干脆混着用。
论文 §2.1 给的分工写得很明确:KDA 层提供位置敏感、近因感知(position-sensitive and recency-aware)的混合,MLA 层提供不受限的全局内容交互(unrestricted global content interaction)。翻成人话:KDA 管「最近发生了什么,按顺序记着」;MLA 管「整篇文章里跟我现在这个词最相关的是哪几个,不管它在多远」。
混合注意力(Hybrid Attention):K3 的做法是逐层混合(layerwise hybrid)——不是在同一层里把两种机制拼起来,而是整层整层地交替。每个 block 里放 3 个 KDA 层,后面跟 1 个 Gated MLA 层,比例 3:1,这个模式在整个骨干里重复。此外,骨干末尾额外放一个 Gated MLA 层,确保最后一层总是执行全局注意力。
一个非常容易混淆的词
「block」这个词在论文里指了两种完全不同的东西。§2.1 里的 block 是4 层(3 KDA + 1 Gated MLA);而 §2.2 的 Block AttnRes 里,block 是12 层。两者只是碰巧都叫 block,读的时候必须靠上下文判断。第4章开头我们会再提醒一次。
论文说了什么,没说什么
说了:比例是 3:1;这个模式在整个骨干重复;末尾额外补一个 Gated MLA,目的是「确保最后一层总是执行全局注意力」;这套混合设计沿用 Kimi Linear。
没说:(一)为什么是 3:1 而不是 7:1 或 1:1——这份报告里没有任何关于混合比例的消融实验或曲线;(二)为什么「最后一层必须是全局注意力」很重要——论文只陈述了这个设计目标,没给理由,也没给对照实验;(三)骨干的第一层是 KDA 还是 MLA,正文没有明说(从 Figure 2 的画法和 3.7 节的层数对账看,是从 KDA 开始的)。
答辩:如果我是审稿人
你说 3:1,可你整篇报告没给一条比例的消融曲线。凭什么不是 7:1?7:1 明显更省缓存。这个数字是不是从别的工作抄来的?
参考防守(先自己组织语言再看)
诚实的防守只能分三步走。第一,承认事实:这份报告确实没给比例消融,论文明说这套混合设计是沿用 Kimi Linear 的 hybrid design,也就是说 3:1 的证据在那篇工作里,不在这里。第二,给出机制层面的辩护:比例控制的是「全局注意力层的密度」,比例越大越省缓存但全局取回的机会越少;3:1 意味着每 4 层就有一次不受限的全局交互,在任何 4 层的窗口内信息都不会被纯压缩通道彻底截断。第三,指出这个辩护的边界:以上是定性的,它不能区分 3:1 和 5:1。一个诚实的作者应该说「我们采用了先前工作验证过的比例,没有在 K3 规模上重新搜索」,而不是暗示 3:1 是本文的发现。反过来,如果审稿人要求必须做消融,一个合理的反驳是:在 2.78T 参数规模上做比例消融的成本极高,用小模型做出来的最优比例也未必外推得上去——但这是成本论证,不是证据。
假设骨干严格按「(3 KDA + 1 Gated MLA) 重复 B 次,最后再补 1 个 Gated MLA」搭建,总层数是 93。请写出关于 B 的方程并解出 B,再算出 KDA 层和 MLA 层各有多少。
变式:如果作者不补末尾那一层,在保持 3:1 和 93 层总数的前提下还能搭出来吗?如果不能,最接近的层数是多少?(提示:93 能不能被 4 整除。)
3.3 MLA:把一个 token 的键值压成一个小向量
不用 MLA 会怎样
推理时每生成一个新词,都要把之前每个 token 的键(K)和值(V)拿出来算一遍;为了不重复计算就把它们存起来,这就是 KV 缓存(KV cache)。缓存大小 = token 数 × 每个 token 要存的数字个数。标准多头注意力里后一项是「所有头的 K 和 V 拼起来」,K3 有 96 个头(表 1)、上下文能到 1M token——两个数一乘,缓存就成了推理时最先撑爆的东西。
多头潜注意力(Multi-head Latent Attention, MLA):由 DeepSeek-V2 提出。它的想法是:既然缓存太大,那就别缓存 K 和 V 本身,改成缓存一个能重建出它们的小东西。具体做法是把每个 token 的键值表示压进一个低维潜向量(latent vector):
| 符号 | 是什么 | 直觉 |
|---|---|---|
| xt | 第 t 个位置进入这一层的输入向量,长度 7,168 | 这个 token 此刻的「完整状态」 |
| Wc | 训练中学出来的压缩矩阵(down-projection) | 一台学会了「怎么摘要」的压缩机 |
| ct | 压出来的潜向量,维度远小于 7,168 | 这个 token 留给后人查阅的「名片」 |
推理时只缓存 ct。真正要算注意力的时候,再用学到的上投影(learned up-projections)把 ct 重建成各个头的内容键和值。论文的原话是:这个分解「在保留全局 token 到 token 注意力的同时,减少了 KV 缓存的足迹」。Kimi K2 和 K2.5 都用了 MLA,K3 把它保留在周期性出现的全局注意力层里。
代价出在哪?至少三处。第一是计算换存储:每次算注意力都要多做一次上投影,把小向量重新撑回大矩阵,省下的显存是用额外乘法换的,论文没讨论这部分开销有多大。第二是表达力受限:所有头的键和值都必须从同一个低维 ct 里长出来,压得越狠,不同头拿到的信息越接近;论文没有量化这一损失。第三是复杂度没变:注意力「每个 token 看所有前面 token」的平方级计算量,MLA 一点没减少。
常见误解
很多人以为「MLA 把 KV 压小了,所以注意力也变快了」。其实压缩改变的是你要存多少,不是你要算多少次配对。真正把长上下文的算力压下来的是 KDA 那一类线性注意力。这正好解释了 K3 为什么两种都要:MLA 解决缓存里的「保真」,KDA 解决长度上的「代价」。
一个容易看错的数字
表 1 里有一行「Latent MoE Dimension: 3584 (0.5×)」。那是 Stable LatentMoE 的潜维度,跟 MLA 压缩出来的 ct 没有关系。K3 的 MLA 潜向量到底多少维,论文这一节和表 1 都没有给出。凡是要用到「压缩比是多少」的推理,都要先承认这个数我们不知道。
用一句话回答:MLA 在推理时缓存的是什么?再用一句话回答:如果某次推理的上下文只有 20 个 token,MLA 相对标准多头注意力还有优势吗?
变式:假设有人把 ct 的维度设得和「所有头的 K、V 拼起来」一样大,MLA 会退化成什么?这时它还叫「压缩」吗?
3.4 NoPE:位置这件事,交给谁管
为什么会有位置编码这个东西
注意力机制本身对顺序是没感觉的:它做的事是「拿我的查询和每个键比相似度,再按相似度加权求和」,整个过程没有出现「谁在前谁在后」。把词打乱,纯注意力对每个词算出的结果完全一样。可「狗咬人」和「人咬狗」显然不是一回事。所以通常要额外告诉模型位置,最常见的是 RoPE(Rotary Position Embedding,旋转位置编码):按位置给查询和键转一个角度,位置差越远、转角差越大。
无位置编码(No Position Encoding, NoPE):和 Kimi K2、K2.5 不同,K3 沿用 Kimi Linear 的混合设计,对所有 MLA 层采用 NoPE——它们的查询和键上不施加任何显式位置编码。
敢这么干的理由,论文写得很直白:夹在中间的 KDA 层本身就是位置敏感、近因感知的(它按顺序一步步更新状态,把顺序换掉结果就变了),而 MLA 层负责提供不受限的全局内容交互。也就是说,位置信息在进入 MLA 层之前,已经被 KDA 层写进隐藏向量里了;MLA 层不需要再被单独告知一遍,它可以专心做「按内容找相关」这件事。
还有第二个好处,纯工程但很实在:论文指出,这种分工避免了在扩展上下文长度时去改位置编码的参数,比如重调 RoPE 的频率基,或者套用 YaRN 这类插值方法。K3 的训练上下文从 K2 的 128K 一路推到 1M(表 1,8 倍),如果位置编码是显式的,每一次扩长都要重新调参、重新验证;NoPE 直接把这个环节从流程里删掉了。
打个比方
KDA 像边听边记的速记员,记下来的东西天然带着先后顺序;MLA 像事后翻笔记的人,不需要笔记本上再印页码,因为速记员写下的内容本身就带着时间感。
类比失效处:速记员的笔记是显式文字,谁都能读出顺序;而 KDA 写进隐藏向量的「位置感」是隐式的,没人能保证它在 1M token 尺度上仍然精确、仍然能被 MLA 层解读出来。
这是理由,不是证据
§2.1.2 这一段完整地给出了 NoPE 的 rationale(分工 + 免调参),但没有给出任何 NoPE 对比 RoPE 的实验。论文后面 §3.4 会说 K3 因此能直接外推到 1M token 上下文而无需任何位置编码改造——那是对结果的陈述,同样属于论文自述。想判断 NoPE 到底行不行,本章的材料是不够的。
答辩:如果我是审稿人
你的 NoPE 完全建立在「KDA 层会提供位置信息」这个假设上。那我问你:如果把 KDA 换成一个不带 decay、不带门控的普通线性注意力,你的 MLA 层还知道位置吗?如果不知道,那你所谓的「NoPE 更好」其实只是「KDA 顺便干了位置编码的活」,你凭什么把它记在 NoPE 头上?
参考防守(先自己组织语言再看)
这个质疑基本是成立的,只能承认前提然后限定主张。防守要点:第一,明确承认 NoPE 在 K3 里不是一个独立成立的设计,它是「混合架构」这个整体的一部分——论文自己的措辞就是「中间的 KDA 层提供位置敏感与近因感知的混合」,这句话本身已经把依赖关系说出来了,作者并没有声称 NoPE 单独可用。第二,因此正确的主张不是「NoPE 比 RoPE 好」,而是「在存在位置敏感的线性注意力层的前提下,全局层的显式位置编码是冗余的,去掉它能省下扩长时的调参环节」。第三,审稿人假设的那个改动(换成无 decay 的线性注意力)确实会破坏这个前提:如果线性注意力对历史是均匀累加而没有任何随距离衰减的结构,它就不再区分先后,MLA 层也就失去了位置信息来源——这恰恰说明该前提是必要的,而不是说明论文错了。第四,仍然要承认的缺口:论文没有做这个消融,所以「KDA 提供的位置信息够不够用」在 1M 尺度上仍是未验证的。
有人看完这一节说:「既然 MLA 层完全没有位置编码,那 K3 就分不清『狗咬人』和『人咬狗』。」请构造一个具体的反驳,说明这句话错在哪;然后反过来,构造一个对 K3 的改动,使得这句话变成对的。
反过来构造:把骨干里所有 KDA 层都换成对历史均匀求和、不带任何衰减或递推顺序的机制(也就是让每一层都对顺序无感),同时保持 MLA 层的 NoPE。这时整个网络从头到尾没有任何模块能区分顺序,「狗咬人 / 人咬狗」就真的分不清了。这个反例的价值在于:它证明 NoPE 的正确性不是 MLA 层自己的性质,而是整个混合架构的性质——换掉看似无关的另一半,结论就崩了。
变式:把改动换成「保留 KDA,但把 3:1 改成 1:99(几乎全是 MLA 层)」。这时位置信息还在吗?它「够用」吗?说说你的判断依据,以及论文有没有回答这个问题。
3.5 输出门:让每个 token 自己决定听多少(式 7)
为什么要在注意力后面加一道闸门
全局注意力读回来的是一份「加权平均意见」。问题是,这份意见对某些 token 极其有用(比如一个代词,需要回头找它指代谁),对另一些 token 几乎是噪声(比如一个标点)。如果不加闸门,注意力的输出会原样被写回主干,噪声也一并写进去。加一道由 token 自己控制的闸门,就能做到「这条通道我要听,那条通道我关掉」。
K3 给 MLA 加了一个输入相关、逐通道、满秩的输出门。记 ōt 为位置 t 上未经门控的 MLA 输出,门控后的输出是:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| yt | 第 t 个位置上这一层最终吐出来的向量 | 过完闸门、整理好,准备交给下一层 |
| ōt | 没加门的 MLA 输出(字母上面一横表示「未门控」) | 注意力刚读回来的原始意见 |
| xt | 这一层在位置 t 的输入向量 | 「我是谁」——闸门要根据它来开合 |
| Wg | 门投影矩阵,满秩 | 把「我是谁」翻译成「每条通道开多大」 |
| Sigmoid(·) | 逐元素把任意实数压到 (0, 1) 之间的函数 | 每条通道的音量旋钮,0 是静音、1 是全开 |
| ⊙ | 逐元素相乘(哈达玛积) | 把音量旋钮一对一地拧在每条通道上 |
| Wo | 输出投影矩阵 | 把门控后的结果映射回主干的形状 |
唯一需要额外解释的词是满秩。Wg 把 7,168 维的 xt 映射成一个同样长的门向量。如果这个映射是低秩的——比如先压到 32 维再撑回去——那么无论输入是什么,所有可能的门向量都被困在一个 32 维子空间里,很多通道只能被绑在一起开关。满秩则意味着门向量原则上可以指向 7,168 维空间里的任何方向,每条通道可以独立开关。论文的说法是:这个门让每个 token 去调制它从全局注意力读到的通道;Wg 满秩这一点与 K3 里 KDA 的新参数化保持一致。
顺手对照一下
KDA 的输出门(式 6)和这里的式 7 长得几乎一模一样,唯一的差别是:KDA 那一版先对 ōt 做了 head-wise 的 RMSNorm 再乘门,而式 7 里没有这一步。论文没有解释为什么全局注意力这边不需要这个归一化。这属于「看得见但没被交代」的设计细节。
把式 7 里的 Wg 换成低秩形式 Wg = AB,其中 B 把 7,168 维压到 8 维、A 再撑回 7,168 维。请说明:这样一来,模型做不到哪一类事情?再回答:Sigmoid 换成不加约束的线性函数(也就是干脆去掉 Sigmoid)会有什么后果?
第二问:Sigmoid 保证门值落在 (0, 1),它同时给出两个性质——一是有明确的语义(0 关、1 开,是「比例」而不是「放大倍数」),二是有界,不会把注意力输出的幅值放大到失控。去掉它以后,门变成一个无界的线性缩放,既可能放大成几百倍(在低精度算术下极易溢出),也可能变成负数导致符号翻转,训练稳定性会变差。注意这只是机制层面的分析,论文本身没有做这个消融。
变式:把 Wgxt 里的 xt 换成 ōt(用注意力的输出自己来算门),门还是「输入相关」的吗?这个改动会让门失去什么信息?
3.6 把注意力输出留在 FP32,以及被逼出来的内核重设计
为什么要管几位浮点数这种小事
计算机里的小数是有限精度的:BF16 一个数占 2 字节,FP32 占 4 字节。位数少就省显存、算得快,但每一步都有舍入误差。平时误差有正有负会互相抵消,可 flash attention 为了省显存把注意力分块累加,这个累加过程的舍入误差不是对称的,而是系统性偏向一边——论文的原话是 biased rounding error。有偏的误差不会抵消,只会越加越多。
K3 的处理是:训练时把注意力输出保持在 FP32,采用文献 [98] 的方法纠正这个有偏舍入误差。这听起来只是「多用点精度」,但它在 GPU 上引发了一连串连锁反应,论文把这条链完整写了出来,值得跟着走一遍。
tile(分块)与片上占用:GPU 算大矩阵时不会一次搬进整个矩阵,而是切成小块(tile),一块块搬进共享内存(shared memory)里算。共享内存是紧挨着计算单元的一小块存储,快得多也小得多,寸土寸金。
把输出从 16 位改成 32 位,意味着输出 tile 的片上占用直接翻倍。共享内存就那么大,有东西变胖就得有东西让位。论文的解法是重新设计训练内核:让变胖的输出 tile 去和 KV 暂存缓冲区(KV staging buffers,从显存往片上搬 K、V 时的中转区)重叠,而不是像原来那样和 query tile 重叠。腾出来的共享内存用于更深的 KV 流水线——同时在途的 K、V 块更多,搬运和计算叠得更好,计算单元等待更少。论文的结论是:这样反而换来更高的训练吞吐。
这里有一步论文没讲
论文没有解释为什么和 query tile 重叠不行,也没有给出任何吞吐提升的具体数字。一个合理的猜测是:query tile 在整块计算期间要被反复读取,生命周期贯穿始终;而 KV 暂存缓冲区是流水线式滚动使用的,生命周期与输出 tile 互补,才可以共用同一块地方。但这是我们的推测,不是论文的说法。
有人说:「把注意力输出从 BF16 改成 FP32,占用翻倍,训练一定变慢。」但论文说这个改动配合内核重设计之后反而提高了训练吞吐。请构造一个具体的场景解释这怎么可能,并指出这个反例成立需要什么前提。
前提:(一)原始内核确实是访存/延迟受限而不是算术受限;(二)输出 tile 与 KV 缓冲区的生命周期确实互补,能安全共用同一块共享内存;(三)额外的 FP32 写回带宽不会成为新瓶颈。这三条只要有一条不成立,「精度更高反而更快」就不再成立——所以这不是一条普适规律,而是一次针对具体瓶颈的工程胜利。论文本身没有给出吞吐数字,我们无法验证提升幅度。
变式:如果 FP32 只在推理时启用(不训练),这套内核重设计还有意义吗?推理和训练在共享内存的使用模式上有什么不同?(提示:想想推理解码阶段每次只处理一个新 token。)
3.7 对账时间:69 + 24 = 93,可 69 ÷ 24 = 2.875
现在把论文表 1 里跟本章相关的几行拉出来,做一次真正的核对。这一节的目的不是记住数字,而是练一件事:拿到一张表,能不能从里面把架构反推出来。
| 项 | Kimi K2 | Kimi K3 | Δ |
|---|---|---|---|
| 层数(#Layers) | 61 | 93 | ↑52% |
| 隐藏维度 | 7,168 | 7,168 | = |
| 注意力头数 | 64 | 96 | ↑50% |
| 注意力机制 | MLA | Hybrid KDA–MLA | — |
| 注意力层构成 | 61 MLA | 69 KDA + 24 MLA | — |
| 训练上下文长度 | 128K | 1M | 8× |
自己推一遍:从「69 KDA + 24 MLA」倒推出骨干长什么样
先做加法:69 + 24 等于多少?这个数和表里哪一行对上了?对上意味着什么?
想好了再看
69 + 24 = 93,正好是「层数」那一行。这条对账很重要,它告诉我们:K3 的每一层都是注意力层,「注意力层构成」这一行覆盖了全部 93 层,没有第三种层混在里面。凡是核对架构,第一步永远是先确认总数守恒——不然后面每一步都建在沙子上。
论文说比例是 3:1。那么 69 ÷ 24 应该等于 3 才对。你算一下,它等于几?
想好了再看
69 ÷ 24 = 2.875,不等于 3。这里必须停下来,别急着说「差不多就是 3」。差 0.125 看着小,但架构里的层数是整数,整数对不上就一定有结构性的原因,不可能是「四舍五入」。当初会想到追这一步,是因为一条经验:论文里给的比例和表里给的数字对不上时,多半是有个边界项没算进去。
回想 §2.1 除了「3 KDA + 1 MLA 重复」之外,还说了一句什么?把那句话对应的层单独拿出来,再算一次比值。
想好了再看
那句话是:「骨干末尾额外放一个 Gated MLA 层,确保最后一层总是执行全局注意力。」这一层不属于任何 block。把它从 24 里减掉:24 − 1 = 23 个 MLA 层属于 block。再算比值:69 ÷ 23 = 3,正好。这就是 2.875 的全部来源——比例 3:1 只对「重复部分」成立,末尾那一层是周期之外的边界修正。
现在反推 block 个数和总层数,验证一遍闭环。
想好了再看
block 个数 B = 23(因为每个 block 有 1 个 MLA)。总层数 = 4 × 23 + 1 = 92 + 1 = 93 ✓。KDA = 3 × 23 = 69 ✓,MLA = 23 + 1 = 24 ✓。三个数全部闭合,说明我们对架构的重建是自洽的。注意:「23 个 block」这个数字论文从头到尾没有直接写出来,是我们自己算的——但它是被表 1 唯一确定的。
最后一问:如果作者没有补末尾那一层,「最后一层是全局注意力」这个性质还成立吗?
想好了再看
其实是成立的——因为每个 block 的最后一层本来就是 Gated MLA(模式是 3 个 KDA 在前、1 个 MLA 在后)。所以如果骨干正好以一个完整 block 结尾,第 92 层已经是全局注意力了。这就产生一个论文没有回答的问题:既然如此,补第 93 层的真正动机是什么?可能是为了凑一个特定的总层数,可能是为了让最后一层的表征在进入输出头之前再做一次全局整合,也可能只是经验上更好。论文只陈述了做法和「确保最后一层做全局注意力」这个说法,没有给理由,也没有给消融。这是本章最值得打问号的一处。
综合题。已知 K3 有 93 层、其中 24 层是 Gated MLA,隐藏维度 7,168,训练上下文 1M token。现在假设有人提议:「把比例从 3:1 改成 1:1,让模型的长文本能力更强。」请回答三问:(甲)改完之后 MLA 层大约有多少层?(乙)这个改动会让推理时的 KV 缓存大致变成原来的几倍?(丙)除了缓存,这个改动还会牵动本章讲过的哪些设计?其中哪一条会直接失效?
(乙)需要缓存潜向量的层数从 24 变成约 46,KV 缓存接近翻倍(约 1.9 倍)。注意这里用的是「缓存量正比于 MLA 层数」这个关系——KDA 层的状态是固定大小的,不随上下文长度增长,所以长上下文下缓存几乎全部来自 MLA 层。
(丙)至少牵动三处。第一,算力:全局注意力是长度平方级的,MLA 层翻倍意味着长上下文下的注意力计算量也接近翻倍,1M token 时这非常贵——所以「1:1 让长文本能力更强」这个说法本身就可疑,它换来的更可能是长文本成本更高。第二,NoPE 的前提被削弱:位置信息全靠 KDA 层提供,KDA 层从 69 降到约 46,位置敏感的层少了三分之一,而 MLA 层依然完全无位置编码——这一条最接近「直接失效」,因为它是被论文明确写成依赖关系的(论文原话:中间的 KDA 层提供位置敏感与近因感知的混合)。第三,末尾补层那条规则需要重新推导(4B+1 = 93 的算式不再适用)。
最该警惕的是:论文没有给任何比例消融,所以「改成 1:1 会怎样」我们只能做机制层面的推断,没有实验依据。任何声称「1:1 一定更好/更差」的说法,在这份材料里都缺证据。
变式:反过来提议「改成 7:1,省一半缓存」。用同样的三问分析一遍。哪一个方向的风险更难被察觉?(提示:缓存爆掉会立刻报错,而位置信息不够用只会让长文本质量悄悄变差。)
本章小结
K3 沿三个维度扩展信息流:序列长度交给混合注意力(本章)、网络深度交给 AttnRes(第4章)、模型宽度交给 Stable LatentMoE。在序列方向上,两种机制整层整层地交替:每个 block 是 3 个 KDA 加 1 个 Gated MLA,比例 3:1,全网重复;骨干末尾再补一个 Gated MLA,使最后一层一定是全局注意力。表 1 的「69 KDA + 24 MLA / 93 层」正是 4 × 23 + 1 的算术指纹。
Gated MLA 这一半做了三件事。压缩缓存:只缓存潜向量 ct = Wcxt,算注意力时用学到的上投影重建键和值——省的是显存,不是算力。去掉位置编码:所有 MLA 层用 NoPE,位置信息由中间的 KDA 层隐式提供,顺带免掉扩长时重调 RoPE 频率基或套 YaRN 的麻烦。加一道满秩输出门(式 7),让每个 token 逐通道决定从全局注意力听进来多少。此外,训练时注意力输出保持 FP32 以纠正 flash attention 的有偏舍入误差,代价是输出 tile 片上占用翻倍,于是内核改为与 KV 暂存缓冲区重叠,反而换来更高的训练吞吐。
本章标记为存疑或论文未说明的地方:3:1 这个比例没有消融,证据在被引用的 Kimi Linear 工作里而不在本文;「末尾必须补一层全局注意力」只有做法没有理由(而且每个 block 本来就以 MLA 结尾);MLA 潜向量 ct 的维度没有给出;式 7 相比 KDA 的式 6 少了一步 RMSNorm,论文没解释;内核为什么不能和 query tile 重叠、重设计带来多少吞吐提升,都没有交代;NoPE 只给了设计理由,没有对照实验;2.5 倍缩放效率是论文自述且是架构、数据、训练配方打包的结果。
Chapter 3 · Gated MLA and hybrid attention: a division of labour between two kinds of memory
In one sentence: K3 does not treat “attention” as one thing. It splits the work between two mechanisms — KDA remembers as it goes down the sentence and carries order naturally, Gated MLA looks back and flips through the whole text at will, free of any positional constraint. This chapter makes clear in what ratio they are mixed, what each of them supplies, and why the MLA layers can drop position encoding altogether.
After this chapter you should be able to
- Say in your own words which three dimensions K3 expands information flow along, and which module handles each one
- Work backwards from “69 KDA + 24 MLA” in Table 1 to how many blocks the backbone has, and explain why 69÷24 is not 3
- Explain why caching only the latent vector ct saves GPU memory, and where the three costs fall
- State the premise NoPE depends on inside K3, and construct a change that breaks that premise
- Read Equation 7 symbol by symbol, and explain the difference between a “full-rank gate” and a low-rank gate
- List separately, for this chapter, what the paper gave a reason for and what the paper merely did
3.1 The map first: K3 pushes information flow out in three directions
Start with the most basic thing. A language model takes a passage of text and first chops it into tokens(token, roughly “a word or a piece of a word”), and each token becomes a string of numbers. In K3 that string has 7,168 numbers in it (Hidden Dimension in Table 1 of the paper). After that, everything the model does is one thing over and over: let those numbers look at each other and mix. “Designing an architecture”, plainly put, is deciding who mixes with whom, how they mix, and how many times.
The framework the paper gives at the opening of §2 is this: mixing can happen along three distinct directions, and K3 extends along each of the three.
The first direction is sequence length, that is, how tokens mix with each other. K3’s approach is called hybrid attention(Hybrid Attention): each block holds 3 KDA layers plus 1 Gated MLA layer, which the paper says “provides an efficient mechanism for long-context token mixing while preserving selective, high-capacity attention”. The second direction is network depth, that is, how layers mix with layers: attention residuals(Attention Residuals, AttnRes) let every module “pick and choose” from the word embeddings, the current block and earlier blocks, instead of only accumulating uniformly along a single residual chain — that is the subject of Chapter 4. The third direction is model width, that is, how the 7,168 channels inside a single token mix: every attention layer is followed by a Stable LatentMoE layer doing sparse channel mixing, and each token activates only 16 of the 896 routed experts.
Why this has to be split into three directions
Because the bottleneck in each of the three is completely different. Longer sentences run into the cache and quadratic complexity; a deeper network runs into having to squeeze information through one thin residual chain; wider layers run into parameter count and communication volume. Only by looking at them separately can you understand why K3 introduces three apparently unrelated new modules at once.
Read this carefully
In this passage the paper says that these changes, together with an improved training and data recipe, deliver roughly a 2.5× gain in overall scaling efficiency relative to Kimi K2. This is a self-reported result, taken from a scaling curve the authors fitted themselves (Figure 7 of the paper), and it is a number for “architecture + data + training recipe” bundled together. The paper does not split the credit, so you cannot read it as “hybrid attention alone gives 2.5×”.
An analogy
Like expanding a library: the shelves can run longer (sequence), the building can go higher (depth), the racks on each floor can be wider (width).
Where the analogy breaks: in a library the three directions do not interfere with each other, but in a neural network all three share the same GPU memory — going deeper eats directly into the budget for going wider. In Chapter 4 you will see that the cost of expanding in the depth direction is precisely memory, which forced the authors into blocking.
Which dimension and which module does each of the three descriptions below correspond to? (one) “Let layer 40 read the output of layer 3 directly.” (two) “Let the 1000th token see the 1st token.” (three) “Let the 7,168 numbers of this token be processed by 16 experts separately.”
Variant: “extending the context from 128K to 1M” mainly moves which dimension? Then think one level further — does it drag the cost of the other two dimensions along with it? (Hint: every token has to leave a trace in every layer.)
3.2 Three KDA to one Gated MLA: the ratio, and the extra layer at the end
Why one kind of attention alone will not do
Global attention (MLA belongs to this class) lets every token look back at all the tokens before it: distant details can be retrieved exactly, but the compute grows with the square of the length, and at inference time the keys and values of every token have to be kept cached the whole way — at 1M tokens that cache is absurdly large. Linear attention (KDA belongs to this class) is the reverse: it compresses the history into a fixed-size state and passes it along, so the cost grows linearly with length and the cache does not grow at all, but “compression” necessarily loses information. Each side has one fatal weakness, so you simply mix them.
The division of labour given in §2.1 of the paper is stated plainly: KDA layers provide position-sensitive, recency-aware(position-sensitive and recency-aware) mixing, and MLA layers provide unrestricted global content interaction(unrestricted global content interaction). In plain words: KDA handles “what happened recently, remembered in order”; MLA handles “which few things in the whole document are most relevant to the word I am on now, however far away they are”.
Hybrid attention: K3’s approach is a layerwise hybrid(layerwise hybrid) — not stitching the two mechanisms together inside one layer, but alternating whole layers at a time. Each block holds 3 KDA layers followed by 1 Gated MLA layer, a ratio of 3:1, and this pattern repeats throughout the backbone. On top of that, one extra Gated MLA layer is placed at the end of the backbone, making sure the last layer always performs global attention.
A word that is very easy to confuse
The word “block” refers to two completely different things in the paper. The block in §2.1 is 4 layers (3 KDA + 1 Gated MLA); in the Block AttnRes of §2.2, a block is 12 layers. The two just happen to share the name block, and while reading you have to tell them apart from context. This site will remind you once more at the start of Chapter 4.
What the paper says, and what it does not
Says: the ratio is 3:1; the pattern repeats throughout the backbone; one extra Gated MLA is added at the end, in order to “ensure the last layer always performs global attention”; this hybrid design is carried over from Kimi Linear.
Does not say: (one) why 3:1 rather than 7:1 or 1:1 — this report contains no ablation and no curve for the mixing ratio at all; (two) why “the last layer must be global attention” matters — the paper only states the design goal, gives no reason and no controlled experiment; (three) whether the first layer of the backbone is KDA or MLA is never spelled out in the text (from the way Figure 2 is drawn and the layer-count reconciliation in section 3.7, it starts with KDA).
Defend it: if I were a reviewer
You say 3:1, but nowhere in the whole report is there a single ablation curve for the ratio. On what grounds is it not 7:1? 7:1 obviously saves more cache. Did you copy this number from someone else’s work?
A reference defence (put it in your own words first, then look)
An honest defence can only go in three steps. First, concede the fact: this report really does not give a ratio ablation, and the paper says outright that this hybrid design follows the hybrid design of Kimi Linear — which means the evidence for 3:1 lives in that work, not here. Second, offer a defence at the level of mechanism: the ratio controls “the density of global attention layers”; the larger the ratio the more cache is saved but the fewer chances there are for global retrieval; 3:1 means one unrestricted global interaction every 4 layers, so within any window of 4 layers information is never cut off entirely by pure compression channels. Third, point out the boundary of that defence: the above is qualitative and cannot tell 3:1 from 5:1. An honest author should say “we adopted a ratio validated by prior work and did not re-search it at K3 scale”, rather than implying that 3:1 is a finding of this paper. Conversely, if the reviewer insists an ablation is required, a reasonable rebuttal is: running a ratio ablation at 2.78T parameters is extremely expensive, and the optimal ratio found on a small model will not necessarily extrapolate upward — but that is a cost argument, not evidence.
Suppose the backbone is built strictly as “(3 KDA + 1 Gated MLA) repeated B times, then 1 more Gated MLA added at the end”, and the total layer count is 93. Write down the equation for B and solve for B, then work out how many KDA layers and how many MLA layers there are.
Variant: if the authors did not add that final layer, could it still be built while keeping 3:1 and a total of 93 layers? If not, what is the closest layer count? (Hint: is 93 divisible by 4.)
3.3 MLA: squeezing one token’s keys and values into a small vector
What happens without MLA
At inference time, every new word generated requires going through the key (K) and value (V) of every earlier token again; to avoid recomputing them you store them, and that is the KV cache(KV cache). Cache size = number of tokens × number of numbers stored per token. In standard multi-head attention the second factor is “the K and V of all heads concatenated”, K3 has 96 heads (Table 1) and the context reaches 1M tokens — multiply the two numbers and the cache becomes the first thing to blow up at inference time.
Multi-head latent attention: introduced by DeepSeek-V2. The idea is that since the cache is too large, do not cache K and V themselves — cache instead a small thing that can reconstruct them. Concretely, the key-value representation of each token is compressed into a low-dimensional latent vector(latent vector):
| Symbol | What it is | Intuition |
|---|---|---|
| xt | The input vector entering this layer at position t, length 7,168 | The “complete state” of this token at this moment |
| Wc | The compression matrix learned during training (down-projection) | A compressor that has learned “how to summarise” |
| ct | The latent vector that comes out, with dimension far below 7,168 | The “business card” this token leaves behind for later readers |
At inference time only ct is cached. When the attention actually has to be computed, learned up-projections(learned up-projections) rebuild ct into the content keys and values of each head. The paper’s own words: this factorisation “reduces the KV cache footprint while preserving global token-to-token attention”. Kimi K2 and K2.5 both used MLA, and K3 keeps it in the periodically occurring global attention layers.
Where does the cost fall? In at least three places. First, compute traded for storage: every attention computation has to do one more up-projection, pushing the small vector back out into a large matrix, so the memory saved is bought with extra multiplications, and the paper does not discuss how large that overhead is. Second, limited expressiveness: the keys and values of all heads have to grow out of the same low-dimensional ct, so the harder the compression, the more alike the information different heads receive; the paper does not quantify this loss. Third, the complexity is unchanged: MLA does not reduce one bit of the quadratic compute of attention’s “every token looks at every token before it”.
A common misreading
Many people assume “MLA shrank the KV, so attention got faster too”. In fact what compression changes is how much you have to store, not how many pairings you have to compute. What actually brings down the compute of long context is linear attention of the KDA kind. That is exactly why K3 wants both: MLA solves “fidelity” in the cache, KDA solves “cost” in the length.
A number that is easy to misread
Table 1 has a row reading “Latent MoE Dimension: 3584 (0.5×)”. That is the latent dimension of Stable LatentMoE, and the ct that MLA compresses out has nothing to do with it. How many dimensions K3’s MLA latent vector actually has is not given either in this section of the paper or in Table 1. Any reasoning that needs “what the compression ratio is” has to start by admitting this site does not know that number.
Answer in one sentence: what does MLA cache at inference time? Then answer in one more sentence: if the context of some inference run is only 20 tokens, does MLA still have an advantage over standard multi-head attention?
Variant: suppose someone sets the dimension of ct as large as “the K and V of all heads concatenated”. What does MLA degenerate into? Is it still called “compression” at that point?
3.4 NoPE: who is put in charge of position
Why position encoding exists at all
The attention mechanism itself has no sense of order: what it does is “compare my query with every key for similarity, then take a weighted sum by similarity”, and nowhere in that process does “who comes first and who comes later” appear. Shuffle the words and pure attention computes exactly the same result for each word. But “dog bites man” and “man bites dog” are plainly not the same thing. So the model is usually told the position separately, most commonly with RoPE(Rotary Position Embedding): rotate the query and the key by an angle according to position, so the further apart the positions, the larger the difference in angle.
No position encoding: unlike Kimi K2 and K2.5, K3 follows the hybrid design of Kimi Linear and uses NoPE for all MLA layers — no explicit position encoding of any kind is applied to their queries and keys.
The paper states the reason for daring to do this quite bluntly: the KDA layers sandwiched in between are themselves position-sensitive and recency-aware (they update the state step by step in order, so change the order and the result changes), while the MLA layers are responsible for supplying unrestricted global content interaction. In other words, the position information has already been written into the hidden vectors by the KDA layers before it reaches an MLA layer; the MLA layer does not need to be told again separately, and can concentrate on “finding what is relevant by content”.
There is a second benefit, purely engineering but very real: the paper points out that this division of labour avoids having to change position-encoding parameters when the context length is extended — retuning RoPE’s frequency base, say, or applying an interpolation method like YaRN. K3’s training context is pushed all the way from K2’s 128K to 1M (Table 1, 8×), and if the position encoding were explicit, every extension would mean retuning and revalidating; NoPE simply deletes that step from the pipeline.
An analogy
KDA is like a stenographer taking notes as they listen: what gets written down carries the order of events naturally. MLA is like the person leafing through the notes afterwards, who does not need page numbers printed in the notebook, because what the stenographer wrote already carries a sense of time.
Where the analogy breaks: the stenographer’s notes are explicit text and anyone can read the order off them, whereas the “sense of position” KDA writes into the hidden vectors is implicit, and nobody can guarantee it stays precise at the scale of 1M tokens, or that the MLA layers can still decode it.
This is a reason, not evidence
This passage in §2.1.2 gives the rationale for NoPE in full (division of labour + no parameter retuning), but gives no experiment at all comparing NoPE against RoPE. Later, in §3.4, the paper says that K3 can therefore extrapolate directly to a 1M token context with no position-encoding modification — that is a statement about a result, and it is equally self-reported by the paper. To judge whether NoPE actually works, the material in this chapter is not enough.
Defend it: if I were a reviewer
Your NoPE rests entirely on the assumption that “the KDA layers will supply position information”. So let me ask you: if KDA were replaced by an ordinary linear attention with no decay and no gating, would your MLA layers still know about position? If not, then your so-called “NoPE is better” is really just “KDA does the position encoding’s job on the side”, and on what grounds do you credit it to NoPE?
A reference defence (put it in your own words first, then look)
This challenge basically holds, and the only move is to concede the premise and then narrow the claim. The points of the defence: first, admit outright that NoPE in K3 is not a design that stands on its own — it is part of the whole that is the “hybrid architecture”. The paper’s own wording is “the interleaved KDA layers provide position-sensitive and recency-aware mixing”, and that sentence already states the dependency; the authors never claim NoPE is usable on its own. Second, the correct claim is therefore not “NoPE is better than RoPE” but “given the presence of position-sensitive linear attention layers, explicit position encoding in the global layers is redundant, and dropping it saves the retuning step when the context is extended”. Third, the change the reviewer hypothesises (swapping in a linear attention with no decay) really would break that premise: if the linear attention accumulates history uniformly with no structure that decays with distance, it no longer distinguishes earlier from later, and the MLA layers lose their source of position information — which is precisely why that premise is necessary, not why the paper is wrong. Fourth, the gap that still has to be conceded: the paper does not run this ablation, so “is the position information KDA supplies enough” remains unverified at the 1M scale.
Someone finishes this section and says: “Since the MLA layers have no position encoding at all, K3 cannot tell ‘dog bites man’ from ‘man bites dog’.” Construct a concrete rebuttal showing where this is wrong; then go the other way and construct a change to K3 that makes the statement true.
Constructing the reverse: replace every KDA layer in the backbone with a mechanism that sums history uniformly, with no decay and no recurrence order (that is, make every layer order-blind), while keeping NoPE in the MLA layers. Now no module anywhere in the network can distinguish order, and “dog bites man / man bites dog” really does become indistinguishable. The value of this counterexample is that it shows the correctness of NoPE is not a property of the MLA layer itself but a property of the whole hybrid architecture — swap out the apparently unrelated other half and the conclusion collapses.
Variant: change the modification to “keep KDA, but change 3:1 to 1:99 (almost all MLA layers)”. Is the position information still there? Is it “enough”? Say what you are basing your judgement on, and whether the paper answers this question.
3.5 The output gate: letting each token decide for itself how much to listen (Equation 7)
Why put a gate after attention
What global attention reads back is a “weighted average opinion”. The problem is that this opinion is extremely useful for some tokens (a pronoun, say, which needs to look back for what it refers to) and almost pure noise for others (a punctuation mark, say). Without a gate, the output of attention is written back into the trunk as is, noise included. Add a gate controlled by the token itself and you get “this channel I listen to, that channel I shut off”.
K3 adds an input-dependent, per-channel, full-rank output gate to MLA. Write ōt for the MLA output at position t before gating; the gated output is:
| Symbol | What it is | Intuition |
|---|---|---|
| yt | The vector this layer finally emits at position t | Through the gate, tidied up, ready to hand to the next layer |
| ōt | The MLA output with no gate applied (the bar over the letter means “ungated”) | The raw opinion attention has just read back |
| xt | The input vector of this layer at position t | “Who I am” — the gate opens and closes according to it |
| Wg | The gate projection matrix, full-rank | Translates “who I am” into “how far each channel opens” |
| Sigmoid(·) | A function that squashes any real number into (0, 1), element by element | A volume knob for each channel: 0 is mute, 1 is fully open |
| ⊙ | Element-wise multiplication (Hadamard product) | Fits a volume knob onto every channel, one to one |
| Wo | The output projection matrix | Maps the gated result back to the shape of the trunk |
The only word that needs extra explanation is full-rank. Wg maps the 7,168-dimensional xt into a gate vector of the same length. If that map were low-rank — squeezed down to 32 dimensions first and then pushed back out, say — then whatever the input, every possible gate vector would be trapped in a 32-dimensional subspace, and many channels could only be switched on and off tied together. Full-rank means the gate vector can in principle point in any direction of the 7,168-dimensional space, so every channel can be switched independently. The paper puts it this way: this gate lets each token modulate the channels it reads from global attention; the fact that Wg is full-rank is consistent with the new parameterisation of KDA in K3.
A quick comparison while you are here
KDA’s output gate (Equation 6) and Equation 7 here look almost exactly alike, and the only difference is this: that version applies a head-wise RMSNorm to ōt before multiplying by the gate, and Equation 7 does not have that step. The paper does not explain why the global attention side does not need this normalisation. This is a design detail of the “visible but never accounted for” kind.
Replace Wg in Equation 7 with the low-rank form Wg = AB, where B squeezes 7,168 dimensions down to 8 and A pushes them back out to 7,168. Explain: what class of thing can the model now not do? Then answer: what are the consequences of replacing Sigmoid with an unconstrained linear function (that is, of simply removing Sigmoid)?
Second question: Sigmoid guarantees that the gate values land in (0, 1), and it gives two properties at once — first, a clear meaning (0 closed, 1 open, a “proportion” and not a “gain factor”), and second, boundedness, so the magnitude of the attention output is never blown up out of control. Remove it and the gate becomes an unbounded linear scaling, which could amplify by hundreds of times (extremely easy to overflow in low-precision arithmetic) or go negative and flip the sign, and training stability would get worse. Note that this is analysis at the level of mechanism only; the paper itself does not run this ablation.
Variant: in Wgxt, replace xt with ōt (computing the gate from attention’s own output). Is the gate still “input-dependent”? What information does this change cost the gate?
3.6 Keeping the attention output in FP32, and the kernel redesign it forced
Why a small thing like how many bits a float has matters
Decimals in a computer have finite precision: one BF16 number takes 2 bytes, FP32 takes 4. Fewer bits means less memory and faster arithmetic, but every step carries a rounding error. Normally errors are positive and negative and cancel each other out, but flash attention, to save memory, accumulates attention in blocks, and the rounding error of that accumulation is not symmetric — it is systematically biased to one side. The paper’s own phrase is biased rounding error. Biased errors do not cancel, they only pile up.
K3’s handling: during training it keeps the attention output in FP32, using the method of reference [98] to correct this biased rounding error. This sounds like nothing more than “spend a bit more precision”, but on a GPU it sets off a chain of consequences, and the paper writes the whole chain out — it is worth walking through.
tile and on-chip occupancy: when a GPU computes a large matrix it does not move the whole matrix in at once, it cuts it into small blocks (tiles) and moves them in one at a time to compute in shared memory(shared memory). Shared memory is a small piece of storage right next to the compute units, far faster and far smaller, and every inch of it is precious.
Going from 16 bits to 32 bits means the on-chip occupancy of the output tile doubles outright. Shared memory is only so big; if something gets fatter, something else has to give way. The paper’s solution is to redesign the training kernel: it is now with the KV staging buffers(KV staging buffers, the transit area used when moving K and V from GPU memory onto the chip) that the fattened output tile is made to overlap, instead of with the query tile as before. The shared memory freed up goes to a deeper KV pipeline — more K and V blocks in flight at once, transfer and compute overlapping better, compute units waiting less. The paper’s conclusion: this buys higher training throughput instead.
There is one step the paper leaves out
The paper does not explain why overlapping with the query tile will not do, and gives no concrete number for the throughput gain. A reasonable guess: the query tile is read repeatedly for the duration of the whole block computation, so its lifetime spans everything, whereas the KV staging buffers are used in a rolling, pipelined way, so their lifetime is complementary to the output tile’s and the two can share the same space. But this is this site’s guess, not what the paper says.
Someone says: “Change the attention output from BF16 to FP32, the occupancy doubles, and training must get slower.” But the paper says that combined with the kernel redesign this change raises training throughput instead. Construct a concrete scenario explaining how that is possible, and state what premises this counterexample needs in order to hold.
Premises: (one) the original kernel really is memory/latency bound rather than arithmetic bound; (two) the lifetimes of the output tile and the KV buffers really are complementary, so the two can safely share the same shared memory; (three) the extra FP32 write-back bandwidth does not become a new bottleneck. If even one of the three fails, “higher precision is faster instead” no longer holds — so this is not a universal law but an engineering win against one specific bottleneck. The paper itself gives no throughput number, so this site cannot verify the size of the gain.
Variant: if FP32 were enabled only at inference (not in training), would this kernel redesign still make sense? How do inference and training differ in how they use shared memory? (Hint: think about the decode phase of inference processing only one new token at a time.)
3.7 Reconciliation time: 69 + 24 = 93, but 69 ÷ 24 = 2.875
Now pull out the rows of Table 1 in the paper that bear on this chapter and do a real check. The point of this section is not to memorise numbers, it is to practise one thing: given a table, can you reconstruct the architecture from it.
| Item | Kimi K2 | Kimi K3 | Δ |
|---|---|---|---|
| Layers (#Layers) | 61 | 93 | ↑52% |
| Hidden dimension | 7,168 | 7,168 | = |
| Attention heads | 64 | 96 | ↑50% |
| Attention mechanism | MLA | Hybrid KDA–MLA | — |
| Attention layer make-up | 61 MLA | 69 KDA + 24 MLA | — |
| Training context length | 128K | 1M | 8× |
Derive it yourself: work back from “69 KDA + 24 MLA” to what the backbone looks like
Start with addition: what is 69 + 24? Which row of the table does that number match? What does the match mean?
Think it through, then look
69 + 24 = 93, exactly the “Layers” row. This reconciliation matters: it tells you that every layer in K3 is an attention layer — the “Attention layer make-up” row covers all 93 layers, with no third kind of layer mixed in. Whenever you check an architecture, the first step is always to confirm that the total is conserved — otherwise every step after it is built on sand.
The paper says the ratio is 3:1. So 69 ÷ 24 ought to equal 3. Work it out: what does it equal?
Think it through, then look
69 ÷ 24 = 2.875, not 3. You have to stop here; do not rush to say “close enough to 3”. A gap of 0.125 looks small, but layer counts in an architecture are integers, and when integers do not match there is always a structural reason — it cannot be “rounding”. What made this worth chasing in the first place is a rule of thumb: when the ratio the paper gives and the numbers in the table do not match, there is usually a boundary term that has not been counted.
Think back to §2.1: besides “3 KDA + 1 MLA, repeated”, what else did it say? Take the layer that sentence refers to out on its own, and compute the ratio again.
Think it through, then look
The sentence is: “one extra Gated MLA layer is placed at the end of the backbone, so that the last layer always performs global attention.” That layer belongs to no block. Subtract it from 24: 24 − 1 = 23 MLA layers belong to blocks. Compute the ratio again: 69 ÷ 23 = 3, exactly. That is the whole source of the 2.875 — the 3:1 ratio holds only for the “repeated part”, and the layer at the end is a boundary correction outside the cycle.
Now work back to the number of blocks and the total layer count, and check that it closes.
Think it through, then look
Number of blocks B = 23 (because each block has 1 MLA). Total layers = 4 × 23 + 1 = 92 + 1 = 93 ✓. KDA = 3 × 23 = 69 ✓, MLA = 23 + 1 = 24 ✓. All three numbers close, which means your reconstruction of the architecture is self-consistent. Note: the number “23 blocks” is nowhere written down directly in the paper — you worked it out yourself — but it is uniquely determined by Table 1.
A last question: if the authors had not added that layer at the end, would the property “the last layer is global attention” still hold?
Think it through, then look
It would — because the last layer of every block is already a Gated MLA (the pattern is 3 KDA first, then 1 MLA). So if the backbone ended on exactly one complete block, layer 92 would already be global attention. That raises a question the paper does not answer: in that case, what is the real motivation for adding layer 93? Maybe to reach a particular total layer count, maybe to let the last layer's representation go through one more global integration before it enters the output head, maybe it is just better empirically. The paper states only the practice and the claim that it “ensures the last layer performs global attention” — it gives no reason and no ablation. This is the place in this chapter that most deserves a question mark.
Synthesis question. K3 has 93 layers, 24 of them Gated MLA, hidden dimension 7,168, training context 1M tokens. Now suppose someone proposes: “change the ratio from 3:1 to 1:1, so the model is stronger on long text.” Answer three things: (a) after the change, roughly how many MLA layers are there? (b) roughly how many times the original would the KV cache at inference become? (c) apart from the cache, which of the designs covered in this chapter does this change disturb? And which one of them breaks outright?
(b) the number of layers that must cache latent vectors goes from 24 to about 46, so the KV cache nearly doubles (about 1.9×). Note that this uses the relation “cache size is proportional to the number of MLA layers” — the state of a KDA layer is fixed size and does not grow with context length, so at long context almost all of the cache comes from the MLA layers.
(c) at least three things are disturbed. First, compute: global attention is quadratic in length, so doubling the MLA layers nearly doubles the attention compute at long context, and at 1M tokens that is very expensive — which makes “1:1 gives stronger long-text ability” suspect in itself; what it more likely buys is a higher long-text cost. Second, the premise of NoPE is weakened: position information comes entirely from the KDA layers, and the KDA layers drop from 69 to about 46, a third fewer position-sensitive layers, while the MLA layers still have no positional encoding at all — this is the one closest to “breaking outright”, because it is written into the paper explicitly as a dependency (the paper's words: the KDA layers in between provide a position-sensitive and recency-aware mixture). Third, the rule about the patched layer at the end has to be re-derived (the formula 4B+1 = 93 no longer applies).
The thing to watch most: the paper gives no ablation on the ratio at all, so “what happens if you change it to 1:1” is something you can only reason about at the level of mechanism, with no experimental basis. Any claim that “1:1 is definitely better/worse” lacks evidence in this material.
Variant: take the opposite proposal — “change it to 7:1 and save half the cache”. Analyse it with the same three questions. Which direction carries the risk that is harder to notice? (Hint: a cache blow-up throws an error immediately, whereas position information running short only makes long-text quality quietly worse.)
Chapter summary
K3 widens the flow of information along three dimensions: sequence length goes to hybrid attention (this chapter), network depth goes to AttnRes (Chapter 4), model width goes to Stable LatentMoE. In the sequence direction the two mechanisms alternate whole layer by whole layer: each block is 3 KDA plus 1 Gated MLA, ratio 3:1, repeated across the network; then one more Gated MLA is added at the end of the backbone, so the last layer is certainly global attention. Table 1's “69 KDA + 24 MLA / 93 layers” is exactly the arithmetic fingerprint of 4 × 23 + 1.
The Gated MLA half does three things. It compresses the cache: only the latent vector ct = Wcxt is cached, and the keys and values are rebuilt with learned up-projections when attention is computed — what it saves is memory, not compute. It removes positional encoding: every MLA layer uses NoPE, position information is supplied implicitly by the KDA layers in between, and this incidentally avoids the trouble of retuning the RoPE frequency base or applying YaRN when the context is extended. It adds a full-rank output gate (Eq. 7), which lets each token decide channel by channel how much it takes in from global attention. On top of that, the attention output is kept in FP32 during training to correct flash attention's biased rounding error; the cost is that the on-chip footprint of the output tile doubles, so the kernel was changed to overlap with the KV staging buffers instead, which in turn bought higher training throughput.
Places this chapter marks as doubtful or as unexplained by the paper: the 3:1 ratio has no ablation, and the evidence sits in the cited Kimi Linear work rather than in this paper; “a global attention layer must be added at the end” has a practice but no reason (and every block already ends in an MLA anyway); the dimension of the MLA latent vector ct is not given; Eq. 7 is missing one RMSNorm step compared with KDA's Eq. 6, and the paper does not explain it; why the kernel cannot overlap with the query tile, and how much throughput the redesign brings, are never accounted for; NoPE comes with a design rationale only, no controlled experiment; the 2.5× scaling efficiency is the paper's own claim and is a packaged result of architecture, data and training recipe.
第4章 Attention Residuals:让「深度」也用上注意力
一句话导语:Transformer 当年用注意力取代了 RNN 沿时间的顺序传递,AttnRes 把同一套方法论搬到深度上——让第 40 层不必顺着残差链一路继承,而是像挑选 token 一样,从所有先前层里挑着取。这一章讲清这个类比为什么成立、在哪失效,以及为什么它最后必须被「块化」才养得起。
学完这一章你应该能做到
- 说清标准残差连接为什么是个瓶颈,并把它和 RNN 沿时间的瓶颈一一对应起来
- 逐符号读懂式 8 和式 9,解释伪查询 ql = wl 不依赖输入,为什么注意力权重仍然是数据相关的
- 解释为什么真正的代价是 O(Ld) 显存而不是 O(L2d) 算力
- 说清式 10 里「块内第 1 层」和「第 2 层及以后」为什么写法不一样
- 自己核对:93 层怎么切成 8 块、为什么末块是不满的、为什么算上词嵌入是 9 个
- 指出 Block AttnRes 相对全注意力残差牺牲了什么
进门先记住一件事
「block(块)」这个词在论文里有两个互不相同的含义。第3章里的 block 是 4 层(3 个 KDA + 1 个 Gated MLA);本章 Block AttnRes 里的 block 是 12 层。它们是两套独立的划分,本章出现的「块」一律指后者。
4.1 灵魂所在:把「时间上的老问题」搬到「深度」上
标准残差连接不够用在哪
先说清什么是残差连接(residual connection):深层网络里每一层不直接输出新结果,而是输出「上一层的结果 + 我这层算出的修正量」,即 hl = hl−1 + fl(hl−1)。这样梯度能顺着加号一路传回去,网络才堆得到上百层。
代价是:所有先前的信息都被压进一个状态 hl 沿深度往下传。第 40 层想用第 3 层的某个特征,只能指望它在中间 36 次加法里没被冲淡。论文的原话是:这是一个瓶颈,让人想起 RNN 沿时间的瓶颈。
这个类比是本章的灵魂。RNN(Recurrent Neural Network,循环神经网络)处理句子是从左到右一个词一个词读,每读一个就更新一次隐藏状态;到第 100 个词时,前面 99 个词的信息全都挤在那一个状态里,想调用第 3 个词的细节只能希望它还没被冲掉。序列建模领域怎么解决的?Transformer 用注意力取代了循环:每个位置不再只能看「上一步传下来的状态」,而是用数据相关的权重直接访问所有先前位置,需要谁就给谁高权重。
把这段话里的「位置」全部换成「层」,就是 AttnRes 的全部想法。论文写得非常直接:AttnRes 把同样的方法论用到深度上——每一层从所有先前层里有选择地检索表示,而不是均匀地累加它们。
打个比方
标准残差像传话游戏:每个人只能听前一个人说,再把自己的补充加进去传给下一个。传到第 40 个人时,第 3 个人说的原话早就面目全非。AttnRes 则是给第 40 个人一份所有人的发言记录,他自己决定重点听谁的。
类比失效处:传话游戏里「听谁的」是人临场判断,而 AttnRes 的查询是层的固定参数(下一节会看到),更像「第 40 号听众有一个长期固定的口味」。另外,发言记录不是免费的——把所有层的输出都留着,正是本章后半段要解决的显存问题。
这不是本文提出的方法
AttnRes 在论文里带着引用 [57](Kimi Team 的 Attention Residuals 预印本)。也就是说,K3 是采用了这个模块,方法本身的完整实验证据在那篇工作里。本章后面会看到,「N ≈ 8 就能恢复大部分收益」这条实证结论同样是引用 [57],不是本报告的实验。
把下面四个概念在「序列维度」和「深度维度」之间配对:(甲)RNN 的隐藏状态;(乙)Transformer 的注意力;(丙)标准残差连接;(丁)AttnRes。哪两个是「同一个问题」,哪两个是「同一个解法」?
变式:类比可以推得更远吗?序列维度上有「因果掩码」(一个词只能看前面的词),深度维度上的对应物是什么?它是天然成立的还是需要额外施加?
4.2 全注意力残差:伪查询、键值和 softmax 核(式 8、式 9)
要把注意力搬到深度上,得先回答三个问题:谁来当查询?谁来当键和值?权重怎么算?论文逐个给了答案。
伪查询(pseudo-query):对每一层 l,定义一个该层专属的、可学习的查询向量 ql = wl ∈ ℝd。请特别注意「pseudo(伪)」这个字的含义:在普通注意力里,查询是由当前输入算出来的(q = Wqx);而这里的 ql 不依赖任何输入,它就是这一层自己的一组参数,训练完之后固定不变。
键和值则来自各层的输出,两者取同一个东西:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| l | 当前正在计算的层号 | 「我是第几层」 |
| i | 被检索的源的编号,从 0 数到 l − 1 | 「我可以翻阅的第 i 份材料」 |
| h1 | 词嵌入(token embedding) | 最原始的、没被任何层加工过的输入 |
| fi(hi) | 第 i 层这个模块本身算出来的输出 | 「第 i 层的贡献」,注意是模块输出而非累加后的状态 |
| ki, vi | 第 i 个源的键与值,二者相等 | 「这份材料的索引」和「材料内容」是同一份东西 |
| ql = wl | 第 l 层的可学习伪查询,d 维(K3 中 d = 7,168) | 「第 l 层的长期口味偏好」 |
i = 0 这一项要单独拿出来说:它把词嵌入也放进了可检索的源里。这意味着无论网络多深,任何一层都有一条直达最原始输入的通道,永远不必担心原始信息在中途被磨没了。这个细节在下一节的块化里会被专门保留。
接下来是权重。论文用的是 softmax 核 φ(q, k) = exp(q⊤ RMSNorm(k)),其中 RMSNorm 的作用是防止那些输出幅值大的层主导权重。然后归一化:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| φ(q, k) | 核函数 exp(q⊤RMSNorm(k)):先把 k 归一化,再和 q 做点积,最后取指数 | 「这份材料对我有多合胃口」的未归一化打分,恒为正 |
| q⊤RMSNorm(k) | 两个向量的点积(⊤ 表示转置,把列向量放倒好做乘法) | 方向越一致,分越高 |
| RMSNorm | 把向量按自身的均方根缩放到统一尺度 | 「只看方向,不看嗓门大小」 |
| αi→l | 第 i 层流向第 l 层的注意力权重,所有 i 加起来等于 1 | 第 l 层分配注意力的「预算比例」 |
| Σj=0l−1 | 对所有可用的源求和(分母就是归一化项) | 把所有打分加起来当分母,正是 softmax 的做法 |
| hl | 第 l 层的输入状态,是所有先前源的加权平均 | 「我按自己的口味把材料混了一份摘要」 |
为什么非要在核函数里塞一个 RMSNorm
因为 exp 是指数函数,对输入极其敏感。某一层若输出幅值特别大,它和任何查询的点积都会很大,取 exp 后直接把分母吃掉,α 几乎全给它,其他层被挤到接近 0——AttnRes 就退化成「所有层都只读那个大嗓门层」,选择性荡然无存。加上 RMSNorm 之后比的是方向而非幅值,想拿高权重必须内容对得上。论文的原话正是:RMSNorm 防止大幅值输出的层主导权重。
最后是代价。论文分得很清楚:网络深度是适中的(L < 100),所以这个完整形式的 O(L2d) 算术开销是负担得起的;真正的实际开销是 O(Ld) 的显存——要把所有层的输出一直留着——以及在流水线并行下的跨阶段通信。
给这两个复杂度一点尺度感
用 K3 的数字自己算一遍(以下是我们的量级估算,论文没有给这些数字):L = 93、d = 7,168。算术那边,每个 token 大约要做 93 × 94 ÷ 2 ≈ 4,371 次「查询-键」配对,每次是 7,168 维点积,合计约 3,100 万次乘加,即约 6,300 万次浮点运算;而 K3 每个 token 激活 104.2B 参数,光前向就是约 2,080 亿次浮点运算——AttnRes 的算术占比大约万分之三,确实可以忽略。
显存那边完全是另一回事:每个 token 要留住 93 × 7,168 ≈ 66.7 万个数,按 BF16 每个 2 字节算,约 1.3 MB / token。一条 100 万 token 的序列就是 1.3 TB 量级——单卡显存是 80 GB 这个数量级,根本装不下。这就是为什么论文说瓶颈是显存不是算力。
答辩:如果我是审稿人
你用「Transformer 的注意力能用数据相关的权重访问所有先前位置」来论证 AttnRes 的合理性。可你的伪查询 ql = wl 是一个训练完就固定的参数,跟输入毫无关系。那你的 α 到底哪里「数据相关」了?这个类比是不是偷换概念?
参考防守(先自己组织语言再看)
防守的关键是:注意力权重由一对向量决定,查询固定不代表权重固定。α 的计算式是 exp(ql⊤RMSNorm(ki)),其中 ki = fi(hi) 是第 i 层对当前这个 token 算出来的输出——它当然随输入变化。所以同一个第 40 层,处理不同 token 时会得到不同的 α 分布:查询是「固定的口味」,但「菜」每次都不一样,点到哪道菜自然不同。
不过审稿人的质疑并非全无道理,诚实的回答要承认一个真实的削弱:标准注意力里查询和键都随输入变,AttnRes 只有键随输入变,表达力严格更弱——它无法表达「这个 token 需要第 3 层,那个 token 需要第 20 层,而且第 3 层和第 20 层的输出恰好长得一样」这类需要靠查询侧区分的情形。所以准确的说法是:AttnRes 是注意力的一个受限版本,权重仍是数据相关的,但相关性只经由键侧进入。论文没有讨论这个取舍,也没有给「让查询也依赖输入」的对照实验。至于为什么这么设计,一个合理的猜测是省参数和省计算(查询若依赖输入,每层要多一个 d×d 的投影矩阵),但这是我们的推测,论文未说明。
如果把 φ 里的 RMSNorm 去掉,写成 φ(q, k) = exp(q⊤k),训练中最可能出现什么现象?为什么这个现象会让 AttnRes「白做」?
为什么算「白做」:AttnRes 的全部价值在于有选择地检索——不同层根据内容挑不同的源。一旦权重被幅值绑架,所有层都只读同一个源,这既不是「选择性检索」,也不比标准残差好,反而多付了显存。RMSNorm 把每个键缩放到统一尺度,使得点积只反映方向的一致程度,一个层要想被选中就必须内容真的对得上。论文的措辞——「RMSNorm 防止大幅值输出的层主导权重」——说的就是这件事。
变式:如果改成对查询 ql 也做 RMSNorm,会解决同样的问题吗?(提示:查询是每层一个固定参数,它的幅值影响的是这一层 α 分布的「尖锐程度」,而不是层与层之间谁压过谁。)
有人断言:「因为 ql 是固定参数,所以对同一个模型来说,第 40 层从第 3 层拿到的权重 α3→40 是一个常数,跟输入什么句子无关。」请构造一个具体的反例说明这句话是错的;然后再构造一个特殊情形,在那个情形下这句话反而是对的。
反过来的特殊情形:假如第 3 层是一个常数层——不管输入什么都输出同一个向量(比如权重全为 0 再加一个固定偏置),并且其余所有层也都是常数层。这时所有键都与输入无关,α 才真的成为常数。更弱一点的版本:只要所有 fi 的输出方向都与输入无关(RMSNorm 之后完全相同),α 也会是常数——注意这里只需要方向不变,因为 RMSNorm 已经抹掉了长度。
这个反例的价值:它精确定位了 AttnRes 的数据相关性来自哪一侧。答案是键侧。上面那个答辩框讨论的正是「只有键侧相关」带来的表达力限制。
变式:再构造一个情形,使得虽然键随输入变化,但 α 几乎不随输入变化。(提示:想想如果 ql 的幅值极小会怎样——点积全都接近 0,exp 全都接近 1。)
4.3 Block AttnRes:把 93 层切成几个块(式 10)
为什么必须块化
上一节算过:完整形式的 O(Ld) 显存意味着每个 token 要留住 93 层全部的输出。而且在流水线并行(把网络的不同层放到不同 GPU 上,像流水线一样接力)下,这些层输出还得跨设备传输——通信量同样是 O(Ld)。算术可以忽略,显存和通信不行。所以论文说:为了降低这个开销,我们把层分块。
做法分三步。第一步,把 L 层切成 N 个块,每块 S = L/N 层。第二步,块内不再保留每层的输出,而是把它们求和规约成单个块表示:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| ℬn | 第 n 块所包含的层号集合 | 「第 n 组里有哪几层」 |
| bn | 第 n 块的块表示:块内所有层输出之和 | 把一组人的发言压成一份会议纪要 |
| bni | 块内前 i 层的部分和 | 「纪要写到第 i 个人为止的半成品」 |
| b0 = h1 | 第 0 号「块」被特意定成词嵌入本身 | 永远保留一条通往原始输入的路 |
| S = L/N | 每块的层数 | 「几个人合写一份纪要」 |
第三步,跨块才做全注意力,而且只对 N 个块级表示做。具体到块 n 里的第 i 层,值矩阵是:
V = [b0, b1, …, bn−1, bni−1]⊤ (i ≥ 2,块内后续层)
| 符号 | 是什么 | 直觉 |
|---|---|---|
| V | 值矩阵:把当前层可用的所有「源向量」摞成的一张表 | 「我现在能翻阅的材料清单」 |
| b0, …, bn−1 | 已经完工的、前面所有块的块表示(外加词嵌入) | 已归档的历次会议纪要 |
| bni−1 | 当前块内、我前面那 i−1 层的部分和 | 本次会议记到我发言之前的草稿 |
| ⊤ | 转置符号,表示这些向量按行摞成矩阵 | 只是排版方式,不改内容 |
| 键与权重 | 仍按式 8、式 9 计算(论文明确这样说) | 换的只是「有哪些源」,打分规则没变 |
为什么两种情况写法不一样?关键在于「当前块内已经完成了几层」。如果我是块内第 1 层,这个块里在我之前一层都没有,部分和是空的,没有 bn0 可放(放零向量只会白占一个注意力名额、白分走一份权重)。从第 2 层起,我前面已有 i−1 层的输出,把它们的部分和挂进清单,就能读到「同块内刚刚发生的事」。注意这带来一个不对称:块内的近期信息只以「部分和」这一整团的形式出现,无法拆开、单独给块内第 1 层和第 2 层不同的权重。这正是块化付出的代价。
最后,论文说最终的输出层聚合全部 N 个块表示——也就是说,模型最顶端仍然能一次性看到所有块级摘要。
式 10 中,块 n 的第 1 层的值矩阵里没有 bn 的任何成分,而第 3 层有 bn2。请回答:(甲)为什么第 1 层不能放 bn 本身?(乙)第 3 层为什么放的是 bn2 而不是 bn?(丙)从第 1 层到第 S 层,值矩阵的行数怎么变化?
(乙)同理,第 3 层只能读已经算完的部分,即块内前 2 层的和 bn2。上标 i−1 是一个严格的因果约束——它在深度方向上扮演的角色,恰好等同于序列注意力里的因果掩码。
(丙)块 n 的第 1 层有 n 行(b0 到 bn−1),第 2 层起变成 n+1 行(多了那个部分和),并且之后一直是 n+1 行——因为不管块内走到第几层,来自当前块的贡献永远只占一行(内容在更新,行数不变)。这正是块化能把开销压到 O(Nd) 的技术原因:任何一层的值矩阵行数都不超过 N+1,与总层数 L 无关。
变式:如果把式 10 改成「块内每层单独作为一行保留,跨块才求和」,行数会变成多少?这样做还能把开销压到 O(Nd) 吗?(提示:算一算最后一块最后一层的行数。)
4.4 块化换来了什么:显存、通信,以及推理时的状态
论文把收益列成三条,值得逐条对着看。
第一条,显存和通信从 O(Ld) 降到 O(Nd)。这是块化的初衷。用 K3 的数字感受一下(以下为我们的量级估算,论文未给出这些数字):不块化时每 token 要留 93 × 7,168 ≈ 66.7 万个数(BF16 约 1.3 MB);块化后只需留 9 个源 × 7,168 ≈ 6.45 万个数(约 129 KB),大约降到十分之一。流水线并行下跨阶段要传的东西同样按这个比例缩小。
第二条,块结构限定了推理时的状态大小。用完整形式的话,走到第 93 层时手里必须攥着 92 份历史层输出;块化之后永远只有至多 N+1 份。状态有了上界,显存规划才做得出来。
第三条,更好地合并两路结果。论文说,块结构使得并行算出的块间结果可以通过 online softmax 与顺序累加的块内部分和更好地合并,显著降低推理时间开销。拆开看:块间的 b0, …, bn−1 是已经定型的,可以一次性批量算完;块内的部分和却是边走边长的,每过一层就变一次。两路时间特性完全不同,分开算再合并才划算。
online softmax(在线 softmax):softmax 的分母是所有打分之和,朴素写法必须先把全部打分算完才能归一化。online softmax 则边算边归一化:一路维护「目前见过的最大值」和「目前累计的和」,来一批新分数就按比例修正已有结果。它的价值是让两批分别算出的部分结果能正确合并,不必等齐——正好对上「块间批量并行 + 块内顺序更新」这个模式。
答辩:如果我是审稿人
你在 §2.2 开头论证说,标准残差的毛病是「均匀累加」,要换成「有选择地检索」。可你的 Block AttnRes 在块内做的事情就是 bn = Σ fj(hj)——原封不动的均匀求和。那你在块内不就是退回标准残差了吗?既然如此,你凭什么说 AttnRes 解决了那个瓶颈?
参考防守(先自己组织语言再看)
这个质疑抓得很准,防守必须先承认:是的,块内确实退化成了等权求和,块化就是在这里做的妥协。然后从三个层面辩护。
其一,粒度不同不等于没解决。原来的瓶颈是「93 层信息全挤进一个状态」,现在变成「至多 12 层挤成一份纪要,纪要之间可以有选择地检索」。压缩比从 93:1 变成 12:1,跨块的选择性是全新的能力——第 80 层可以给第 1 块高权重、给第 5 块低权重,这在标准残差里做不到。
其二,b0 = h1 这个设定不是随手写的:它保证词嵌入永远单独占一个源,永远不会被求和稀释掉。作者显然清楚「求和会丢东西」,所以专门把最重要的那一项拎出来了。
其三,这是有意的代价而非疏漏:论文明确说块化的目的是把 O(Ld) 压到 O(Nd),并且引用 [57] 说 N ≈ 8 就能恢复大部分收益。「大部分」这个词本身就承认了损失的存在。
最后要坦白的缺口:这份报告里没有给出 K3 规模上「完整 AttnRes vs Block AttnRes」的对照数据,「大部分收益」的证据在被引用的工作里,不在这里。审稿人若追问「在 2.78T 规模上还成立吗」,本文回答不了。
设 L = 93、d = 7,168、N = 8(算上词嵌入源共 9 个)。(甲)估算完整 AttnRes 与 Block AttnRes 每个 token 需要留存的数字个数,并算出比值。(乙)如果有人把 N 从 8 提到 31(每块 3 层),显存收益会怎么变?他还能享受到块化的好处吗?
(乙)N = 31 时每块 3 层,需留存 32 × 7,168 ≈ 22.9 万个数,相对完整形式只省到约 2.9 倍。收益大幅缩水,因为 N 越接近 L,块化就越接近完整形式(N = L 时两者完全一样)。他仍能享受一点好处,但已经不足以解决论文关心的显存与通信问题。
这里要看到的规律是:N 是一个在「保真」和「省资源」之间取舍的旋钮,N 小则省得多、块内损失大;N 大则保真但省不下来。论文引用 [57] 说 N ≈ 8 能恢复大部分收益,K3 就取在这个点上。提醒:以上数字是我们按论文的复杂度式子自己算的量级,论文没有给出具体的显存数值。
变式:取 N = 1(整个网络只有一个块)会发生什么?把式 10 代进去,看看它退化成了什么结构。这个结论和 4.1 节的动机对得上吗?
4.5 对账:93 层是怎么切成 8 块的
论文给的实证结论是:N ≈ 8 在各种模型规模上都能恢复大部分收益(引用 [57])。K3 据此做的划分,原文是这么写的:把它的层划分成 8 个块、块大小为 12 层,产生一个不完整的末块,算上词嵌入层共 9 个块。
这句话读快了会出事,必须自己算一遍。
自己推一遍:93 层、块大小 12、8 个块,这三个数能同时成立吗
先做最直白的乘法:8 个块 × 每块 12 层 = 多少层?和 93 对得上吗?
想好了再看
8 × 12 = 96,比 93 多了 3 层。所以「8 块 × 12 层」不可能全部是满的——这正是论文那句「giving a partial final block(产生一个不完整的末块)」要交代的事。碰到这种对不上的数,第一反应不该是「作者写错了」,而是去找哪一项不是满的。
改用除法:93 ÷ 12 等于几?余数是多少?
想好了再看
93 ÷ 12 = 7 余 9。也就是 7 个满块(12 层)+ 1 个只有 9 层的末块,7 × 12 + 9 = 84 + 9 = 93 ✓。所以「8 个块」的准确含义是:7 个满块加 1 个不满的末块,合计 8 个。
那「共 9 个块」的第 9 个是什么?
想好了再看
是词嵌入层,也就是式 10 里的 b0 = h1。它不是真正的一个「层块」,而是被当成一个额外的信息源永远挂在清单最前面。8 + 1 = 9 ✓。这也解释了为什么上一节估算显存时我们用的是 9 而不是 8。
再往前想一步:12 这个块大小和第3章的 4 层注意力 block 有什么关系?
想好了再看
12 = 3 × 4。第3章算过,注意力的周期是 4 层(3 KDA + 1 Gated MLA)。如果两种划分是对齐的,那么一个 12 层的 AttnRes 块恰好装下 3 个完整的注意力 block,也就是 9 个 KDA + 3 个 MLA。更妙的是末块:84 层 = 21 个注意力 block,剩下 9 层 = 2 个完整注意力 block(8 层)+ 骨干末尾补的那 1 个 Gated MLA,正好 9 层,而 21 + 2 = 23 个 block 也和第3章推出的数字对上。
但必须说清楚:论文从来没有说这两种划分是对齐的,也没有说 AttnRes 的块边界落在哪里。上面这套对账是我们的推断,它只证明「对齐」这个假设与所有已知数字自洽,不证明作者就是这么实现的。
原文这句话本身是有歧义的
论文写的是「partition its layers into 8 blocks with 12-layer size」。字面读是「8 个块,每块 12 层」,乘出来是 96,和 93 层对不上。只有结合后半句「giving a partial final block」才能确定是 7 个满块 + 1 个 9 层的末块。论文没有明确写出末块的层数是 9,这个数是我们从 93 − 7×12 推出来的。同样,论文也没有说 AttnRes 的块边界与注意力 block 的边界是否对齐、末块里具体是哪 9 层。
综合题。已知 K3 有 93 层、AttnRes 分成 8 块(前 7 块各 12 层)、d = 7,168,且第3章已推出骨干是 (3 KDA + 1 Gated MLA) × 23 + 1 个 Gated MLA。请回答:(甲)末块有几层?(乙)在「两种划分对齐」的假设下,第 5 块里各有几个 KDA 层和几个 MLA 层?(丙)第 93 层(最后一层,Gated MLA)在做 AttnRes 时,它的值矩阵最多有几行?(丁)如果把 AttnRes 块大小从 12 改成 6(N ≈ 16),(甲)到(丙)三个答案分别怎么变?这样改的利与弊各是什么?
(乙)12 层 = 3 个完整的注意力周期(每周期 3 KDA + 1 MLA),所以第 5 块有 9 个 KDA + 3 个 Gated MLA。前提是两种划分对齐,而论文没有说明这一点。
(丙)第 93 层属于第 8 块(末块),且不是该块第 1 层,所以值矩阵包含 b0 到 b7(8 行)加上末块内部分和(1 行),共 9 行。注意这个数和「共 9 个块」是一致的,也印证了 4.3 节的结论:行数上界是 N+1,与 L = 93 无关。
(丁)块大小改成 6 后:93 ÷ 6 = 15 余 3,变成 15 个满块 + 1 个 3 层末块,共 16 块,末块 3 层;6 层 = 1.5 个注意力周期,不再是整数个周期,块边界会切在注意力 block 中间,「对齐」这个假设直接失效;最后一层的值矩阵最多 16 + 1 = 17 行。
利:块内被等权求和吞掉的层从 12 降到 6,选择性更细,理论上更接近完整 AttnRes。弊:显存与通信从约 9 份升到约 17 份,几乎翻倍,块化的初衷被削掉一半;推理时的状态上界也随之变大;而且论文引用的实证是「N ≈ 8 已能恢复大部分收益」,继续加大 N 的边际回报很可能很小。要强调的是:本报告没有给 K3 规模上不同 N 的对照数据,上面关于「利」的判断是机制推断,不是实验结论。
变式:如果 K3 的层数不是 93 而是 96,用块大小 12 会得到什么?那时论文里「不完整的末块」这句话还需要吗?再想一层:作者是先定层数还是先定块大小?(提示:93 = 4 × 23 + 1 这个式子是被第3章的注意力结构逼出来的。)
4.6 后面还会再见到它:训练时的 checkpointing 与推理时的两阶段内核
AttnRes 的开销不只是「显存里多存几份东西」,它同时牵动训练系统和推理系统的实现。论文在后面的系统章节给了两个配套优化,本章先埋线头,第10、11章会展开。
训练这边:块表示在块的边界层生成一次,之后被块内所有层共享、常驻 GPU;而且整个 AttnRes 计算被 checkpointing(重计算:反向时不保存中间结果,临时再算一遍)整个包住,所以每一层为反向保存的激活与标准残差架构完全相同——也就是说,引入 AttnRes 不会让训练显存额外膨胀。流水线并行下则采用基于缓存的流水线通信,只增量传输新生成的块。
推理这边:Block AttnRes 采用两阶段调度——一次批量的块间遍历,每个块只读一次缓存的块表示;之后每层通过 online softmax 合并把块内部分和折进来。这正是 4.4 节第三条收益的工程落地形式。
请构造两个极端情形:(甲)一个使 Block AttnRes 与完整 AttnRes 严格等价的参数设置;(乙)一个使二者差别最大的情形——要具体说出「块化到底丢掉了什么能力」,最好举一个它做不到的具体任务。
(乙)取 N = 1(整个网络一个块)。这时跨块注意力只剩下 b0(词嵌入)和块内部分和两个源,几乎完全退回标准残差的「均匀累加」。丢掉的具体能力是:无法在同一个块内部做出区分。举个具体任务——假设第 3 层学到了「识别引号」,第 9 层学到了「识别缩进」,而第 40 层在处理代码时只想要缩进、不想要引号。完整 AttnRes 可以给第 9 层高权重、给第 3 层接近 0 的权重;但只要 3 和 9 落在同一个块里,它们已经被加成了一个向量,第 40 层只能整份要或整份不要,再也无法把它们分开加权。这就是块化的本质代价,也是上面答辩框里审稿人抓住的那一点。
顺带说明为什么 K3 取 N = 8:它落在两个极端之间,论文引用 [57] 称这个取值能恢复大部分收益——注意这是被引用工作的结论,本报告没有复现该对照实验。
变式:如果块内不用等权求和,而是用一组可学习的固定权重加权求和(每层一个标量),能不能挽回(乙)里丢掉的能力?为什么?(提示:那组权重会随 token 变化吗?和 4.2 节讨论过的「数据相关性来自哪一侧」联系起来想。)
本章小结
AttnRes 的出发点是一个类比:标准残差连接把所有先前信息压进单个状态 hl 沿深度传递,这个瓶颈和 RNN 沿时间的瓶颈是同一回事;序列建模领域早已用注意力解决过它,AttnRes 就把同一套方法论搬到深度上——每一层从所有先前层里有选择地检索,而不是均匀累加。
技术实现分两步。完整形式给每层一个可学习的伪查询 ql = wl(不依赖输入),把词嵌入和各层输出同时当作键和值(式 8),用带 RMSNorm 的 softmax 核算权重(式 9)。它的算术开销 O(L2d) 在 L < 100 时可以接受,真正的负担是 O(Ld) 显存和流水线并行下的跨阶段通信。Block AttnRes 把 L 层切成 N 块,块内求和成一份块表示、并特意令 b0 = h1 保住词嵌入,跨块才做全注意力(式 10 两种写法的差异来自因果性)。收益是显存与通信从 O(Ld) 降到 O(Nd)、推理状态有了上界、块间并行结果能用 online softmax 与块内顺序部分和更好地合并。K3 取 N ≈ 8:93 层切成 7 个满 12 层的块加 1 个 9 层的末块,算上词嵌入共 9 个源。
本章标记为存疑或论文未说明的地方:「8 blocks with 12-layer size」字面乘出来是 96,与 93 层不符,末块是 9 层这个数是我们推出来的,论文未明写;AttnRes 块边界与第3章的 4 层注意力 block 是否对齐,论文完全没说,本章第 4 步对账是我们的推断;「N ≈ 8 恢复大部分收益」引用的是文献 [57],本报告没有在 K3 规模上给出对照数据;「完整 AttnRes vs Block AttnRes」在 2.78T 规模上的差距没有数据;伪查询为什么不依赖输入(省参数?防过拟合?)论文未解释;本章所有显存与算力的具体数值都是我们按论文的复杂度式子做的量级估算,论文没有给出这些数字。
Chapter 4 · Attention Residuals: letting “depth” use attention too
In one sentence: Transformer once replaced the RNN's sequential passing along time with attention; AttnRes moves the same methodology onto depth — layer 40 no longer has to inherit everything down the residual chain, it picks from all earlier layers the way you pick tokens. This chapter makes clear why the analogy holds, where it fails, and why in the end it has to be “blocked” before you can afford to keep it.
After this chapter you should be able to
- Say clearly why the standard residual connection is a bottleneck, and line it up one for one with the RNN's bottleneck along time
- Read Eq. 8 and Eq. 9 symbol by symbol, and explain why the pseudo-query ql = wl does not depend on the input yet the attention weights are still data-dependent
- Explain why the real cost is O(Ld) memory and not O(L2d) compute
- Say clearly why Eq. 10 writes “layer 1 inside a block” and “layer 2 and after” differently
- Check for yourself: how 93 layers are cut into 8 blocks, why the last block is not full, and why counting the token embedding makes it 9
- Point out what Block AttnRes gives up relative to full attention residuals
Remember one thing before you go in
The word “block” has two different meanings in the paper. The block in Chapter 3 is 4 layers (3 KDA + 1 Gated MLA); the block in this chapter's Block AttnRes is 12 layers. They are two independent partitions, and every “block” in this chapter means the latter.
4.1 The heart of it: moving “an old problem in time” onto “depth”
Where the standard residual connection falls short
First, what a residual connection(residual connection) is: in a deep network each layer does not output a new result directly, it outputs “the previous layer's result + the correction my layer computed”, i.e. hl = hl−1 + fl(hl−1). Gradients can then travel all the way back along the plus sign, which is what lets a network stack to over 100 layers.
The price is that all earlier information is squeezed into one state hl and carried down along depth. If layer 40 wants some feature from layer 3, all it can do is hope the feature was not diluted by the 36 additions in between. The paper's own words: this is a bottleneck, reminiscent of the bottleneck of RNNs along time.
This analogy is the heart of the chapter. An RNN(Recurrent Neural Network) reads a sentence left to right, one word at a time, updating its hidden state at every word; by the 100th word the information from the previous 99 words is all crammed into that one state, and to call up a detail from the 3rd word you can only hope it has not been washed out. How did sequence modelling solve this? Transformer replaced recurrence with attention: each position is no longer limited to “the state handed down from the previous step” — it uses data-dependent weights to reach every earlier position directly, giving high weight to whichever one it needs.
Replace every “position” in that paragraph with “layer” and you have the whole idea of AttnRes. The paper puts it very directly: AttnRes applies the same methodology to depth — each layer selectively retrieves representations from all earlier layers instead of accumulating them uniformly.
An analogy
The standard residual is like a game of telephone: each person can only hear the person before them, adds their own bit, and passes it on. By the 40th person, what the 3rd person actually said is long unrecognisable. AttnRes instead hands the 40th person a transcript of what everyone said and lets them decide whose words to weigh.
Where the analogy breaks down: in telephone, “whose words to weigh” is a judgement the person makes on the spot, whereas the AttnRes query is a fixed parameter of the layer (you will see this in the next section) — more like “listener number 40 has one long-standing taste”. Also, the transcript is not free: keeping the output of every layer around is exactly the memory problem the second half of this chapter has to solve.
This is not a method this paper proposes
AttnRes comes with citation [57] in the paper (the Kimi Team's Attention Residuals preprint). That is, K3 adopts this module; the full experimental evidence for the method itself is in that work. Later in this chapter you will see that the empirical conclusion “N ≈ 8 recovers most of the gain” is also cited to [57], not an experiment in this report.
Pair the following four concepts across the “sequence dimension” and the “depth dimension”: (a) the RNN hidden state; (b) Transformer attention; (c) the standard residual connection; (d) AttnRes. Which two are “the same problem”, and which two are “the same solution”?
Variant: can the analogy be pushed further? The sequence dimension has a “causal mask” (a word can only see the words before it) — what is its counterpart along depth? Does it hold automatically, or does it have to be imposed on top?
4.2 Full attention residuals: pseudo-query, keys and values, and the softmax kernel (Eq. 8, Eq. 9)
To move attention onto depth you have to answer three questions first: who plays the query? who plays the keys and values? how are the weights computed? The paper answers them one at a time.
Pseudo-query: for each layer l, define a learnable query vector that belongs to that layer alone, ql = wl ∈ ℝd. Pay particular attention to what the word “pseudo” means here: in ordinary attention the query is computed from the current input (q = Wqx); the ql here does not depend on any input — it is simply a set of parameters belonging to this layer, fixed once training is done.
The keys and values come from the layer outputs, and the two are the same thing:
| Symbol | What it is | Intuition |
|---|---|---|
| l | the index of the layer being computed right now | “which layer am I” |
| i | the index of the source being retrieved, counting from 0 to l − 1 | “document number i that I may leaf through” |
| h1 | the token embedding | the rawest input, not yet processed by any layer |
| fi(hi) | the output computed by the module that is layer i itself | “layer i's contribution” — note this is the module's output, not the accumulated state |
| ki, vi | the key and the value of source i; the two are equal | “the index of this document” and “the document's contents” are one and the same |
| ql = wl | the learnable pseudo-query of layer l, d-dimensional (d = 7,168 in K3) | “layer l's long-standing taste” |
The i = 0 term deserves a separate mention: it puts the token embedding into the retrievable sources as well. That means however deep the network is, every layer has a channel straight to the rawest input and never has to worry about the original information being worn away en route. This detail is deliberately kept when the next section blocks things up.
Next, the weights. The paper uses the softmax kernel φ(q, k) = exp(q⊤ RMSNorm(k)), in which the job of RMSNorm is to stop layers with large-magnitude outputs from dominating the weights. Then normalise:
| Symbol | What it is | Intuition |
|---|---|---|
| φ(q, k) | the kernel function exp(q⊤RMSNorm(k)): normalise k first, take the dot product with q, then exponentiate | an unnormalised score for “how much this document suits my taste”, always positive |
| q⊤RMSNorm(k) | the dot product of two vectors (⊤ means transpose, laying the column vector on its side so it multiplies) | the more the directions agree, the higher the score |
| RMSNorm | scales a vector by its own root mean square onto a common scale | “look only at direction, not at how loud the voice is” |
| αi→l | the attention weight flowing from layer i to layer l; summed over all i it equals 1 | layer l's “budget shares” for allocating attention |
| Σj=0l−1 | a sum over all available sources (the denominator is the normalising term) | adding up every score to use as the denominator is exactly what softmax does |
| hl | the input state of layer l, a weighted average of all earlier sources | “I mixed the material into a summary that suits my taste” |
Why an RMSNorm has to be stuffed inside the kernel function
Because exp is an exponential function and is extremely sensitive to its input. If some layer's output magnitude is unusually large, its dot product with any query will be large, and after exp it eats the denominator outright: α goes almost entirely to that layer and the others are squeezed towards 0 — AttnRes degenerates into “every layer reads only the loud layer” and the selectivity is gone. With RMSNorm added, what gets compared is direction rather than magnitude, and a layer that wants high weight has to match on content. The paper's own words are exactly this: RMSNorm prevents layers with large-magnitude outputs from dominating the weights.
Finally, the cost. The paper separates it cleanly: network depth is moderate (L < 100), so the O(L2d) arithmetic cost of this full form is affordable; the real practical cost is the O(Ld) memory — every layer's output has to be kept around — plus the cross-stage communication under pipeline parallelism.
A sense of scale for these two complexities
Work it out yourself with K3's numbers (what follows is our own order-of-magnitude estimate; the paper does not give these numbers): L = 93, d = 7,168. On the arithmetic side, each token needs roughly 93 × 94 ÷ 2 ≈ 4,371 “query-key” pairings, each of them a 7,168-dimensional dot product, which comes to about 31 million multiply-adds, i.e. about 63 million floating-point operations; K3, meanwhile, activates 104.2B parameters per token, so the forward pass alone is about 208 billion floating-point operations — AttnRes's share of the arithmetic is around three parts in ten thousand, which really can be ignored.
Memory is another matter entirely: every token has to hold on to 93 × 7,168 ≈ 667,000 numbers, and at 2 bytes each in BF16 that is about 1.3 MB / token. A single sequence of 1 million tokens is on the order of 1.3 TB — a single card's memory is on the order of 80 GB, so it does not fit at all. This is why the paper says the bottleneck is memory, not compute.
Defend it: if I were a reviewer
You use “Transformer attention can reach all earlier positions with data-dependent weights” to argue that AttnRes is reasonable. But your pseudo-query ql = wl is a parameter that is frozen once training ends and has nothing to do with the input. So where exactly is your α “data-dependent”? Isn't the analogy a switch of concepts?
A defence to compare against (put it in your own words first)
The key to the defence: an attention weight is decided by a pair of vectors, and a fixed query does not mean a fixed weight. α is computed as exp(ql⊤RMSNorm(ki)), where ki = fi(hi) is what layer i computed for this particular token — which of course varies with the input. So one and the same layer 40 gets a different α distribution when it processes different tokens: the query is “a fixed taste”, but the “dishes” are different every time, so which dish gets ordered naturally differs.
Still, the reviewer's challenge is not baseless, and an honest answer has to admit a real weakening: in standard attention both the query and the key vary with the input, while in AttnRes only the key does, so the expressive power is strictly lower — it cannot express cases that have to be told apart on the query side, such as “this token needs layer 3, that token needs layer 20, and the outputs of layer 3 and layer 20 happen to look identical”. So the accurate statement is: AttnRes is a restricted version of attention; the weights are still data-dependent, but the dependence enters only through the key side. The paper does not discuss this trade-off, and gives no controlled experiment for “let the query depend on the input too”. As for why it is built this way, a reasonable guess is that it saves parameters and compute (if the query depended on the input, every layer would need one more d×d projection matrix), but this is our speculation; the paper does not say.
If you drop the RMSNorm from φ and write φ(q, k) = exp(q⊤k), what is most likely to happen during training? And why would that make AttnRes “wasted effort”?
Why this counts as “wasted effort”: the whole value of AttnRes lies in selective retrieval — different layers picking different sources according to content. Once the weights are hijacked by magnitude, every layer reads the same single source, which is neither selective retrieval nor better than the standard residual; it has merely paid extra memory. RMSNorm scales every key onto a common scale, so that the dot product reflects only how far the directions agree, and a layer that wants to be picked has to genuinely match on content. The paper's wording — “RMSNorm prevents layers with large-magnitude outputs from dominating the weights” — is saying exactly this.
Variant: if you also applied RMSNorm to the query ql, would that solve the same problem? (Hint: the query is one fixed parameter per layer; its magnitude affects how “sharp” that layer's α distribution is, not which layer overpowers which.)
Someone asserts: “because ql is a fixed parameter, then for one and the same model the weight α3→40 that layer 40 gets from layer 3 is a constant, independent of what sentence you feed in.” Construct a concrete counterexample showing this sentence is wrong; then construct a special case in which the sentence is in fact right.
The special case in the other direction: suppose layer 3 is a constant layer — whatever the input it outputs the same vector (weights all 0 plus a fixed bias, say) — and every other layer is a constant layer too. Then none of the keys depends on the input, and α really does become a constant. A slightly weaker version: it is enough that the direction of every fi output is independent of the input (identical after RMSNorm) for α to be constant — note that only the direction has to stay fixed here, because RMSNorm has already erased length.
What this counterexample is worth: it pins down exactly which side the data dependence of AttnRes comes from. The answer is the key side. The defence box above is discussing precisely the limit on expressive power that “only the key side is dependent” brings.
Variant: construct one more case in which the keys do vary with the input but α barely varies with it. (Hint: think about what happens if ql has a very small magnitude — the dot products are all close to 0 and the exponentials all close to 1.)
4.3 Block AttnRes: cutting 93 layers into a few blocks (Eq. 10)
Why it has to be blocked
We worked it out in the last section: the O(Ld) memory of the full form means every token has to hold on to the outputs of all 93 layers. And under pipeline parallelism (different layers of the network sit on different GPUs and hand off like a production line) those layer outputs also have to be transferred across devices — the communication volume is O(Ld) as well. The arithmetic can be ignored; the memory and the communication cannot. So the paper says: to reduce this cost, we partition the layers into blocks.
It is done in three steps. Step one: cut the L layers into N blocks of S = L/N layers each. Step two: inside a block, stop keeping each layer's output and instead sum them down into a single block representation:
| Symbol | What it is | Intuition |
|---|---|---|
| ℬn | the set of layer indices contained in block n | “which layers are in group n” |
| bn | the block representation of block n: the sum of the outputs of every layer in the block | compressing what a group of people said into one set of minutes |
| bni | the partial sum over the first i layers of the block | “the half-finished minutes, written up to person i” |
| b0 = h1 | “block” number 0 is deliberately defined to be the token embedding itself | always keeps one road open to the original input |
| S = L/N | the number of layers per block | “how many people co-write one set of minutes” |
Step three: full attention is done only across blocks, and only over the N block-level representations. Concretely, for layer i inside block n, the value matrix is:
V = [b0, b1, …, bn−1, bni−1]⊤ (i ≥ 2, the later layers inside the block)
| Symbol | What it is | Intuition |
|---|---|---|
| V | the value matrix: a table stacking up every “source vector” available to the current layer | “the list of material I can leaf through right now” |
| b0, …, bn−1 | the finished block representations of all earlier blocks (plus the token embedding) | the minutes of past meetings, already filed |
| bni−1 | the partial sum of the i−1 layers before me inside the current block | the draft of this meeting up to just before I speak |
| ⊤ | the transpose sign, meaning these vectors are stacked as the rows of a matrix | only a matter of layout, it does not change the content |
| keys and weights | still computed by Eq. 8 and Eq. 9 (the paper says so explicitly) | all that changed is “which sources exist”; the scoring rule did not |
Why are the two cases written differently? It comes down to “how many layers inside the current block are already finished”. If I am layer 1 of the block, not one layer in this block comes before me, the partial sum is empty, and there is no bn0 to put in (putting in a zero vector would only take up an attention slot for nothing and take away a share of the weight for nothing). From layer 2 on, the outputs of the i−1 layers before me already exist, and hanging their partial sum on the list lets me read “what has just happened inside my own block”. Note the asymmetry this brings: recent information inside the block appears only as one lump, the “partial sum”; it cannot be taken apart to give layer 1 and layer 2 of the block different weights. This is exactly the price blocking pays.
Finally, the paper says the final output layer aggregates all N block representations — that is, the very top of the model can still see every block-level summary at once.
In Eq. 10, the value matrix of layer 1 of block n contains no component of bn at all, while layer 3 has bn2. Answer this: (a) why can layer 1 not put in bn itself? (b) why does layer 3 put in bn2 rather than bn? (c) how does the number of rows of the value matrix change from layer 1 to layer S?
(b) same reason: layer 3 can read only the part that is already finished, i.e. the sum of the first 2 layers of the block, bn2. The superscript i−1 is a strict causal constraint — the role it plays along depth is exactly the role the causal mask plays in sequence attention.
(c) layer 1 of block n has n rows (b0 through bn−1); from layer 2 on it becomes n+1 rows (that partial sum is added), and stays n+1 from then on — because however far into the block you go, the contribution from the current block always occupies exactly one row (the content updates, the row count does not). This is the technical reason blocking can squeeze the cost down to O(Nd): the value matrix of any layer has at most N+1 rows, independent of the total number of layers L.
Variant: if you changed Eq. 10 to “keep every layer inside a block as its own row, and only sum across blocks”, what would the number of rows become? Could you still squeeze the cost down to O(Nd) that way? (Hint: count the rows for the last layer of the last block.)
4.4 What blocking buys: memory, communication, and the state at inference time
The paper lists three benefits, and they are worth going through one at a time.
First, memory and communication drop from O(Ld) to O(Nd). This was the point of blocking. Get a feel for it with K3's numbers (what follows is our own order-of-magnitude estimate; the paper does not give these numbers): without blocking, every token has to keep 93 × 7,168 ≈ 667,000 numbers (about 1.3 MB in BF16); with blocking it only has to keep 9 sources × 7,168 ≈ 64,500 numbers (about 129 KB), roughly a tenth as much. What has to be sent across stages under pipeline parallelism shrinks in the same proportion.
Second, the block structure bounds the size of the state at inference time. With the full form, by the time you reach layer 93 you have to be clutching 92 historical layer outputs; after blocking there are never more than N+1. Only once the state has an upper bound can you plan memory at all.
Third, the two paths of results merge better. The paper says the block structure lets the block-level results computed in parallel merge better, through online softmax, with the sequentially accumulated within-block partial sums, significantly reducing the inference-time cost. Taken apart: across blocks, b0, …, bn−1 are already settled and can be computed in one batch; the within-block partial sum, by contrast, grows as you walk, changing once per layer. The two paths have completely different timing characteristics, so it only pays to compute them separately and then merge.
online softmax: the denominator of a softmax is the sum of all the scores, so the naive way has to finish computing every score before it can normalise. online softmax instead normalises as it goes: it maintains “the largest value seen so far” and “the sum accumulated so far”, and each time a new batch of scores arrives it corrects the existing result proportionally. Its value is that partial results computed in two separate batches can be merged correctly without waiting for each other — which matches the pattern “batch-parallel across blocks + sequential updates inside a block” exactly.
Defend it: if I were a reviewer
At the top of §2.2 you argue that the flaw of the standard residual is “uniform accumulation”, and that it has to be replaced by “selective retrieval”. But what your Block AttnRes does inside a block is bn = Σ fj(hj) — a uniform sum, untouched. Inside a block, haven't you fallen right back to the standard residual? In that case, on what grounds do you say AttnRes solved that bottleneck?
A defence to compare against (put it in your own words first)
This challenge is well aimed, and the defence has to concede first: yes, inside a block it does degenerate into an equal-weight sum; that is where blocking makes its compromise. Then defend on three levels.
One, a different granularity is not the same as not solving it. The original bottleneck was “the information of 93 layers all squeezed into one state”; it is now “at most 12 layers squeezed into one set of minutes, and the minutes can be retrieved selectively”. The compression ratio goes from 93:1 to 12:1, and cross-block selectivity is a brand-new ability — layer 80 can give block 1 a high weight and block 5 a low one, which a standard residual cannot do.
Two, b0 = h1 was not written down casually: it guarantees that the token embedding always occupies a source of its own and is never diluted away by a sum. The authors clearly know that “summing loses things”, so they deliberately pulled the most important term out.
Three, this is a deliberate price, not an oversight: the paper states plainly that the purpose of blocking is to squeeze O(Ld) down to O(Nd), and cites [57] for N ≈ 8 recovering most of the gain. The word “most” is itself an admission that something is lost.
The gap to own up to at the end: this report gives no controlled data at K3 scale for “full AttnRes vs Block AttnRes”; the evidence for “most of the gain” is in the cited work, not here. If the reviewer presses with “does it still hold at 2.78T scale”, this paper cannot answer.
Let L = 93, d = 7,168, N = 8 (9 sources in all, once the token embedding source is counted). (a) Estimate how many numbers full AttnRes and Block AttnRes each have to keep per token, and work out the ratio. (b) If someone raised N from 8 to 31 (3 layers per block), what would happen to the memory saving? Would they still enjoy the benefit of blocking?
(b) with N = 31 each block is 3 layers, so 32 × 7,168 ≈ 229,000 numbers have to be kept, which is a saving of only about 2.9 times relative to the full form. The benefit shrinks badly, because the closer N gets to L, the closer blocking gets to the full form (at N = L the two are exactly the same). They still enjoy a little benefit, but not enough to solve the memory and communication problems the paper cares about.
The pattern to see here is that N is a knob that trades off between “fidelity” and “saving resources”: small N saves a lot but loses a lot inside the block; large N is faithful but saves nothing. The paper cites [57] for N ≈ 8 recovering most of the gain, and K3 sits at that point. A reminder: the numbers above are orders of magnitude we worked out ourselves from the paper's complexity expressions; the paper gives no concrete memory figures.
Variant: what happens if you take N = 1 (the whole network is a single block)? Substitute it into Eq. 10 and see what structure it degenerates into. Does that conclusion line up with the motivation in section 4.1?
4.5 Reconciling the numbers: how 93 layers get cut into 8 blocks
The empirical result the paper gives is: N ≈ 8 recovers most of the benefit across model scales (citing [57]). The split K3 makes on that basis is written like this in the original: partition its layers into 8 blocks with 12-layer size, giving a partial final block, for 9 blocks in total counting the token embedding layer.
Read that sentence too fast and you will get it wrong. You have to do the arithmetic yourself.
Derive it yourself: 93 layers, block size 12, 8 blocks — can these three numbers hold at once?
Start with the most direct multiplication: 8 blocks × 12 layers each = how many layers? Does that match 93?
Think it through, then look
8 × 12 = 96, which is 3 layers more than 93. So “8 blocks × 12 layers” cannot all be full — this is exactly what the paper's “giving a partial final block” is there to account for. When numbers fail to line up like this, your first reaction should not be “the authors got it wrong”, it should be to go find which item is not full.
Switch to division: what is 93 ÷ 12? What is the remainder?
Think it through, then look
93 ÷ 12 = 7 remainder 9. That is, 7 full blocks (12 layers) + 1 final block holding only 9 layers, 7 × 12 + 9 = 84 + 9 = 93 ✓. So the exact meaning of “8 blocks” is: 7 full blocks plus 1 partial final block, 8 in all.
Then what is the 9th of those “9 blocks in total”?
Think it through, then look
It is the token embedding layer, that is, b0 = h1 in Eq. 10. It is not really a “block of layers”; it is treated as an extra information source that sits permanently at the front of the list. 8 + 1 = 9 ✓. This also explains why this site used 9 and not 8 when it estimated memory in the previous section.
One step further: how does the block size 12 relate to the 4-layer attention block from Chapter 3?
Think it through, then look
12 = 3 × 4. Chapter 3 worked it out: the attention period is 4 layers (3 KDA + 1 Gated MLA). If the two partitions are aligned, then a 12-layer AttnRes block holds exactly 3 complete attention blocks, that is, 9 KDA + 3 MLA. The final block is neater still: 84 layers = 21 attention blocks, and the remaining 9 layers = 2 complete attention blocks (8 layers) + the 1 Gated MLA tacked onto the end of the backbone, exactly 9 layers, while 21 + 2 = 23 blocks also matches the number Chapter 3 derived.
But this has to be said plainly: the paper never says the two partitions are aligned, and it never says where the AttnRes block boundaries fall. The reconciliation above is this site's inference; it only shows that the “aligned” assumption is consistent with every known number, it does not show that the authors implemented it that way.
The paper's own sentence is ambiguous
What the paper writes is “partition its layers into 8 blocks with 12-layer size”. Read literally that is “8 blocks, 12 layers each”, which multiplies out to 96 and does not match 93 layers. Only together with the second half, “giving a partial final block”, can you pin it down to 7 full blocks + 1 final block of 9 layers. The paper never states outright that the final block has 9 layers; that number is one this site derived from 93 − 7×12. Likewise, the paper does not say whether the AttnRes block boundaries align with the attention block boundaries, or which 9 layers are in the final block.
A synthesis question. K3 has 93 layers, AttnRes is split into 8 blocks (the first 7 hold 12 layers each), d = 7,168, and Chapter 3 already derived that the backbone is (3 KDA + 1 Gated MLA) × 23 + 1 Gated MLA. Answer these: (a) how many layers are in the final block? (b) under the assumption that “the two partitions are aligned”, how many KDA layers and how many MLA layers are in block 5? (c) when layer 93 (the last layer, a Gated MLA) does AttnRes, how many rows does its value matrix have at most? (d) if the AttnRes block size is changed from 12 to 6 (N ≈ 16), how does each of the three answers (a) to (c) change? What is the upside and what is the downside of changing it that way?
(b) 12 layers = 3 complete attention periods (3 KDA + 1 MLA per period), so block 5 holds 9 KDA + 3 Gated MLA. This assumes the two partitions are aligned, and the paper does not say so.
(c) Layer 93 belongs to block 8 (the final block) and is not layer 1 of that block, so the value matrix contains b0 through b7 (8 rows) plus the partial sum inside the final block (1 row), 9 rows in all. Note that this number agrees with “9 blocks in total”, and it bears out the conclusion of §4.3: the upper bound on rows is N+1, independent of L = 93.
(d) With the block size changed to 6: 93 ÷ 6 = 15 remainder 3, which becomes 15 full blocks + 1 final block of 3 layers, 16 blocks in all, the final block having 3 layers; 6 layers = 1.5 attention periods, no longer a whole number of periods, so a block boundary would cut through the middle of an attention block and the “aligned” assumption fails outright; the value matrix of the last layer has at most 16 + 1 = 17 rows.
Upside: the layers swallowed by the equal-weight sum inside a block drop from 12 to 6, selectivity is finer, and in theory it is closer to full AttnRes. Downside: memory and communication rise from about 9 copies to about 17, nearly double, which cuts away half the point of blocking; the upper bound on the inference-time state grows with it; and the empirical result the paper cites is “N ≈ 8 already recovers most of the benefit”, so the marginal return on pushing N higher is very likely small. This has to be stressed: the paper gives no comparison data for different N at K3 scale, and the judgement about the “upside” above is an inference from the mechanism, not an experimental result.
Variant: if K3 had 96 layers instead of 93, what would block size 12 give you? Would the paper's phrase “partial final block” still be needed then? Go one level deeper: did the authors fix the layer count first or the block size first? (Hint: the expression 93 = 4 × 23 + 1 is forced out by the attention structure in Chapter 3.)
4.6 You will meet it again: checkpointing during training and the two-stage kernel at inference
The cost of AttnRes is not only “a few more things kept in memory”; it pulls on how both the training system and the inference system are implemented. The paper gives two matching optimisations in the later systems chapters. This chapter only lays down the thread, Chapters 10 and 11 pick it up.
On the training side: the block representation is generated once at the block's boundary layer, then shared by every layer in the block and kept resident on the GPU; and the whole AttnRes computation is put under checkpointing (recomputation: intermediate results are not saved for the backward pass, they are computed again on the spot) in its entirety, so the activations each layer saves for the backward pass are exactly the same as in a standard residual architecture — that is, bringing in AttnRes does not make training memory swell any further. Under pipeline parallelism it uses cache-based pipeline communication, transmitting only the newly generated blocks incrementally.
On the inference side: Block AttnRes uses a two-stage schedule — one batched pass across blocks, in which each block's cached block representation is read exactly once; then every layer folds the in-block partial sum in through an online softmax merge. This is precisely the engineering form of the third benefit in §4.4.
Construct two extreme cases: (a) a parameter setting that makes Block AttnRes strictly equivalent to full AttnRes; (b) a case that makes the difference between the two largest — say concretely “what capability blocking actually loses”, ideally with a specific task it cannot do.
(b) Take N = 1 (the whole network is one block). Cross-block attention is then left with only two sources, b0 (the token embedding) and the in-block partial sum, which falls back almost entirely to the “uniform accumulation” of the standard residual. The concrete capability lost is this: it cannot make distinctions inside one block. Take a specific task — suppose layer 3 has learned to “spot quotation marks” and layer 9 has learned to “spot indentation”, and layer 40, working on code, wants the indentation and not the quotation marks. Full AttnRes can give layer 9 a high weight and layer 3 a weight close to 0; but once 3 and 9 fall inside the same block, they have already been added into one vector, and layer 40 can only take the whole thing or leave it, with no way left to weight them apart. This is the essential cost of blocking, and it is the point the reviewer seized on in the defend-it box above.
One more note on why K3 takes N = 8: it sits between the two extremes, and the paper cites [57] as saying this value recovers most of the benefit — note that this is the cited work's conclusion, and the paper does not reproduce that comparison experiment.
Variant: if the sum inside a block were not an equal-weight sum but a weighted sum with a set of learnable fixed weights (one scalar per layer), could that win back the capability lost in (b)? Why? (Hint: do those weights change with the token? Connect it to “which side the data dependence comes from”, discussed in §4.2.)
Chapter summary
AttnRes starts from an analogy: the standard residual connection presses all previous information into a single state hl and passes it along the depth, and this bottleneck is the same thing as the RNN's bottleneck along time; sequence modelling solved it with attention long ago, and AttnRes carries the same methodology over to depth — every layer retrieves selectively from all previous layers instead of accumulating them uniformly.
The technical implementation comes in two steps. The full form gives every layer a learnable pseudo-query ql = wl (not dependent on the input), takes the token embedding and each layer's output as both keys and values (Eq. 8), and computes the weights with a softmax kernel carrying an RMSNorm (Eq. 9). Its arithmetic cost O(L2d) is acceptable when L < 100; the real burden is the O(Ld) memory and the cross-stage communication under pipeline parallelism. Block AttnRes cuts the L layers into N blocks, sums each block into one block representation and deliberately sets b0 = h1 to keep the token embedding, and does full attention only across blocks (the difference between the two forms in Eq. 10 comes from causality). The benefits are memory and communication dropping from O(Ld) to O(Nd), an upper bound on the inference state, and cross-block results computed in parallel merging better with the sequential in-block partial sums through online softmax. K3 takes N ≈ 8: 93 layers cut into 7 blocks of a full 12 layers plus 1 final block of 9 layers, 9 sources in total counting the token embedding.
Points this chapter marks as doubtful or left unexplained by the paper: “8 blocks with 12-layer size” multiplies out literally to 96, which does not match 93 layers, and the number 9 for the final block is one this site derived, never written out in the paper; whether the AttnRes block boundaries align with the 4-layer attention block of Chapter 3 the paper does not address at all, and step 4 of this chapter's reconciliation is this site's inference; “N ≈ 8 recovers most of the benefit” cites reference [57], and the paper gives no comparison data at K3 scale; there is no data on the gap between “full AttnRes vs Block AttnRes” at 2.78T scale; why the pseudo-query does not depend on the input (saving parameters? guarding against overfitting?) the paper does not explain; every specific memory and compute figure in this chapter is an order-of-magnitude estimate this site made from the paper's complexity expressions, and the paper does not give these numbers.
第5章 Stable LatentMoE:把「宽度」做稀疏,而不炸掉
一句话导语:这一章回答「怎么把一层里的小网络从 384 个扩到 896 个、还不让训练崩掉」——K3 用三件事做到:把专家搬进一个更窄的潜空间、在上投影前插一个 RMSNorm、给激活值装两道软限位,再用一个不需要学习率的分位数规则把 896 个专家的负载摆平。
学完这一章你应该能做到
- 用自己的话解释「总参数 2.78 万亿、每个 token 只用 1042 亿」是怎么做到的,并算出 K3 的稀疏度
- 指出 LatentMoE 把哪两个「宽度」拆开了,以及拆开之后省的是哪一笔账
- 把「极端稀疏放大的两个失效模式」和「三个对策」一一对应上,并说清哪个治哪个
- 验证 β·tanh(x/β) 的两个极限,并自己推出 SiTU-GLU 的输出上界为什么是 β₁β₂
- 手算一遍 Quantile Balancing:从路由分数出发,求出每个专家的新偏置,并解释为什么它不需要学习率
- 构造一个反例,说明 QB 一步之后负载仍可能不是恰好均衡
5.1 先搞懂 MoE:为什么「参数多」可以不等于「算得慢」
先把舞台搭好。一个 Transformer 层里有两件事:注意力负责让不同的词互相看一眼(前面几章讲过),前馈网络(Feed-Forward Network, FFN) 负责让每个词各自再想一想。
前馈网络(Feed-Forward Network, FFN):一个只作用在单个 token 上的小网络。它拿到这个 token 的 d 维向量,先用一个矩阵把它升到一个更宽的隐藏维 h,做一次非线性变换,再用另一个矩阵压回 d 维。它不看别的 token,纯粹是「就这一个向量做一次加工」。
不用 MoE 会怎样
想让模型装下更多东西,最直接的办法是把 FFN 的隐藏维 h 调大。但 FFN 的参数量和每个 token 的计算量都正比于 d·h——参数翻倍,算力也跟着翻倍。这条路是 1:1 的:想多记一点东西,就得多算一点。对 2.78 万亿参数这个量级来说,这条路走不通。
混合专家(Mixture-of-Experts, MoE):不把 FFN 做成一个大的,而是做成很多个小的(每个叫一个专家),再配一个路由器(router)。路由器给每个 token 打分,只挑分数最高的 k 个专家真正跑一遍,其余的专家这一次完全不参与计算。
这就把参数量和计算量解耦了。假设有 n 个专家、每个 token 只用 k 个:参数量正比于 n,每 token 计算量只正比于 k。n 可以往上堆,k 保持不动。模型「知道的东西」变多了,但每个 token 走的路没变长。
稀疏度(sparsity):论文里指专家总数除以每 token 激活的专家数,n/k。这个数越大,说明每个 token 用掉的专家占比越小,模型越「稀疏」。
打个比方
把一个 MoE 层想成一家有 896 名坐诊医生的超级医院。病人(token)挂号时,分诊台(路由器)看一眼症状,只把他转给最对口的 16 位医生会诊。医院的「知识总量」是 896 位医生的总和,但每个病人只占用 16 位医生的时间。
类比失效处:真实的医院里医生是互斥的资源,会诊完就被占用了;MoE 里所有 token 是同一个批次并行处理的,一个专家可以同时服务很多 token——这恰恰是后面「负载均衡」问题的根源:某位医生可能被一整批病人同时挤爆,而另一位一个病人都没有。另外,路由器不是真的「懂医」,它只是一个学出来的线性打分器。
K3 的具体数字(论文 Table 1):路由专家 n = 896 个,每 token 激活 k = 16 个,另外还有 Ns = 2 个共享专家(shared experts)——共享专家不参与挑选,每个 token 都必过,负责那些「谁都用得上」的通用变换。这个「共享 + 路由」的组织方式论文说是沿用 DeepSeekMoE 的做法。
K3 每层有 896 个路由专家,每个 token 激活 16 个。(a)算出稀疏度。(b)K2 是 384 个路由专家、每 token 激活 8 个,稀疏度是多少?哪一代更稀疏?
变式:如果 K3 保持 896 个专家不变,但把激活数从 16 降回 8,稀疏度变成多少?每 token 的路由分支计算量会怎么变?(提示:稀疏度和计算量是反着走的,把稀疏度做大总是有代价的。)
论文 Table 1 给出 K3 总参数 2.78T、激活参数 104.2B。算一下 2780 ÷ 104.2 ≈ 26.7。可我们刚算出稀疏度是 56。这两个数都是「总的除以用到的」,为什么差了一倍多?
变式:如果把共享专家数 Ns 从 2 减到 0(其他不变),这个比值会更靠近 56 还是更远离?如果反过来把共享专家加到 8 个呢?
常见误解
很多人以为「稀疏 = 偷工减料 = 效果差」。其实在固定的每 token 计算预算下,MoE 是在问另一个问题:同样只算这么多,我是让所有 token 都走同一条窄路,还是让它们各自走一条更对口的路?论文的立场是后者更值——所以才要把专家池从 384 扩到 896。但这句话论文没有在 §2.3 给出对照实验,属于设计动机而非本节的实验结论。
5.2 LatentMoE:把「模型宽度」和「专家宽度」拆成两件事
专家池扩大听起来只是「多存点参数」,但它有一笔隐藏的账。常规 MoE 里,每个被选中的专家拿到的是完整的 d 维 token 表示。在大规模训练里,专家分散在不同的显卡上,token 要通过网络被送到选中的专家那儿去(这一步叫 dispatch),算完再送回来。选了 k 个专家,就要送 k 份。论文的原话是:通信量和专家权重的流量都随路由重数(routing multiplicity)增长。k 从 8 涨到 16,这两笔账就直接翻倍。
LatentMoE:把「整个模型有多宽」和「路由专家有多宽」当成两个可以分别设定的数。共享专家保留全宽通路(d 维进、d 维出)做通用变换;专门化的路由专家则在一个宽度只有 ℓ 的紧凑潜空间(compact latent space) 里工作。
具体做法:先用一个下投影矩阵 W↓ 把 token 从 d 维压到 ℓ 维,得到 z = W↓x;被送去 dispatch 的是 z,不是 x。所有路由专家都是 ℓ → ℓ 的小网络。等专家算完、加权汇总成一个 ℓ 维向量之后,再用一个上投影矩阵 W↑ 送回 d 维,和共享分支相加。
| 项 | K2 | K3 |
|---|---|---|
| 隐藏维 d | 7,168 | 7,168 |
| Latent MoE 维度 ℓ | — | 3,584(d 的 0.5×) |
| 每专家 MoE 隐藏维 | 2,048 | 3,072 |
| 路由专家数 n | 384 | 896 |
| 每 token 激活专家数 k | 8 | 16 |
| 共享专家数 Ns | 1 | 2 |
| 激活函数 | SwiGLU | SiTU-GLU |
论文说常规 MoE 里「每个被选中的专家收到完整的 d 维表示,所以通信量和专家权重流量随路由重数增长」。用 K3 的数字(d=7168、ℓ=3584、k=16)估一估:LatentMoE 把每个 token 的 dispatch 数据量降到了原来的几分之几?为什么论文特别强调「随路由重数增长」这件事?
为什么强调「随路由重数增长」:因为这两笔开销里 k 是乘数。K3 把 k 从 8 提到 16,如果宽度不变,这两笔账就直接翻倍;把宽度减半,正好抵消掉这次翻倍。所以 LatentMoE 是「激活专家数翻倍」这个决定的前提条件,不是一个可有可无的优化。(论文只说 compact latent space of width ℓ,并未给出通信量的具体数字;上面的 0.5 是我们用 Table 1 的 ℓ/d = 0.5 推出来的。)
变式:假设有人把 ℓ 进一步压到 d 的 0.25×(即 1792),通信量再减半。这样做的代价是什么?(想想路由专家还能表达多少东西,以及 W↓ 丢掉的信息还能不能被 W↑ 补回来。论文没有做这个消融。)
5.3 钥匙:极端稀疏放大的两个失效模式
这一节是全章的钥匙。前面两节讲的都是「怎么把规模做上去」,这一节讲「做上去之后什么东西会坏」。论文的措辞很直白:这种极端稀疏放大了原始设计的两个失效模式。后面 2.3.1、2.3.2、2.3.3 三个小节,就是针对这两个失效模式的三个对策。先记住这个对应关系,后面每一节读起来都会有方向。
失效模式一:路由分支的激活值爆炸
看图 5-1 的下面那条路:W↓ → 一个带门控的多分支专家前馈网络 → W↑。论文说这把它们「串成了一条近乎四次连续矩阵乘法的链条」,并称这个结构是病态的(ill-conditioned);再叠加 2.8 万亿参数的规模,结果就是路由分支内部的激活值爆炸。
为什么「连乘」是个问题
矩阵乘法会缩放向量。如果每一步平均把向量的长度放大 1.5 倍,连乘四次就是 1.5⁴ ≈ 5 倍;如果是 2 倍,四次就是 16 倍。训练过程中这些矩阵是在变的,没人保证它们乖乖地把放大倍数控制在 1 附近。链条越长,偏离 1 的效果被复利放大得越厉害。更麻烦的是训练用的是低精度数字格式,数值一旦冲得太大就会溢出,整步训练直接作废。
读的时候要小心
论文说「近乎四次连续矩阵乘法」,但没有点名是哪四个矩阵。按 LatentMoE 的结构最自然的数法是:W↓(第 1 次)→ 专家内部的门/上投影(第 2 次,两个矩阵并列所以只算一层深度,这大概就是「近乎」两个字的来源)→ 专家内部的下投影(第 3 次)→ W↑(第 4 次)。这是我们的推断,不是论文原文。另外论文用了「ill-conditioned」(病态)这个词,但正文没有给任何定量证据——没有条件数、没有激活范数曲线、没有「不加对策就发散」的训练曲线。这是一个纯粹的作者主张。
失效模式二:近 10³ 个专家的负载摆不平
第二个问题和数值无关,和「分工」有关。路由器要在 896 个专家之间分配 token。如果分配严重不均——有的专家被一整批 token 挤爆,有的一个 token 都没分到——训练就会出问题。论文的说法是:平衡将近 10³ 个专家的负载,已经超出了现有免辅助损失偏置更新还能良好工作的范围。
为什么不均衡有害,论文给了两条理由(在 2.3.3 里):一是拖慢专家并行训练——专家分散在不同显卡上,一步的耗时由最忙的那张卡决定,别的卡只能干等;二是可能让部分专家训练不足——长期分不到 token 的专家梯度稀少,学不出东西,最后变成一堆死参数,白占显存。
读的时候要小心
「896 个专家超出了现有方法的良好工作范围」同样是一个断言。论文正文没有给出「用原来的定步长方法在 896 个专家上会怎么坏」的曲线或数字。我们只能接受这是作者从自己的训练经验里得出的判断。
| 失效模式 | 对策 | 在哪一节 |
|---|---|---|
| 路由分支激活爆炸 | 在上投影 W↑ 之前插一个 RMSNorm | 2.3.1 → 本章 5.4 |
| 把激活函数换成 SiTU-GLU | 2.3.2 → 本章 5.5 | |
| 近 10³ 个专家的负载均衡 | Quantile Balancing(QB) | 2.3.3 → 本章 5.6 |
假设一个团队复现 K3,但只实现了 Quantile Balancing,没做 RMSNorm 也没换 SiTU-GLU。(a)他们最可能先遇到哪种故障?(b)反过来,如果只做了 RMSNorm 和 SiTU-GLU、不做 QB,会遇到什么?(c)这两组故障的性质有什么根本不同?——一个「重跑就能好」,一个「重跑也好不了」,哪个是哪个,为什么?
(a)只做 QB:负载是平了,但路由分支的激活爆炸没治——链条还是那条四次连乘的链条,规模还是 2.8T。会看到训练损失出现尖峰甚至发散,低精度算术溢出。QB 在这里完全帮不上忙,因为它只改「谁被选中」,一个字都没改「被选中之后算出来的数有多大」。
(b)只做 RMSNorm + SiTU-GLU:数值稳住了,训练不会炸,但会慢——每步被最忙的专家卡住;而且会有一批专家长期分不到 token,训练不足,最后 896 个专家里真正起作用的可能只有一小半,等于白花了参数。
(c)根本不同在于可挽回性。数值爆炸是显性的、当场发作的:loss 变 NaN,你立刻知道出事了,回滚到上一个 checkpoint 换个配置重来就行。负载不均衡是隐性的、累积的:训练全程都「正常」跑完了,loss 曲线也许还挺好看,但那些饿死的专家已经永久地没学到东西——这笔损失是训练结束才发现、且无法回滚修复的(要修就得重训)。这就是为什么论文要花整整一个小节、外加两个附录来对付负载均衡,而两个数值对策各自只有一小段。
变式:这三个对策之间有没有互相帮忙的地方?具体说:RMSNorm 把路由分支的尺度归一化了,SiTU-GLU 又给激活值加了硬上界——这两件事是不是重复劳动,去掉一个行不行?(想想 RMSNorm 作用在专家全部算完之后的汇总向量上,而 SiTU-GLU 作用在每个专家内部;一个管出口,一个管过程。)
5.4 式 11 与 Normalized LatentMoE:一个 RMSNorm 放在哪里的学问
现在把一层 Stable LatentMoE 的完整前向写下来。论文式 11 分两行:第一行算路由分支的汇总,第二行把两个分支合起来。
y = ∑j=1Ns Esharedj(x) + W↑ RMSNorm(u)
| 符号 | 是什么 | 直觉 |
|---|---|---|
| x | 这一层的输入,一个 d=7168 维的向量,代表一个 token | 「这个词现在的样子」 |
| 𝒯k(x) | 路由器为 x 选中的那 k=16 个专家的编号集合 | 分诊台开出的会诊名单 |
| pi | 第 i 个被选中专家的路由权重,由 5.6 节的式 13 定义,在名单内归一化到和为 1 | 「这位专家的意见占几成」 |
| W↓ | 下投影矩阵,形状 ℓ×d = 3584×7168 | 把 token 压进潜空间的入口 |
| Eroutedi | 第 i 个路由专家,一个 ℝℓ → ℝℓ 的前馈网络(内部隐藏维 3072) | 潜空间里的一位专科医生 |
| u | 聚合后的路由表示,ℓ=3584 维 | 16 位医生意见的加权综合 |
| Ns | 共享专家个数,K3 每层固定为 2 | 不管什么病都要挂的两个全科 |
| Esharedj | 第 j 个共享专家,ℝd → ℝd,全宽 | 每个 token 都必过的通用变换 |
| RMSNorm | 均方根归一化:把向量除以自己各分量平方均值的平方根,使其「长度」标准化 | 不管进来的向量多长多短,出去都是标准长度 |
| W↑ | 上投影矩阵,形状 d×ℓ = 7168×3584 | 把潜空间的结论翻译回全宽 |
| y | 这一层 MoE 的输出,d=7168 维 | 共享意见 + 专科意见 |
先把式子读顺:第一行说「把 x 压进潜空间,送给 16 个选中的专家,各自算完之后按权重 pi 加权求和,得到 u」;第二行说「共享专家直接吃全宽的 x,它们的输出相加,再加上 u 归一化之后送回全宽的结果」。
整个 2.3.1 节讲的就是式 11 第二行里那个 RMSNorm。论文说:原始的 LatentMoE 是直接把 W↑ 作用在 u 上的,而 u 的尺度会随着「选中了哪些专家」和「路由权重怎么分布」而变化。K3 在专家聚合和上投影之间插了一个 RMSNorm,降低路由分支对尺度变化的敏感度,然后才和全宽的共享分支相加。
为什么位置这么关键
RMSNorm 插在「聚合之后、上投影之前」,正好卡在四次连乘链条的第三次和第四次之间——也就是在数值一路被放大之后、被送回全宽通路之前的最后一个关口。放在这里,前面三步累积的任何尺度漂移都会被一次性抹平。
如果放到 W↑ 之后(即归一化 W↑u),那 W↑ 这次 7168×3584 的大矩阵乘法仍然要在没归一化的输入上做,溢出风险还在。如果放到专家之前(归一化 W↓x),它管不到专家内部和加权求和过程中产生的漂移。
常见误解
很多人以为「pi 已经归一化到和为 1 了,所以 u 是一个凸组合,尺度自然就稳了」。这是错的。权重和为 1 只保证 u 是 16 个专家输出的加权平均,但被平均的那些向量本身可以长可以短、可以互相抵消。不同 token 选中的是不同的 16 个专家,这 16 个专家的典型输出幅度可以差很多倍。下面这道题就让你亲手造一个例子。
有人主张:既然式 13 保证 ∑i pi = 1,那 u 的长度就被夹在专家输出长度的最小值和最大值之间,不会乱跑,RMSNorm 是多余的。请构造两个具体的反例推翻它:(a)一个例子说明同样的专家输出长度下,‖u‖ 可以从接近 0 变到 1;(b)一个例子说明不同 token 之间 ‖u‖ 可以差一个数量级。
(a)令 ‖v1‖ = ‖v2‖ = 1,p = (0.5, 0.5)。方向相同时 u = v1,‖u‖ = 1;方向相反时 u = 0,‖u‖ = 0。同样的权重、同样长度的专家输出,‖u‖ 在 [0, 1] 上任意取值。凸组合的界只对每个坐标分量成立,对向量的范数不成立。
(b)设专家 A 的典型输出范数是 0.5、专家 B 是 5(不同专家的权重矩阵学出来的尺度本来就不同)。token 甲选中两个 A 类专家 → ‖u‖ ≈ 0.5;token 乙选中两个 B 类专家 → ‖u‖ ≈ 5。相差 10 倍。而且被选中的专家是逐 token 动态变化的,所以这个 10 倍的抖动是逐 token 发生的,不是一个可以被下一层的固定权重吸收掉的常数偏移。
再补一刀:p 的集中程度也在变。p=(1,0,…,0) 时 u 就是单个专家的输出;p 均匀时 u 是 16 个向量的平均,如果它们方向不一致,平均会显著缩短(大致按 1/√16 的量级)。这两种情形的尺度差异同样是逐 token 的。
这就是 RMSNorm 要解决的东西:把这些逐 token 的、由「选了谁」和「怎么分权重」引起的尺度抖动,在进入 W↑ 之前一次性抹平。
变式:RMSNorm 把 u 的长度抹平了——那「这 16 个专家一致认为该做一个很大的修改」这条信息不就丢了吗?这算不算 RMSNorm 的代价?(提示:想想 RMSNorm 通常带一个可学习的增益参数,它能补回什么、补不回什么。论文并没有写清楚这里的 RMSNorm 是否带可学习增益。)
读的时候要小心
论文说这个额外的 RMSNorm「除了稳定训练之外,还持续改善验证损失和下游基准」(consistently improves validation loss and downstream benchmarks)。这是论文自述,正文没有给出任何消融数字——改善了多少、在哪些基准上、和什么比较,一概没有。另外论文也没有说明这个 RMSNorm 是否带可学习增益、共享分支是否也做了类似处理。
5.5 SiTU-GLU:给两个乘性因子各装一道软限位
第二个数值对策治的是专家内部。要理解它,得先看清楚现在通用的激活函数问题出在哪。
门控线性单元(Gated Linear Unit, GLU):把输入分别过两个矩阵,得到「门分支」和「上分支」两个向量,然后逐元素相乘(记作 ⊙)。原始 GLU 的门分支是 Sigmoid(Wgx),上分支是 Wux。「门」的作用是:sigmoid 的输出在 0 和 1 之间,等于给上分支的每个通道配一个 0~1 的开关,决定这个通道的信息放多少过去。
SwiGLU:把 GLU 的 sigmoid 门换成 Swish,即 Swish(x) = x·Sigmoid(x)。论文说它在 Transformer 里表现很好,后来被大模型广泛采用——但也明说「对它为什么有效的完整解释仍然是开放问题」。K2 用的就是 SwiGLU。
| 门分支 | 上分支 | |
|---|---|---|
| GLU | σ(x) | x |
| SwiGLU | x·σ(x) | x |
| SiTU-GLU | β1 tanh(x/β1) · σ(x) | β2 tanh(x/β2) |
SwiGLU 在极端稀疏下的麻烦
论文的诊断很精确:SwiGLU 的两个乘性因子都是无界的。门分支 xσ(x) 在 x 很大时约等于 x,没有上限;上分支就是 x 本身,也没有上限。于是当两个分支的同一个坐标恰好都很大时,乘积会产生激活离群值(activation outliers)——一个远大于周围数值的孤立大数。在低精度算术里,这直接抬高了溢出的风险。
那退回原始 GLU 行不行?论文说:GLU 的 sigmoid 门确实避免了门的无界增长(sigmoid 最大就是 1),但它不保留 Swish 在正半轴那段近似线性的响应——sigmoid 门在 x 大的时候会饱和到 1,梯度趋近 0,丢掉了 Swish「输入越大、门开得越大」这个性质。
所以要找的东西是:既能压住大值增长,又保留 SwiGLU 那种局部的、偏正半轴的响应特性。
K3 的答案是一个光滑封顶(smooth cap) 函数:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| x | 要被封顶的那个数(实际是向量的每一个坐标各自过一遍) | 原始数值 |
| β | 软上限,一个人为设定的正数 | 「最多允许到这么大」 |
| x/β | 先把 x 按 β 缩小,送进 tanh | 把 tanh 的拐弯位置从 1 挪到 β |
| tanh | 双曲正切,输出永远落在 (−1, 1) | 限位器本体 |
| β·tanh(·) | 再乘回 β,于是输出落在 (−β, β) | 把缩小的量纲还原回去 |
把它同时作用在 Swish 门的线性因子上(即 xσ(x) 里的那个 x,注意 sigmoid 那一半不动)和上分支上,就得到 Sigmoid Tanh Unit GLU(SiTU-GLU):
| 符号 | 是什么 | 直觉 |
|---|---|---|
| x | 专家前馈网络的输入向量(在 K3 的路由专家里是 ℓ=3584 维) | 要加工的原料 |
| Wg | 门分支的投影矩阵 | 算「每个通道该开多大」 |
| Wu | 上分支的投影矩阵 | 算「要放过去的内容」 |
| tanh | 双曲正切,把实数压到 (−1, 1) 之间;原点附近近似等于自变量,两端饱和 | 软的限位器 |
| β1 | 门分支的软上限,K3 取 4 | 门这一半最多贡献 4 |
| β2 | 上分支的软上限,K3 取 25 | 内容这一半最多贡献 25 |
| ⊙ | 逐元素相乘(Hadamard 积):两个同形状向量对应位置相乘 | 「开关」乘「内容」,逐通道各管各的 |
| Sigmoid | 把实数压到 (0, 1) 之间的 S 形函数 | 0~1 的开关本身 |
为什么 sigmoid 那一半不用封顶
Swish 写成 x·σ(x),是「线性因子 x」乘「sigmoid 因子」。sigmoid 本来就在 (0,1) 里,天生有界,不需要再封。会跑掉的只有那个裸的 x。所以 SiTU-GLU 只把它换成 β1tanh(x/β1),sigmoid 原样保留——这样 x 小的时候门的行为和 Swish 几乎一样,x 大的时候被 β₁ 拦住。
自己推一遍:softcap 的两个极限,和为什么输出上界正好是 β₁β₂
已知 tanh(z) ≈ z(当 z 很小)。那么当 x 远小于 β 时,β tanh(x/β) 大约等于多少?
想好了再看
令 z = x/β。x 远小于 β 就意味着 z 很小,于是 tanh(z) ≈ z = x/β,两边乘 β 得 β tanh(x/β) ≈ β · (x/β) = x。结论:在原点附近,softcap 就是恒等映射,什么也没做。
当初为什么会想到用 tanh:因为需要的正是这样一个函数——「小的时候不管,大的时候管」。tanh 是最常见的、原点处斜率为 1 且两端饱和的光滑函数。除以 β 再乘 β 这个操作,作用就是把 tanh 的「拐弯位置」从 1 挪到 β:想让它在多大的数值上开始拦,就把 β 设成多大。当 x 远大于 β 时呢?β tanh(x/β) 趋向什么?
想好了再看
z = x/β 变得很大,tanh(z) → 1,所以 β tanh(x/β) → β。负方向同理趋向 −β。所以对任意 x,都有 |β tanh(x/β)| < β——这是一个严格的上界,不是「通常不会超过」。
现在看式 12 的两个中括号。第一个中括号(门分支)的绝对值最多是多少?第二个(上分支)呢?
想好了再看
门分支 = β1tanh(·/β1) · σ(·)。第一个因子的绝对值 < β₁(上一步的结论),第二个因子 σ 的取值在 (0,1),所以整个门分支的绝对值 < β₁ · 1 = β₁ = 4。
上分支 = β2tanh(·/β2),绝对值 < β₂ = 25。
注意这里 sigmoid 起了双重作用:它既是「门」的语义,又顺手保证了门分支的界不会超过 β₁。两个中括号逐元素相乘。输出的每个坐标的绝对值最多是多少?为什么是相乘而不是相加?
想好了再看
逐元素相乘意味着输出的第 c 个坐标 = 门分支第 c 个坐标 × 上分支第 c 个坐标。两个数相乘,绝对值就是绝对值相乘:|f(x)| < β1 · β2 = 4 × 25 = 100。这正是论文 Figure 4 里标的 |f(x)| ≤ β1β2 = 100。
是相乘不是相加,因为 GLU 结构本身就是「门 ⊙ 内容」。这也正是问题的来源:两个各自无界的因子相乘,误差不是相加而是相乘的——两边同时偏大,乘积就平方级别地偏大。SwiGLU 里这两个因子都无界,所以论文说「同时出现的大坐标会产生激活离群值」。SiTU-GLU 把两个因子分别封住,乘积的界就自动是两个界的乘积。最后一步:为什么 β₁ 和 β₂ 要取得不一样(4 和 25),而不是都取 10(乘积同样是 100)?
想好了再看
因为两个分支的角色不同。门分支的语义是「开多大」,本来就不该是个大数——Swish 门在实践中主要工作在小值区间,β₁ = 4 已经覆盖了它的正常工作范围,超过 4 的门几乎一定是异常值,该拦。上分支承载的是「内容」,数值幅度天然更大,封得太狠会把正常信号一起削掉,所以给到 25。
但要诚实地说:以上是对论文取值的合理解释,不是论文给的理由。论文只写了「we set β₁ = 4 for the gate branch and β₂ = 25 for the up branch」,没有给消融实验,也没有解释这两个数是怎么选出来的、为什么差了六倍多。
论文对 SiTU-GLU 的总结是:缩放后的 tanh 在原点附近近似线性、在大幅值处有界,因此 SiTU-GLU 既保留了 SwiGLU 的局部响应,又控制住了乘积里的两个因子。Figure 4 画的就是这三条曲线在同一个区间 x ∈ [−10, 100] 上的对比:SiTU-GLU(红线)在原点附近紧贴 SwiGLU,在大的正输入处逼近 100 这条界,而 SwiGLU 一路往上冲、没有上界。
K3 取 β₁ = 4、β₂ = 25。(a)SiTU-GLU 的输出绝对值上界是多少?(b)如果有人把 β₂ 从 25 改成 100,上界变成多少?(c)β₂ = 100 时,输入 x = 3 处上分支的输出大约是多少?
变式:如果把 β₁ 和 β₂ 都设成 10⁶(近乎无穷大),SiTU-GLU 会退化成什么?(一个一个分支验算:门分支 β1tanh(x/β1)·σ(x) → ?上分支 β2tanh(x/β2) → ?)
论文说 SiTU-GLU「在原点附近紧贴 SwiGLU」。请证明这件事:写出 SiTU-GLU 在 x 很小时的标量表达式,并说明它为什么等于 SwiGLU 的标量表达式。然后回答:为什么这个性质对「换掉一个已经被广泛验证的激活函数」这件事特别重要?
SiTU-GLU(x) ≈ [x·σ(x)] · [x] = SwiGLU(x)。逐项相同。
为什么重要:SwiGLU 是被无数模型验证过的设计,它的好处(论文自己也说「完整解释仍然开放」)大部分体现在正常数值范围内的行为上。SiTU-GLU 的设计目标是只改异常区、不动正常区——正常区一模一样,所以 SwiGLU 那些说不清但确实有效的好处被完整继承;异常区被限位器拦住,所以溢出风险被消掉。这是一种「非侵入式修补」的思路:新方法在旧方法的工作区间上是旧方法的一阶近似,只在旧方法出问题的地方才不同。如果换成一个在原点附近就和 SwiGLU 差很多的激活函数,那所有超参数(学习率、初始化尺度等)都要重调,风险大得多。
变式:β₁ = 4 意味着「远小于 4」才算原点附近。可 sigmoid 在 x = 4 时已经是 0.982 了,门基本全开。那在 x = 4 这个不大不小的地方,SiTU-GLU 的门分支和 SwiGLU 的门分支差多少?(算一算 4·tanh(1)·σ(4) 和 4·σ(4),tanh(1) ≈ 0.762。)这个偏差大到会有影响吗?
有人质疑:「既然目的只是不让数值超过 β,直接用硬截断 clip(x, −β, β) 不就行了?tanh 又慢又绕。」请构造一个具体情形,说明硬截断会带来软封顶没有的问题。(提示:想想训练是怎么进行的。)
用硬截断:输出 = 25(被削平)。反向传播时这一路的梯度乘以 clip 的导数 0,得到 0。于是产生这个 30 的那些参数收不到任何梯度信号——没有力把 x 往回拉。如果这时别的因素(比如权重衰减、别的样本)不恰好把它拉回来,这个通道就永久卡在饱和区,等于报废了。而且它是无声的:前向输出看起来完全正常(就是 25),你不会在 loss 上看到任何异常。
用 softcap:输出 = 25·tanh(30/25) = 25·tanh(1.2) ≈ 25 × 0.834 ≈ 20.9,导数 = 1 − tanh²(1.2) ≈ 1 − 0.696 = 0.304,还有相当可观的梯度。即使 x = 100(x/β = 4),导数 = 1 − tanh²(4) ≈ 1 − 0.99933 ≈ 6.7×10−4——小,但不是 0,仍然有一条把它往回拉的路径。
第二个反例:不连续的二阶行为。clip 在 x = β 处导数从 1 突然跳到 0,这个折点会让优化过程在边界附近抖动;tanh 处处光滑,没有这个问题。
论文把「与硬截断的比较」放在了附录 B,正文只提了一句「§B gives ... comparison with hard clamping」,完整内容见第14章。
变式:反过来构造一个「硬截断反而更好」的角度——比如从计算速度、或者从「界是严格的 β 而不是渐近的 β」这两点出发。(软封顶的输出永远达不到 β,只是无限逼近;硬截断可以正好取到 β。这个区别在什么场景下重要?)
5.6 Quantile Balancing:把偏置直接设成「对得上目标负载的那个分位数」
这是本章最难的一节,也是 K3 这一部分最有意思的想法。慢慢来。
问题:过热的专家和饿死的专家
路由器是学出来的,没有任何东西天生保证它把 token 均匀地分给 896 个专家。实际发生的是:少数几个专家变成「热门」,一批 token 全往那里挤;另一批专家几乎没人光顾,论文 Figure 5(a) 里用更深的圆表示过热的专家(overheated),用淡化的圆表示利用不足的专家,用虚线圆表示濒死的专家(dying experts,一个 token 都没分到)。
论文给的两条危害前面提过,这里再落实一下:拖慢专家并行训练——专家分布在不同显卡上,一步的墙钟时间由负载最重的那张卡决定;可能让部分专家训练不足——饿死的专家几乎收不到梯度,训练完还是初始化时的样子。
K3 的路由框架:免辅助损失的偏置
怎么让路由变均匀?一个老办法是加辅助损失:在训练目标里额外加一项「惩罚不均衡」的损失。但这样做会污染主目标——模型为了均衡而牺牲语言建模效果。K3 明确说自己用的是免辅助损失路由(auxiliary-loss-free routing):不加损失项,而是给每个专家配一个偏置 bj,只加在用于 Top-k 选择的那个分数上。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| xi | 这一批里第 i 个 token 的表示 | 第 i 个「病人」 |
| Wr | 路由器的权重矩阵(si = Sigmoid(Wrxi)) | 分诊台的打分规则,跟着训练一起学 |
| si | token i 对全部 n 个专家的路由分数,每个分量在 (0,1) | 「这个病人和每位医生的匹配度」 |
| si,j | si 的第 j 个分量,即 token i 对专家 j 的分数 | 一个具体的匹配度 |
| b | 长度 n 的偏置向量,每个专家一个,与 token 无关 | 给冷门专家的「加分」、给热门专家的「减分」 |
| argtopk | 取最大的 k 个的编号(不是取值) | 开出会诊名单 |
| 𝒯i | token i 选中的 k 个专家的编号集合 | 名单本身 |
| pi,j | 混合权重:用原始分数 s,不含 b,在选中的 k 个之间归一化 | 名单内各位专家的话语权 |
整个设计的关键:b 出现在第一个式子里,不出现在第二个式子里
请把这句话读三遍。b 参与了「选谁」,但完全不参与「选中之后各占几成」。论文的原话是:因为 b 被从 pi,j 里省略了,它调节的是分发(dispatch),而不改变混合权重,也不改变路由器基于梯度的优化。
这带来三个后果:
(1)模型输出不被负载均衡污染。假如 b 进了 p,那「为了均衡给冷门专家的加分」就会直接变成「这个冷门专家在最终答案里的话语权变大」——你为了让机器跑得均匀,篡改了模型的回答。现在不会。
(2)路由器的梯度是干净的。b 不在 p 里,所以反向传播到 Wr 的梯度里不含 b 的贡献。路由器还是按语言建模损失去学它认为对的偏好,均衡机制在另一条轨道上运行。
(3)代价是:b 没有梯度可用,只能另外用一条规则去更新它。这条规则就是 2.3.3 的全部内容。
老办法的困境:一个调不好的步长
原来的免辅助损失方法用一条定步长符号更新规则:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| ℓj(t) | 专家 j 在第 t 步实际收到的 token 数(负载) | 这位医生今天看了几个病人 |
| ℓ̄ | 平均负载 | 「本该看几个」 |
| sign(·) | 符号函数:正数取 +1,负数取 −1 | 只看方向,不看差多少 |
| γ | 固定步长(一个超参数) | 每次挪多大一步 |
读法:如果专家 j 的负载低于平均,sign 为 +1,就给它的偏置加 γ,下一步更容易被选中;反之减 γ。注意 sign 把「差多少」这个信息全丢了——差 1 个 token 和差 1000 个 token,动作完全一样,都是挪 γ。
论文指出这里的困境:γ 在「适应太慢」和「震荡」之间权衡。γ 小了,负载偏离目标之后要很多步才能纠回来;γ 大了,会冲过头,然后往回冲,来回震荡。而 LatentMoE 把每层路由专家池扩到 896 个之后,这个权衡更难做了——专家越多,每个专家的负载统计噪声越大(平均只有 mk/896 个 token),也就越容易把噪声当成信号乱挪。
打个比方
老办法像用一个只有「热一点 / 冷一点」两个按钮的空调,每按一次固定改 0.5 度。想从 30 度调到 22 度,得按 16 次;按快了又会一路冲到 18 度再往回调。QB 则是直接输入「22 度」——它不是朝目标挪一步,而是解出能达到目标的那个设定值。
类比失效处:空调的「22 度」是一个可以直接设定的物理量,而专家的负载不能直接设定——你只能设 bj,负载是 bj 加上路由分数之后 Top-k 竞争的结果。QB 巧的地方就在于它找到了一个办法,把「我要的负载」反解成「该设的 bj」。而且这个反解只在「其他条件固定」的假设下精确——5.6 末尾会看到这个假设不总成立。
QB 的核心:把偏置设成一个分位数
分位数(quantile):把一堆数从小到大排好,「q 分位数」就是排在 q 这个比例位置上的那个数。比如 100 个数的 0.9 分位数,就是排序后第 90 个数——有 90% 的数不超过它,10% 的数超过它。中位数就是 0.5 分位数。
分位数均衡(Quantile Balancing, QB):论文的定义是「把每个专家的偏置设成与它的目标负载相匹配的那个路由分数分位数」。注意这里没有步长、没有学习率——它是一个直接赋值,不是一次挪动。
先把符号摆清楚。考虑一个训练批次:m 个 token,n 个专家,Top-k 选择。总共要发出 mk 个「token→专家」的分配。均分到 n 个专家头上,每个专家的目标负载是
| 符号 | 是什么 | 直觉 |
|---|---|---|
| m | 这一批里的 token 总数(真实训练中是全局批次,百万量级) | 今天来了多少病人 |
| n | 路由专家总数,K3 每层 896 | 有多少位坐诊医生 |
| k | 每个 token 激活的专家数,K3 是 16 | 每个病人看几位医生 |
| mk | 这一批要发出的「token→专家」分配总数 | 今天总共要开多少次会诊 |
| q | 目标负载:完全均衡时每个专家该收到的 token 数 | 「每位医生本该看几个病人」 |
| := | 「定义为」,不是待求解的等式 | 这是我们自己定的目标 |
(a)论文 Figure 5 的例子是 m=8 个 token、n=4 个专家、k=1。算出目标负载 q。(b)在 K3 的真实设置下(n=896、k=16),如果一个全局批次有 100 万个 token,每个专家的目标负载是多少?(c)验证一下:q/m 等于什么?
变式:如果 m=10、n=4、k=1,q = 2.5——一个专家不可能收到半个 token。这时「让计数恰好等于 q」这句话还成立吗?论文的推导里假设了什么?(提示:论文说 「Assuming no ties」,但没有讨论 q 不是整数的情况。真实规模下 m 是百万级,这个问题被稀释了,但推导的严格性有个缺口。)
第一步的巧妙:从 Top-(k+1) 里白捡一个门槛
QB 只用一次前向传播就推出下一步的偏置。第一个动作是:把路由时的 Top-k 换成对有偏分数 si + b(t) 的 Top-(k+1)。
取出来的 k+1 个数里:前 k 个就是实际走的路由(和原来一模一样),第 (k+1) 个是一个门槛 αi(t)——它的含义是:一个专家要想挤进 token i 的 Top-k,它的有偏分数必须超过这个值。
自己推一遍:为什么这个门槛来得这么便宜
把 token i 对 n 个专家的有偏分数从大到小排好,记为 v(1) ≥ v(2) ≥ … ≥ v(n)。哪些专家进了 Top-k?一个「新来的」专家要挤进去,分数需要超过哪个数?
想好了再看
进 Top-k 的是 v(1)…v(k)。一个新来的要挤进去,就得把现在的第 k 名挤出去,所以它得超过当前的第 (k+1) 名 v(k+1)——超过第 (k+1) 名,它就排到了第 k 位或更前。所以门槛 αi = v(k+1)。
当初为什么会想到这一步:因为我们要问的问题是「假如给专家 j 的偏置调一调,它能不能进 token i 的名单」。这个问题的答案只依赖于一个数——名单的准入线。而准入线就是第 (k+1) 名的分数。现在的关键问题:把 Top-k 改成 Top-(k+1),会不会改变实际的路由结果?
想好了再看
不会。Top-(k+1) 返回的是排序后的前 k+1 个,其中前 k 个就是 Top-k 的结果,一个不多一个不少。第 (k+1) 个只是被顺手读出来当门槛用,不参与路由。所以 QB 对前向计算的唯一改动是「排序的时候多取一个数」——多取一个数的开销,相对于 16 个专家的前馈计算,可以忽略。
论文的原话是:「Taking the cutoff from Top-(k+1) routing avoids a separate token-side quantile.」如果不这么做,你就得为每个 token 单独再算一次「它的第 k+1 高分是多少」,那是对 896 个分数的又一次统计——现在这个数是 Top-k 排序的副产品,白捡的。门槛 αi(t) 是用有偏分数 si+b(t) 算的,也就是说旧偏置藏在门槛里。这会不会让更新变成一团乱麻?
想好了再看
论文特意点了这件事:「the old bias enters the update only through the cutoffs」——旧偏置只通过门槛进入更新,别的地方不出现。式 14 里的间隔用的是原始分数 si,j 减去有偏门槛 αi(t),而不是有偏分数减有偏门槛。这样解出来的 b̂j(t+1) 是一个新的绝对值,不是「在旧值上加一个增量」。这正是「直接赋值而不是挪一步」的技术实现。
第二步:固定门槛,反解偏置
现在把所有 token 的门槛 αi(t) 钉住不动,只问一件事:给专家 j 换一个候选偏置 b̂j,它会收到几个 token?答案是数一数有多少个 token 的分数越过了门槛:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| 1[·] | 指示函数:中括号里的条件成立时取 1,不成立时取 0 | 「算不算一个」的开关 |
| b̂j(t+1) | 专家 j 的候选新偏置(戴帽子表示还没做最后的减均值) | 试着给这位医生加多少分 |
| αi(t) | token i 的准入门槛,来自 Top-(k+1) 的第 k+1 个有偏分数 | 第 i 个病人的会诊准入线 |
| si,j − αi(t) | 论文称为间隔(margin):专家 j 对 token i 「差多少才够格」 | 正数=够格有余,负数=还差这么多 |
把条件挪一挪:si,j + b̂j > αi 等价于 si,j − αi > −b̂j。左边是间隔,右边是一个阈值。于是这个计数就变成了「有多少个间隔超过了阈值 −b̂j」。
这个计数关于阈值 −b̂j 是单调递减的:阈值越高,能越过的间隔越少(阈值定到 +∞,一个都过不去;定到 −∞,全都过得去)。单调 ⇒ 想让计数等于任何一个目标值,都有唯一的阈值可以做到(在无并列的假设下)。这就是「解方程」而不是「挪一步」的数学依据。
令计数 = q,解是什么?把专家 j 这一列的 m 个间隔从大到小排好,要让恰好 q 个严格超过阈值,阈值就得落在第 (q+1) 大的间隔上——这样前 q 个严格超过它,第 (q+1) 个正好等于它(不算超过),后面的都小于它。所以
换句话说:阈值 −b̂j(t+1) 就等于「专家 j 这一列 m 个间隔里第 (q+1) 大的那个」。
最后一步换个说法。「m 个数里第 (q+1) 大」,用分位数怎么表达?有 q 个数在它上面,占比 q/m;而我们在前面验证过 q/m = k/n。所以它下面的占比是 1 − k/n,也就是这批间隔的 (1−k/n) 分位数。写成论文的式 14:
b(t+1) ← b̂(t+1) − mean( b̂(t+1) ) 1
| 符号 | 是什么 | 直觉 |
|---|---|---|
| s:,j | 分数矩阵的第 j 列:全部 m 个 token 对专家 j 的分数 | 「这位医生对全体病人的匹配度」 |
| α(t) | 长度 m 的门槛向量,每个 token 一个 | 每个病人各自的准入线 |
| s:,j − α(t) | 逐元素相减,得到 m 个间隔 | 这位医生对每个病人「差多少」 |
| quantile1−k/n | 取这 m 个间隔的 (1−k/n) 分位数(K3 是 1−1/56 ≈ 0.982 分位数) | 「排在前 1.79% 的门槛线」 |
| 前面的负号 | 因为阈值是 −b̂j,解出阈值后要取负得到偏置 | 间隔越高的专家(说明它太抢手)拿到越负的偏置 |
| mean(b̂) 1 | 所有专家候选偏置的平均值,乘以全 1 向量(即每个分量都减这个平均值) | 把整体的公共偏移抹掉 |
第二行为什么可以随便减一个常数
Top-k 是在同一个 token 内部比较 n 个专家的分数。如果给所有专家的偏置都减去同一个数 c,那每个专家的分数都降了 c,相对大小完全不变,排序不变,Top-k 结果一个字都不变。所以减均值是「免费的」——不改变任何路由行为,只是把 b 这个向量的整体水平拉回 0 附近,防止它在长期训练中无限漂移。
因果性:为什么更新要等到下一步
论文说:「For causality, the update takes effect only in the next step, i.e., a batch is never routed with a bias derived from itself.」——一个批次绝不会用从它自己推出来的偏置来路由。
为什么必须这样?因为 QB 的偏置是从这个批次的分数反解出来的。如果立刻用回这个批次,等于是「先看答案再考试」:模型的路由决定依赖于它自己这一批的统计,训练和推理的行为就对不上了(推理时你面对的是单个请求,不可能有「这一批的分位数」)。论文也说明了:推理时偏置是冻结的——训练结束时的 b 定死,不再更新。
把论文 Figure 5 的例子完整走一遍
论文 Figure 5 用 m=8 个 token、n=4 个专家、k=1 演示了一遍 QB:初始负载是严重不均的 (4, 3, 1, 0)——E₁ 过热,E₄ 是一个 token 都没有的濒死专家;目标负载 q = 8×1/4 = 2;QB 一步之后变成 (2, 2, 2, 2)。
论文没有给出具体的分数数值,下面这组数是我们自己构造的一个满足论文全部条件的算例(m、n、k、初始负载、目标负载、最终负载都与论文一致)。建议拿纸笔跟着算一遍,这一节的所有概念都会在这个表格里落地。设当前偏置 b(t) = 0(初始状态)。
| token | E₁ | E₂ | E₃ | E₄ | 门槛 αi | 去了哪 |
|---|---|---|---|---|---|---|
| t₁ | 0.92 | 0.60† | 0.30 | 0.20 | 0.60 | E₁ |
| t₂ | 0.88 | 0.52† | 0.26 | 0.18 | 0.52 | E₁ |
| t₃ | 0.48† | 0.84 | 0.38 | 0.14 | 0.48 | E₂ |
| t₄ | 0.74 | 0.40 | 0.34 | 0.68† | 0.68 | E₁ |
| t₅ | 0.42† | 0.76 | 0.22 | 0.28 | 0.42 | E₂ |
| t₆ | 0.32 | 0.64 | 0.61† | 0.29 | 0.61 | E₂ |
| t₇ | 0.70 | 0.25 | 0.44 | 0.65† | 0.65 | E₁ |
| t₈ | 0.52† | 0.16 | 0.62 | 0.42 | 0.52 | E₃ |
| 初始负载 | 4 | 3 | 1 | 0 | — | 严重不均 |
注意 k=1 时 Top-(k+1) 就是 Top-2,所以门槛 αi 就是每行的第二高分。第二步,把每个分数减去它所在行的门槛,得到间隔:
| token | E₁ | E₂ | E₃ | E₄ |
|---|---|---|---|---|
| t₁ | +0.32 | 0.00 | −0.30 | −0.40 |
| t₂ | +0.36 | 0.00 | −0.26 | −0.34 |
| t₃ | 0.00 | +0.36 | −0.10 | −0.34 |
| t₄ | +0.06 | −0.28 | −0.34 | 0.00 |
| t₅ | 0.00 | +0.34 | −0.20 | −0.14 |
| t₆ | −0.29 | +0.03 | 0.00 | −0.32 |
| t₇ | +0.05 | −0.40 | −0.21 | 0.00 |
| t₈ | 0.00 | −0.36 | +0.10 | −0.10 |
| 第 (q+1)=3 大 | +0.06 | +0.03 | −0.10 | −0.10 |
| 新偏置 b̂j | −0.06 | −0.03 | +0.10 | +0.10 |
怎么读最后两行:以 E₁ 那一列为例,8 个间隔排序后是 0.36, 0.32, 0.06, 0.05, 0.00, 0.00, 0.00, −0.29。q=2,所以取第 q+1 = 3 大的那个,是 0.06。取负得 b̂1 = −0.06。验算:严格大于 0.06 的间隔恰好有 2 个(0.36 和 0.32)——正是目标负载。四列都可以这样验一遍。E₃ 和 E₄ 这两个冷门专家拿到的是正偏置(+0.10),E₁ 和 E₂ 这两个热门专家拿到负偏置,方向完全符合直觉。
(论文式 14 第二行还要减去均值:mean(b̂) = (−0.06−0.03+0.10+0.10)/4 = 0.0275,减完得 b = (−0.0875, −0.0575, +0.0725, +0.0725)。前面说过这一步不改变任何 Top-k 结果,所以下面直接用 b̂ 验证。)
第三步,用新偏置重新路由:把表 5-4 的每一列各自加上 b̂j,再取每行最大。例如 t₄ 这一行变成 E₁: 0.74−0.06 = 0.68、E₂: 0.40−0.03 = 0.37、E₃: 0.34+0.10 = 0.44、E₄: 0.68+0.10 = 0.78 —— t₄ 从 E₁ 改投 E₄。全部八行算完:
最终负载 (2, 2, 2, 2)。只动了三条边,四个专家全部达到目标负载,而且没有任何一步用到学习率或步长——每个偏置都是从一次前向传播的统计里直接解出来的。
式 13 和式 14 里 b 有两个「不影响什么」的性质:(a)b 不出现在 pi,j 里;(b)给所有 bj 减去同一个常数,Top-k 不变。请分别说明这两条各自保护了什么。然后回答一个更尖的问题:既然 (b) 说公共偏移无所谓,为什么论文还要专门写这一步?
(a)保护的是模型输出和路由器的学习不被均衡机制污染。论文原话:「it regulates dispatch without altering the mixture weights or the gradient-based optimization of the router」。如果 b 进了 p,那么「为了均衡给冷门专家 +0.1」就会变成「这个冷门专家在答案里多占 10% 的话语权」——你为了机器效率篡改了模型的判断。而且 b 会出现在反向传播路径上,路由器学到的偏好会被均衡目标扭曲。分开之后,路由器只管「谁最合适」,偏置只管「谁该被叫上场」,两条轨道互不干涉。
(b)保护的是数值卫生。式 14 第一行解出的 b̂ 是绝对值,其整体水平取决于这一批间隔的绝对位置——完全可能每一步都整体偏正或整体偏负,累积几十万步之后 b 的所有分量都漂到很大的地方。虽然这不影响路由(由 (b) 保证),但会影响数值精度:一堆大数相减求排序,有效位数会被吃掉;而且日志里看到 b 全是 ±1e5 会让人以为出了问题。减均值把 b 钉在均值 0 上,让它的每个分量都保持「相对偏移」的语义——正数=这个专家需要被扶一把,负数=需要被压一压。
更深的一层:正因为公共偏移不影响任何东西,式 14 第一行才有权利只关心「相对关系」——它解出的 n 个值本质上只有 n−1 个自由度,第二行只是挑了一个规范的代表。
变式:如果把第二行改成「减去 b̂ 的中位数」而不是均值,会有什么不同?路由结果会变吗?(不会。)那两种选择有实质区别吗?(提示:想想少数几个极端偏置对均值和对中位数的影响。论文没有讨论这个选择。)
QB 的推导保证了:在门槛 α 固定的前提下,专家 j 恰好有 q 个 token 越过它的门槛。但真实的路由是每个 token 各自取 Top-k。请构造一个具体的小例子(建议 m=4, n=2, k=1),说明 QB 一步之后负载可能不是恰好 (q, …, q)。
门槛(每行第二高):α = (0.10, 0.20, 0.30, 0.55)。初始负载 = (4, 0)。
间隔(s − α):E₁ 列 = (+0.80, +0.60, +0.40, +0.05);E₂ 列 = (−0.80, −0.60, −0.40, −0.05)。
取第 3 大:E₁ 列排序 0.80, 0.60, 0.40, 0.05 → b̂1 = −0.40。E₂ 列排序 −0.05, −0.40, −0.60, −0.80 → b̂2 = +0.60。
验算「越过门槛」的计数:E₁ 有 2 个间隔严格大于 0.40(0.80 和 0.60),对上了;E₂ 有 2 个间隔严格大于 −0.60(−0.05 和 −0.40),也对上了。两个专家都「应该」拿到 2 个 token。
可是真实路由:新的有偏分数为 E₁ = (0.50, 0.40, 0.30, 0.20),E₂ = (0.70, 0.80, 0.90, 1.15)。每行取最大 → 四个 token 全部去了 E₂。负载从 (4, 0) 变成 (0, 4) —— 完全翻转,比之前还不均衡。
为什么会这样:E₁ 和 E₂ 各自解偏置时都假设「对方的门槛不变」,但它们同时生效之后互相踩了脚。t₁ 和 t₂ 对 E₁ 越过了门槛,对 E₂ 也越过了,可它们只能各去一个地方——两个专家都以为自己拿到了这两票,实际只有一个能拿。
诚实地说:论文正文只给了「计数等于 q」这一步的推导,把真正的「balanced-assignment derivation」推给了附录 C(见第14章),正文没有说明在什么条件下这个交换是精确的。上面这个极端例子是刻意构造的(只有 2 个专家、分数高度对称);真实规模下 n=896、m 是百万级,单个专家的偏置变化对某个 token 的排序影响很小,所以实际效果会好得多。但「QB 一步就精确均衡」这个说法,从正文给出的论证里是推不出来的。
变式:把上面的例子改成 n=4 个专家、m=8(也就是表 5-4 那个成功的例子),为什么它就没有翻车?两者的区别在哪?(提示:数一数在新偏置下,每个 token 到底有几个专家越过了它的门槛。成功的例子里恰好每个 token 都有且只有 1 个。)另外想想:既然 QB 的更新到下一步才生效、而且每一步都会重算,那「单步不精确」这件事有多严重?
「把 Top-k 换成 Top-(k+1)」看起来只是多取一个数,但它同时办成了三件事。请把这三件事说清楚:(a)为什么这样取门槛不改变路由结果本身?(b)为什么这样就不用另算一个 token 侧的分位数?(c)为什么门槛必须用有偏分数算,而式 14 的间隔却用原始分数减它?最后综合回答:如果把这三件事拆开单做,QB 的开销会变成什么样?
(a)不改变路由:Top-(k+1) 返回的前 k 个元素就是 Top-k 的结果。第 (k+1) 个被读出来只作统计用,不参与 dispatch、不参与 p 的归一化。所以 QB 对模型的前向行为是零侵入的——这一点很重要,意味着开不开 QB 不会改变模型这一步算出来的东西,唯一的差别在下一步的偏置。
(b)省掉 token 侧分位数:论文原话「Taking the cutoff from Top-(k+1) routing avoids a separate token-side quantile」。QB 要的门槛,本质上是每个 token 的分数分布的一个「上 k 分位点」。如果不从路由里拿,就要对每个 token 的 896 个分数再做一次选择——而 Top-k 排序已经在做这件事了,只是原来把第 k+1 个扔掉了。QB 做的是把一个本来被丢弃的中间量捡回来用。这是整个方法「几乎不要钱」的根本原因。
(c)有偏门槛 + 原始分数:门槛必须是有偏的,因为「能不能进名单」是在有偏分数上比较的,准入线自然长在有偏的尺度上。而间隔用原始分数 si,j 减它,解出来的 b̂j 就直接是「新偏置本身」——它已经把「要补偿掉旧偏置」这件事吸收进去了。如果间隔也用有偏分数,解出来的是「相对旧值的调整量」(论文 Figure 5(b) 里那条红色虚线画的正是 bj(t) − b̂j(t+1)),还得再做一次加减才能得到新偏置。用原始分数是更直接的写法,也让「旧偏置只通过门槛进入更新」这句话成立——旧偏置的影响被限制在一个明确的、单一的通道里。
综合:拆开单做的话,QB 需要(1)正常的 Top-k 路由,(2)额外一遍对每 token 896 个分数的第 k+1 大选择,(3)额外一遍把旧偏置从解里剥出来的运算。合起来做,这三件事变成「排序时多取一个数」。这就是为什么论文可以说「QB derives the next bias from a single forward pass」——单次前向,没有额外的前向或反向。
变式:如果有人改用 Top-(k+5),取第 k+5 个分数当门槛,会发生什么?(提示:那条线就不再是「进 Top-k 的准入线」了,而是「进 Top-(k+4) 的线」,反解出来的偏置对应的目标就不是 q 了。反过来问:Top-k 本身(取第 k 个当门槛)行不行?)
答辩:如果我是审稿人
你反复强调 QB「不需要学习率」,把这当成相对定步长方法 γ 的主要卖点。可你的直方图估计引入了箱数 B、箱的边界范围、以及「多久更新一次偏置」这些选择;正文里 B 的具体取值一个字都没写。这不就是把一个超参数换成了三个吗?请辩护。
参考防守(先自己组织语言再看)
防守要分清「超参数」的两种性质。
γ 是一个控制回路的增益:它决定系统的动态行为,调错的后果是定性的——太小则负载长期偏离目标,太大则震荡甚至不收敛。而且它的最优值随专家数 n、批次大小、训练阶段而变,论文正是因为 n 涨到 896 让这个权衡变难才要换掉它。
箱数 B 是一个估计精度参数:它决定的是「我算出来的分位数离真值有多远」,误差被箱宽单调地控制住,论文说误差界在附录 D 给出。B 调小一点,结果是偏置略微不准,负载略微偏离 q;不会出现「震荡发散」这种定性失效。关键区别:γ 错了,方法就坏了;B 小了,方法只是精度差一点。论文说几百个箱就够,说明这个精度很容易买到。
更根本的一层:QB 是在解方程——「让计数等于 q」这个方程的解不依赖于任何步长;定步长法是在朝解走,走多快必然是一个自由参数。前者的超参数只影响「解得多准」,后者的超参数影响「走不走得到」。
但必须承认的两点:(1)论文正文确实没给 B 的取值、箱边界如何确定、偏置更新频率,也没给任何关于 B 的消融,这个防守只能靠附录 D 的误差界撑着。(2)如果间隔的分布高度集中(比如全挤在一个箱里),箱宽带来的偏差就不再是小量——论文没有讨论这种病态情形。这是一个诚实的开放问题。
答辩:如果我是审稿人
QB 强行让每个专家拿到恰好 q = mk/n 个 token。可数据本身并不均匀——如果某个批次里 90% 是代码,难道不应该让擅长代码的专家多干活吗?你这是为了系统效率在牺牲专家的专业化。而且 QB 比定步长法「更硬」(每步直接解到目标,而不是挪一小步),这个牺牲反而更极端。请辩护。
参考防守(先自己组织语言再看)
三层防守。
第一层:偏置不进混合权重。这是式 13 最关键的设计。b 只改「谁被叫上场」,不改「上场之后各占几成话语权」,也不进入路由器的梯度。所以路由器仍然完全按语言建模损失去学「代码 token 应该配什么样的专家」这件事——被均衡改变的只是名单的边缘部分(那些分数接近门槛的专家),分数遥遥领先的专家该被选还是被选。专业化的核心信号并没有被动过。
第二层:均衡的动机不是审美。专家并行下每步的耗时由最忙的卡决定,一个 10 倍不均衡意味着大部分卡在空转;长期分不到 token 的专家收不到梯度,最后是一堆随机初始化的死参数占着显存。这两条论文都明说了。不均衡不是「让模型更专业」,而是「让 896 个专家里的一部分白买了」。
第三层:目标负载是对全局批次算的。q 定义在整个全局批次上(论文说直方图代表的是「token 怎么分片都一样」的全局池化批次),百万级 token 的混合分布比单个 micro-batch 稳定得多。「90% 是代码」这种极端情形在全局批次尺度上被稀释了。
但要承认:这确实是一个真实的取舍,而论文没有给出任何关于「QB 是否损害了专家专业化」的实验证据——正文只给了「不均衡拖慢训练、可能训练不足」这一侧的定性理由,另一侧的代价完全没有测量。审稿人这一刀扎在了论文的软处。另外,「QB 更硬所以牺牲更大」这个观察也很难反驳:定步长法至少在一步之内只挪 γ,QB 一步就解到目标。论文对此没有讨论。
5.7 直方图估计:真实规模下这个分位数根本收不齐
表 5-4 那个 8×4 的例子里,求分位数就是「把 8 个数排个序,取第 3 大」。真实训练里做不到。
论文把困难说得很清楚:式 14 里的分位数是对整个全局批次算的,这些间隔的数量以百万计,而且分散在各个 rank 和各个梯度累积步上——同一个专家的间隔,一部分在第 3 号卡上、一部分在第 47 号卡上、一部分要等到这一步的第 8 次累积才产生。要精确求分位数,就得把它们全都收集到一处再排序。论文的判断是:训练时这不可行。
为什么「排序」在分布式训练里这么贵
求分位数需要全局排序,而排序不能分而治之地合并——你不能让每张卡各自排好序然后简单拼一下就完事,必须真的把数据搬到一起。上百万个数 × 896 个专家 × 每一层,光是网络传输就足以把训练拖垮,而这只是为了算几个用来调度的偏置。
直方图(histogram):把数值范围切成若干个箱(bin),只记录「落在每个箱里的数有多少个」,不保留每个数具体是多少。比如把 [−1, 1] 切成 200 个箱,每个箱宽 0.01,那么 100 万个数就被压缩成 200 个计数。
QB 的做法是:给每个专家维护一个它自己的间隔直方图。每张卡各自统计自己那部分 token 产生的箱计数,然后一次 all-reduce 把各 rank 的箱计数加起来,再从汇总后的计数里把分位数恢复出来(从最大的箱往下累加,加到超过目标个数为止,那个箱的位置就是分位数)。
为什么「可加」是这里的全部关键
论文的原话值得逐句拆:「Because counts are additive, the histogram represents the pooled global batch regardless of how tokens are sharded」——因为计数是可加的,所以直方图代表的是「不管 token 怎么分片都一样」的全局池化批次。
意思是:第 3 号卡说「[0.05, 0.06) 这个箱里有 120 个」,第 47 号卡说「有 85 个」,加起来就是 205 个,和把这些 token 放在同一张卡上统计出来的结果一模一样。分片方式、梯度累积的次数、卡的数量,全都不影响最终的箱计数。所以这个估计不是「近似了全局批次」,它在计数这个层面上就是全局批次。论文说估计的是全批次分位数,精确到箱宽(up to the bin width)——唯一的误差来源是「箱内的数具体在哪」这个信息被丢掉了,而这个误差被箱宽严格框住。
通信代价:论文说只有每专家几百个箱。对比一下要传的原始数据——每专家上百万个间隔。压缩了三四个数量级。
论文明确说:这个直方图估计器就是实践中真正使用的方法(「This histogram estimator is the method we use in practice」)——式 14 里那个精确的 quantile 是理想化的写法。更详细的描述和它的误差界,论文放在附录 D,见第14章。
有人提出一个更省事的方案:「让每张卡用自己手上的间隔各算一个分位数,然后 all-reduce 求平均,得到全局分位数。」这样传的数据更少(每专家只传 1 个数,而不是几百个箱计数)。请构造一个具体的反例说明这个方案是错的,并指出它错在哪个数学性质上。
反例:卡 A = {0, 0, 0},中位数 0;卡 B = {10, 20, 30},中位数 20。两者平均 = 10。
真实情况:合并后 {0, 0, 0, 10, 20, 30},排序取中间(第 3、4 个的平均)= (0+10)/2 = 5。5 ≠ 10,差了一倍。
更极端的反例:卡 A = {0, 0, 0, 0, 0, 0, 0, 0, 0, 100},中位数 0;卡 B = {0, 0, 0, 0, 0, 0, 0, 0, 0, 100},中位数 0。平均 = 0,这次恰好对了。现在把卡 B 换成 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},中位数 1,平均 = 0.5;真实合并后 20 个数的中位数 = 0.5——又对了。但这只是因为两卡数据量相同且分布对称,纯属巧合。只要两张卡的数据量不同(真实训练里各 rank 的 token 数、以及某个专家在各 rank 上的间隔数,完全可能不同),甚至连「加权平均」都救不回来。
错在哪个性质上:分位数是一个顺序统计量,它由「有多少个数小于某个值」决定,而不是由数值的算术运算决定。可加的是计数,不是分位数。所以正确的合并方式必须先把可加的量(箱计数)加起来,再从合并后的计数里恢复分位数——这正是论文的做法。论文那句「Because counts are additive」不是一句修辞,它精确地指出了唯一能被安全 all-reduce 的东西是什么。
顺带一提:如果 all-reduce 传的是「原始间隔的和」或者「平方和」,那是可加的,可以得到全局的均值和方差——但均值和方差不能反推出分位数,除非你假设分布是某个已知形状(比如高斯)。直方图的好处正是不需要任何分布假设。
变式:反过来问——既然计数可加这么好用,为什么不干脆把箱数开到 100 万、让直方图变成「精确的」?(提示:算一算通信量。896 个专家 × 100 万个箱 × 每层 × 每步,是多少个数?再对比论文说的「几百个箱」。另外想想:箱开得太细,每个箱里只有 0 或 1 个数,直方图就退化成了原始数据本身。)
读的时候要小心
关于直方图估计,论文正文没有给出:箱数 B 的具体取值、箱边界如何确定(是固定范围还是自适应的?)、偏置多久更新一次(每步还是每若干步?)、以及箱宽带来的误差在实际训练中有多大。论文只说「几百个箱」和「误差界见附录 D」。复现这一部分的人会在这里遇到实打实的空白。
本章小结
这一章讲的是同一件事的三个面:怎么把宽度做稀疏,而不让它炸掉。
做稀疏靠 LatentMoE:把「整个模型多宽」(d = 7168)和「路由专家多宽」(ℓ = 3584)拆成两个独立的数。共享专家留在全宽通路上做通用变换,896 个路由专家在减半的潜空间里工作。被 dispatch 的是压缩后的 z = W↓x,通信量和专家权重流量都砍半,正好抵消掉「激活专家数从 8 提到 16」带来的翻倍。稀疏度 896/16 = 56。
不炸掉靠三件事,对应论文点名的两个失效模式:
- 失效模式一(路由分支激活爆炸)——路由通路把 W↓、门控多分支专家前馈网络、W↑ 串成近乎四次连续矩阵乘法的链条,叠加 2.8 万亿参数规模。
对策 1:Normalized LatentMoE——在专家聚合和上投影之间插一个 RMSNorm(式 11 第二行),把「选了哪些专家、权重怎么分」引起的逐 token 尺度抖动抹平。论文自述这还持续改善验证损失和下游基准(无数字)。
对策 2:SiTU-GLU(式 12)——SwiGLU 的两个乘性因子都无界,同时出现的大坐标会产生激活离群值、抬高低精度溢出风险。用 softcap(x, β) = β tanh(x/β) 分别封住 Swish 门的线性因子(β₁ = 4)和上分支(β₂ = 25)。原点附近近似线性所以行为几乎等同 SwiGLU,大幅值处有界所以 |f| ≤ β1β2 = 100。 - 失效模式二(近 10³ 个专家的负载均衡)——超出了定步长符号更新还能良好工作的范围。
对策 3:Quantile Balancing(式 13、式 14)——免辅助损失路由给每个专家配偏置 bj,只进 Top-k 选择、不进混合权重 p、不进路由器的梯度。QB 不再「朝目标挪 γ」,而是直接解出偏置:用 Top-(k+1) 白捡每个 token 的准入门槛 αi,把「专家 j 收到几个 token」写成「有多少个间隔 si,j − αi 超过阈值 −b̂j」,利用单调性反解出 b̂j = 第 (q+1) 大间隔的相反数 = 间隔的 (1−k/n) 分位数的相反数。再减去公共偏移(不改 Top-k),下一步才生效(因果性),推理时冻结。真实规模下分位数用可加的直方图 + 一次 all-reduce 估计,代价每专家几百个箱。
本章标注为存疑或论文未说明的地方
- 「近乎四次连续矩阵乘法」没有点名是哪四个矩阵;「病态结构」(ill-conditioned)没有任何定量证据(无条件数、无激活范数曲线、无失败训练曲线)。
- 额外 RMSNorm「持续改善验证损失和下游基准」是论文自述,正文没有给任何消融数字;也没说明它是否带可学习增益、共享分支是否也做类似处理。
- β₁ = 4、β₂ = 25 只是给出取值,没有消融、没有解释为什么两者差六倍多。
- SiTU-GLU 与硬截断的比较、局部展开、形式化的输出界,正文全部推给附录 B。
- 「896 个专家超出现有免辅助损失偏置更新的良好工作范围」是断言,没有支持数据。
- 正文的 QB 推导只证明了「门槛固定时每个专家恰好有 q 个 token 越过门槛」,而真实路由是逐 token 取 Top-k,两者不等价(q5-11 里我们构造了一个 (4,0) → (0,4) 的反例)。真正的 balanced-assignment 推导被推给附录 C,正文没有说明取等的条件。
- 推导假设「无并列」(no ties),也没有讨论 q = mk/n 不是整数时怎么办。
- 直方图的箱数 B、箱边界如何确定、偏置更新频率,正文一概未给;误差界推给附录 D。
- 「QB 是否损害了专家专业化」这一侧的代价,论文完全没有测量。
- LatentMoE 相对常规 MoE 到底省了多少通信量,论文没给数字(本章的 0.5× 是我们用 Table 1 的 ℓ/d 推的);ℓ 取 0.5d 也没有消融。
- Table 1 的「每专家 MoE 隐藏维 3072」没有区分是路由专家的还是也包括共享专家。
Chapter 5 · Stable LatentMoE: making “width” sparse without blowing it up
In one sentence: this chapter answers “how do you grow the little networks inside one layer from 384 to 896 without the training falling over” — K3 does it with three things: move the experts into a narrower latent space, insert an RMSNorm before the up-projection, fit the activations with two soft limiters; then level the load across the 896 experts with a quantile rule that needs no learning rate.
After this chapter you should be able to
- Explain in your own words how “2.78 trillion parameters in total, only 104.2 billion used per token” is done, and work out K3's sparsity
- Point out which two “widths” LatentMoE pulls apart, and which bill pulling them apart saves
- Line up “the two failure modes that extreme sparsity amplifies” with “the three countermeasures” one for one, and say clearly which treats which
- Check the two limits of β·tanh(x/β), and derive for yourself why the output of SiTU-GLU is bounded above by β₁β₂
- Work Quantile Balancing through by hand: start from the routing scores, find each expert's new bias, and explain why it needs no learning rate
- Construct a counterexample showing that after one step of QB the load may still not be exactly balanced
5.1 First get MoE straight: why “more parameters” need not mean “slower to compute”
First set the stage. A Transformer layer does two things: attention lets the different words take a look at each other (earlier chapters covered this), and the feed-forward network(Feed-Forward Network, FFN) lets each word think a bit more on its own.
Feed-forward network: a small network that acts on a single token only. It takes that token's d-dimensional vector, uses one matrix to lift it to a wider hidden dimension h, applies one non-linear transformation, then uses another matrix to squeeze it back to d dimensions. It does not look at any other token; it is purely “one round of processing on this one vector”.
What happens if you do not use MoE
If you want the model to hold more, the most direct move is to turn up the FFN's hidden dimension h. But both the FFN's parameter count and its compute per token are proportional to d·h — double the parameters and the compute doubles with them. This road is 1:1: to remember a bit more you have to compute a bit more. At the scale of 2.78 trillion parameters this road does not go anywhere.
Mixture-of-Experts: instead of making the FFN one big one, make it many small ones (each is called an expert), and add a router(router). The router scores every token and picks only the k highest-scoring experts to actually run; the rest take no part in the computation this time round.
That decouples parameter count from compute. Suppose there are n experts and each token uses only k of them: the parameter count is proportional to n, the compute per token is proportional only to k. You can pile n up and hold k fixed. The model “knows” more, but the path each token walks has not got any longer.
Sparsity: in the paper this means the total number of experts divided by the number activated per token, n/k. The larger this number, the smaller the share of experts each token uses up, and the more “sparse” the model.
An analogy
Think of one MoE layer as a giant hospital with 896 doctors on duty. When a patient (a token) registers, the triage desk (the router) glances at the symptoms and sends them on to just the 16 best-matched doctors for a consultation. The hospital's “total knowledge” is the sum of all 896 doctors, but each patient takes up the time of only 16 of them.
Where the analogy breaks down: in a real hospital a doctor is an exclusive resource — once in a consultation they are taken; in MoE all the tokens of one batch are processed in parallel, and one expert can serve many tokens at once — which is exactly the root of the “load balancing” problem later on: one doctor may be swamped by a whole batch of patients at once while another gets no patient at all. Also, the router does not really “know medicine”; it is just a learned linear scorer.
K3's actual figures (paper Table 1): n = 896 routed experts, k = 16 activated per token, plus Ns = 2 shared experts(shared experts) — the shared experts take no part in the selection, every token must go through them, and they handle the general-purpose transformations that “everyone needs”. The paper says this “shared + routed” arrangement follows DeepSeekMoE.
K3 has 896 routed experts per layer and activates 16 per token. (a) Work out the sparsity. (b) K2 has 384 routed experts and activates 8 per token — what is its sparsity? Which generation is sparser?
Variant: if K3 keeps its 896 experts but drops the activated count from 16 back to 8, what does sparsity become? And what happens to the routed-branch compute per token? (Hint: sparsity and compute run in opposite directions; making sparsity larger always has a price.)
Paper Table 1 gives K3 a total of 2.78T parameters and 104.2B activated. Work it out: 2780 ÷ 104.2 ≈ 26.7. But we just computed the sparsity as 56. Both numbers are “the total divided by what gets used” — why do they differ by more than a factor of two?
Variant: if the number of shared experts Ns is cut from 2 to 0 (everything else unchanged), does this ratio move closer to 56 or further from it? And what if instead you raise the shared experts to 8?
A common misreading
Many people assume “sparse = cutting corners = worse results”. In fact, under a fixed compute budget per token, MoE is asking a different question: given that either way I only get to compute this much, do I send every token down the same narrow path, or let each one take a path that suits it better? The paper's position is that the second is worth more — which is why the expert pool goes from 384 to 896. But the paper gives no controlled experiment for this in §2.3; it is a design motivation, not an experimental conclusion of that section.
5.2 LatentMoE: splitting “model width” and “expert width” into two separate things
Enlarging the expert pool sounds like nothing more than “storing a few more parameters”, but there is a hidden bill. In a regular MoE, every selected expert receives the full d-dimensional token representation. In large-scale training the experts are spread across different GPUs, so the token has to be sent over the network to the experts that were picked (this step is called dispatch), and sent back once they are done. Pick k experts and you send k copies. The paper's own words: it is with the routing multiplicity(routing multiplicity) that both the communication volume and the expert-weight traffic grow. Take k from 8 to 16 and both bills double outright.
LatentMoE: treat “how wide the whole model is” and “how wide a routed expert is” as two numbers you can set separately. The shared experts keep a full-width path (d dimensions in, d dimensions out) for general-purpose transformations; the specialised routed experts work inside a compact latent space(compact latent space) of width only ℓ.
Concretely: first use a down-projection matrix W↓ to squeeze the token from d dimensions down to ℓ dimensions, giving z = W↓x; what gets dispatched is z, not x. Every routed expert is a small ℓ → ℓ network. Once the experts are done and their outputs have been weighted and pooled into one ℓ-dimensional vector, an up-projection matrix W↑ sends it back to d dimensions, where it is added to the shared branch.
| Item | K2 | K3 |
|---|---|---|
| Hidden dimension d | 7,168 | 7,168 |
| Latent MoE dimension ℓ | — | 3,584 (0.5× d) |
| MoE hidden dimension per expert | 2,048 | 3,072 |
| Number of routed experts n | 384 | 896 |
| Experts activated per token k | 8 | 16 |
| Number of shared experts Ns | 1 | 2 |
| Activation function | SwiGLU | SiTU-GLU |
The paper says that in a regular MoE “each selected expert receives the full d-dimensional representation, so the communication volume and the expert-weight traffic grow with the routing multiplicity”. Use K3's numbers (d=7168, ℓ=3584, k=16) to estimate: to what fraction of the original does LatentMoE cut the dispatch data per token? And why does the paper make a point of “grows with the routing multiplicity”?
Why the emphasis on “grows with the routing multiplicity”: because k is a multiplier in both of these costs. K3 raises k from 8 to 16, and if the width stayed the same both bills would double outright; halving the width cancels that doubling exactly. So LatentMoE is a precondition for the decision to “double the number of activated experts”, not an optional optimisation. (The paper only says compact latent space of width ℓ; it gives no concrete figure for the communication volume. The 0.5 above is ours, derived from ℓ/d = 0.5 in Table 1.)
Variant: suppose someone squeezes ℓ further down to 0.25× of d (i.e. 1792), halving the communication again. What does that cost? (Think about how much a routed expert can still express, and whether the information W↓ throws away can still be recovered by W↑. The paper does not run this ablation.)
5.3 The key: the two failure modes that extreme sparsity amplifies
This section is the key to the whole chapter. The previous two sections were about “how to get the scale up”; this one is about “what breaks once it is up”. The paper's wording is blunt: this extreme sparsity amplifies two failure modes of the original design. The three subsections that follow — 2.3.1, 2.3.2, 2.3.3 — are the three countermeasures aimed at those two failure modes. Fix this correspondence in your head first and every later section will have a direction to it.
Failure mode one: activations blow up in the routed branch
Look at the lower path in Figure 5-1: W↓ → a gated multi-branch expert feed-forward network → W↑. The paper says this strings them into “a chain of nearly four consecutive matrix multiplications”, and calls the structure ill-conditioned(ill-conditioned); pile the scale of 2.8 trillion parameters on top of that and the result is activations blowing up inside the routed branch.
Why a “chain of multiplications” is a problem
Matrix multiplication scales vectors. If each step multiplies a vector's length by 1.5 on average, four of them in a row give 1.5⁴ ≈ 5×; if it is 2, four steps give 16×. These matrices change during training, and nobody guarantees they will obediently keep their gain near 1. The longer the chain, the more any departure from 1 is compounded. Worse, training uses low-precision number formats, and once a value shoots up too far it overflows and the whole step is thrown away.
Read this carefully
The paper says “nearly four consecutive matrix multiplications”, but it never names which four matrices. The most natural count given LatentMoE's structure is: W↓ (the 1st) → the gate / up projection inside the expert (the 2nd; two matrices side by side, so they count as only one layer of depth, which is probably where the word “nearly” comes from) → the down projection inside the expert (the 3rd) → W↑ (the 4th). This is our inference, not the paper's own text. The paper also uses the word “ill-conditioned”, but the body gives no quantitative evidence at all — no condition numbers, no activation-norm curves, no training curve showing “it diverges without the fix”. This is purely a claim by the authors.
Failure mode two: the load across nearly 10³ experts will not level out
The second problem has nothing to do with numerics and everything to do with the division of labour. The router has to spread tokens across 896 experts. If the spread is badly uneven — some experts swamped by a whole batch of tokens, others not getting a single one — training goes wrong. The paper puts it this way: balancing the load across nearly 10³ experts is already beyond the regime in which existing auxiliary-loss-free bias updates still work well.
The paper gives two reasons why imbalance is harmful (in 2.3.3): one, it slows down expert-parallel training — the experts are spread over different GPUs, the time for one step is set by the busiest card, and the other cards can only wait; two, it can leave some experts undertrained — an expert that goes a long time without being given tokens gets few gradients, learns nothing, and ends up a pile of dead parameters taking up memory for nothing.
Read this carefully
“896 experts is beyond the regime in which existing methods work well” is likewise an assertion. The body of the paper gives no curve and no number for “how the old fixed-step-size method breaks at 896 experts”. All you can do is accept this as a judgement the authors drew from their own training experience.
| Failure mode | Countermeasure | Which section |
|---|---|---|
| Activations blow up in the routed branch | Insert an RMSNorm before the up-projection W↑ | 2.3.1 → 5.4 of this chapter |
| Swap the activation function for SiTU-GLU | 2.3.2 → 5.5 of this chapter | |
| Load balancing across nearly 10³ experts | Quantile Balancing (QB) | 2.3.3 → 5.6 of this chapter |
Suppose a team reproduces K3 but implements only Quantile Balancing, with no RMSNorm and no switch to SiTU-GLU. (a) Which kind of failure are they most likely to hit first? (b) The other way round: if they do only RMSNorm and SiTU-GLU and skip QB, what do they hit? (c) What is fundamentally different about the nature of these two sets of failures? — one is “a rerun fixes it”, the other is “a rerun does not fix it”; which is which, and why?
(a) QB only: the load is level, but the activation blow-up in the routed branch is untreated — the chain is still that four-multiplication chain and the scale is still 2.8T. You will see spikes in the training loss, possibly divergence, and overflow in low-precision arithmetic. QB is no help here at all, because it only changes “who gets selected” and does not change one thing about “how large the numbers computed after selection are”.
(b) RMSNorm + SiTU-GLU only: the numerics hold, training will not blow up, but it will be slow — every step is stuck behind the busiest expert; and a batch of experts will go a long time without tokens, be undertrained, so that in the end perhaps only a smallish share of the 896 experts really does anything, which means the parameters were spent for nothing.
(c) The fundamental difference is recoverability. A numerical blow-up is overt and goes off on the spot: the loss goes NaN, you know at once that something broke, and you roll back to the previous checkpoint, change the configuration and start again. Load imbalance is covert and cumulative: the run finishes “normally” from beginning to end, the loss curve may even look quite good, but the starved experts have permanently learned nothing — that loss is only discovered when training ends, and cannot be repaired by rolling back (repairing it means retraining). That is why the paper spends a whole subsection plus two appendices on load balancing, while the two numerical countermeasures get only a short paragraph each.
Variant: do these three countermeasures help each other anywhere? Concretely: RMSNorm normalises the scale of the routed branch, and SiTU-GLU puts a hard upper bound on the activations — is that duplicated effort, and would dropping one be fine? (Think about how RMSNorm acts on the aggregated vector after all the experts have finished, while SiTU-GLU acts inside each expert; one polices the exit, the other polices the process.)
5.4 Eq. 11 and Normalized LatentMoE: the art of where to put one RMSNorm
Now write down the full forward pass of one Stable LatentMoE layer. Eq. 11 of the paper comes in two lines: the first computes the routed branch's aggregate, the second joins the two branches together.
y = ∑j=1Ns Esharedj(x) + W↑ RMSNorm(u)
| Symbol | What it is | Intuition |
|---|---|---|
| x | The input to this layer, a d=7168-dimensional vector standing for one token | “what this word looks like right now” |
| 𝒯k(x) | The set of indices of the k=16 experts the router selected for x | the consultation list the triage desk writes out |
| pi | The routing weight of the i-th selected expert, defined by Eq. 13 in §5.6, normalised within the list to sum to 1 | “what share this expert's opinion gets” |
| W↓ | The down-projection matrix, shape ℓ×d = 3584×7168 | the entrance that squeezes a token into the latent space |
| Eroutedi | The i-th routed expert, a ℝℓ → ℝℓ feed-forward network (internal hidden dimension 3072) | a specialist doctor inside the latent space |
| u | The aggregated routed representation, ℓ=3584 dimensions | the weighted synthesis of 16 doctors' opinions |
| Ns | The number of shared experts, fixed at 2 per layer in K3 | the two generalists you see whatever the illness |
| Esharedj | The j-th shared expert, ℝd → ℝd, full width | the general-purpose transformation every token must pass through |
| RMSNorm | Root-mean-square normalisation: divide the vector by the square root of the mean of the squares of its components, standardising its “length” | however long or short the vector coming in, what goes out has a standard length |
| W↑ | The up-projection matrix, shape d×ℓ = 7168×3584 | translates the latent space's conclusion back to full width |
| y | The output of this MoE layer, d=7168 dimensions | the general opinion + the specialist opinion |
First read the equation until it runs smoothly: line one says “squeeze x into the latent space, hand it to the 16 selected experts, and once each has finished, sum their outputs weighted by pi to get u”; line two says “the shared experts eat the full-width x directly, their outputs are added together, and then you add the result of normalising u and sending it back to full width”.
The whole of 2.3.1 is about that one RMSNorm on the second line of Eq. 11. The paper says: the original LatentMoE applied W↑ to u directly, and the scale of u varies with “which experts got selected” and “how the routing weights are distributed”. K3 inserts an RMSNorm between expert aggregation and up-projection, lowering the routed branch's sensitivity to changes of scale, and only then adds it to the full-width shared branch.
Why the position matters so much
Placing the RMSNorm “after aggregation, before up-projection” lands it exactly between the third and the fourth of the four chained multiplications — that is, at the last gate after the values have been amplified all the way along and before they are sent back into the full-width path. Put there, any scale drift accumulated over the first three steps is wiped out in one go.
Put it after W↑ instead (i.e. normalise W↑u) and that 7168×3584 large matrix multiplication of W↑ still has to run on un-normalised input, so the overflow risk is still there. Put it before the experts (normalising W↓x) and it cannot reach the drift produced inside the experts and during the weighted sum.
A common misreading
Many people assume “pi is already normalised to sum to 1, so u is a convex combination and its scale is naturally stable”. That is wrong. Weights summing to 1 only guarantee that u is a weighted average of the 16 experts' outputs, but the vectors being averaged can be long or short and can cancel each other out. Different tokens select different sets of 16 experts, and the typical output magnitudes of those 16 experts can differ by many times over. The question below has you build such an example with your own hands.
Someone argues: since Eq. 13 guarantees ∑i pi = 1, the length of u is pinned between the smallest and the largest expert output length, cannot wander off, and RMSNorm is redundant. Construct two concrete counterexamples to knock that down: (a) one showing that with the same expert output lengths, ‖u‖ can go from near 0 to 1; (b) one showing that ‖u‖ can differ by an order of magnitude between different tokens.
(a) Let ‖v1‖ = ‖v2‖ = 1 and p = (0.5, 0.5). When they point the same way u = v1 and ‖u‖ = 1; when they point opposite ways u = 0 and ‖u‖ = 0. Same weights, same expert output lengths, and ‖u‖ takes any value on [0, 1]. The convex-combination bound holds for each coordinate, not for the norm of the vector.
(b) Say expert A has a typical output norm of 0.5 and expert B has 5 (the scales different experts' weight matrices train to are different in the first place). Token one selects two A-type experts → ‖u‖ ≈ 0.5; token two selects two B-type experts → ‖u‖ ≈ 5. A factor of 10 apart. And which experts get selected changes dynamically from token to token, so that factor-of-10 jitter happens token by token; it is not a constant offset that the next layer's fixed weights could absorb.
One more cut: the concentration of p also varies. With p=(1,0,…,0), u is just a single expert's output; with p uniform, u is the average of 16 vectors, and if they do not point the same way the average shortens markedly (roughly on the order of 1/√16). The difference in scale between these two situations is likewise token by token.
This is what RMSNorm is there to deal with: to wipe out in one go, before anything enters W↑, this per-token scale jitter caused by “who got selected” and “how the weights were split”.
Variant: RMSNorm wipes out the length of u — so is the message “these 16 experts agree that a very large correction is called for” not simply lost? Does that count as a cost of RMSNorm? (Hint: think about how RMSNorm usually carries a learnable gain parameter, and what that can and cannot restore. The paper does not make clear whether the RMSNorm here has a learnable gain.)
Read this carefully
The paper says this extra RMSNorm “beyond stabilising training, consistently improves validation loss and downstream benchmarks”. That is the paper's own account, and the body gives no ablation numbers at all — how much it improved, on which benchmarks, compared with what: none of it. The paper also does not say whether this RMSNorm carries a learnable gain, or whether the shared branch gets similar treatment.
5.5 SiTU-GLU: a soft limiter fitted to each of the two multiplicative factors
The second numerical fix treats the inside of an expert. To understand it you first have to see clearly where the problem lies in the activation function everyone currently uses.
Gated Linear Unit: put the input through two separate matrices to get two vectors, the “gate branch” and the “up branch”, then multiply them element by element (written ⊙). In the original GLU the gate branch is Sigmoid(Wgx) and the up branch is Wux. What the “gate” does is this: the output of sigmoid lies between 0 and 1, which amounts to fitting every channel of the up branch with a 0~1 switch that decides how much of that channel's information gets through.
SwiGLU: swap GLU's sigmoid gate for Swish, that is, Swish(x) = x·Sigmoid(x). The paper says it does well inside Transformers and was later adopted widely by large models — but it also states outright that “a complete explanation of why it works remains an open question”. SwiGLU is what K2 uses.
| Gate branch | Up branch | |
|---|---|---|
| GLU | σ(x) | x |
| SwiGLU | x·σ(x) | x |
| SiTU-GLU | β1 tanh(x/β1) · σ(x) | β2 tanh(x/β2) |
The trouble with SwiGLU under extreme sparsity
The paper's diagnosis is precise: both of SwiGLU's multiplicative factors are unbounded. The gate branch xσ(x) is roughly equal to x when x is large, with no ceiling; the up branch is x itself, also with no ceiling. So when the same coordinate happens to be large in both branches, the product produces activation outliers(activation outliers) — an isolated large number far bigger than the values around it. In low-precision arithmetic this directly raises the risk of overflow.
So can you just fall back to the original GLU? The paper says: GLU's sigmoid gate does avoid unbounded growth in the gate (sigmoid tops out at 1), but it does not preserve the roughly linear response Swish has on the positive half-axis — the sigmoid gate saturates to 1 when x is large, its gradient goes to 0, and Swish's property that “the larger the input, the wider the gate opens” is thrown away.
So what has to be found is this: something that holds down the growth of large values and still keeps SwiGLU's local, positive-half-axis-leaning response.
K3's answer is a smooth cap(smooth cap) function:
| Symbol | What it is | Intuition |
|---|---|---|
| x | the number being capped (in practice every coordinate of the vector goes through it separately) | the raw value |
| β | the soft ceiling, a positive number you set by hand | “this big is the most it may get” |
| x/β | first shrink x by β, then feed it to tanh | moves the place where tanh bends from 1 to β |
| tanh | hyperbolic tangent, its output always lands in (−1, 1) | the limiter itself |
| β·tanh(·) | multiply by β again, so the output lands in (−β, β) | restores the scale that was shrunk away |
Apply it to the linear factor of the Swish gate (that is, inside xσ(x), that one x — note that the sigmoid half is left alone) and to the up branch at the same time, and you get Sigmoid Tanh Unit GLU(SiTU-GLU):
| Symbol | What it is | Intuition |
|---|---|---|
| x | the input vector of the expert feed-forward network (in K3's routed experts it is ℓ=3584 dimensions) | the raw material to be worked on |
| Wg | the projection matrix of the gate branch | works out “how wide each channel should open” |
| Wu | the projection matrix of the up branch | works out “the content to let through” |
| tanh | hyperbolic tangent, squashes a real number into (−1, 1); near the origin it is approximately the input itself, and it saturates at both ends | the soft limiter |
| β1 | the soft ceiling of the gate branch, K3 sets it to 4 | the gate half contributes at most 4 |
| β2 | the soft ceiling of the up branch, K3 sets it to 25 | the content half contributes at most 25 |
| ⊙ | element-wise multiplication (the Hadamard product): two vectors of the same shape multiplied position by position | “switch” times “content”, each channel minding its own |
| Sigmoid | the S-shaped function that squashes a real number into (0, 1) | the 0~1 switch itself |
Why the sigmoid half needs no cap
Swish written out as x·σ(x) is “the linear factor x” times “the sigmoid factor”. Sigmoid already lives in (0,1); it is bounded by nature and needs no further cap. The only thing that can run away is that bare x. So SiTU-GLU replaces just that with β1tanh(x/β1) and keeps the sigmoid as it was — this way the gate behaves almost exactly like Swish when x is small, and is stopped by β₁ when x is large.
Derive it yourself: the two limits of softcap, and why the output bound is exactly β₁β₂
You know that tanh(z) ≈ z (when z is small). So when x is far smaller than β, what is β tanh(x/β) roughly equal to?
Think it through, then look
Let z = x/β. x being far smaller than β means z is small, so tanh(z) ≈ z = x/β, and multiplying both sides by β gives β tanh(x/β) ≈ β · (x/β) = x. Conclusion: near the origin, softcap is the identity map, it does nothing at all.
Why tanh was the thing to reach for in the first place: because what is needed is exactly this kind of function — “leave the small ones alone, deal with the large ones”. tanh is the most common smooth function with slope 1 at the origin that saturates at both ends. Dividing by β and then multiplying by β again does one thing: it moves tanh's “bend” from 1 to β. Whatever size of value you want it to start blocking at, set β to that.And when x is far larger than β? What does β tanh(x/β) tend to?
Think it through, then look
z = x/β becomes large, tanh(z) → 1, so β tanh(x/β) → β. In the negative direction it tends to −β the same way. So for any x at all, |β tanh(x/β)| < β — this is a strict bound, not a “usually will not go over”.
Now look at the two square brackets in Eq. 12. What is the most the absolute value of the first bracket (the gate branch) can be? And the second (the up branch)?
Think it through, then look
Gate branch = β1tanh(·/β1) · σ(·). The absolute value of the first factor is < β₁ (the conclusion of the previous step), and the second factor σ takes values in (0,1), so the absolute value of the whole gate branch is < β₁ · 1 = β₁ = 4.
Up branch = β2tanh(·/β2), absolute value < β₂ = 25.
Note that sigmoid is doing double duty here: it carries the meaning of the “gate”, and along the way it guarantees that the bound on the gate branch does not go past β₁.The two brackets are multiplied element by element. What is the most the absolute value of each coordinate of the output can be? And why is it a product rather than a sum?
Think it through, then look
Element-wise multiplication means coordinate c of the output = coordinate c of the gate branch × coordinate c of the up branch. Multiply two numbers and the absolute value is the product of the absolute values: |f(x)| < β1 · β2 = 4 × 25 = 100. This is exactly the |f(x)| ≤ β1β2 = 100 marked in the paper's Figure 4.
It is a product and not a sum because the GLU structure itself is “gate ⊙ content”. And that is precisely where the problem comes from: when two factors that are each unbounded get multiplied, the errors do not add, they multiply — if both sides run large at once, the product runs large at a squared rate. In SwiGLU both of these factors are unbounded, which is why the paper says “large coordinates that occur at the same time produce activation outliers”. SiTU-GLU caps the two factors separately, and the bound on the product is then automatically the product of the two bounds.Last step: why are β₁ and β₂ set to different values (4 and 25) rather than both to 10 (the product is 100 either way)?
Think it through, then look
Because the two branches play different roles. The gate branch means “how wide to open”, and it was never supposed to be a large number — in practice the Swish gate works mainly in the small-value range, β₁ = 4 already covers its normal working range, and a gate above 4 is almost certainly an outlier and ought to be stopped. The up branch carries the “content”, whose magnitude is naturally larger; cap it too hard and you shave off normal signal along with the rest, so it gets 25.
But this has to be said honestly: the above is a reasonable explanation of the paper's values, not the reason the paper gives. The paper only writes “we set β₁ = 4 for the gate branch and β₂ = 25 for the up branch”, with no ablation, and no explanation of how the two numbers were chosen or why they differ by more than a factor of six.
The paper's summary of SiTU-GLU is: the scaled tanh is approximately linear near the origin and bounded at large magnitudes, so SiTU-GLU keeps SwiGLU's local response while holding both factors in the product under control. What Figure 4 draws is exactly the comparison of these three curves over one and the same interval, x ∈ [−10, 100]: SiTU-GLU (the red line) hugs SwiGLU near the origin and approaches the bound of 100 at large positive inputs, while SwiGLU charges upward the whole way with no ceiling.
K3 sets β₁ = 4, β₂ = 25. (a) What is the upper bound on the absolute value of SiTU-GLU's output? (b) If someone changes β₂ from 25 to 100, what does the bound become? (c) With β₂ = 100, what is the output of the up branch at input x = 3, roughly?
Variant: if β₁ and β₂ are both set to 10⁶ (near enough infinite), what does SiTU-GLU degenerate into? (Check one branch at a time: gate branch β1tanh(x/β1)·σ(x) → ? up branch β2tanh(x/β2) → ?)
The paper says SiTU-GLU “hugs SwiGLU near the origin”. Prove it: write out the scalar expression for SiTU-GLU when x is small, and explain why it equals the scalar expression for SwiGLU. Then answer this: why does that property matter so much for the business of “replacing an activation function that has already been validated widely”?
SiTU-GLU(x) ≈ [x·σ(x)] · [x] = SwiGLU(x). Term for term the same.
Why it matters: SwiGLU is a design validated by countless models, and its benefits (the paper itself says “a complete explanation remains open”) show up mostly in its behaviour inside the normal numerical range. SiTU-GLU's design goal is to change the abnormal region only and leave the normal region alone — the normal region is identical, so all of SwiGLU's hard-to-pin-down but genuinely effective benefits are inherited whole; the abnormal region is stopped by the limiter, so the overflow risk is removed. This is a “non-invasive patch” way of thinking: over the range where the old method works, the new method is a first-order approximation of the old one, and it differs only where the old one breaks. Swap in an activation function that already differs a lot from SwiGLU near the origin and every hyperparameter (learning rate, initialisation scale and so on) has to be retuned, which is far riskier.
Variant: β₁ = 4 means only “far smaller than 4” counts as near the origin. But sigmoid at x = 4 is already 0.982, so the gate is essentially all the way open. At x = 4, then, neither small nor large, how far apart are SiTU-GLU's gate branch and SwiGLU's gate branch? (Work out 4·tanh(1)·σ(4) and 4·σ(4), with tanh(1) ≈ 0.762.) Is that deviation big enough to matter?
Someone objects: “If the whole point is just to keep the value from going past β, why not use hard clipping, clip(x, −β, β), and be done with it? tanh is slow and roundabout.” Construct a concrete situation showing that hard clipping brings a problem the soft cap does not. (Hint: think about how training actually proceeds.)
With hard clipping: output = 25 (flattened off). During backpropagation the gradient along this path is multiplied by clip's derivative, 0, and comes out 0. So the parameters that produced that 30 receive no gradient signal at all — there is no force pulling x back. If nothing else at that point (weight decay, say, or other samples) happens to pull it back, the channel is stuck in the saturated region for good, which is to say it is scrap. And it is silent: the forward output looks perfectly normal (it is 25), and you will see nothing unusual in the loss.
With softcap: output = 25·tanh(30/25) = 25·tanh(1.2) ≈ 25 × 0.834 ≈ 20.9, derivative = 1 − tanh²(1.2) ≈ 1 − 0.696 = 0.304, still a sizeable gradient. Even at x = 100 (x/β = 4), the derivative = 1 − tanh²(4) ≈ 1 − 0.99933 ≈ 6.7×10−4 — small, but not 0, and there is still a path pulling it back.
A second counterexample: discontinuous second-order behaviour. At x = β clip's derivative jumps abruptly from 1 to 0, and that kink makes the optimisation jitter near the boundary; tanh is smooth everywhere and has no such problem.
The paper puts “the comparison with hard clipping” in Appendix B, and the main text mentions it in one line only, “§B gives ... comparison with hard clamping”; for the full content see Chapter 14.
Variant: construct the opposite angle, one on which “hard clipping is actually better” — starting, say, from computation speed, or from “the bound is a strict β rather than an asymptotic β”. (The soft cap's output never reaches β, it only approaches it without limit; hard clipping can hit β exactly. In what setting does that difference matter?)
5.6 Quantile Balancing: set the bias straight to “the quantile that matches the target load”
This is the hardest section in the chapter, and the most interesting idea in this part of K3. Take it slowly.
The problem: overheated experts and starved experts
The router is learned, and nothing about it guarantees that it spreads tokens evenly over the 896 experts. What actually happens: a few experts become “popular” and a whole crowd of tokens piles into them; another set of experts gets almost no visitors. In the paper's Figure 5(a) a darker circle marks an overheated expert (overheated), a faded circle marks an under-used expert, and a dashed circle marks a dying expert (dying experts, not a single token assigned).
The two harms the paper gives came up earlier; here they get made concrete. It slows down expert-parallel training — the experts sit on different GPUs, and the wall-clock time of one step is set by the most heavily loaded card. It can leave some experts under-trained — a starved expert receives almost no gradient, and when training ends it still looks the way it did at initialisation.
K3's routing framework: an auxiliary-loss-free bias
How do you make routing even? One old method is to add an auxiliary loss: put an extra “penalise imbalance” term into the training objective. But that pollutes the main objective — the model gives up language modelling quality in exchange for balance. K3 says explicitly that it uses auxiliary-loss-free routing(auxiliary-loss-free routing): no loss term; instead each expert gets a bias bj, and it is added only to the score used for the Top-k selection.
| Symbol | What it is | Intuition |
|---|---|---|
| xi | the representation of the i-th token in this batch | the i-th “patient” |
| Wr | the router's weight matrix (si = Sigmoid(Wrxi)) | the triage desk's scoring rule, learned along with everything else |
| si | the routing scores of token i over all n experts, every component in (0,1) | “how well this patient matches each doctor” |
| si,j | si's j-th component, i.e. token i's score for expert j | one particular match score |
| b | a bias vector of length n, one per expert, independent of the token | a “bonus” for the unpopular experts, a “penalty” for the popular ones |
| argtopk | of the largest k, take the indices (not the values) | writing out the consultation list |
| 𝒯i | the set of indices of the k experts token i selected | the list itself |
| pi,j | the mixture weight: uses the raw score s, without b, normalised across the k selected | how much say each expert on the list gets |
The key to the whole design: b appears in the first equation and not in the second
Read that sentence three times. b takes part in “who gets picked”, and takes no part at all in “once picked, who gets what share”. The paper's own words: because b is omitted from pi,j, it regulates dispatch without altering the mixture weights or the gradient-based optimisation of the router.
That has three consequences:
(1) The model's output is not polluted by load balancing. If b entered p, then “the bonus handed to an unpopular expert for the sake of balance” would turn straight into “this unpopular expert gets more say in the final answer” — to make the machine run evenly you would have tampered with the model's answer. As it stands, you have not.
(2) The router's gradient is clean. b is not in p, so the gradient that flows back to Wr carries no contribution from b. The router still learns whatever preferences it thinks right from the language modelling loss, and the balancing mechanism runs on a separate track.
(3) The price: b has no gradient available, so it can only be updated by a separate rule. That rule is the entire content of §2.3.3.
The trouble with the old method: a step size you cannot get right
The original auxiliary-loss-free method used a fixed-step sign update rule:
| Symbol | What it is | Intuition |
|---|---|---|
| ℓj(t) | the number of tokens expert j actually received at step t (its load) | how many patients this doctor saw today |
| ℓ̄ | the average load | “how many they were supposed to see” |
| sign(·) | the sign function: +1 for a positive number, −1 for a negative one | direction only, never by how much |
| γ | a fixed step size (a hyperparameter) | how big a step to take each time |
How to read it: if expert j's load is below average, sign is +1, so γ is added to its bias and it is easier to pick at the next step; otherwise γ is subtracted. Notice that sign throws away every bit of the information about “by how much” — being 1 token short and being 1000 tokens short produce exactly the same action, a move of γ.
The paper points out the bind here: γ trades off “adapts too slowly” against “oscillates”. Make γ small and it takes many steps to pull the load back once it drifts off target; make γ large and it overshoots, then overshoots back, oscillating. And once LatentMoE widened the routed expert pool to 896 per layer, the trade-off got harder — the more experts there are, the noisier each expert's load statistic is (on average only mk/896 tokens), and the easier it is to mistake noise for signal and move for nothing.
An analogy
The old method is like an air conditioner with only two buttons, “warmer” and “cooler”, each press changing the setting by a fixed 0.5 degrees. To get from 30 degrees to 22 you have to press 16 times; press too fast and you sail all the way down to 18 and have to come back. QB types in “22 degrees” directly — it does not step towards the target, it solves for the setting that reaches it.
Where the analogy breaks down: the air conditioner's “22 degrees” is a physical quantity you can set directly, whereas an expert's load cannot be set directly — all you can set is bj, and the load, after bj is added to the routing scores, is the outcome of the Top-k competition. What is clever about QB is that it found a way to invert “the load I want” into “the bj I should set”. And that inversion is exact only under the assumption that everything else is held fixed — at the end of 5.6 you will see that the assumption does not always hold.
The heart of QB: set the bias to a quantile
Quantile: sort a pile of numbers from small to large; the “q quantile” is the number sitting at the fraction q along that order. For example, the 0.9 quantile of 100 numbers is the 90th number after sorting — 90% of the numbers do not exceed it and 10% do. The median is the 0.5 quantile.
Quantile Balancing: the paper defines it as “setting each expert's bias to the routing-score quantile that matches its target load”. Note that there is no step size here and no learning rate — it is a direct assignment, not a move.
First get the symbols straight. Consider one training batch: m tokens, n experts, Top-k selection. In total mk “token→expert” assignments have to go out. Split evenly over the n experts, each expert's target load is
| Symbol | What it is | Intuition |
|---|---|---|
| m | the total number of tokens in this batch (in real training this is the global batch, on the order of a million) | how many patients came in today |
| n | the total number of routed experts, 896 per layer in K3 | how many doctors are on duty |
| k | the number of experts each token activates, 16 in K3 | how many doctors each patient sees |
| mk | the total number of “token→expert” assignments this batch has to hand out | how many consultations there are to hold today |
| q | the target load: how many tokens each expert should receive under perfect balance | “how many patients each doctor is supposed to see” |
| := | “is defined as”, not an equation waiting to be solved | this is a target we set ourselves |
(a) The example in the paper's Figure 5 has m=8 tokens, n=4 experts, k=1. Work out the target load q. (b) Under K3's real settings (n=896, k=16), if a global batch holds 1 million tokens, what is each expert's target load? (c) Check it: what does q/m equal?
Variant: if m=10, n=4, k=1, then q = 2.5 — an expert cannot receive half a token. Does the sentence “make the count exactly equal q” still hold here? What did the paper's derivation assume? (Hint: the paper says “Assuming no ties”, but never discusses the case where q is not an integer. At real scale m is in the millions and the problem is diluted, but there is a gap in the rigour of the derivation.)
The clever first move: getting a cutoff for free out of Top-(k+1)
QB derives the next step's bias from a single forward pass. The first move: the Top-k done at routing time becomes, on the biased scores si + b(t), a Top-(k+1).
Of the k+1 numbers you pull out: the first k are the routing that actually happens (identical to before), and the (k+1)-th is a cutoff αi(t) — meaning: for an expert to squeeze into token i's Top-k, its biased score has to exceed this value.
Derive it yourself: why this cutoff comes so cheap
Sort token i's biased scores over the n experts from large to small, writing them v(1) ≥ v(2) ≥ … ≥ v(n). Which experts made the Top-k? For a “newcomer” expert to squeeze in, which number does its score have to exceed?
Think it through first, then look
The ones in the Top-k are v(1)…v(k). For a newcomer to squeeze in it has to push out the current k-th, so it has to exceed the current (k+1)-th v(k+1) — beat the (k+1)-th and it lands at position k or better. So the cutoff is αi = v(k+1).
Why anyone would think of this step in the first place: because the question we want to ask is “if expert j's bias were adjusted a little, could it get onto token i's list”. The answer to that depends on exactly one number — the admission line for the list. And the admission line is the score of the (k+1)-th.Now the key question: does changing Top-k into Top-(k+1) change the actual routing result?
Think it through first, then look
No. Top-(k+1) returns the first k+1 after sorting, and the first k of those are the Top-k result, not one more and not one fewer. The (k+1)-th is only read off in passing to serve as a cutoff; it takes no part in routing. So the only change QB makes to the forward computation is “take one more number when you sort” — and the cost of one more number, next to the feed-forward computation of 16 experts, is negligible.
The paper's own words: “Taking the cutoff from Top-(k+1) routing avoids a separate token-side quantile.” If you did not do it this way, you would have to compute “what is its k+1-th highest score” separately for every token, which is another pass of statistics over 896 scores — as it is, that number is a by-product of the Top-k sort, picked up for free.The cutoff αi(t) is computed from the biased scores si+b(t), which means the old bias is hidden inside the cutoff. Does that turn the update into a tangle?
Think it through first, then look
The paper makes a point of this: “the old bias enters the update only through the cutoffs” — the old bias enters the update only through the cutoffs, and appears nowhere else. The margin in Eq. 14 is the raw score si,j minus the biased cutoff αi(t), not the biased score minus the biased cutoff. The b̂j(t+1) that comes out is a new absolute value, not “the old value plus an increment”. That is exactly how “assign directly instead of stepping” gets implemented.
Step two: hold the cutoffs fixed, invert for the bias
Now take every token's cutoff αi(t) and pin it in place, then ask exactly one thing: if expert j is given a candidate bias b̂j, how many tokens does it receive? The answer is to count how many tokens' scores clear the cutoff:
| Symbol | What it is | Intuition |
|---|---|---|
| 1[·] | the indicator function: 1 when the condition in the brackets holds, 0 when it does not | the switch for “does this one count” |
| b̂j(t+1) | expert j's candidate new bias (the hat means the final mean subtraction has not been done yet) | how many points you are trying out on this doctor |
| αi(t) | token i's admission cutoff, taken from the k+1-th biased score of the Top-(k+1) | the admission line for patient number i's consultation |
| si,j − αi(t) | what the paper calls the margin (margin): how far expert j is from qualifying for token i | positive = qualifies with room to spare, negative = short by this much |
Move the condition around: si,j + b̂j > αi is equivalent to si,j − αi > −b̂j. The left-hand side is the margin, the right-hand side is a threshold. So the count turns into “how many margins exceed the threshold −b̂j”.
In the threshold −b̂j, this count is monotonically decreasing: the higher the threshold, the fewer margins can clear it (set the threshold at +∞ and none get through; set it at −∞ and all of them do). Monotone ⇒ for any target value you want the count to equal, there is a unique threshold that does it (under the assumption of no ties). This is the mathematical basis for “solving an equation” rather than “taking a step”.
Set the count = q; what is the solution? Sort expert j's column of m margins from large to small. For exactly q of them to strictly exceed the threshold, the threshold has to land on the (q+1)-th largest margin — then the first q strictly exceed it, the (q+1)-th is exactly equal to it (which does not count as exceeding), and everything after is smaller. So
In other words: the threshold −b̂j(t+1) equals “the (q+1)-th largest of the m margins in expert j's column”.
One last change of wording. How do you say “the (q+1)-th largest of m numbers” as a quantile? There are q numbers above it, a fraction q/m; and we checked earlier that q/m = k/n. So the fraction below it is 1 − k/n, which makes it the (1−k/n) quantile of this batch of margins. Written as the paper's Eq. 14:
b(t+1) ← b̂(t+1) − mean( b̂(t+1) ) 1
| Symbol | What it is | Intuition |
|---|---|---|
| s:,j | column j of the score matrix: the scores of all m tokens for expert j | “how well this doctor matches every patient” |
| α(t) | a cutoff vector of length m, one per token | each patient's own admission line |
| s:,j − α(t) | element-wise subtraction, giving m margins | how far this doctor falls short for each patient |
| quantile1−k/n | of these m margins, take the (1−k/n) quantile (for K3 that is the 1−1/56 ≈ 0.982 quantile) | “the line that marks off the top 1.79%” |
| the minus sign in front | because the threshold is −b̂j, you negate the solved threshold to get the bias | the higher an expert's margins (meaning it is too much in demand), the more negative the bias it gets |
| mean(b̂) 1 | the mean of every expert's candidate bias, times the all-ones vector (i.e. every component has that mean subtracted) | wipe out the offset they all share |
Why the second line may subtract any constant it likes
Top-k compares, within one single token, the scores of n experts. If every expert's bias has the same number c subtracted from it, then every expert's score drops by c, the relative sizes are untouched, the ordering is untouched, and the Top-k result does not change by one character. So subtracting the mean is “free” — it changes no routing behaviour at all, it merely pulls the overall level of the vector b back to around 0 and stops it drifting without bound over a long training run.
Causality: why the update has to wait for the next step
The paper says: “For causality, the update takes effect only in the next step, i.e., a batch is never routed with a bias derived from itself.” — no batch is ever routed with a bias worked out from that same batch.
Why does it have to be this way? Because QB's bias is inverted out of this batch's scores. Using it straight back on this batch amounts to “seeing the answers before sitting the exam”: the model's routing decisions would depend on the statistics of its own batch, and training and inference behaviour would stop lining up (at inference you face a single request, and there is no such thing as “this batch's quantile”). The paper also states it: at inference the bias is frozen — the b you have at the end of training is fixed and never updated again.
Walking the paper's Figure 5 example all the way through
The paper's Figure 5 runs QB once with m=8 tokens, n=4 experts, k=1: the initial load is a badly skewed (4, 3, 1, 0) — E₁ is overheated and E₄ is a dying expert with not one token; the target load is q = 8×1/4 = 2; one step of QB turns it into (2, 2, 2, 2).
The paper gives no concrete score values. The numbers below are a worked example this site constructed that satisfies every condition the paper states (m, n, k, the initial load, the target load and the final load all match the paper). Get a pen and paper and work through it — every concept in this section comes down to earth in this table. Take the current bias to be b(t) = 0 (the initial state).
| token | E₁ | E₂ | E₃ | E₄ | cutoff αi | where it went |
|---|---|---|---|---|---|---|
| t₁ | 0.92 | 0.60† | 0.30 | 0.20 | 0.60 | E₁ |
| t₂ | 0.88 | 0.52† | 0.26 | 0.18 | 0.52 | E₁ |
| t₃ | 0.48† | 0.84 | 0.38 | 0.14 | 0.48 | E₂ |
| t₄ | 0.74 | 0.40 | 0.34 | 0.68† | 0.68 | E₁ |
| t₅ | 0.42† | 0.76 | 0.22 | 0.28 | 0.42 | E₂ |
| t₆ | 0.32 | 0.64 | 0.61† | 0.29 | 0.61 | E₂ |
| t₇ | 0.70 | 0.25 | 0.44 | 0.65† | 0.65 | E₁ |
| t₈ | 0.52† | 0.16 | 0.62 | 0.42 | 0.52 | E₃ |
| initial load | 4 | 3 | 1 | 0 | — | badly skewed |
Note that when k=1, Top-(k+1) is just Top-2, so the cutoff αi is simply the second-highest score in each row. Step two: subtract from every score the cutoff of its own row, giving the margins:
| token | E₁ | E₂ | E₃ | E₄ |
|---|---|---|---|---|
| t₁ | +0.32 | 0.00 | −0.30 | −0.40 |
| t₂ | +0.36 | 0.00 | −0.26 | −0.34 |
| t₃ | 0.00 | +0.36 | −0.10 | −0.34 |
| t₄ | +0.06 | −0.28 | −0.34 | 0.00 |
| t₅ | 0.00 | +0.34 | −0.20 | −0.14 |
| t₆ | −0.29 | +0.03 | 0.00 | −0.32 |
| t₇ | +0.05 | −0.40 | −0.21 | 0.00 |
| t₈ | 0.00 | −0.36 | +0.10 | −0.10 |
| (q+1)=3rd largest | +0.06 | +0.03 | −0.10 | −0.10 |
| new bias b̂j | −0.06 | −0.03 | +0.10 | +0.10 |
How to read the last two rows: take the E₁ column. Sorted, its 8 margins are 0.36, 0.32, 0.06, 0.05, 0.00, 0.00, 0.00, −0.29. q=2, so you take the (q+1) = 3rd largest, which is 0.06. Negate it to get b̂1 = −0.06. Check: there are exactly 2 margins strictly greater than 0.06 (0.36 and 0.32) — precisely the target load. You can check all four columns this way. E₃ and E₄, the two unpopular experts, get positive biases (+0.10), and E₁ and E₂, the two popular ones, get negative biases. The direction matches intuition exactly.
(The second line of the paper's Eq. 14 also subtracts the mean: mean(b̂) = (−0.06−0.03+0.10+0.10)/4 = 0.0275, and after subtracting you get b = (−0.0875, −0.0575, +0.0725, +0.0725). As said above, that step changes no Top-k result, so what follows checks things directly with b̂.)
Step three: route again with the new biases — add b̂j to each column of Table 5-4 and take the largest in each row. Row t₄, for instance, becomes E₁: 0.74−0.06 = 0.68, E₂: 0.40−0.03 = 0.37, E₃: 0.34+0.10 = 0.44, E₄: 0.68+0.10 = 0.78 — t₄ switches from E₁ to E₄. Working out all eight rows:
The final load is (2, 2, 2, 2). Only three edges moved, all four experts reached the target load, and not one step of this used a learning rate or a step size — every bias was solved straight out of the statistics of a single forward pass.
In Eq. 13 and Eq. 14, b has two “does not affect anything” properties: (a) b does not appear in pi,j; (b) subtracting the same constant from every bj leaves Top-k unchanged. Say what each of these protects. Then answer a sharper question: if (b) says the common offset makes no difference, why does the paper bother to write that step out at all?
(a) What it protects is the model's output and the router's learning from being polluted by the balancing mechanism. The paper's own words: “it regulates dispatch without altering the mixture weights or the gradient-based optimization of the router”. If b entered p, then “+0.1 to an unpopular expert for the sake of balance” would become “this unpopular expert takes 10% more of the say in the answer” — you would be tampering with the model's judgement for the sake of machine efficiency. And b would sit on the backward path, so the preferences the router learns would be distorted by the balancing objective. Once they are separated, the router deals only with “who fits best” and the bias only with “who should be called on”, two tracks that do not interfere.
(b) What it protects is numerical hygiene. The b̂ solved for in the first line of Eq. 14 is an absolute value, and its overall level depends on where this batch's margins sit in absolute terms — it is entirely possible for every step to come out skewed positive or skewed negative as a whole, so that after a few hundred thousand accumulated steps every component of b has drifted somewhere very large. That does not affect routing (guaranteed by (b)), but it does affect numerical precision: subtracting and sorting a pile of large numbers eats significant digits; and seeing b come out as all ±1e5 in the logs makes people think something has gone wrong. Subtracting the mean pins b at mean 0 and keeps every component meaning “a relative offset” — positive = this expert needs a hand up, negative = needs pushing down.
A deeper layer: precisely because the common offset affects nothing, the first line of Eq. 14 is entitled to care only about “relative relationships” — the n values it solves for really have only n−1 degrees of freedom, and the second line just picks a canonical representative.
Variant: if the second line were changed to “subtract b̂'s median” instead of its mean, what would be different? Would the routing result change? (No.) Is there any substantive difference between the two choices? (Hint: think about what a handful of extreme biases does to the mean and to the median. The paper does not discuss this choice.)
QB's derivation guarantees that with the cutoffs α held fixed, exactly q tokens clear expert j's cutoff. But real routing has each token take its own Top-k. Construct a concrete small example (m=4, n=2, k=1 is suggested) showing that after one step of QB the load may not be exactly (q, …, q).
Cutoffs (the second-highest in each row): α = (0.10, 0.20, 0.30, 0.55). Initial load = (4, 0).
Margins (s − α): the E₁ column = (+0.80, +0.60, +0.40, +0.05); the E₂ column = (−0.80, −0.60, −0.40, −0.05).
Take the 3rd largest: the E₁ column sorts to 0.80, 0.60, 0.40, 0.05 → b̂1 = −0.40. The E₂ column sorts to −0.05, −0.40, −0.60, −0.80 → b̂2 = +0.60.
Check the “clears the cutoff” count: E₁ has 2 margins strictly greater than 0.40 (0.80 and 0.60), which matches; E₂ has 2 margins strictly greater than −0.60 (−0.05 and −0.40), which also matches. Both experts “should” get 2 tokens.
But the real routing: the new biased scores are E₁ = (0.50, 0.40, 0.30, 0.20) and E₂ = (0.70, 0.80, 0.90, 1.15). Take the largest in each row → all four tokens go to E₂. The load goes from (4, 0) to (0, 4) — completely flipped, and less balanced than before.
Why this happens: E₁ and E₂ each solved for their bias assuming “the other one's cutoff does not move”, but once both take effect they tread on each other. t₁ and t₂ clear E₁'s cutoff and clear E₂'s as well, yet each of them can go to only one place — both experts thought they had those two votes, and in fact only one of them can have them.
Said honestly: the body of the paper gives only the derivation of the “count equals q” step, and pushes the real “balanced-assignment derivation” off to Appendix C (see Chapter 14); the body never says under what conditions that exchange is exact. The extreme example above was built deliberately (only 2 experts, and highly symmetric scores); at real scale n=896 and m is in the millions, so one expert's change of bias has very little effect on the ordering for any given token, and the practical result is far better. But the claim that “one step of QB balances exactly” cannot be derived from the argument the body of the paper gives.
Variant: change the example above to n=4 experts and m=8 (that is, the successful example in Table 5-4) — why does that one not fall over? Where is the difference? (Hint: count how many experts actually clear each token's cutoff under the new biases. In the successful example every token has exactly 1, no more and no fewer.) And think about this too: since QB's update only takes effect at the next step, and gets recomputed at every step, how serious is “one step is not exact” really?
“Replace Top-k with Top-(k+1)” looks like nothing more than taking one extra number, but it gets three things done at once. Set out all three clearly: (a) why does taking the cutoff this way not change the routing result itself? (b) why does it mean you do not have to compute a separate token-side quantile? (c) why must the cutoff be computed from the biased scores while the margin in Eq. 14 subtracts it from the raw scores? Finally, put it together: if these three things were done separately, what would QB's cost look like?
(a) It does not change the routing: the first k elements Top-(k+1) returns are the Top-k result. The (k+1)-th is read out for statistics only; it takes no part in dispatch and no part in normalising p. So QB is zero-intrusion on the model's forward behaviour — which matters, because it means turning QB on or off changes nothing the model computes at this step; the only difference is the bias at the next step.
(b) It saves the token-side quantile: the paper's own words, “Taking the cutoff from Top-(k+1) routing avoids a separate token-side quantile”. The cutoff QB wants is essentially an “upper-k quantile point” of each token's score distribution. If you did not take it out of the routing, you would have to run another selection over each token's 896 scores — and the Top-k sort is already doing that, it just used to throw the k+1-th away. What QB does is pick back up an intermediate quantity that was being discarded. That is the root reason the whole method is “almost free”.
(c) Biased cutoff + raw score: the cutoff has to be biased, because “can it get onto the list” is compared on the biased scores, so the admission line naturally grows on the biased scale. And because the margin subtracts it from the raw score si,j, the b̂j that comes out is directly “the new bias itself” — it has already absorbed the job of compensating for the old bias. If the margin used the biased scores too, what you solve for is “an adjustment relative to the old value” (the red dashed line in the paper's Figure 5(b) draws exactly bj(t) − b̂j(t+1)), and you would have to do one more addition to get the new bias. Using the raw score is the more direct way to write it, and it is what makes the sentence “the old bias enters the update only through the cutoffs” true — the old bias's influence is confined to one explicit, single channel.
Putting it together: done separately, QB would need (1) the normal Top-k routing, (2) an extra pass selecting the k+1-th largest of each token's 896 scores, and (3) an extra pass peeling the old bias out of the solution. Done together, the three become “take one more number when you sort”. That is why the paper can say “QB derives the next bias from a single forward pass” — a single forward pass, with no extra forward or backward.
Variant: what happens if someone switches to Top-(k+5) and takes the k+5-th score as the cutoff? (Hint: that line is no longer “the admission line for the Top-k”, it is “the line for getting into the Top-(k+4)”, and the bias you invert out of it corresponds to a target that is no longer q. Turn it around: would Top-k itself do — taking the k-th as the cutoff?)
Defend it: if I were a reviewer
You keep stressing that QB “needs no learning rate”, and treat that as its main selling point over the fixed-step method's γ. But your histogram estimate brings in the number of bins B, the range of the bin edges, and the choice of “how often to update the bias”; and the body of the paper does not write a single word about the value of B. Have you not just swapped one hyperparameter for three? Defend it.
A reference defence (put it in your own words first)
The defence has to separate two kinds of “hyperparameter”.
γ is the gain of a control loop: it decides the dynamic behaviour of the system, and getting it wrong fails qualitatively — too small and the load stays off target for a long time, too large and it oscillates or even fails to converge. Its optimal value also moves with the number of experts n, the batch size and the stage of training; the paper wants to replace it precisely because n rising to 896 made that trade-off harder.
The number of bins B is an estimation-accuracy parameter: it decides “how far the quantile I compute is from the true one”, the error is controlled monotonically by the bin width, and the paper says the error bound is given in Appendix D. Turn B down a little and the result is a slightly inaccurate bias and a load slightly off q; you do not get a qualitative failure like “oscillate and diverge”. The key difference: get γ wrong and the method breaks; make B small and the method is only a bit less precise. The paper says a few hundred bins is enough, which shows this accuracy is easy to buy.
A more fundamental layer: QB is solving an equation — the solution of “make the count equal q” does not depend on any step size; the fixed-step method is walking towards the solution, and how fast you walk is necessarily a free parameter. The hyperparameter of the first only affects “how accurately you solve”; the hyperparameter of the second affects “whether you get there at all”.
But two things have to be admitted: (1) the body of the paper genuinely does not give the value of B, how the bin edges are fixed, or the bias update frequency, and gives no ablation on B at all, so this defence rests entirely on the error bound in Appendix D. (2) If the distribution of margins is highly concentrated (all crammed into one bin, say), the bias the bin width introduces is no longer a small quantity — and the paper does not discuss that pathological case. This is an honest open question.
Defend it: if I were a reviewer
QB forces every expert to take exactly q = mk/n tokens. But the data itself is not uniform — if 90% of some batch is code, should the experts that are good at code not be doing more of the work? You are sacrificing expert specialisation for system efficiency. And QB is “harder” than the fixed-step method (each step solves straight to the target instead of nudging a little), which makes the sacrifice more extreme, not less. Defend it.
A reference defence (put it in your own words first)
Three layers of defence.
Layer one: the bias does not enter the mixture weights. This is the most important design choice in Eq. 13. b only changes “who gets called on”, not “what share of the say each one has once on stage”, and it does not enter the router's gradient. So the router still learns “what kind of expert a code token should be matched with” entirely from the language modelling loss — what balancing changes is only the fringe of the list (the experts whose scores sit close to the cutoff), while an expert far out in front on score gets picked either way. The core signal of specialisation has not been touched.
Layer two: the motive for balancing is not aesthetic. Under expert parallelism the time of each step is set by the busiest card, and a 10× imbalance means most cards are idling; an expert that goes without tokens for a long time gets no gradient, and you end up with a pile of randomly initialised dead parameters taking up GPU memory. The paper states both of these outright. Imbalance is not “making the model more specialised”, it is “wasting the money spent on part of the 896 experts”.
Layer three: the target load is computed over the global batch. q is defined over the whole global batch (the paper says the histogram stands for a globally pooled batch, for which “how the tokens are sharded makes no difference”), and the mixed distribution of a million tokens is far more stable than that of a single micro-batch. An extreme case like “90% is code” is diluted at the scale of the global batch.
But it has to be admitted: this really is a genuine trade-off, and the paper gives no experimental evidence at all on whether QB damages expert specialisation — the body gives only the qualitative reasoning on one side (“imbalance slows training and can leave experts under-trained”), and the cost on the other side is never measured. The reviewer's blade has gone into a soft spot of the paper. And the observation that “QB is harder, so the sacrifice is larger” is hard to argue with too: the fixed-step method at least moves only γ within one step, while QB solves straight to the target in one. The paper does not discuss this.
5.7 Histogram estimation: at real scale this quantile simply cannot be gathered
In that 8×4 example in Table 5-4, finding the quantile just means “sort the 8 numbers and take the 3rd largest”. In real training you cannot do that.
The paper states the difficulty plainly: the quantile in Eq. 14 is computed over the entire global batch, the number of these margins runs into the millions, and they are scattered across ranks and across gradient accumulation steps — the margins of one and the same expert sit partly on card 3, partly on card 47, and partly do not exist until the 8th accumulation of this step. To get the quantile exactly, you would have to collect all of them in one place and sort them. The paper's judgement: at training time this is not feasible.
Why “sorting” is so expensive in distributed training
Finding a quantile needs a global sort, and sorting does not merge divide-and-conquer style — you cannot let each card sort its own part and then simply stitch the pieces together; the data really has to be moved into one place. Over a million numbers × 896 experts × every layer: the network traffic alone is enough to drag training to a halt, and all this is just to compute a few biases used for dispatch.
Histogram: cut the range of values into a number of bins (bin), and record only “how many numbers fall into each bin”, keeping nothing about what each number actually is. For example, cut [−1, 1] into 200 bins, each bin 0.01 wide, and 1 million numbers are compressed into 200 counts.
What QB does: keep, for every expert, a histogram of its own margins. Each card counts up the bin counts produced by its own share of the tokens, then a single all-reduce adds the bin counts of all ranks together, and the quantile is recovered from the pooled counts (accumulate downwards from the largest bin until you pass the target count; the position of that bin is the quantile).
Why “additive” is the whole key here
The paper's own words are worth taking apart clause by clause: “Because counts are additive, the histogram represents the pooled global batch regardless of how tokens are sharded” — because counts are additive, what the histogram represents is the pooled global batch, “the same no matter how the tokens are sharded”.
What that means: card 3 says “this bin, [0.05, 0.06), has 120 in it”, card 47 says “it has 85”, add them and you get 205, exactly the same result as counting those tokens on one single card. The way things are sharded, the number of gradient accumulations, the number of cards — none of it affects the final bin counts. So this estimate is not “an approximation of the global batch”; at the level of counts it is the global batch. The paper says what is estimated is the full-batch quantile, accurate to within the bin width (up to the bin width) — the only source of error is that the information “where inside the bin each number sits” has been thrown away, and that error is boxed in strictly by the bin width.
Communication cost: the paper says only a few hundred bins per expert. Compare that with the raw data you would otherwise send — over a million margins per expert. Three or four orders of magnitude of compression.
The paper says so explicitly: this histogram estimator is the method actually used in practice (“This histogram estimator is the method we use in practice”) — the exact quantile in Eq. 14 is an idealised way of writing it. The fuller description and its error bound the paper puts in Appendix D, see Chapter 14.
Someone proposes an easier scheme: “let each card compute a quantile from the margins it happens to hold, then all-reduce and average them to get the global quantile.” That sends less data (1 number per expert instead of a few hundred bin counts). Construct a concrete counterexample showing this scheme is wrong, and point out which mathematical property it gets wrong.
The counterexample: card A = {0, 0, 0}, median 0; card B = {10, 20, 30}, median 20. The average of the two = 10.
The truth: merged, {0, 0, 0, 10, 20, 30}; sort and take the middle (the average of the 3rd and 4th) = (0+10)/2 = 5. 5 ≠ 10, off by a factor of two.
A more extreme counterexample: card A = {0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, median 0; card B = {0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, median 0. The average = 0, and this time it happens to be right. Now swap card B for {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, median 1, average = 0.5; the true median of the 20 merged numbers = 0.5 — right again. But that is only because the two cards hold the same amount of data and the distributions are symmetric; pure coincidence. As soon as the two cards hold different amounts of data (in real training the token count on each rank, and the number of margins a given expert has on each rank, can perfectly well differ), not even a “weighted average” can rescue it.
Which property it gets wrong: a quantile is an order statistic; it is decided by “how many numbers are smaller than a given value”, not by arithmetic on the values. What is additive is the counts, not the quantiles. So the correct way to merge must add the additive quantity (the bin counts) first and then recover the quantile from the merged counts — which is exactly what the paper does. The paper's line “Because counts are additive” is not rhetoric; it points precisely at the one thing that can safely be all-reduced.
By the way: if what the all-reduce sends is “the sum of the raw margins” or “the sum of squares”, that is additive, and it gets you the global mean and variance — but mean and variance cannot be turned back into a quantile unless you assume the distribution has some known shape (Gaussian, say). The virtue of the histogram is exactly that it needs no distributional assumption at all.
Variant: ask it the other way round — if additive counts are this useful, why not just push the number of bins up to 1 million and make the histogram “exact”? (Hint: work out the communication. 896 experts × 1 million bins × every layer × every step — how many numbers is that? Then compare it with the paper's “a few hundred bins”. And think about this too: cut the bins too fine and each bin holds only 0 or 1 numbers, and the histogram degenerates into the raw data itself.)
Read this carefully
On histogram estimation, the main text of the paper does not give: the actual value of the bin count B, how the bin edges are fixed (a fixed range, or adaptive?), how often the bias is updated (every step, or every few steps?), or how large the error from the bin width is in real training. The paper says only “a few hundred bins” and “the error bound is in Appendix D”. Anyone reproducing this part will hit a real, solid blank here.
Chapter summary
This chapter is three faces of one and the same thing: how to make width sparse without letting it blow up.
Making it sparse rests on LatentMoE: it splits “how wide the whole model is” (d = 7168) and “how wide a routed expert is” (ℓ = 3584) into two independent numbers. The shared experts stay on the full-width path doing the general-purpose transform, and the 896 routed experts work in a latent space of half the width. What gets dispatched is the compressed z = W↓x, so both the communication volume and the expert weight traffic are halved, which exactly cancels the doubling brought by “raising the number of activated experts from 8 to 16”. Sparsity 896/16 = 56.
Not blowing up rests on three things, answering the two failure modes the paper names:
- Failure mode one (activation explosion in the routed branch) — the routed path strings W↓, the gated multi-branch expert feed-forward network and W↑ into a chain of nearly four consecutive matrix multiplications, on top of a scale of 2.8 trillion parameters.
Answer 1: Normalized LatentMoE — insert an RMSNorm between the expert aggregation and the up-projection (the second line of Eq. 11), smoothing away the per-token scale jitter caused by “which experts were picked and how the weights were split”. The paper reports of itself that this also keeps improving validation loss and downstream benchmarks (no numbers).
Answer 2: SiTU-GLU (Eq. 12) — both multiplicative factors of SwiGLU are unbounded, and large coordinates that show up in both at once produce activation outliers and raise the risk of low-precision overflow. Use softcap(x, β) = β tanh(x/β) to cap the linear factor of the Swish gate (β₁ = 4) and the up branch (β₂ = 25) separately. Near the origin it is approximately linear, so the behaviour is almost identical to SwiGLU; at large magnitudes it is bounded, so |f| ≤ β1β2 = 100. - Failure mode two (load balancing for close to 10³ experts) — beyond the range where a fixed-step sign update still works well.
Answer 3: Quantile Balancing (Eq. 13, Eq. 14) — auxiliary-loss-free routing gives every expert a bias bj that enters only the Top-k selection, not the mixture weights p and not the router's gradient. QB no longer “moves γ towards the target”; it solves for the bias directly: it gets each token's admission cutoff αi for free out of Top-(k+1), writes “how many tokens expert j receives” as “how many margins si,j − αi exceed the threshold −b̂j”, and uses monotonicity to invert for b̂j = the negative of the (q+1)-th largest margin = the negative of the (1−k/n) quantile of the margins. Then subtract the common offset (which does not change Top-k), take effect only at the next step (causality), and freeze at inference time. At real scale the quantile is estimated with an additive histogram + a single all-reduce, at a cost of a few hundred bins per expert.
Points this chapter marks as doubtful or left unexplained by the paper
- “nearly four consecutive matrix multiplications” never names which four matrices; “ill-conditioned structure” (ill-conditioned) has no quantitative evidence of any kind (no condition numbers, no activation-norm curves, no failed training curves).
- the extra RMSNorm “keeps improving validation loss and downstream benchmarks” is the paper's own report, and the main text gives no ablation numbers at all; nor does it say whether it carries a learnable gain, or whether the shared branch gets similar treatment.
- β₁ = 4 and β₂ = 25 are simply given as values, with no ablation and no explanation of why the two differ by more than a factor of six.
- the comparison of SiTU-GLU against hard clipping, the local expansion and the formal output bound are all pushed to Appendix B by the main text.
- “896 experts is beyond the range where existing auxiliary-loss-free bias updates work well” is an assertion, with no supporting data.
- the QB derivation in the main text only proves “with the cutoffs held fixed, exactly q tokens cross the cutoff for each expert”, whereas real routing takes Top-k per token, and the two are not equivalent (in q5-11 this site built a (4,0) → (0,4) counterexample). The real balanced-assignment derivation is pushed to Appendix C, and the main text never says under what conditions equality holds.
- the derivation assumes “no ties” (no ties), and never discusses what to do when q = mk/n is not an integer.
- the histogram's bin count B, how the bin edges are fixed, and how often the bias is updated are none of them given in the main text; the error bound is pushed to Appendix D.
- the cost on the “does QB damage expert specialisation” side the paper does not measure at all.
- how much communication LatentMoE actually saves against a conventional MoE is a number the paper does not give (the 0.5× in this chapter is what this site derived from ℓ/d in Table 1); nor is there any ablation for taking ℓ = 0.5d.
- “MoE hidden dimension 3072 per expert” in Table 1 does not distinguish whether that is the routed experts' or includes the shared experts too.
第6章 原生视觉与 Per-Head Muon
一句话导语:这一章回答两个问题——为什么 K3 敢把视觉编码器从零开始训(而不是像几乎所有人那样拿一个 SigLIP 来打底),以及为什么要把优化器的正交化操作细到「每个注意力头单独做一次」。
学完这一章你应该能做到
- 用自己的话说清「原生多模态」和「给语言模型接一个图像模型」在信息流上到底差在哪
- 复述论文放弃 SigLIP 初始化的主要理由,并指出这个结论被作者自己加了什么限定词
- 算出一张 3584×3584 的图片,在 patch size 14、pixel-shuffle 2×2 之后会变成多少个 token
- 解释「整矩阵正交化」和「逐头正交化」会导致不同头的更新尺度出现什么差别
- 指出论文在这两节里哪些地方只给了直觉、没给证据
6.1 原生多模态:让「看」和「写」活在同一条 token 流里
先说不用它会怎样。今天绝大多数「能看图的大模型」是这么拼出来的:先有一个只会读文字的语言模型,训好了;再有一个只会看图的视觉模型,也训好了;然后加一个小小的转接头,再花一个专门的阶段把两边「对齐」,让视觉模型输出的东西能被语言模型读懂。这个事后补的阶段,论文叫事后模态对齐(post-hoc modality-alignment stage)。
原生多模态(natively multimodal):论文对 K3 的原话是,文本、图像和视频由同一个共享骨干在同一个上下文里处理,没有事后的模态对齐阶段。
差别听上去只是流程顺序,实际影响的是信息的宽度。想象模型正在做一件很典型的事:写一段网页代码,把它渲染出来,看看效果,再回去改代码。在拼接式的方案里,「看」这一步是由另一个模型完成的,它必须把看到的东西压缩成一段文字描述,再交回给语言模型——按钮偏了三个像素、某个渐变的方向反了、第七帧的动画抖了一下,这些细节能不能活着穿过那段文字描述,全看运气。论文把这种交接叫跨模型交接(cross-model hand-off),而 K3 的设计目标就是彻底没有这一步。
在 K3 里,渲染出来的结果和产生它的代码活在同一个 token 流里。模型写代码、检查截图或视频帧、再迭代改视觉产物(用户界面、图形、视频),全程不换模型。论文明确说,这就是第 1 节里那种「长时程、视觉在环」(long-horizon, vision-in-the-loop)行为的架构基础。
一个很容易混淆的地方
很多人以为「原生多模态」就是「输入能塞图片」。其实能塞图片的模型遍地都是,拼接式方案也能塞。真正的分界线在于:视觉信息进入模型之后,是和文字 token 平起平坐地留在同一段上下文里、被同一套参数处理,还是先被翻译成文字再进来。K3 属于前者,而且论文强调这套共享骨干是从训练第一天起就同时优化语言和视觉的(这一点在第7章的训练配方里会再出现)。
下面两个系统,哪一个符合论文说的「原生多模态」?为什么?
甲:一个训好的语言模型 + 一个训好的图像编码器,中间加转接层,再用一批图文配对数据专门训练一个阶段让两者对齐。
乙:训练一开始就把图像切成的 token 和文字 token 混在同一段序列里,用同一个下一 token 预测目标一起训。
变式:如果甲在对齐阶段之后又把整个系统端到端微调了很久,它算原生多模态吗?(提示:想想「表示是被谁塑造的」,以及先定型的参数会不会留下痕迹——论文没有讨论这种中间情况,这是可以自己论证的开放题。)
6.2 MoonViT-V2:把视觉编码器完全从零训起来
这是 K3 相对 Kimi K2.5 的一个关键改变,论文用斜体强调了它:K3 的视觉编码器 MoonViT-V2 是完全从零、用下一 token 预测训练的。
下一 token 预测(next-token prediction, NTP):就是语言模型那个最朴素的训练目标——给你前面的内容,猜下一个 token 是什么。这里的新意是,连视觉编码器的参数也由这个目标来训。
此前大家为什么不这么做
过去的常规做法(包括 Kimi K2.5 自己)是用 SigLIP 这类对比预训练(contrastive pre-training)模型来初始化视觉编码器。对比预训练的思路大致是:给模型一堆「图片—说明文字」的配对,让它把配对的拉近、不配对的推远。这样练出来的编码器已经懂很多视觉概念,所以大家相信「预训练的视觉知识能给模型开个头」,省时间也省数据。K3 把这个前提整个丢掉了。
论文给的主要理由是训练稳定性。把一个预训练好的编码器接到大语言模型上做联合优化,会不稳:SigLIP 初始化的 MoonViT-3D 表现出持续更高的梯度范数,而且频繁尖峰;而从零训练的 MoonViT-V2 全程稳定。这就是 Figure 6 那张图在说的事——横轴是训练步数(7k 到 30k),纵轴是视觉塔的梯度范数,蓝线(SigLIP 初始化)整体更高、尖刺更多,红线(从零)压得又低又平;右边还放大了 14k–16k 这一小段,让你看清即使在「看起来都还好」的区间里,蓝线的抖动也明显更大。
梯度范数(gradient norm):把这一步所有梯度当成一个超长向量,量一下它的长度。它大致告诉你「模型这一步想改多狠」。持续偏高说明训练一直在被大幅拉扯,突然的尖峰则往往是灾难性更新的前兆——真炸起来会让损失曲线飞掉,几千步的算力就白烧了。在千亿、万亿参数这个尺度上,一次崩溃的代价高到没人想赌。
还有第二个理由,关于表示被谁塑造。用 NTP 训练,编码器的表示是直接被语言建模目标塑造的;而对比损失塑造出来的表示,论文的说法是偏好全局语义、忽略细粒度的文字和结构线索。用大白话讲:对比预训练只要能把「这是一张猫的照片」和文字对上就得分了,它没有动力去精确记住猫身上第几根胡须、界面上按钮里那行小字写的什么、表格的第三列和第四列是怎么对齐的。而 K3 要干的活恰恰是看截图改代码、读 CAD 图纸、认 OCR,全是细粒度文字和结构。
论文的结论是:MoonViT-V2 在视觉评测上追平了 SigLIP 初始化的基线,说明对比预训练作为多模态语言模型的初始化不是必需的。
读的时候要小心这几个字
原文的措辞是 「matches the SigLIP-initialized baseline」——是追平,不是超过。也就是说从零训练在指标上并没有赚,赚的是稳定性和表示的性质。而且作者自己在结论后面加了限定:at scale(在这个规模上)。这句话反过来读就是:在小规模上,对比预训练的初始化很可能仍然是有用的,甚至是必需的。此外,「追平」是论文自述的评测结论,这几页没有给出具体的评测表或分数,读者无法核对是在哪些评测集上追平的。
打个比方
SigLIP 初始化像是雇一个已经在别的公司干了十年的老员工:上手快,但他带着一套自己的工作习惯,你的流程要迁就他,磨合期可能很难受。从零训练像是招应届生:前期慢,但他的所有习惯都是照你的业务长出来的。
类比失效处:真实的老员工不会让公司「崩溃」,而神经网络训练是真的会因为不稳定而彻底失败;另外这个类比会让人以为从零训练一定更慢更差,但论文的结果是追平,并没有付出指标上的代价。
Figure 6 是「视觉塔的梯度范数 vs 训练步数」,两条曲线。有人看完说:「红线一直比蓝线低,说明从零训练的模型学得少、更新得不够,所以效果肯定更差。」这个推理错在哪?
变式:如果反过来,红线(从零)的梯度范数明显更高,但最终评测分数一样,你会怎么解读?这还能不能支持「从零训练更稳」这个论点?
论文说「对比预训练作为多模态语言模型的初始化不是必需的(at scale)」。请你构造一个具体场景,在那个场景里这个结论会失效——也就是不用 SigLIP 初始化就明显吃亏。说清楚你改动了哪个前提。
变式:换个方向想——有没有可能在更大的规模上,SigLIP 初始化的劣势会更严重?给一个理由。(提示:不稳定性和模型规模的关系,以及论文说 Per-Head Muon 「在更大规模上改善训练稳定性」这句话透露的信息。)
答辩:如果我是审稿人
你用一张梯度范数曲线(Figure 6)当作放弃 SigLIP 初始化的主要证据。可梯度范数高本身并不是失败——只要没炸、最终损失更低就行。你有没有展示 SigLIP 初始化那条路真的训崩过?还是说你只是因为看着心慌就换了方案?
参考防守(先自己组织语言再看)
可以承认这一击打中了要害:论文确实没有在这两页展示任何一次真实的训练崩溃,也没有给出两条路的最终损失对比,Figure 6 是消融实验里的过程指标。合理的防守有三层。第一层是风险论证:在万亿参数、上下文最长 100 万 token 的训练里,一次不可恢复的发散意味着巨大的算力损失,工程上会主动避开「看起来会炸」的配置,即使还没炸——这是成本不对称下的理性选择,不是心慌。第二层是结果背书:既然从零训练追平了基线,那么为了稳定性放弃初始化就是零代价的选择,此时不需要再证明另一条路一定会崩,只需要证明它没有额外收益。第三层是机制论证:NTP 训练让表示被语言建模目标直接塑造,这条理由和稳不稳定无关,是独立成立的。至于审稿人的质疑本身,最诚实的回应是——论文没有给出「SigLIP 初始化在同等预算下最终损失是多少」这个关键数字,这确实是一个空缺。
6.3 MoonViT-V2 的架构:27 层、无偏置、时空分解、pixel-shuffle
整条视觉通路很短,一句话就能说完:图像先由 MoonViT-V2 编码,再由一个轻量 MLP 投影器(lightweight MLP projector)映射进大语言模型。这个整体设计沿用了 Kimi K2.5 的视觉通路。
MoonViT-V2 本身是一个 27 层的视觉 Transformer,大约 0.4B 参数(第7章的 Table 1 里给的精确数字是 401M)。0.4B 是 4 亿个数——听着不小,但把它放在 K3 总共 2.78 万亿参数旁边,占比大约万分之一点四,视觉编码器在整个模型里其实是很薄的一层皮。它采用 RMSNorm,并且去掉了线性投影和注意力投影里的所有偏置项。论文明说,这个设计是为了进一步稳定从零训练——注意这和上一节是同一条主线:既然选择了从零训练这条更难走的路,架构上就要处处减少可能出问题的自由度。
图像和视频用完全共享的参数处理,做法沿用 MoonViT-3D:注意力被分解(factorized)成两趟,一趟是帧内空间注意力(同一帧里的各个小块互相看),一趟是帧间时间注意力(不同帧的同一位置互相看);此外还有时间池化沿时间维进一步压缩 token。为什么要分解?因为如果让所有帧的所有小块两两互看,代价随 token 数平方增长,视频根本算不动;拆成两趟之后,一段视频的注意力开销就被压下来了。
补充:什么是「完全共享参数」
意思是处理静态图像和处理视频帧用的是同一套权重,不存在「图像分支」和「视频分支」两套参数。一张静态图片可以看成只有一帧的视频,时间那一趟注意力退化成什么也不用比。这样做省参数,也让图像上学到的能力天然迁移到视频上。
pixel-shuffle:把 token 数砍到四分之一
投影进语言模型之前,还有一步 pixel-shuffle,做 2×2 下采样,把视觉 token 的数量减少到四分之一。论文给这一步的理由非常直接:这样才能让高达 3584×3584 像素的输入,在 100 万 token 的上下文里仍然负担得起。
这里值得停下来自己算一算,因为算完你才会对「高分辨率有多贵」有真实的感觉。视觉 Transformer 处理图片的第一步是把图片切成边长为 P 的小方块,每个小方块叫一个 patch,之后每个 patch 就是一个 token。K3 的 patch size 是 14(Table 1)。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| H, W | 输入图片的高和宽,单位是像素 | 论文给的上限是 3584×3584 |
| P | patch 的边长,K3 取 14(Table 1 的 ViT patch size) | 切图的「刀距」,越小切得越细、token 越多 |
| H/P, W/P | 横竖各能切出多少个 patch | 3584 ÷ 14 = 256,所以是 256×256 的格子 |
| 1/4 | pixel-shuffle 2×2 下采样带来的压缩比 | 每 2×2 共 4 个相邻 patch 合成 1 个 token |
| Ntoken | 最终进入语言模型上下文的视觉 token 数 | 要和 100 万的总预算比大小的那个数 |
代进去:3584 ÷ 14 = 256,256 × 256 = 65536 个 patch;再除以 4,得到 16384 个 token。也就是说,一张顶格分辨率的图,占掉 100 万 token 上下文的大约 1.6%。如果没有 pixel-shuffle,同一张图要吃掉 65536 个 token,约 6.6%——放十几张图就把上下文塞满了。四倍的差别,决定的是「能不能在一次对话里翻十几屏截图」。
论文没写的一点
pixel-shuffle 的标准做法是把空间上相邻的格子搬进特征维度,所以 token 变少、每个 token 变「厚」,信息并不是被扔掉了,而是被重新打包了。但论文在这两页只写了「token 数减少到四分之一」,没有说明投影器前后的具体维度,也没说明是否伴随维度扩张。上图右侧「每个 token 更厚」是按 pixel-shuffle 的通用定义画的,不是论文的原始陈述。
一张 1792×896 的截图,patch size 14,经过 2×2 pixel-shuffle 之后会变成多少个视觉 token?占 100 万 token 上下文的百分之几?
变式:如果把 pixel-shuffle 改成 4×4 下采样,同一张 3584×3584 的图会变成多少 token?这么做的代价可能是什么?(提示:想想 OCR 要认清小字需要多细的空间粒度。)
MoonViT-V2 去掉了线性投影和注意力投影里的所有偏置项,论文说这「进一步稳定了从零优化」。请解释:为什么「从零训练」这个决定会让作者更倾向于做这种减法?如果他们还是用 SigLIP 初始化,这个改动的必要性会变高还是变低?
变式:注意力被分解成「帧内空间」和「帧间时间」两趟,而不是让所有帧的所有 patch 两两互看。假设一段视频有 F 帧、每帧 N 个 patch,请分别写出两种做法的注意力对数(成对比较的次数)随 F 和 N 增长的形式,看看差多少。
6.4 Per-Head Muon:让每个注意力头拿到「同一量级」的更新
先补两个零基础的前置概念,不然这一节没法读。
优化器(optimizer):模型里有一大堆数(参数)。训练时,反向传播会告诉你「每个数往哪个方向挪一点点能让预测更准」,这个方向叫梯度。但梯度只给方向和一个原始大小,具体每个数该挪多少是优化器决定的。优化器就是那个把原始梯度加工成实际更新量的规则。加工得好,训练又快又稳;加工得差,要么原地打转,要么直接发散。
正交化(orthogonalization):一个矩阵可以看成一台「把输入向量拉伸再旋转」的机器。麻烦在于,它对不同方向的拉伸倍数往往差得离谱——某个方向拉 100 倍,另一个方向只拉 0.01 倍。正交化就是把所有方向的拉伸倍数统统拉平到差不多相同,只保留「转向」的部分。用在优化器上,效果是:更新的方向结构留着,但各个方向的力度被拉齐,不会出现某一两个方向独吞了整步更新的情况。
Newton–Schulz 迭代:一种只用几次矩阵乘法就能近似把矩阵正交化的方法,好处是不用做昂贵的矩阵分解,在 GPU 上跑得动。这是 Muon 优化器里真正干活的那一步。
打个比方
正交化像是给一支合唱团做音量均衡:每个人唱的音(方向)不动,但把嗓门大的压下来、嗓门小的抬上去,让整体听起来是一个和声,而不是一个人在吼、其他人在陪衬。
类比失效处:合唱团的「人」是离散的个体,而矩阵的「方向」是连续的、彼此正交的抽象方向,不能一一对应到具体的某个参数;另外正交化是对整个矩阵的所有奇异值一起做的,不是逐个元素做归一化。
回到正题。K3 沿用 K2 的做法,对矩阵参数用 Muon 优化器。K3 的新东西是:对注意力投影再细化成一个「逐头」的变体。
原来的做法是把整个 Q、K、V 投影矩阵拿来做 Newton–Schulz 正交化。Per-Head Muon 改成:把它们的动量矩阵沿头维切分,对每个头的块单独正交化。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| M | 某个投影矩阵(Q 或 K 或 V)对应的动量矩阵 | 「最近几步梯度的加权累积」,比单步梯度平滑 |
| Mi | 把 M 沿头维切开后,第 i 个头对应的那一块 | 只属于第 i 个注意力头的那部分更新信息 |
| h | 注意力头的个数 | K3 是 96 个(Table 1) |
| NS(·) | Newton–Schulz 正交化操作 | 把这一块的各方向力度拉平 |
| [ … | … ] | 把各块按原来的位置拼回去 | 形状不变,只是「分区域施工」 |
补充:这个式子的来源
论文这一节没有给编号公式,只有文字描述(「partition their momentum matrices along the head dimension and orthogonalize each head's block separately」)。上面的式子是按这段文字写出来的示意,Muon 的完整定义在论文引用的文献里,本报告没有展开。
论文给的直觉写得很清楚,值得逐句拆:整矩阵正交化把所有头当成单个耦合的块。于是,梯度或动量尺度大的头会主导那个共享的更新方向,而尺度小的头得到的更新归一化不足。逐头正交化则让各头的更新尺度拉齐。
为什么会这样?因为正交化是「看整个矩阵的整体结构,然后拉平」。如果 96 个头挤在同一个矩阵里一起被拉平,那么这个「整体结构」主要是由数值大的那几个头贡献的——正交化拉平的是整体的方向力度,不是每个头内部的方向力度。结果就是:大头把整步更新的方向绑架了,小头即使被拉平了也还是相对很小,等于没有被真正归一化。切开之后,每一块各自拉平,96 个头就各自拿到了「力度相当」的更新。
论文说这样做在实践上带来三个好处:更均衡的跨头学习动态、在更大规模上改善训练稳定性、以及略微降低优化器开销——最后这一条的理由是,对细高的逐头块做 Newton–Schulz 比对整个投影矩阵便宜。
把这一章的几件事串起来回答:K3 的注意力头从 K2 的 64 个增加到 96 个(Table 1),同时论文说 Per-Head Muon 「在更大规模上改善训练稳定性」,又说 MoonViT-V2 从零训练的主要动机也是稳定性。请论证:为什么「头变多」这件事会让整矩阵正交化的问题变得更严重?并说明这三件事共同指向的是一个什么样的工程判断。
变式:论文说逐头正交化「略微降低优化器开销,因为对细高的逐头块做 Newton–Schulz 比对整个投影矩阵便宜」。请你只用「矩阵乘法的代价和矩阵形状有关」这一条常识,说明为什么切成若干细高块之后总代价会下降。再问一句:既然更省,为什么论文只说「略微」?(提示:优化器那一步在整个训练的算力里占多大比重。)
答辩:如果我是审稿人
Per-Head Muon 你只给了一段直觉,说大头会主导共享的更新方向。可是「整矩阵一起正交化」本来就是 Muon 设计里的一个特性——它就是要在整个矩阵的层面上做谱意义上的均衡。你凭什么认为按头切开还算是在做同一件事?切开之后各块之间的相对尺度信息被丢掉了,万一那个信息是有用的呢?
参考防守(先自己组织语言再看)
先承认前半句是对的:切开确实丢掉了跨头的相对尺度信息,这是一个真实的代价,论文也没有论证那个信息无用。防守的落点在「注意力头本来就是并列的独立子空间」这个结构事实上:多头注意力的设计初衷就是让每个头去看不同的关系模式,头与头之间在功能上是并列的,不是一个整体表示的不同坐标轴;既然投影矩阵在语义上本来就是 h 个独立的小投影拼在一起,那么把归一化的粒度对齐到这个语义边界,比对齐到「拼接后的矩阵」这个人为边界更自然。换句话说,需要被辩护的其实是整矩阵那一侧——它把 h 个本无耦合关系的块强行当成一个谱结构来均衡。至于「相对尺度信息有没有用」,可以承认这是一个开放问题:如果某些头本来就应该学得更快,逐头正交化会抹掉这个差异。论文没有回答它,只报告了实践上更均衡、更稳定。真正的软肋在于论文没有给出任何消融——没有「整矩阵 vs 逐头」的损失曲线对比,读者只能接受作者的定性陈述。
本章小结
这一章其实只讲了一个判断:在 K3 这个规模上,训练稳定性比任何单点收益都值钱。三个看起来无关的设计都是这个判断的产物。
原生多模态解决的是信息通路问题——文本、图像、视频由同一个共享骨干在同一个上下文里处理,没有事后对齐阶段,渲染结果和产生它的代码活在同一条 token 流里,所以「写代码—看效果—改代码」这个循环全程不用换模型。MoonViT-V2 从零用下一 token 预测训练,放弃了 SigLIP 初始化,主要理由是联合优化时预训练编码器会让梯度范数持续偏高并频繁尖峰(Figure 6),次要理由是让表示被语言建模目标而不是偏好全局语义的对比损失塑造;代价是没有性能红利,结论是追平,而且作者自己加了「在这个规模上」的限定。架构上是 27 层、401M 参数、RMSNorm、无偏置,图像视频共享参数、注意力分解成空间和时间两趟,投影前 2×2 pixel-shuffle 把 token 砍到四分之一,让 3584×3584 的输入只占 16384 个 token。Per-Head Muon 把 Newton–Schulz 正交化的粒度从整个 Q/K/V 投影矩阵细化到每个头的块,避免大尺度的头绑架共享的更新方向。
需要记住的空缺:论文在这两页没有给出 MoonViT-V2 与 SigLIP 基线的具体评测数字,没有给 Per-Head Muon 的消融,也没有说「更大规模」的界线在哪。这些都是作者的主张而非被展示的证据。
Chapter 6 · Native vision and Per-Head Muon
In one sentence: this chapter answers two questions — why K3 dares to train its vision encoder from scratch (instead of laying a SigLIP underneath it the way almost everyone else does), and why the optimiser's orthogonalisation is refined all the way down to “done separately for each attention head”.
After this chapter you should be able to
- Say in your own words where “natively multimodal” and “bolting an image model onto a language model” actually differ in the way information flows
- Restate the paper's main reason for giving up SigLIP initialisation, and point out the qualifier the authors themselves attached to that conclusion
- Work out how many tokens a 3584×3584 image turns into after patch size 14 and a 2×2 pixel-shuffle
- Explain what difference whole-matrix orthogonalisation and per-head orthogonalisation make to the update scale of different heads
- Point out where in these two sections the paper gives only intuition and no evidence
6.1 Natively multimodal: letting “seeing” and “writing” live in one token stream
Start with what happens without it. Almost every “large model that can look at pictures” today is bolted together like this: first there is a language model that can only read text, trained; then a vision model that can only look at images, also trained; then you add a small adapter head and spend a dedicated stage “aligning” the two sides, so that what the vision model puts out can be read by the language model. That stage patched on afterwards is what the paper calls a post-hoc modality alignment(post-hoc modality-alignment stage).
Natively multimodal: the paper's own words about K3 are that text, images and video are processed by one shared backbone inside one context, with no post-hoc modality-alignment stage.
The difference sounds like nothing more than the order of the steps, but what it actually affects is the width of the information. Picture the model doing something very typical: writing a piece of web page code, rendering it, looking at the result, then going back to change the code. In the bolted-together scheme the “looking” step is done by another model, which has to compress what it saw into a piece of text description and hand that back to the language model — a button three pixels out of place, a gradient running the wrong way, a shudder in the animation on the seventh frame: whether those details survive the trip through that text description is down to luck. The paper calls this kind of handover a cross-model hand-off(cross-model hand-off), and K3's design goal is to have no such step at all.
In K3 the rendered result and the code that produced it live in the same token stream. The model writes code, inspects a screenshot or a video frame, then iterates on the visual artefact (user interface, graphics, video), never switching models along the way. The paper says outright that this is the architectural basis for the “long-horizon, vision-in-the-loop”(long-horizon, vision-in-the-loop) behaviour of Section 1.
A place where it is very easy to get confused
Many people take “natively multimodal” to mean “you can feed it images”. Models you can feed images to are everywhere; the bolted-together scheme takes them too. The real dividing line is this: once visual information is inside the model, does it stay in the same context on equal footing with text tokens and get processed by the same set of parameters, or is it translated into text first and only then let in? K3 is the former, and the paper stresses that this shared backbone optimises language and vision together from the first day of training (this comes up again in the training recipe of Chapter 7).
Which of the two systems below fits what the paper means by “natively multimodal”? Why?
A: a trained language model + a trained image encoder, with an adapter layer between them, followed by a dedicated training stage on a batch of image-text pairs to align the two.
B: from the start of training, the tokens an image is cut into and text tokens are mixed into the same sequence and trained together under the same next-token prediction objective.
Variant: if A then fine-tunes the whole system end to end for a long time after the alignment stage, does it count as natively multimodal? (Hint: think about “who shapes the representation”, and about whether parameters that were set in their ways first leave traces behind — the paper does not discuss this middle case, so this is an open question you can argue out for yourself.)
6.2 MoonViT-V2: training the vision encoder completely from scratch
This is one of the key changes in K3 relative to Kimi K2.5, and the paper puts it in italics: K3's vision encoder MoonViT-V2 is trained entirely from scratch with next-token prediction.
Next-token prediction: the plainest training objective a language model has — given what came before, guess what the next token is. What is new here is that even the vision encoder's parameters are trained by this objective.
Why nobody did this before
The usual practice in the past (Kimi K2.5 itself included) was to initialise the vision encoder from a contrastive pre-training(contrastive pre-training) model of the SigLIP kind. The idea of contrastive pre-training runs roughly like this: give the model a pile of “image—caption” pairs and have it pull the matching ones together and push the non-matching ones apart. An encoder trained that way already understands a great many visual concepts, so everyone believed that “pre-trained visual knowledge gives the model a head start”, saving time and data. K3 throws that premise out entirely.
The main reason the paper gives is training stability. Attaching a pre-trained encoder to a large language model and optimising them jointly is unstable: the SigLIP-initialised MoonViT-3D shows persistently higher gradient norms and frequent spikes, while MoonViT-V2 trained from scratch is stable throughout. This is what the plot in Figure 6 is saying — the horizontal axis is training steps (7k to 30k), the vertical axis is the gradient norm of the vision tower, the blue line (SigLIP-initialised) sits higher overall with more spikes, the red line (from scratch) is pressed low and flat; on the right, the short stretch from 14k to 16k is blown up so you can see that even in a range where “everything looks fine”, the blue line wobbles noticeably more.
Gradient norm: take all the gradients at this step as one very long vector and measure its length. It tells you roughly “how hard the model wants to change itself at this step”. Persistently high means training is being yanked about hard the whole way through, and a sudden spike is often the precursor of a catastrophic update — when it really does blow up the loss curve flies off and thousands of steps of compute are burned for nothing. At the scale of hundreds of billions or trillions of parameters, one collapse costs so much that nobody wants to take the bet.
There is a second reason, about who shapes the representation. Trained with NTP, the encoder's representation is shaped directly by the language modelling objective; the representation a contrastive loss shapes, in the paper's words, favours global semantics and ignores fine-grained textual and structural cues. In plain words: contrastive pre-training scores as soon as it can match “this is a photo of a cat” to the text, and it has no incentive to remember precisely which whisker on the cat is which, what the line of small type inside a button on an interface says, or how the third and fourth columns of a table line up. And the work K3 has to do is exactly that — look at a screenshot and edit code, read a CAD drawing, do OCR — all of it fine-grained text and structure.
The paper's conclusion: MoonViT-V2 matches the SigLIP-initialised baseline on the vision evaluations, which shows that contrastive pre-training is not necessary as an initialisation for a multimodal language model.
Read these few words carefully
The original wording is “matches the SigLIP-initialized baseline” — it matches, it does not beat. Which is to say that training from scratch gains nothing on the metrics; what it gains is stability and the character of the representation. And the authors themselves attach a qualifier after the conclusion: at scale (at this scale). Read that the other way round and it says: at small scale, contrastive pre-trained initialisation is quite possibly still useful, even necessary. Beyond that, “matches” is an evaluation conclusion the paper states about itself; these pages give no concrete evaluation table or scores, so the reader cannot check which evaluation sets it matches on.
An analogy
SigLIP initialisation is like hiring someone who has already spent ten years at another company: they get going fast, but they bring a set of working habits of their own, your processes have to accommodate them, and the settling-in period can be painful. Training from scratch is like hiring a new graduate: slow at first, but every habit they have has grown out of your business.
Where the analogy breaks down: a real experienced hire will not make the company “collapse”, whereas training a neural network really can fail outright from instability; and the analogy makes it sound as if training from scratch must be slower and worse, when the paper's result is a match, with no price paid on the metrics.
Figure 6 is “gradient norm of the vision tower vs training steps”, two curves. Someone looks at it and says: “the red line is below the blue line all the way, which means the from-scratch model learns less and does not update enough, so it must perform worse.” Where does that reasoning go wrong?
Variant: turn it round — if the red line (from scratch) had a clearly higher gradient norm but the final evaluation scores came out the same, how would you read that? Could it still support the claim “training from scratch is more stable”?
The paper says “contrastive pre-training is not necessary as an initialisation for a multimodal language model (at scale)”. Construct a concrete scenario in which that conclusion fails — one where not using SigLIP initialisation clearly costs you. Say clearly which premise you changed.
Variant: think in the other direction — could the disadvantage of SigLIP initialisation be more severe at an even larger scale? Give a reason. (Hint: the relationship between instability and model size, and what the paper's line about Per-Head Muon “improving training stability at larger scale” gives away.)
Defend it: if I were a reviewer
You use one gradient norm curve (Figure 6) as the main evidence for abandoning SigLIP initialisation. But a high gradient norm is not itself a failure — as long as it does not blow up and the final loss is lower, it is fine. Have you shown that the SigLIP-initialised route ever really did collapse in training? Or did you switch simply because looking at it made you nervous?
A reference defence (put it in your own words first, then look)
You can concede that this one lands: the paper genuinely does not show a single real training collapse in these two pages, nor does it give a comparison of the final loss along the two routes, and Figure 6 is a process metric from an ablation. A reasonable defence has three layers. The first is the risk argument: in a training run with trillions of parameters and a context up to 1 million tokens, one unrecoverable divergence means an enormous loss of compute, so engineering will deliberately steer clear of a configuration that “looks like it will blow up”, even before it has — that is a rational choice under asymmetric costs, not nerves. The second is the endorsement from the result: since training from scratch matches the baseline, giving up initialisation for the sake of stability is a zero-cost choice, and at that point you no longer need to prove the other route must collapse, only that it brings no extra gain. The third is the mechanism argument: NTP training lets the representation be shaped directly by the language modelling objective, and this reason has nothing to do with stability — it stands on its own. As for the reviewer's challenge itself, the most honest reply is this — the paper never gives the key number, “what final loss SigLIP initialisation reaches under the same budget”, and that really is a gap.
6.3 The architecture of MoonViT-V2: 27 layers, no biases, space-time factorisation, pixel-shuffle
The whole vision pathway is short, one sentence covers it: an image is first encoded by MoonViT-V2, then mapped into the large language model by a lightweight MLP projector(lightweight MLP projector). This overall design carries over the vision pathway of Kimi K2.5.
MoonViT-V2 itself is a 27-layer vision Transformer of about 0.4B parameters (the exact figure given in Table 1 of Chapter 7 is 401M). 0.4B is 400 million numbers — which does not sound small, but put it next to K3's 2.78 trillion parameters in total and it is about 0.014% of them: the vision encoder is really a very thin skin on the whole model. It uses RMSNorm, and drops every bias term in the linear projections and the attention projections. The paper says outright that this design is there to further stabilise training from scratch — note that this is the same thread as the previous section: having chosen the harder road of training from scratch, the architecture has to cut down everywhere on degrees of freedom that could go wrong.
Images and video are processed with fully shared parameters, following the approach of MoonViT-3D: attention is factorized(factorized) into two passes, one of intra-frame spatial attention (the patches within one frame looking at each other) and one of inter-frame temporal attention (the same position in different frames looking at each other); on top of that there is temporal pooling, which compresses tokens further along the time dimension. Why factorize? Because if you let every patch of every frame look at every other one, the cost grows with the square of the token count and video simply cannot be computed; split into two passes, the attention cost of a video clip is brought back down.
An addition: what “fully shared parameters” means
It means that processing a static image and processing a video frame use the same set of weights; there are not two sets of parameters, an “image branch” and a “video branch”. A static image can be seen as a video of one frame, for which the temporal pass of attention degenerates into having nothing to compare. This saves parameters, and it also lets ability learned on images transfer to video naturally.
pixel-shuffle: cutting the token count to a quarter
Before the projection into the language model there is one more step, pixel-shuffle, which does a 2×2 downsample and reduces the number of visual tokens to a quarter. The paper's reason for this step is very direct: it is what makes an input of up to 3584×3584 pixels still affordable inside a 1 million token context.
It is worth stopping here to work it out for yourself, because only once you have will you have a real feel for “how expensive high resolution is”. The first thing a vision Transformer does with an image is cut it into small squares of side P; each small square is called a patch, and from then on each patch is one token. K3's patch size is 14 (Table 1).
| Symbol | What it is | Intuition |
|---|---|---|
| H, W | the height and width of the input image, in pixels | the upper limit the paper gives is 3584×3584 |
| P | the side of a patch; K3 takes 14 (the ViT patch size in Table 1) | the “spacing of the cuts”; the smaller it is, the finer the cutting and the more tokens |
| H/P, W/P | how many patches can be cut across and down | 3584 ÷ 14 = 256, so a 256×256 grid |
| 1/4 | the compression ratio the 2×2 pixel-shuffle downsample brings | every 2×2 group of 4 neighbouring patches becomes 1 token |
| Ntoken | the number of visual tokens that finally enter the language model's context | the number you have to weigh against the total budget of 1 million |
Substitute: 3584 ÷ 14 = 256, 256 × 256 = 65536 patches; divide by 4 and you get 16384 tokens. Which is to say that one image at the maximum resolution takes up about 1.6% of a 1 million token context. Without pixel-shuffle the same image would eat 65536 tokens, about 6.6% — a dozen or so images and the context is full. A factor of four decides whether you “can flip through a dozen screens of screenshots inside one conversation”.
One point the paper does not write down
The standard way pixel-shuffle works is to move spatially neighbouring cells into the feature dimension, so the tokens get fewer and each token gets “thicker”; the information is not thrown away, it is repackaged. But in these two pages the paper writes only that “the token count is reduced to a quarter”; it does not state the concrete dimensions before and after the projector, nor whether this comes with an expansion of the dimension. The “each token is thicker” on the right of the figure above is drawn from the general definition of pixel-shuffle, not from a statement in the paper.
A 1792×896 screenshot, patch size 14: how many visual tokens does it become after a 2×2 pixel-shuffle? What percentage of a 1 million token context is that?
Variant: if pixel-shuffle were changed to a 4×4 downsample, how many tokens would the same 3584×3584 image become? What might that cost you? (Hint: think about how fine a spatial granularity OCR needs to make out small type.)
MoonViT-V2 drops every bias term in the linear projections and the attention projections, and the paper says this “further stabilises optimisation from scratch”. Explain: why does the decision to “train from scratch” make the authors more inclined to subtract like this? If they had still used SigLIP initialisation, would the necessity of this change be higher or lower?
Variant: attention is factorized into two passes, “intra-frame spatial” and “inter-frame temporal”, rather than letting every patch of every frame look at every other one. Suppose a video has F frames and N patches per frame; write down for each of the two approaches how the number of attention pairs (pairwise comparisons) grows with F and N, and see how far apart they are.
6.4 Per-Head Muon: giving every attention head an update of “the same magnitude”
First, two prerequisite concepts from zero, or this section cannot be read.
Optimiser: a model holds a big pile of numbers (the parameters). During training, backpropagation tells you “which way to nudge each number a little so the prediction gets more accurate”, and that direction is called the gradient. But the gradient only gives you a direction and a raw size; how far each number should actually move is decided by the optimiser. The optimiser is the rule that works raw gradients into actual update amounts. Work them well and training is fast and stable; work them badly and you either spin in place or diverge outright.
Orthogonalisation: a matrix can be read as a machine that “stretches an input vector and then rotates it”. The trouble is that the stretch factors it applies in different directions are usually wildly apart — 100× in one direction, only 0.01× in another. Orthogonalisation flattens the stretch factors of every direction to roughly the same value and keeps only the “turning” part. Used in an optimiser, the effect is this: the directional structure of the update stays, but the strength along each direction is levelled, so you never get one or two directions swallowing the whole step.
Newton–Schulz iteration: a way to orthogonalise a matrix approximately using only a few matrix multiplications. The advantage is that it needs no expensive matrix decomposition and will run on a GPU. This is the step that does the real work inside the Muon optimiser.
An analogy
Orthogonalisation is like levelling the volume across a choir: the note each person sings (the direction) is untouched, but the loud voices are pushed down and the quiet ones lifted up, so the whole thing sounds like a chord instead of one person bellowing while the rest fill in behind.
Where the analogy breaks down: the “people” in a choir are discrete individuals, whereas the “directions” of a matrix are continuous, mutually orthogonal abstract directions that cannot be matched one-to-one to any particular parameter; also, orthogonalisation is done to all the singular values of the whole matrix at once, it is not an element-by-element normalisation.
Back to the point. K3 carries over K2's approach: matrix parameters use the Muon optimiser. What is new in K3 is that for the attention projections this is refined further into a “per-head” variant.
The original approach takes the whole Q, K, V projection matrix and runs Newton–Schulz orthogonalisation on it. Per-Head Muon changes that to: partition their momentum matrices along the head dimension and orthogonalise each head's block separately.
| Symbol | What it is | Intuition |
|---|---|---|
| M | the momentum matrix belonging to one projection matrix (Q or K or V) | “a weighted accumulation of the gradients of the last few steps”, smoother than a single-step gradient |
| Mi | after M is cut along the head dimension, the block belonging to head i | the part of the update information that belongs to attention head i alone |
| h | the number of attention heads | 96 in K3 (Table 1) |
| NS(·) | the Newton–Schulz orthogonalisation operation | levels the strength of every direction inside this block |
| [ … | … ] | stitch the blocks back into their original positions | the shape does not change, the work is just “done one zone at a time” |
A note: where this expression comes from
This section of the paper gives no numbered equation, only a description in words (“partition their momentum matrices along the head dimension and orthogonalize each head's block separately”). The expression above is a sketch written out from that sentence; the full definition of Muon lives in the reference the paper cites, and the report does not spell it out.
The intuition the paper gives is written clearly and is worth taking apart clause by clause: whole-matrix orthogonalisation treats all heads as a single coupled block. So heads with a large gradient or momentum scale dominate that shared update direction, while heads with a small scale get updates that are under-normalised. Per-head orthogonalisation instead brings the update scales of the heads into line.
Why does it come out that way? Because orthogonalisation is “look at the overall structure of the whole matrix, then flatten it”. If 96 heads are crammed into one matrix and flattened together, then that “overall structure” is contributed mainly by the few heads with large values — orthogonalisation flattens the directional strength of the whole, not the directional strength inside each head. The result: the big heads hijack the direction of the entire step, and the small heads, even after being flattened, are still relatively tiny, which amounts to never having been normalised at all. Once it is cut apart, each block is flattened on its own, and the 96 heads each get an update of “comparable strength”.
The paper says this brings three benefits in practice: more balanced learning dynamics across heads, better training stability at larger scale, and slightly lower optimiser overhead — the reason given for the last one is that running Newton–Schulz on the tall, thin per-head blocks is cheaper than on the whole projection matrix.
Answer this by stringing together several things from this chapter: K3's attention heads go from K2's 64 up to 96 (Table 1); at the same time the paper says Per-Head Muon “improves training stability at larger scale”, and it also says the main motivation for training MoonViT-V2 from scratch was stability. Argue it: why does “more heads” make the problem with whole-matrix orthogonalisation worse? And say what kind of engineering judgement these three things jointly point to.
Variant: the paper says per-head orthogonalisation “slightly lowers optimiser overhead, because running Newton–Schulz on the tall, thin per-head blocks is cheaper than on the whole projection matrix”. Using only the one piece of common sense that “the cost of a matrix multiplication depends on the shape of the matrix”, explain why the total cost goes down once it is cut into several tall, thin blocks. Then one more question: if it saves that much, why does the paper only say “slightly”? (Hint: how big a share of the whole training run's compute that optimiser step takes.)
Defend it: if I were a reviewer
For Per-Head Muon you give only a paragraph of intuition, saying the big heads dominate the shared update direction. But “orthogonalising the whole matrix together” is a feature of Muon's design in the first place — it is meant to balance in the spectral sense at the level of the entire matrix. On what grounds do you hold that cutting it up by head is still doing the same thing? Once it is cut, the relative scale information between the blocks is thrown away. What if that information is useful?
A reference defence (put it in your own words first, then look)
Start by granting the first half: cutting it up does throw away the relative scale information across heads, that is a real cost, and the paper does not argue that the information is useless. The defence lands on the structural fact that “attention heads are parallel independent subspaces to begin with”: the design intent of multi-head attention is for each head to look at a different pattern of relations, so heads stand side by side in function, they are not different coordinate axes of one whole representation; since the projection matrix is semantically already h independent small projections stitched together, aligning the granularity of normalisation to that semantic boundary is more natural than aligning it to the artificial boundary of “the matrix after stitching”. Put another way, the side that actually needs defending is the whole-matrix one — it forces h blocks that have no coupling between them to be balanced as a single spectral structure. As for “whether the relative scale information is useful”, you can grant that this is an open question: if certain heads genuinely ought to learn faster, per-head orthogonalisation erases that difference. The paper does not answer it, it only reports that in practice things are more balanced and more stable. The real soft spot is that the paper gives no ablation at all — no loss-curve comparison of “whole-matrix vs per-head”, so the reader can only take the authors' qualitative statement on trust.
Chapter summary
This chapter really only makes one judgement: at K3's scale, training stability is worth more than any single-point gain. Three designs that look unrelated are all products of that judgement.
Native multimodality solves the problem of the information pathway — text, images and video are handled by the same shared backbone in the same context, there is no after-the-fact alignment stage, and a rendered result lives in the same token stream as the code that produced it, so the loop of “write code — look at the result — change the code” never needs a change of model. MoonViT-V2 is trained from scratch with next-token prediction, giving up SigLIP initialisation; the main reason is that under joint optimisation a pretrained encoder keeps the gradient norm persistently high with frequent spikes (Figure 6), the secondary reason is to let the representation be shaped by the language modelling objective rather than by a contrastive loss that favours global semantics; the cost is that there is no performance dividend, the conclusion is parity, and the authors themselves added the qualifier “at this scale”. Architecturally it is 27 layers, 401M parameters, RMSNorm, no biases, parameters shared between images and video, attention factorised into a spatial pass and a temporal pass, and a 2×2 pixel-shuffle before the projection that cuts tokens to a quarter, so a 3584×3584 input takes up only 16384 tokens. Per-Head Muon refines the granularity of Newton–Schulz orthogonalisation from the whole Q/K/V projection matrix down to each head's block, so that large-scale heads cannot hijack the shared update direction.
The gaps worth remembering: in these two pages the paper gives no concrete evaluation numbers for MoonViT-V2 against the SigLIP baseline, no ablation for Per-Head Muon, and no statement of where the line for “larger scale” falls. These are all claims by the authors rather than evidence that has been shown.
第7章 预训练:数据、缩放定律、配方与长上下文
一句话导语:这一章回答「K3 到底是怎么被喂大的」——喂什么数据、怎么算出该用多大的学习率和多少 token、从 K2 到 K3 具体改了哪些数字,以及一个 100 万 token 的上下文窗口是怎么一步步撑开的。
学完这一章你应该能做到
- 说清「2.5× 缩放效率增益」到底省下了什么,并在给定算力时算出具体数值
- 复述作者比较余弦衰减和 WSD 时坚持的那条方法论原则,并把它迁移到别的对比实验上
- 读懂 Table 1,指出 K3 相对 K2 的哪些维度变了、哪些刻意没变,并做一致性校验
- 解释为什么「把长文档上采样」不足以让模型获得长程能力,以及论文用什么补上这个缺口
- 说清 NoPE 和 KDA 的关系,以及「直接外推到 1M」这句话的准确含义
7.1 预训练数据:四个文本域,加一整套视觉语料
K3 的预训练语料由四个主要文本域构成——网页文本、代码、数学、知识——外加一份大规模视觉语料。视觉那一份覆盖得很杂:图注、图文交错的文档、OCR、感知、视频,以及视觉编码数据(visual coding data)。整条数据流水线是在 Kimi K2 上建起来、在 K2.5 上精化过的。
文本数据:过滤、配比、改写
每个域都要经过三道处理的组合:规则启发式(rule-based heuristics)过滤、基于分类器的质量打分(classifier-based quality scoring)、以及去重(deduplication)。而每个域最终占多大比例——论文叫域采样率(domain-specific sampling rates)——不是拍脑袋定的,而是由小模型上的消融实验决定。这是一个值得注意的工程习惯:真正的大模型只训一次,训错了没有回头路,所以配方要先在便宜的小模型上试出来。
还有一件更有意思的事:K3 沿用了 K2 的改写配方(rephrasing recipe),对知识和数学两个域的语料做改写。做法有三个要点——用风格与视角多样的提示来改写、分块自回归生成、以及对照源文档做保真度验证。
为什么要改写语料,不改会怎样
同一个知识点在互联网上往往只有一两种写法。模型见到的表述越单一,它越容易把「这个知识」和「这种说法」绑死,换个问法就答不上来。改写就是人为制造多样性:同一段内容用不同风格、不同视角再讲一遍,让模型学到的是内容本身而不是措辞。但改写有个致命风险——模型改着改着就把事实改错了,尤其是数学。所以第三步「对照源文档做保真度验证」不是可有可无的装饰,它是这套做法能不能用的前提。至于验证具体怎么做、通过率多少,论文这一节没有说明。
视觉数据:两套坐标,和「代码配渲染图」
视觉语料沿用 K2.5 的分类体系,把开源数据集和自研的过滤、合成、去重流水线结合起来。有两个细节论文特意点了出来。
第一,训练时坐标监督同时给两种格式:绝对坐标和归一化到 [0,1] 的坐标,目的是实现精确且分辨率鲁棒的定位。这里的「定位」指的是让模型说出「那个按钮在图上的哪个位置」。绝对坐标(比如「第 640 像素」)精确,但换一个分辨率就全错;归一化坐标(比如「横向 0.35 处」)跨分辨率通用,但要还原成像素得乘回去、会损失精度。两种都教,模型才能既准又不挑分辨率。
第二,论文说大幅扩大了程序化多模态数据(programmatic multimodal data):把代码片段与它渲染出来的视觉结果配对,覆盖 SVG、3D 资产、网页、游戏、CAD 图纸五类格式。回想第6章讲的「视觉在环」——模型要能写代码、看渲染结果、再改代码。那个能力不会凭空出现,它需要海量的「这段代码长这样」的配对样本。这一条数据设计和第6章的架构设计是严丝合缝的一对。
训练时同时给绝对坐标和归一化 [0,1] 坐标两种监督。假设只给其中一种,分别会出什么问题?举一个具体的失败例子。
变式:如果只训归一化坐标,但在推理时把图片统一缩放到一个固定分辨率再送进去,是不是就没问题了?(提示:想想固定分辨率会对超大图上的小字造成什么影响,再对照第6章说的 3584×3584 上限。)
7.2 缩放定律:2.5× 到底省下了什么
架构、数据、训练三方面的改动加在一起,定义了一个新的模型家族。问题是——这些改动同时也改变了最优训练配置。K2 那套调好的超参数,直接搬到 K3 上就未必是最优的了。所以论文专门做了缩放定律(scaling law)研究,去重新调这几个关键超参数:batch size、学习率、每参数 token 数(TPP)、模型形状。
缩放定律(scaling law):在一系列小模型上做实验,测出「算力投入」和「最终损失」之间的规律,拟合出一条曲线,再用这条曲线去预测大模型该怎么配。因为真正的大模型训练贵到只能训一次,所以必须先在便宜的规模上把规律摸出来。
每参数 token 数(tokens-per-parameter ratio, TPP):模型有多少参数,就该喂多少 token?这个比值决定了给定算力下,你应该把钱花在「把模型做大」还是「多喂数据」上。
评测是在留出的分布外验证数据(held-out OOD validation data)上做的——也就是训练时完全没见过、而且分布和训练数据不一样的一批数据。用分布外数据评估是为了防止「模型只是把训练分布背下来了」这种假象。
结论就是那个 2.5×:Figure 7 的曲线显示,这些改进合起来带来约 2.5 倍的整体缩放效率增益。
「2.5× 缩放效率」到底是什么意思
先看 Figure 7 长什么样:横轴是 FLOPs(浮点运算次数,也就是「烧了多少算力」,从 1020 到 1021,是对数刻度),纵轴是验证损失(越低越好)。图上两条拟合出来的虚线,上面那条是 Kimi K2,下面那条是 Kimi K3。K3 那条整体往左下方移了。
那个 2.5× 是画在图中间的一支横向箭头。横向意味着:在同一个验证损失的高度上,量一量两条线在算力上差多远。答案是 2.5 倍。翻译成人话就是——要达到同样的效果,K3 只需要 K2 大约 1/2.5 的算力,也就是 40%,省下 60%。反过来说,同样的算力预算下,K3 能到达 K2 到不了的损失水平。
注意箭头的方向:它是横着的,说的是「省算力」,不是「损失低了 2.5 倍」。损失是个几乎不会大幅变动的数,谁也不会说损失降到 1/2.5。
读这个数字时要小心
论文的原话是 「approximately 2.5×」(约 2.5 倍),而且图上只在一个位置标了这支箭头。论文没有说明这个倍数在整个 FLOPs 区间里是否恒定。如果两条拟合直线严格平行,那么水平间距处处相同,2.5× 就是个常数;如果不平行,这个倍数会随算力大小而变,在图外的更大算力处可能更大也可能更小——而真正的 K3 训练规模显然远超图上标注的 1021。此外这是论文自述的、用自家拟合曲线得到的结论,「2.5×」是把架构、数据、训练三类改动打包之后的合计效果,论文这一节没有拆开说各部分各贡献了多少。
假设 K2 需要 1×1021 FLOPs 才能达到某个验证损失。按论文的 2.5× 缩放效率增益,K3 达到同样的验证损失需要多少 FLOPs?节省的比例是多少?如果反过来,K3 也花满 1×1021,你能不能算出它的损失是多少?
变式:如果某个团队报告「我们的新模型有 10× 缩放效率增益」,但他们的两条曲线明显不平行、而且只在最小的那个算力点上量了倍数,你会怎么质疑这个数字?
7.3 余弦衰减 vs WSD:一堂关于「公平比较」的课
这一小段是整章方法论价值最高的地方,值得慢慢读。
学习率调度(learning rate schedule):学习率是「每一步往梯度方向挪多远」。它通常不是常数,而是随训练进程变化的一条曲线,这条曲线就叫调度。
余弦衰减(cosine decay):学习率从峰值开始,按余弦曲线的形状平滑地一路降到最小值。WSD(Warmup Stable Decay):分三段——先预热升上去,然后长时间保持恒定,最后在末尾一段里快速衰减下来。WSD 的卖点是那段恒定期,因为它意味着你可以在任何时刻分叉出一条衰减支来「结束」训练,不必一开始就决定总步数。
K3 的缩放定律研究一致偏好余弦衰减,所以论文把余弦衰减定为默认调度。但重点不是这个结论,而是他们怎么得出这个结论的。
作者是在固定最小学习率的条件下比较两者的。他们承认,此前已有工作报告 WSD 能匹配甚至超过余弦衰减。那为什么这里结论相反?作者给出的解释是一个观察:两种调度的最优超参数差别很大——即使在同样的模型规模和训练 token 预算下,它们各自的最优峰值学习率和最优 batch size 也大不相同。
顺着这个观察,一个很扎实的推论出来了:用一套共享的超参数去比较两种调度,可能只是因为那套超参数更配合其中一个,从而不公平地偏袒它。于是作者的做法是——对每种调度独立做一次缩放定律搜索,各自找到各自的最优超参;在各自最优的设置下再比,结果是余弦衰减始终取得更低的最终损失。
常见误解
很多人会把这一段读成「论文证明了 WSD 不如余弦衰减」。准确的说法要弱得多:在 K3 团队自己的搜索空间、自己的固定最小学习率设定、自己的模型规模和 token 预算下,各自调优之后余弦衰减的最终损失更低。论文自己也明确提到「此前有工作报告 WSD 能匹配甚至超过余弦衰减」,并没有说那些工作错了——它给出的解释是超参数的错配可能导致不同的结论,而这把刀是双刃的,它同样可以指向 K3 自己的实验。
把这条方法论迁移出去。某篇论文说「我们的新激活函数 B 比常用的 A 好」,实验设置是:同一个模型、同一份数据、同一套超参数(学习率、权重衰减、warmup 比例都照搬 A 的常规配置),只把激活函数从 A 换成 B,结果 B 的损失更低。这个实验有没有问题?如果有,该怎么补?
变式:如果作者说「我们没钱对每种方法都做完整的超参搜索」,你能给出一个成本更低但仍然比「共享一套超参」更公平的折中方案吗?
作者已经对每种调度独立做了缩放定律搜索。请你构造一个具体情形:即使做了独立搜索,这个比较仍然不公平。至少给出两条不同的路径,并指明你利用了论文原文里的哪句话。
路径一:搜索维度不对等。论文说两种调度「最优的峰值学习率和 batch size 差别很大」,读起来搜索空间就是这两维。但 WSD 有一个余弦衰减压根不存在的关键超参——衰减段占总步数的比例(等价地,恒定期有多长)。这个比例对 WSD 的最终损失影响极大。如果它被固定成某个默认值而没有进入搜索,那么「对每种调度独立搜索」就只是在 WSD 的一个切片上搜索,WSD 依然可能没被调到最优。论文这几页没有说明搜索空间到底包含哪些维度,这是一个真实的信息缺口。
路径二:共享的约束偏袒一方。原文明写了「在固定最小学习率下比较」。最小学习率对两种调度的意义并不对等:余弦衰减是平滑地趋近这个下限,而 WSD 是在末段快速砸到这个下限,衰减段的形状和终点强耦合。固定成同一个值,很可能对其中一方更合身。这里「固定」这个动作本身就重演了作者批评的那个错误,只不过换了一个变量。
路径三:外推的方向不同。缩放定律是在小规模上拟合、再外推到目标规模的。两种调度的缩放曲线完全可以有不同的斜率——在实验用的小规模上余弦更好,在真实的训练规模上排序却翻转。论文说余弦「始终」取得更低的最终损失,但这个「始终」覆盖的是他们扫过的规模范围,不必然覆盖 K3 的实际训练规模。
这道题的意义在于:公平比较不是一次到位的操作,它是一个可以无限递归的要求——你消除了一层不公平,下一层立刻浮上来。作者做到的比大多数论文都好,但「完全公平」是达不到的,能做的只是把还固定着的东西如实说出来。
变式:换个角度构造。假设两种调度各自调优后最终损失完全相同,你会推荐用哪一个?(提示:WSD 那段恒定期带来的是什么工程上的自由度,而这个自由度对一个要做「从 8K 到 1M 的四阶段课程」的团队值不值钱。)
答辩:如果我是审稿人
你说架构、数据、训练三类改动「合起来」带来 2.5× 缩放效率增益。可是你同时还换了优化器(Per-Head Muon)、换了负载均衡(QB)、换了注意力(混合 KDA–MLA)、换了激活函数(SiTU-GLU)、还从零训了视觉编码器。这么多变量一起动,2.5× 到底是谁的功劳?你甚至无法排除「其中某一项其实是负贡献、被别的项掩盖了」。
参考防守(先自己组织语言再看)
这一击是成立的,论文在这一节确实没有做逐项拆解,2.5× 是一个打包数字。合理的防守有三层。第一层是限定主张的范围:论文原话是 「these improvements collectively deliver」(这些改进合起来带来),从没声称是某一项的功劳;把打包结果如实报告成打包结果,不算越界。第二层是可行性:完整的因子拆解需要在每个改动上各跑一遍缩放定律,代价是组合爆炸;而且这些改动之间存在真实的耦合——比如混合 KDA–MLA 决定了 NoPE 可行,Per-Head Muon 的收益又依赖于头数变多,拆开单测得到的数字未必能加回去。第三层是论文自己确实做了部分局部证据:Figure 6 就是视觉编码器那一项的独立消融。真正无法防守的是审稿人的最后半句——「某一项是负贡献但被掩盖」确实排除不掉,能说的只是「合起来是正的」。诚实的回应是承认这个空缺,并指出这在前沿模型报告里是常态:一次完整训练太贵,全因子消融在这个规模上没人做得起。读者该做的是把 2.5× 当成一个系统级的数字,而不是任何单项技术的背书。
7.4 Table 1:从 K2 到 K3,究竟改了哪些数字
这张表是整篇报告里信息密度最高的一页,值得逐行看。
| 项 | Kimi K2 | Kimi K3 | Δ |
|---|---|---|---|
| 架构 | MoE | MoE | — |
| 层数 | 61 | 93 | ↑52% |
| 总参数 | 1.04T | 2.78T | ↑167% |
| 激活参数 | 32.6B | 104.2B | ↑220% |
| 隐藏维度 | 7,168 | 7,168 | = |
| Latent MoE 维度 | — | 3584(0.5×) | — |
| 每专家的 MoE 隐藏维度 | 2,048 | 3,072 | ↑50% |
| 路由专家数 | 384 | 896 | ↑133% |
| 每 token 激活的专家数 | 8 | 16 | ↑100% |
| 共享专家数 | 1 | 2 | ↑100% |
| 注意力头数 | 64 | 96 | ↑50% |
| 稠密层数 | 1 | 1 | = |
| 词表大小 | 160K | 160K | = |
| 训练上下文长度 | 128K | 1M | 8× |
| 注意力机制 | MLA | 混合 KDA–MLA | — |
| 激活函数 | SwiGLU | SiTU-GLU | — |
| 注意力层构成 | 61 MLA | 69 KDA + 24 MLA | — |
| MTP 层数 | 1 层 | 1 层 | = |
| ViT 总参数 | - | 401M | - |
| ViT 层数 | - | 27 层 | - |
| ViT 的 patch size | - | 14 | - |
| ViT 注意力头数 | - | 12 | - |
先建立尺度感。2.78T 就是 2.78 万亿个数。如果把每个数印在一张纸上,一张纸厚约 0.1 毫米,2.78 万亿张摞起来大约 27.8 万公里——地球到月球平均是 38.4 万公里,这摞纸能走完地月距离的七成。而这么大一摞里,处理每个 token 时真正参与计算的只有 104.2B,约占 3.7%(这是我们自己算的:104.2 ÷ 2780;K2 那边是 32.6 ÷ 1040 ≈ 3.1%,论文没有列出这两个比例)。这就是混合专家(Mixture of Experts, MoE)的意义:模型可以极大,但每个 token 只走其中很小一部分。
再看哪些没变,这往往比变了的更能说明设计意图。隐藏维度死死钉在 7,168 没动,词表还是 160K,稠密层还是 1 层,MTP 还是 1 层。也就是说 K3 没有把模型变宽,它变的是深(61→93 层)和专家的数量与厚度(384→896 个专家,每专家 2048→3072 维),外加每个 token 激活的专家从 8 个翻倍到 16 个。宽度不动大概率是有意的——隐藏维度牵动着几乎所有模块的形状,动它等于整套超参数重来。
最后是几个只在 K3 出现的新条目:Latent MoE 维度 3584(表里标了「0.5×」,正好是隐藏维度 7168 的一半)、注意力从纯 MLA 变成混合 KDA–MLA、激活函数从 SwiGLU 换成 SiTU-GLU、以及整个 ViT 那四行——这四行在 K2 一栏是空的,因为 K2 不是原生多模态模型。
Table 1 里有一处内部一致性可以校验:「层数 93」 和 「注意力层构成 69 KDA + 24 MLA」。请做这个校验,说明它验证了什么。然后回答第二问:K2 是 61 层 / 61 MLA,K3 是 93 层 / 69 KDA + 24 MLA,K3 的全注意力层(MLA)数量相对 K2 是多了还是少了?这对显存开销意味着什么?
变式:算一算 K3 每个 token 实际激活的参数占总参数的比例(104.2B / 2.78T),再和 K2 的(32.6B / 1.04T)比。K3 的这个比例是升了还是降了?这和「每 token 激活专家数从 8 翻倍到 16」是否矛盾?(提示:分母也在变,而且变得更快。)
7.5 训练配方:从第一天就一起训
论文把 K3 的训练策略叫原生多模态训练策略:语言和视觉从训练一开始就联合优化,而不是事后把一个视觉编码器嫁接到已经训好的语言模型上。在这个范式下,视觉 token 和文本 token 在单一的下一 token 预测目标下交错,让共享骨干从一开始就学统一的多模态表示。这一段和第6章 §2.4 是同一件事的两面:那里讲的是架构上为什么这样设计,这里讲的是训练上怎么执行。
具体的配方论文写得很紧凑,逐项列一下:
- 优化器:Per-Head Muon(第6章),外加 K2 引入的权重裁剪机制(weight-clipping)
- MoE 负载均衡:QB(前面讲分位数均衡的那一章)
- 学习率调度:余弦,带 1% 的线性预热
- 权重衰减:全程 0.1
- 上下文长度:预训练从 8k 开始,后续阶段扩到 64k
「1% 线性预热」的意思是:总训练步数的前 1%,学习率从接近 0 线性升到峰值,然后才开始余弦衰减。为什么要预热?因为训练最开始时参数还是随机的,梯度方向极不可靠,这时候用大学习率往前冲很容易一步走废;先小步试探,等梯度稳定了再放开。
论文把「从训练一开始就联合优化语言和视觉」当成优点来讲。请构造两个具体情形,在那些情形下这个做法明显不如「先训语言模型、再嫁接视觉编码器」。要说清你改动了哪个前提条件。
情形一:训练预算不足。联合训练要求你从随机初始化开始把整个万亿参数模型完整训一遍,没有任何复用余地。如果你的算力只够跑几千 GPU 小时,那么「拿一个现成的语言模型,只训一个转接层和一个小视觉编码器」是唯一可行的路——嫁接式方案的核心优势就是可以站在别人已经付过的成本上。这里改动的前提是「预算充足到可以从零训一遍」,而这正是论文默认成立、但对绝大多数团队不成立的前提。
情形二:模块需要独立迭代。联合训练让视觉表示和语言骨干互相塑造、深度耦合。一旦出现更好的视觉编码器,你没法把它插进来——K3 的骨干读的是 MoonViT-V2 那一套表示,换编码器意味着重训。嫁接式方案里视觉编码器和语言模型之间有一个明确的、窄的接口(转接层),换掉一侧只需要重训接口。这改动的前提是「模型是一次性交付的产品」变成「模型需要长期分模块演进」。
可选的第三个情形:模态数据严重失衡。如果视觉数据只占语料的极小比例,联合训练早期视觉塔拿到的梯度信号非常稀薄,而语言侧已经在快速前进,视觉 token 可能长期被骨干当成噪声;此时先把语言模型训扎实、再用集中的图文数据做对齐,反而更有效率。要注意的是,论文并没有讨论任何一种失败情形——它只陈述了自己选择的范式和理由,上面三条是我们按照该做法的结构推出来的,不是论文的结论。
变式:反过来构造。假设某个团队算力管够,但只关心纯文本能力,一点也不需要看图。此时「从第一天就混入视觉 token 一起训」对纯文本能力是帮助、无害还是拖累?给出你的猜想和理由,并说明论文这几页有没有提供任何数据能验证它。
7.6 长上下文扩展:位置编码、数据、四阶段课程
位置编码:干脆不要
位置编码(positional encoding):注意力机制本身是「无序」的——把输入 token 打乱顺序,纯注意力算出来的结果不变。所以必须额外注入「谁在前谁在后」的信息,这就是位置编码。目前最流行的是 RoPE(旋转位置编码)。麻烦在于:位置编码是按训练时见过的长度学出来的,一旦推理时的序列长过训练长度,模型就会遇到没见过的位置,输出崩坏。业界为此发明了一堆补丁——RoPE 重缩放(RoPE rescaling)、插值(interpolation)等等,本质都是把没见过的位置硬压回见过的范围里。
K3 的做法是:不用任何显式位置嵌入,论文称之为 NoPE(no positional embedding)。位置信息通过 KDA 的循环门控与衰减机制隐式编码。为什么这能行?因为 KDA 是循环式的——它一个 token 一个 token 地更新自己的状态,早来的 token 经过更多次衰减,晚来的经过更少,「谁在前谁在后」天然就写在状态里了,不需要额外贴标签。
论文说的结果是:模型可以直接外推到 100 万 token 的上下文,不需要任何位置编码方面的改动(不需要 RoPE 重缩放,也不需要插值)。
「直接外推」这四个字要读准
很容易把它误读成「K3 从来没在长序列上训过,直接就会了」。不是的——7.6 后半段明明白白写着有一个四阶段课程,窗口一路涨到 1M,模型是在 1M 上训练过的。「直接外推」说的是另一件事:不需要改动位置编码。RoPE 那条路上,就算你愿意在长序列上训,你也得先决定怎么处理超出原始训练长度的位置——是重缩放还是插值,怎么调,改完会不会伤害短序列性能。NoPE 把这一整个决策和它带来的一系列副作用全部省掉了。省掉的是「改造成本」,不是「训练成本」。
一个论文没有回答的问题
K3 是混合 KDA–MLA,Table 1 说有 24 层 MLA。MLA 是全注意力层,它本身没有位置感。既然全模型没有任何显式位置编码,那这 24 层的位置信息只能来自残差流里由 KDA 层写进去的隐式信号。这个安排到底稳不稳、24 这个数字是不是有上限、如果 MLA 层再多会不会破坏外推——论文这几页都没有说明。这是全章最值得追问的技术空白之一。
长上下文数据:先洗,再上采样,最后合成
论文指出一个很现实的问题:自然来源的长文档和视频含大量低质内容——近重复、二进制块、截断的文件、视频片段、无效的机器生成日志。想想也对,网上最长的那些「文档」往往是日志、是自动拼接的垃圾页,真正又长又连贯的东西非常少。
所以有一套专门的清洗流水线:精确去重 + 模糊去重,视频再补上跨帧的感知哈希,配合启发式与分类器质量过滤和结构校验。
洗完之后还有第二个问题:真正长而连贯的文档和视频,相对短文本来说太稀缺了。如果按自然比例采样,长样本会被短样本彻底淹没。所以要上采样(upsample),让长上下文的分布在冷却阶段(cooldown)不被短序列压过去。
但论文紧接着说了这一节最重要的一句话:光有长度不等于长程能力。
为什么「长」不等于「会用长」
设想一本 100 万 token 的小说,模型在上面做下一 token 预测。要猜准下一个词,绝大多数时候只需要看前面几百个词——文风、当前句子的语法、刚提到的人名,这些全在附近。前面 99 万 token 的内容对当前这一步几乎毫无帮助。于是模型完全可以在「很长的数据」上训练很久,同时学到一个只看局部的策略,因为那个策略在这份数据上已经足够好了。论文的原话是,注意力会退化成局部模式。喂长数据只保证了「能装下」,没有保证「必须用」。
补法是合成长上下文数据:精心排列并拼接多模态文档和子任务,使得嵌在里面的任务只有靠关注散落在整个 100 万 token 上下文里的信息才能解决。这是一个很漂亮的思路——不是祈祷模型自己学会用远处的信息,而是构造出必须用才能做对的题。论文说,这样做能在目标尺度上训练注意力机制,防止它退化成局部模式。
渐进式上下文扩展:四阶段课程
K3 支持最长 100 万 token 的上下文窗口。做法是随训练推进逐步扩大窗口,走一个四阶段课程:窗口在预训练期间从 8K 涨到 64K,在冷却阶段从 256K 涨到 1M。
理由是算力。长序列的计算非常贵,如果全程都用 1M 的窗口训练,成本会高到不可接受。把昂贵的长序列计算集中在整体训练预算的一小部分里,课程才经济可行,同时又让模型逐步适应越来越长程的依赖。(论文还提到,让百万 token 训练在 KDA 层上变得可行的「序列维切分」技术放在 §5.1.2 讲,不在本章范围内。)
有人说:「既然 NoPE 让 K3 能直接外推到 1M,那这个四阶段课程就是多余的,直接在 8K 上训完然后在 1M 上用就行了。」请指出这个说法错在哪,并说清「外推」这个词在论文语境里到底指的是什么被省掉了。
变式:如果一个模型用的是 RoPE 而不是 NoPE,但同样跑了这个四阶段课程一路训到 1M,它是不是就不需要 RoPE 重缩放了?(提示:想想「训练时见过的最大位置」和「推理时可能遇到的位置」,以及在 1M 上训练 RoPE 的代价。)
把本章和第6章串起来,回答一个总问题:K3 要在 100 万 token 的上下文里做多模态长时程任务,一共需要几件事同时成立?请至少组织出四条,每条说明「如果缺了它会怎样」,并指出其中哪几条属于论文没有给出证据、只给了陈述的部分。
一、位置信息:NoPE + KDA。缺了它,你就得给 1M 的位置做 RoPE 重缩放或插值,多一整套需要调、还可能伤害短序列的改造。K3 让位置信息由 KDA 的循环门控与衰减隐式携带,序列多长都不存在「没见过的位置」。
二、显存:混合 KDA–MLA,MLA 只有 24 层。全注意力层的 KV 缓存随长度线性增长,层数越多、上下文越长,总量越大。K3 总层数涨到 93,全注意力层反而从 K2 的 61 降到 24,靠状态大小不随长度增长的 KDA 层承担大部分层。缺了它,前面所有努力都会先卡在显存上。(这一条是基于两种机制各自特性的推理,论文没有给出显存数字,也没有说明 69 : 24 这个配比的来源。)
三、数据:清洗 + 上采样 + 合成。自然长文档大量是垃圾,所以要洗;洗完还太稀缺,所以要上采样;上采样完还只是「长」,注意力照样会退化成局部模式,所以要合成出「不看遍全程就做不对」的任务。缺了最后一步,模型能装下 1M 却不会用 1M。
四、成本:四阶段渐进课程。窗口在预训练期间 8K→64K、冷却阶段 256K→1M,把昂贵的长序列计算压缩在总预算的一小部分里。缺了它,全程 1M 训练的账根本算不过来。
五、多模态那一侧:pixel-shuffle(第6章)。顶格 3584×3584 的图切成 patch 是 65536 个,2×2 pixel-shuffle 之后是 16384 个,1M 上下文大约能放 61 张这样的顶格图(1000000 ÷ 16384 ≈ 61,我们自己算的)。缺了这一步,同样的上下文只能放约 15 张,「翻十几屏截图做长时程视觉任务」就无从谈起。
证据强度的分层。有直接图表支撑的只有很少一部分:Figure 6(视觉编码器稳定性)、Figure 7(2.5× 缩放效率)、Table 1(架构数字)。而「合成长上下文数据能防止注意力退化成局部模式」「四阶段课程让模型逐步适应长程依赖」「NoPE 使模型可以直接外推到 1M」这几条,论文这几页只给了陈述,没有给消融、没有给长上下文评测分数。此外四个阶段具体是哪四个、各占多少预算、合成数据占比多少、24 层 MLA 是怎么定下来的,也都没有说明。作为读者,正确的姿态是:机制上的因果链条是自洽且可理解的,但「它确实奏效」在这一章里主要是作者的自述。
变式:假设你要把 K3 的上下文再往上推到 1000 万 token,上面五条里哪一条会最先撑不住?给出你的判断和理由。(提示:逐条问「这一条的代价是随长度线性增长、还是不增长、还是平方增长」。)
答辩:如果我是审稿人
你说 NoPE 让 K3 「直接外推到 1M 而不需要任何位置编码改动」。可你自己的表里写着有 24 层 MLA,那是不带任何位置感的全注意力层。它们的位置信息完全依赖前面 KDA 层写进残差流的隐式信号。这个依赖有多脆弱?如果我把 MLA 的比例调高,外推还成不成立?你有没有做过这个消融?
参考防守(先自己组织语言再看)
这一击直指论文的空白,必须先承认:这几页没有给出任何关于 KDA/MLA 比例与外推能力关系的消融,69 : 24 这个配比是怎么定的也没有说明。能做的防守有三层。第一层是机制上的自洽性:MLA 层读的是残差流,而残差流从第一层起就被 KDA 层持续注入了随位置衰减的信号,所以 MLA 层并非「看不到位置」,它是从表示里读到位置,而不是从额外的编码里读到位置——这在原理上没有断裂。第二层是结构上的保障:69 层 KDA 远多于 24 层 MLA,而且按混合架构的常规做法,KDA 层是穿插分布的,任何一层 MLA 之前都有 KDA 层在为它准备带位置信息的表示;换句话说这个依赖不是靠某一层的单点供给。第三层是经验性的:论文报告模型确实工作在 1M 上下文,这本身是对这套安排可行性的存在性证明——虽然是弱证明。真正无法防守的正是审稿人问的那个假设句:「如果我把 MLA 比例调高会怎样」。这是一个纯粹的开放问题,论文没有给出比例的上界,也没有给出外推能力随比例变化的曲线。诚实的回答是:我们只知道 69 : 24 这一个点能用,不知道边界在哪。
本章小结
数据:四个文本域(网页、代码、数学、知识)加一份大规模视觉语料。文本靠规则启发式、分类器质量打分、去重三道过滤,域采样比例由小模型消融决定;知识和数学还要经过多风格改写并对照源文档做保真度验证。视觉侧的两个关键设计是「绝对 + 归一化 [0,1] 双格式坐标监督」和「大幅扩大程序化多模态数据」(代码与它渲染出的 SVG、3D 资产、网页、游戏、CAD 图纸配对)。
缩放定律:因为架构、数据、训练全变了,最优训练配置也变了,所以重做缩放定律来重调 batch size、学习率、TPP 和模型形状。在留出的分布外验证数据上,Figure 7 显示合起来约 2.5 倍的缩放效率增益——同样的验证损失,K3 只要 K2 约 40% 的算力。方法论上最值得学的是余弦衰减与 WSD 的比较:作者发现两种调度的最优超参数差别很大,用共享超参比较会不公平地偏袒其中一个,于是对每种调度独立做缩放定律搜索,在各自最优点上比,结论是余弦衰减始终损失更低。
架构变化(Table 1):宽度不动(隐藏维仍是 7168,词表仍是 160K),变的是深度(61→93 层)、专家规模(384→896 个路由专家,每专家 2048→3072 维,每 token 激活 8→16 个,共享专家 1→2)、注意力头(64→96),总参数 1.04T→2.78T,激活参数 32.6B→104.2B。注意力从纯 MLA 变成混合 KDA–MLA(69 KDA + 24 MLA,加起来正好 93),激活函数 SwiGLU→SiTU-GLU,训练上下文 128K→1M。新增的 ViT 是 401M 参数、27 层、patch size 14、12 个头。
配方与长上下文:语言和视觉从第一天就在单一的下一 token 预测目标下联合优化;优化器是 Per-Head Muon 加 K2 的权重裁剪,MoE 负载均衡用 QB,余弦调度加 1% 线性预热,权重衰减全程 0.1,上下文从 8k 起步、后续扩到 64k。长上下文靠三件事:NoPE 让位置信息由 KDA 隐式携带、从而不需要 RoPE 重缩放或插值;数据要洗(精确+模糊去重、视频用跨帧感知哈希、质量过滤与结构校验)、要上采样、还要合成出「必须看遍全程才能解决」的任务;四阶段课程把窗口从 8K 抬到 64K(预训练)、再从 256K 抬到 1M(冷却),把昂贵的长序列计算压缩在总预算的一小部分里。
本章的空缺清单:2.5× 没有拆解到单项、图上只标了一处;WSD 对比的搜索空间没有说明;四个阶段各是多少、各占多少预算没说;合成长上下文数据的具体构造和占比没说;24 层 MLA 这个比例怎么定的、外推能力随比例怎么变没说;这几页也没有给任何长上下文评测分数。机制讲得清楚,效果主要靠自述。
Chapter 7 · Pre-training: data, scaling laws, the recipe, and long context
In one sentence: this chapter answers “how exactly was K3 fed up to its size” — what data went in, how the right learning rate and the right number of tokens were computed, which numbers actually changed from K2 to K3, and how a context window of 1 million tokens was pushed open one step at a time.
After this chapter you should be able to
- Say clearly what the “2.5× scaling efficiency gain” actually saves, and compute the concrete number for a given compute budget
- Restate the methodological principle the authors held to when they compared cosine decay with WSD, and carry it over to other comparison experiments
- Read Table 1, point out which dimensions changed from K2 to K3 and which were deliberately left alone, and run a consistency check
- Explain why “upsampling long documents” is not enough to give a model long-range ability, and what the paper uses to fill that gap
- Say clearly how NoPE and KDA relate, and what the sentence “extrapolate directly to 1M” precisely means
7.1 Pre-training data: four text domains, plus a whole visual corpus
K3's pre-training corpus is made of four main text domains — web text, code, maths, knowledge — plus a large-scale visual corpus. The visual part covers a very mixed bag: captions, interleaved image–text documents, OCR, perception, video, and visual coding data(visual coding data). The whole data pipeline was built on Kimi K2 and refined on K2.5.
Text data: filtering, mixing ratios, rephrasing
Every domain goes through a combination of three treatments: filtering by rule-based heuristics(rule-based heuristics), classifier-based quality scoring(classifier-based quality scoring), and deduplication(deduplication). And how large a share each domain finally takes — the paper calls these domain-specific sampling rates(domain-specific sampling rates) — is not settled off the top of anyone's head, it is determined by ablations on small models. This is an engineering habit worth noticing: the real large model is trained once, and if you train it wrong there is no going back, so the recipe has to be worked out first on cheap small models.
There is something more interesting still: K3 carries over K2's rephrasing recipe(rephrasing recipe) and rephrases the corpora of two domains, knowledge and maths. The method has three points — rephrase using prompts that vary in style and perspective, generate autoregressively chunk by chunk, and verify fidelity against the source document.
Why rephrase the corpus, and what happens if you do not
The same piece of knowledge usually exists on the internet in only one or two phrasings. The more uniform the wording a model sees, the more easily it welds “this knowledge” to “this way of saying it”, and it cannot answer once you ask differently. Rephrasing manufactures diversity on purpose: tell the same content again in a different style, from a different angle, so that what the model learns is the content itself rather than the wording. But rephrasing carries a fatal risk — the model rewrites its way into getting the facts wrong, maths above all. So the third step, “verify fidelity against the source document”, is not an optional ornament; it is the precondition for the whole approach being usable at all. As for how the verification is actually done and what the pass rate is, this section of the paper says nothing.
Visual data: two coordinate systems, and “code paired with its rendering”
The visual corpus carries over K2.5's taxonomy and combines open-source datasets with in-house filtering, synthesis and deduplication pipelines. Two details the paper goes out of its way to point out.
First, during training coordinate supervision is given in two formats at once: absolute coordinates and coordinates normalised to [0,1], in order to achieve precise and resolution-robust grounding. “Grounding” here means getting the model to say “whereabouts on the image that button is”. Absolute coordinates (“pixel 640”, say) are precise, but change the resolution and they are all wrong; normalised coordinates (“0.35 of the way across”, say) work across resolutions, but to get back to pixels you have to multiply, and you lose precision. Teach both and the model can be accurate without being fussy about resolution.
Second, the paper says it greatly expanded programmatic multimodal data(programmatic multimodal data): pairing code snippets with the visual result they render to, covering five formats — SVG, 3D assets, web pages, games, CAD drawings. Think back to “vision in the loop” from Chapter 6 — the model has to be able to write code, look at the rendering, and revise the code. That ability does not appear out of nowhere; it needs a vast number of paired samples of “this bit of code looks like this”. This piece of data design and the architecture design in Chapter 6 are a tightly fitted pair.
During training, supervision is given in both absolute and normalised [0,1] coordinates. Suppose you gave only one of them — what goes wrong in each case? Give one concrete failure example.
Variant: if you train only on normalised coordinates but at inference time rescale every image to one fixed resolution before feeding it in, does the problem go away? (Hint: think about what a fixed resolution does to small text on a very large image, then compare with the 3584×3584 ceiling from Chapter 6.)
7.2 Scaling laws: what the 2.5× actually saves
The changes to architecture, data and training taken together define a new model family. The problem is that these changes also change the optimal training configuration. K2's tuned hyperparameters, carried straight over to K3, are not necessarily optimal any more. So the paper ran a dedicated scaling law(scaling law) study to re-tune these key hyperparameters: batch size, learning rate, tokens per parameter (TPP), model shape.
Scaling law: run experiments on a series of small models, measure the relationship between “compute invested” and “final loss”, fit a curve, then use that curve to predict how the large model should be configured. Because training the real large model is expensive enough that you only get to do it once, you have to work the relationship out at cheap scales first.
Tokens per parameter: given how many parameters a model has, how many tokens should it be fed? This ratio decides whether, at a given compute budget, you should spend your money on “making the model bigger” or on “feeding it more data”.
The evaluation is done on held-out out-of-distribution validation data(held-out OOD validation data) — that is, a batch of data never seen during training whose distribution also differs from the training data. Evaluating on out-of-distribution data is there to prevent the illusion that “the model has merely memorised the training distribution”.
The conclusion is that 2.5×: the curves in Figure 7 show that these improvements together bring roughly a 2.5-fold overall scaling efficiency gain.
What “2.5× scaling efficiency” actually means
First, what Figure 7 looks like: the horizontal axis is FLOPs (floating-point operations, i.e. “how much compute was burned”, from 1020 to 1021, on a log scale), the vertical axis is validation loss (lower is better). There are two fitted dashed lines on the plot; the upper one is Kimi K2, the lower one Kimi K3. The K3 line has moved down and to the left as a whole.
That 2.5× is a horizontal arrow drawn in the middle of the figure. Horizontal means: at the same height of validation loss, measure how far apart the two lines are in compute. The answer is 2.5-fold. Put plainly — to reach the same result, K3 needs only about 1/2.5 of K2's compute, that is 40%, a saving of 60%. The other way round, at the same compute budget K3 reaches a loss level K2 cannot get to.
Note the direction of the arrow: it is horizontal, and what it says is “compute saved”, not “the loss is 2.5 times lower”. Loss is a number that hardly ever moves much; nobody would say the loss dropped to 1/2.5.
Read this number carefully
The paper's own words are “approximately 2.5×” (about 2.5-fold), and the arrow is drawn at only one position on the plot. The paper does not say whether this factor stays constant across the whole FLOPs range. If the two fitted lines are strictly parallel, the horizontal gap is the same everywhere and 2.5× is a constant; if they are not parallel, the factor varies with the amount of compute, and off the edge of the plot, at larger compute, it could be bigger or smaller — and the real K3 training run is obviously far beyond the 1021 marked on the plot. On top of that this is the paper's own account, obtained from its own fitted curves, and “2.5×” is the combined effect of the architecture, data and training changes bundled together; this section of the paper does not break out how much each part contributed.
Suppose K2 needs 1×1021 FLOPs to reach some validation loss. With the paper's 2.5× scaling efficiency gain, how many FLOPs does K3 need to reach the same validation loss? What fraction is saved? And the other way round: if K3 also spends the full 1×1021, can you work out what its loss is?
Variant: if some team reports “our new model has a 10× scaling efficiency gain”, but their two curves are clearly not parallel and they measured the factor only at the smallest compute point, how would you challenge that number?
7.3 Cosine decay vs WSD: a lesson in “fair comparison”
This short passage is where the methodological value of the whole chapter is highest, and it is worth reading slowly.
Learning rate schedule: the learning rate is “how far you move along the gradient direction at each step”. It is usually not a constant but a curve that changes as training proceeds, and that curve is the schedule.
Cosine decay: the learning rate starts at its peak and falls smoothly all the way to a minimum along the shape of a cosine curve. WSD: three segments — warm up first, then hold constant for a long stretch, then decay quickly over a final stretch. WSD's selling point is that constant stretch, because it means you can branch off a decay leg at any moment to “finish” training, instead of having to decide the total number of steps at the start.
K3's scaling law study consistently prefers cosine decay, so the paper makes cosine decay the default schedule. But the point is not this conclusion, it is how they arrived at it.
The authors compare the two under a fixed minimum learning rate. They acknowledge that earlier work has reported WSD matching or even beating cosine decay. So why is the conclusion the opposite here? The explanation the authors give is an observation: the optimal hyperparameters of the two schedules differ a lot — even at the same model scale and training token budget, their respective optimal peak learning rates and optimal batch sizes are very different.
Following that observation, a very solid inference comes out: comparing two schedules with one shared set of hyperparameters may only show that the set happens to suit one of them, and unfairly favour it. So what the authors do is — run a separate scaling law search for each schedule, letting each find its own optimum; comparing again at their respective optima, the result is that cosine decay consistently reaches the lower final loss.
A common misreading
Many people read this passage as “the paper proved WSD is worse than cosine decay”. The accurate statement is far weaker: within the K3 team's own search space, their own fixed minimum learning rate setting, their own model scale and token budget, cosine decay reached the lower final loss after each was tuned. The paper itself explicitly notes that “earlier work has reported WSD matching or even beating cosine decay”, and does not say that work was wrong — the explanation it offers is that mismatched hyperparameters can lead to different conclusions, and that blade cuts both ways: it can just as well be pointed at K3's own experiments.
Carry this methodology elsewhere. Some paper says “our new activation function B is better than the commonly used A”. The setup is: same model, same data, the same set of hyperparameters (learning rate, weight decay and warmup fraction all copied from A's usual configuration), with only the activation function swapped from A to B, and B comes out with the lower loss. Is there a problem with this experiment? If so, how should it be fixed?
Variant: if the authors say “we could not afford a full hyperparameter search for every method”, can you offer a cheaper compromise that is still fairer than “one shared set of hyperparameters”?
The authors already ran an independent scaling law search for each schedule. Construct a concrete situation in which the comparison is still unfair. Give at least two different routes, and say which sentence of the paper's own text you are using.
Route one: the search dimensions are not equivalent. The paper says the two schedules have “very different optimal peak learning rates and batch sizes”, which reads as though the search space is exactly those two dimensions. But WSD has a key hyperparameter that cosine decay does not have at all — the fraction of the total steps taken by the decay segment (equivalently, how long the constant stretch is). That fraction has an enormous effect on WSD's final loss. If it was fixed at some default and never entered the search, then “an independent search for each schedule” is only a search over one slice of WSD, and WSD may still not have been tuned to its optimum. These pages of the paper do not say which dimensions the search space contained; that is a real information gap.
Route two: the shared constraint favours one side. The text says outright that the comparison was “under a fixed minimum learning rate”. The minimum learning rate does not mean the same thing to the two schedules: cosine decay approaches that floor smoothly, whereas WSD slams down to it over the final stretch, and the shape of the decay segment is tightly coupled to its endpoint. Fixing both to the same value quite likely fits one of them better. Here the act of “fixing” is itself a re-run of the very mistake the authors criticise, only with a different variable.
Route three: the extrapolation goes in different directions. A scaling law is fitted at small scale and then extrapolated to the target scale. The scaling curves of the two schedules can perfectly well have different slopes — cosine better at the small scales used in the experiments, and the ordering flipped at the real training scale. The paper says cosine “consistently” reaches the lower final loss, but that “consistently” covers the range of scales they swept, and does not necessarily cover K3's actual training scale.
The point of this question is that fair comparison is not a one-shot operation, it is a demand that can recurse indefinitely — you remove one layer of unfairness and the next layer surfaces immediately. The authors did better than most papers, but “completely fair” is unreachable; all anyone can do is state plainly what is still being held fixed.
Variant: construct it from the other side. Suppose the two schedules reach exactly the same final loss after each is tuned — which would you recommend? (Hint: what engineering freedom does WSD's constant stretch buy, and is that freedom worth anything to a team that has to run “a four-stage curriculum from 8K to 1M”?)
Defend it: if I were a reviewer
You say the three classes of change — architecture, data, training — “together” deliver a 2.5× scaling efficiency gain. But you also swapped the optimiser (Per-Head Muon), swapped the load balancing (QB), swapped the attention (hybrid KDA–MLA), swapped the activation function (SiTU-GLU), and trained a vision encoder from scratch. With that many variables moving at once, whose credit is the 2.5×? You cannot even rule out that one of those items is a negative contribution masked by the others.
A reference defence (put it in your own words first, then look)
This blow lands. In this section the paper really does not break the effect down item by item; 2.5× is a bundled number. A reasonable defence has three layers. The first is to limit the scope of the claim: the paper's own words are “these improvements collectively deliver”, and it never claims the credit belongs to any one item; reporting a bundled result honestly as a bundled result is not overreach. The second is feasibility: a full factorial breakdown would need a scaling law run for every single change, which is combinatorial explosion; and there is real coupling between these changes — hybrid KDA–MLA is what makes NoPE viable, and the gain from Per-Head Muon depends on there being more heads, so numbers from testing them separately would not necessarily add back up. The third is that the paper does supply some partial local evidence: Figure 6 is an independent ablation of the vision encoder item. What genuinely cannot be defended is the reviewer's last half-sentence — “one item is a negative contribution but is masked” really cannot be ruled out, and all you can say is “together it is positive”. The honest response is to admit the gap and point out that this is the norm in frontier model reports: one full training run is too expensive, and nobody can afford a full factorial ablation at this scale. What a reader should do is treat 2.5× as a system-level number, not as an endorsement of any individual technique.
7.4 Table 1: which numbers actually changed from K2 to K3
This table is the densest page of information in the whole report, and it is worth going through row by row.
| Item | Kimi K2 | Kimi K3 | Δ |
|---|---|---|---|
| Architecture | MoE | MoE | — |
| Layers | 61 | 93 | ↑52% |
| Total parameters | 1.04T | 2.78T | ↑167% |
| Activated parameters | 32.6B | 104.2B | ↑220% |
| Hidden dimension | 7,168 | 7,168 | = |
| Latent MoE dimension | — | 3584 (0.5×) | — |
| MoE hidden dimension per expert | 2,048 | 3,072 | ↑50% |
| Routed experts | 384 | 896 | ↑133% |
| Experts activated per token | 8 | 16 | ↑100% |
| Shared experts | 1 | 2 | ↑100% |
| Attention heads | 64 | 96 | ↑50% |
| Dense layers | 1 | 1 | = |
| Vocabulary size | 160K | 160K | = |
| Training context length | 128K | 1M | 8× |
| Attention mechanism | MLA | Hybrid KDA–MLA | — |
| Activation function | SwiGLU | SiTU-GLU | — |
| Attention layer make-up | 61 MLA | 69 KDA + 24 MLA | — |
| MTP layers | 1 layer | 1 layer | = |
| ViT total parameters | - | 401M | - |
| ViT layers | - | 27 layers | - |
| ViT patch size | - | 14 | - |
| ViT attention heads | - | 12 | - |
First get a sense of scale. 2.78T means 2.78 trillion numbers. If you printed each number on a sheet of paper, and a sheet is about 0.1 mm thick, a stack of 2.78 trillion sheets would be roughly 278,000 kilometres — the average Earth-to-Moon distance is 384,000 kilometres, so the stack covers seven tenths of the way there. And out of a stack that large, only 104.2B actually take part in the computation for each token, about 3.7% (this is our own arithmetic: 104.2 ÷ 2780; on the K2 side it is 32.6 ÷ 1040 ≈ 3.1%, and the paper lists neither ratio). That is what Mixture of Experts(Mixture of Experts, MoE) is for: the model can be enormous, but each token only walks through a very small part of it.
Now look at what did not change, which often says more about design intent than what did. The hidden dimension is nailed to 7,168 and does not budge, the vocabulary is still 160K, dense layers are still 1, MTP is still 1. That is, K3 did not make the model wider; what it changed is depth (61→93 layers) and the number and thickness of the experts (384→896 experts, 2048→3072 dimensions per expert), plus the experts activated per token doubling from 8 to 16. Leaving the width alone is very probably deliberate — the hidden dimension pulls on the shape of almost every module, and touching it means redoing the whole hyperparameter set.
Finally, a few entries that appear only in K3: Latent MoE dimension 3584 (marked “0.5×” in the table, exactly half of the hidden dimension 7168), attention going from pure MLA to hybrid KDA–MLA, the activation function going from SwiGLU to SiTU-GLU, and those four ViT rows — blank in the K2 column, because K2 is not a natively multimodal model.
There is one internal consistency check available in Table 1: “Layers 93” and “Attention layer make-up 69 KDA + 24 MLA”. Do the check, and say what it verifies. Then a second question: K2 is 61 layers / 61 MLA, K3 is 93 layers / 69 KDA + 24 MLA — relative to K2, does K3 have more full attention layers (MLA) or fewer? What does that mean for GPU memory?
Variant: work out what fraction of the total parameters K3 actually activates per token (104.2B / 2.78T), and compare it with K2's (32.6B / 1.04T). Did the fraction go up or down for K3? Does that contradict “experts activated per token doubling from 8 to 16”? (Hint: the denominator is changing too, and changing faster.)
7.5 The training recipe: trained together from day one
The paper calls K3's training strategy a natively multimodal training strategy: language and vision are optimised jointly from the very start of training, rather than grafting a vision encoder onto an already-trained language model afterwards. Under this paradigm, visual tokens and text tokens are interleaved under a single next-token prediction objective, so that the shared backbone learns a unified multimodal representation from the beginning. This passage and §2.4 in Chapter 6 are two sides of the same thing: there it was why the architecture is built this way, here it is how the training carries it out.
The paper writes the actual recipe very compactly; here it is item by item:
- Optimiser: Per-Head Muon (Chapter 6), plus the weight-clipping mechanism(weight-clipping) introduced in K2
- MoE load balancing: QB (the chapter on Quantile Balancing)
- Learning rate schedule: cosine, with 1% linear warmup
- Weight decay: 0.1 throughout
- Context length: pre-training starts at 8k, later stages extend to 64k
“1% linear warmup” means: over the first 1% of the total training steps, the learning rate rises linearly from near 0 to its peak, and only then does cosine decay begin. Why warm up at all? Because at the very start of training the parameters are still random and the gradient direction is extremely unreliable, and charging ahead with a large learning rate at that point can easily wreck things in a single step. Take small probing steps first, and open up once the gradients have settled.
The paper presents “optimising language and vision jointly from the very start of training” as a strength. Construct two concrete situations in which this approach is clearly worse than “train the language model first, then graft on a vision encoder”. Be clear about which premise you are changing.
Situation one: the training budget is not enough. Joint training requires you to train the entire trillion-parameter model once through from random initialisation, with nothing to reuse. If your compute only stretches to a few thousand GPU-hours, then “take an off-the-shelf language model and train only an adapter layer and a small vision encoder” is the only viable route — the core advantage of the grafting approach is that you can stand on costs someone else has already paid. The premise being changed here is “the budget is ample enough to train once from scratch”, which the paper takes for granted and which does not hold for the overwhelming majority of teams.
Situation two: the modules need to iterate independently. Joint training lets the visual representation and the language backbone shape each other, deeply coupled. Once a better vision encoder appears, you cannot plug it in — K3's backbone reads the representations of MoonViT-V2, and swapping the encoder means retraining. In the grafting approach there is a clear, narrow interface between the vision encoder and the language model (the adapter layer), and replacing one side only requires retraining the interface. The premise being changed here is “the model is a one-off delivered product” turning into “the model has to evolve module by module over a long period”.
An optional third situation: severely imbalanced modality data. If visual data makes up only a tiny share of the corpus, the vision tower gets a very thin gradient signal early in joint training while the language side is already moving fast, and visual tokens may be treated as noise by the backbone for a long time; here it is more efficient to train the language model solidly first and then align with concentrated image–text data. Note that the paper does not discuss any failure case at all — it states only the paradigm it chose and its reasons; the three above are worked out by us from the structure of the approach, not conclusions of the paper.
Variant: construct it the other way round. Suppose a team has compute to spare but cares only about pure text ability and has no need to look at images at all. Is “mixing in visual tokens from day one” a help to pure text ability, harmless, or a drag on it? Give your guess and your reasons, and say whether these pages of the paper offer any data that could test it.
7.6 Extending long context: positional encoding, data, a four-stage curriculum
Positional encoding: just do without it
Positional encoding: the attention mechanism is in itself “orderless” — shuffle the input tokens and what pure attention computes does not change. So information about “who comes first and who comes later” has to be injected separately, and that is positional encoding. The most popular one at the moment is RoPE (rotary position embedding). The trouble is this: positional encoding is learned from the lengths seen during training, and once the sequence at inference time is longer than the training length, the model runs into positions it has never seen and the output falls apart. The field has invented a pile of patches for this — RoPE rescaling(RoPE rescaling), interpolation(interpolation) and so on, all of which amount to squeezing the unseen positions back into the range that was seen.
What K3 does is: use no explicit positional embedding at all, which the paper calls NoPE. Positional information is encoded implicitly through KDA's recurrent gating and decay. Why does that work? Because KDA is recurrent — it updates its own state one token at a time, so a token that arrived early has been through more rounds of decay and one that arrived late through fewer, and “who comes first and who comes later” is written into the state naturally, with no extra label needed.
The result the paper states is: the model can extrapolate directly to a 1 million token context, with no changes at all on the positional-encoding side (no RoPE rescaling, no interpolation).
Read the words “extrapolate directly” precisely
It is very easy to misread this as “K3 never trained on long sequences at all and simply knew how”. It is not that — the second half of 7.6 says plainly that there is a four-stage curriculum with the window climbing all the way to 1M, and the model was trained at 1M. “Extrapolate directly” is saying something else: no change to the positional encoding is needed. On the RoPE road, even if you are willing to train on long sequences, you still have to decide first how to handle positions beyond the original training length — rescale or interpolate, how to tune it, whether the change will hurt short-sequence performance. NoPE removes that whole decision and the string of side effects that come with it. What is removed is the “retrofit cost”, not the “training cost”.
A question the paper does not answer
K3 is a hybrid KDA–MLA, and Table 1 says there are 24 MLA layers. MLA is a full-attention layer, and it has no sense of position of its own. Since the model as a whole has no explicit positional encoding anywhere, the positional information for those 24 layers can only come from the implicit signal the KDA layers write into the residual stream. Whether this arrangement is really stable, whether the number 24 is up against some ceiling, whether extrapolation would break if there were more MLA layers — these pages of the paper say nothing about any of it. This is one of the technical gaps in the whole chapter most worth pressing on.
Long-context data: clean it first, then upsample, then synthesise
The paper points at a very practical problem: long documents and videos from natural sources contain a great deal of low-quality content — near-duplicates, binary blobs, truncated files, video fragments, useless machine-generated logs. And that makes sense: the longest “documents” on the web tend to be logs, or junk pages stitched together automatically, and things that are genuinely long and coherent are very rare.
So there is a dedicated cleaning pipeline: exact deduplication + fuzzy deduplication, with cross-frame perceptual hashing added for video, together with heuristic and classifier quality filtering and structural validation.
After the cleaning there is a second problem: documents and videos that are genuinely long and coherent are far too scarce next to short text. Sample at the natural proportions and the long samples are completely drowned by the short ones. So you upsample(upsample), so that the long-context distribution does not get crushed by short sequences during the cooldown phase(cooldown).
But the paper then says the most important sentence in this section: length alone is not long-range capability.
Why “long” is not the same as “able to use long”
Picture a 1 million token novel, with the model doing next-token prediction on it. To guess the next word right, most of the time you only need to look at the previous few hundred words — the style, the grammar of the current sentence, the name just mentioned, all of it nearby. The other 990,000 tokens are of almost no help for this one step. So the model can perfectly well train for a long time on “very long data” and at the same time learn a strategy that only looks locally, because on this data that strategy is already good enough. The paper's own words are that attention will degenerate into local patterns. Feeding it long data only guarantees “it fits”, it does not guarantee “it must be used”.
The fix is synthesising long-context data: carefully arranging and concatenating multimodal documents and sub-tasks, so that the tasks embedded inside can only be solved by attending to information scattered across the whole 1 million token context. This is a rather elegant idea — instead of praying that the model works out on its own how to use distant information, you construct problems that can only be got right by using it. The paper says this trains the attention mechanism at the target scale and stops it degenerating into local patterns.
Progressive context extension: the four-stage curriculum
K3 supports a context window of up to 1 million tokens. The way it gets there is to enlarge the window step by step as training proceeds, along a four-stage curriculum: the window goes from 8K to 64K during pre-training, and from 256K to 1M during the cooldown phase.
The reason is compute. Long sequences are very expensive to compute, and training with a 1M window the whole way would cost an unacceptable amount. Concentrating the expensive long-sequence computation in a small part of the overall training budget is what makes the curriculum economically feasible, while still letting the model adapt gradually to longer and longer dependencies. (The paper also mentions that the “sequence-dimension partitioning” technique which makes million-token training feasible on the KDA layers is covered in §5.1.2, outside the scope of this chapter.)
Someone says: “Since NoPE lets K3 extrapolate directly to 1M, this four-stage curriculum is redundant — just train at 8K and then use it at 1M.” Point out where this claim is wrong, and make clear what the word “extrapolation”, in the paper's context, is actually saying gets removed.
Variant: if a model uses RoPE rather than NoPE but runs the same four-stage curriculum all the way up to 1M, does it then not need RoPE rescaling? (Hint: think about “the largest position seen during training” versus “the positions that may turn up at inference”, and about what it costs to train RoPE at 1M.)
Tie this chapter together with Chapter 6 and answer one overall question: for K3 to do multimodal long-horizon tasks in a 1 million token context, how many things have to hold at the same time? Organise at least four of them, say for each what would happen if it were missing, and point out which of them are parts where the paper gives no evidence, only a statement.
One, positional information: NoPE + KDA. Without it you would have to do RoPE rescaling or interpolation for 1M positions, a whole extra retrofit that needs tuning and may hurt short sequences. K3 has positional information carried implicitly by KDA's recurrent gating and decay, so however long the sequence gets there is no such thing as “a position never seen”.
Two, memory: hybrid KDA–MLA, with only 24 MLA layers. The KV cache of a full-attention layer grows linearly with length, so the more such layers and the longer the context, the larger the total. K3's total layer count rises to 93, yet the full-attention layers actually drop from K2's 61 to 24, with most of the layers carried by KDA layers whose state size does not grow with length. Without it, everything above would hit the memory wall first. (This item is an inference from the properties of the two mechanisms; the paper gives no memory figures, and does not explain where the 69 : 24 ratio comes from.)
Three, data: cleaning + upsampling + synthesis. Natural long documents are largely junk, so they have to be cleaned; after cleaning they are still too scarce, so they have to be upsampled; after upsampling they are still only “long”, and attention will degenerate into local patterns all the same, so you have to synthesise tasks that cannot be got right without reading the whole span. Without that last step, the model can hold 1M but will not use 1M.
Four, cost: the four-stage progressive curriculum. The window goes 8K→64K during pre-training and 256K→1M during cooldown, compressing the expensive long-sequence computation into a small part of the total budget. Without it, the bill for training at 1M the whole way simply does not add up.
Five, the multimodal side: pixel-shuffle (Chapter 6). A maximum-resolution 3584×3584 image cut into patches gives 65536 of them, and after 2×2 pixel-shuffle 16384, so a 1M context holds about 61 such maximum-resolution images (1000000 ÷ 16384 ≈ 61, we worked this out ourselves). Without this step the same context holds only about 15, and “flipping through a dozen-odd screens of screenshots to do a long-horizon visual task” would be out of the question.
Grading the strength of the evidence. Only a very small part of this has direct figures or tables behind it: Figure 6 (visual encoder stability), Figure 7 (2.5× scaling efficiency), Table 1 (the architecture numbers). Whereas for “synthetic long-context data prevents attention degenerating into local patterns”, “the four-stage curriculum lets the model adapt gradually to long-range dependencies” and “NoPE lets the model extrapolate directly to 1M”, these pages of the paper give statements only, no ablations, no long-context evaluation scores. On top of that, exactly which four stages there are, how much of the budget each takes, what share the synthetic data makes up, and how the 24 MLA layers were settled on, are all left unexplained. As a reader, the right posture is: the causal chain at the mechanism level is self-consistent and understandable, but “it really does work” is, in this chapter, mostly the authors' own account.
Variant: suppose you want to push K3's context further up, to 10 million tokens. Which of the five above gives out first? Give your judgement and your reasons. (Hint: ask of each one “does the cost of this item grow linearly with length, not grow at all, or grow quadratically”.)
Defend it: if I were a reviewer
You say NoPE lets K3 “extrapolate directly to 1M with no changes to positional encoding at all”. But your own table says there are 24 MLA layers, and those are full-attention layers with no sense of position whatsoever. Their positional information depends entirely on the implicit signal the preceding KDA layers write into the residual stream. How fragile is that dependence? If I raise the proportion of MLA, does the extrapolation still hold? Have you run that ablation?
A reference defence (put it in your own words first)
This blow lands straight on a gap in the paper and has to be conceded first: these pages give no ablation at all on the relation between the KDA/MLA ratio and extrapolation ability, and never explain how the 69 : 24 ratio was settled. The defence that can be made has three layers. The first is consistency at the mechanism level: MLA layers read the residual stream, and from the very first layer the residual stream has been continuously injected by KDA layers with a signal that decays with position, so an MLA layer is not “blind to position” — it reads position out of the representation rather than out of an extra encoding, and there is no break in principle here. The second is a structural safeguard: 69 KDA layers far outnumber 24 MLA layers, and by the usual practice for hybrid architectures the KDA layers are interleaved throughout, so before any MLA layer there are KDA layers preparing a representation carrying positional information for it; in other words the dependence does not rest on a single point of supply. The third is empirical: the paper reports that the model does work at a 1M context, which is in itself an existence proof that the arrangement is viable — a weak proof, but a proof. What genuinely cannot be defended is exactly the hypothetical the reviewer asks about: “what would happen if I raised the MLA proportion”. That is a purely open question; the paper gives no upper bound on the ratio, and no curve of extrapolation ability against the ratio. The honest answer is: we only know that this one point, 69 : 24, works; we do not know where the boundary is.
Chapter summary
Data: four text domains (web, code, maths, knowledge) plus one large-scale visual corpus. Text passes three filters — rule-based heuristics, classifier quality scoring, deduplication — and the domain sampling proportions were decided by ablations on small models; knowledge and maths also go through multi-style rewriting with a fidelity check against the source document. The two key designs on the vision side are “dual-format coordinate supervision, absolute + normalised [0,1]” and “a large expansion of programmatic multimodal data” (code paired with the SVGs, 3D assets, web pages, games and CAD drawings it renders).
Scaling laws: because the architecture, the data and the training all changed, the optimal training configuration changed too, so the scaling laws were redone to retune batch size, learning rate, TPP and model shape. On held-out out-of-distribution validation data, Figure 7 shows a combined scaling efficiency gain of about 2.5× — for the same validation loss, K3 needs only about 40% of K2's compute. What is most worth learning methodologically is the comparison of cosine decay against WSD: the authors found that the optimal hyperparameters for the two schedules differ a great deal, and that comparing them under shared hyperparameters would unfairly favour one of them, so they ran the scaling-law search independently for each schedule and compared them at their respective optima; the conclusion is that cosine decay is consistently lower in loss.
Architecture changes (Table 1): the width does not move (hidden dimension still 7168, vocabulary still 160K); what changes is the depth (61→93 layers), the expert scale (384→896 routed experts, 2048→3072 dimensions per expert, 8→16 activated per token, shared experts 1→2) and the attention heads (64→96); total parameters 1.04T→2.78T, activated parameters 32.6B→104.2B. Attention goes from pure MLA to hybrid KDA–MLA (69 KDA + 24 MLA, which adds up to exactly 93), the activation function SwiGLU→SiTU-GLU, the training context 128K→1M. The newly added ViT is 401M parameters, 27 layers, patch size 14, 12 heads.
The recipe and long context: language and vision are optimised jointly from day one under a single next-token prediction objective; the optimiser is Per-Head Muon plus K2's weight clipping, MoE load balancing uses QB, a cosine schedule with 1% linear warmup, weight decay 0.1 throughout, context starting at 8k and later extended to 64k. Long context rests on three things: NoPE has positional information carried implicitly by KDA, so no RoPE rescaling or interpolation is needed; the data has to be cleaned (exact + fuzzy deduplication, cross-frame perceptual hashing for video, quality filtering and structural validation), has to be upsampled, and then has to be synthesised into tasks that “can only be solved by reading the whole span”; the four-stage curriculum lifts the window from 8K to 64K (pre-training) and then from 256K to 1M (cooldown), compressing the expensive long-sequence computation into a small part of the total budget.
This chapter's list of gaps: the 2.5× is never broken down into individual items, and only one point is marked on the figure; the search space for the WSD comparison is not described; what each of the four stages is and how much of the budget each takes is not said; the specific construction of the synthetic long-context data and its share is not said; how the ratio of 24 MLA layers was settled, and how extrapolation ability varies with the ratio, is not said; these pages also give no long-context evaluation scores at all. The mechanisms are explained clearly; that they work is mostly the authors' own account.
第8章 后训练:从模仿到自我进化,再压回一个模型
一句话导语:预训练让模型学会「说话」,后训练让它学会「做事」——而 K3 的做法是先教会它模仿(SFT),再让它在三个领域、三档思考长度上自己练成九个专家(RL),最后把九个专家压回同一个模型(MOPD),并且从头到尾都按「上线后要用的那个精度」来训练。
学完这一章你应该能做到
- 用自己的话说清 SFT、RL、MOPD 三个阶段各解决什么问题,为什么顺序不能换
- 解释「部分 rollout」为什么会制造数据陈旧,以及为什么「把更新约束在局部邻域」能救它
- 给定初始预算 b0 和乘子 τ,算出一条轨迹会不会被判 −1
- 逐符号读懂式 15 的逐 token 蒸馏奖励,并判断它对某个 token 是正还是负
- 说出 Σ min(p,q) 的几何含义,并证明两个分布相同时它等于 1
- 指出 MXFP4 量化、草稿模型初始化技巧各自的适用边界与论文没说清的地方
8.1 全局图:为什么后训练要分三段走
先想一个问题。预训练结束时,模型见过海量文本,能把句子接得很顺,但它并不知道「人类现在要它干什么」,更不知道「该在什么时候停下来去调用一个工具、看看返回结果再继续」。这就像一个把整座图书馆背下来的人——知识满分,但你让他去帮你订张机票,他会开始背《航空运输管理条例》。
不做后训练会怎样
会得到一个「知识渊博但不会办事」的模型。所有关于「听懂指令」「按格式回答」「该动手时动手」的能力,都不是靠多读书能自动长出来的,必须专门训。论文把这套流程叫后训练(Post-Training)。
论文原文把 K3 的后训练概括成一个三阶段范式:先用监督微调(Supervised Fine-Tuning, SFT)初始化基线智能体能力;再用强化学习(Reinforcement Learning, RL)在不同的推理努力等级上培养出各领域的专家;最后用多教师在策略蒸馏(Multi-Teacher On-Policy Distillation, MOPD)把这些领域专属的策略合并回单一模型。
打个比方
SFT 像是让学徒照着师傅的录像模仿——先把动作做对,哪怕不懂为什么。RL 像是让他自己上场比赛,赢了加分输了扣分,慢慢练出师傅都没有的手感;而且不是练一个全能选手,是分头练出九个「专项冠军」。MOPD 则像是让这九个冠军轮流带同一个新人,最后新人一个人就能顶九个。
类比失效处:真实的九个专家不是九个人,而是同一个基座模型的九份不同权重;而且 MOPD 的「带徒弟」不是让学生看教师的完整答案,而是学生自己写、教师逐个 token 打分。这一点后面 8.7 会讲透。
注意这个顺序不是随便排的。RL 需要一个「已经会办事」的起点,否则它在巨大的动作空间里瞎撞、几乎拿不到任何正奖励,学不动;这就是 SFT 被称为冷启动(cold-start)的原因。而 MOPD 必须放最后,因为它要蒸馏的对象——九个专家——得先被 RL 练出来才存在。
有人提议:既然 RL 最能提升能力,干脆跳过 SFT,让预训练模型直接进 RL,省一大笔标注钱。按论文的说法,这个方案的主要风险是什么?
变式:如果任务改成「回答一道单选题」(一步就出结果,四个选项猜中概率 25%),跳过 SFT 直接 RL 还会有同样的问题吗?为什么这说明「SFT 是否必需」取决于任务的时程长度?
8.2 SFT:让模型先学会照着做
监督微调(Supervised Fine-Tuning, SFT):给模型看大量「问题 + 标准回答过程」的配对,让它把标准回答的每一个 token 的概率调高。本质上就是「照着抄」,抄到形成肌肉记忆。
K3 的 SFT 有一个很特别的地方:训练数据里的轨迹(trajectory,指一条完整的「思考—调用工具—看返回—再思考」的记录)不是纯手写的,而是用先前 Kimi 系列的领域专用模型合成出来的,然后再过多阶段验证和人在环标注(human-in-the-loop annotation)。
为什么要用旧模型合成数据
一条复杂智能体轨迹可能有几十步工具调用、上万个 token。让人从零手写,成本高到不可行,而且人写的「思考过程」往往和模型真实的思考方式对不上,学起来反而别扭。用已有的领域专用模型批量生产、再让人筛选和修正,是把人力用在「判断对错」而不是「从零创作」上——判断比创作便宜得多。
论文说这些步骤「共同产出了一个大规模指令数据集,赋予 Kimi K3 自适应推理、精确工具调用,以及在长时程智能体场景中稳健执行的能力」。所有数据都用他们基于 XTML(eXtensible Token Markup Language,可扩展 token 标记语言)的对话模板序列化,细节在论文附录 F,本书第14章会讲。
还有一个容易被跳过但很关键的细节:量化感知训练(Quantization-Aware Training, QAT)从 SFT 阶段就开始应用了,权重用 MXFP4、激活用 MXFP8。也就是说,K3 不是「先训练好一个高精度模型,最后再压缩」,而是从后训练第一天起就按压缩后的样子训练。这个设计的深意留到 8.8 讲。
常见做法是训练完全精度模型后再做「训练后量化」(把权重直接四舍五入到低精度)。K3 却从 SFT 就开始 QAT。请解释:为什么「最后再压」这件事,在有 RL 的流程里比在纯 SFT 的流程里更危险?
变式:如果一家公司只做 SFT、不做 RL,事后量化和 QAT 的差距会变小还是变大?如果它做 RL 但部署时用的是全精度,QAT 还有意义吗?
8.3 RL:不是训一个全才,而是训九个专家
拿到冷启动模型后,论文说「RL 对于解锁更高阶的推理与执行能力至关重要」。但 K3 的 RL 组织方式和很多人想的不一样。
直觉做法是:有多少类任务,就训多少个专门的 RL 模型。K3 没有这么做。论文原话是「不是为单个任务训练专门的 RL 模型,而是把 RL 扩展到三大领域,每个领域涵盖广泛的子任务,并在每个推理努力等级上为每个领域训练单一专家」。三大领域是:
| 领域 | 覆盖的子任务 |
|---|---|
| 通用任务(general tasks) | 通用体验、视觉、推理、忠实性、搜索能力、知识工作任务 |
| 通用智能体(general agents) | 长时程助理任务、深度研究、段落级写作 |
| 编码智能体(coding agents) | 软件工程(SWE)、编码体验、内核任务、网页开发 |
再把这三个领域专家与三个推理努力等级(reasoning effort level) {low, high, max} 相交叉,得到总共九个专家模型。所谓推理努力等级,通俗说就是「允许它想多久」——low 是快答,max 是深思。
为什么按领域切,而不按任务切
按任务切会得到几十个模型,每个模型的数据都很少,而且任务之间的共性(比如「怎么读工具返回值」)要被重复学几十遍。按领域切,一个专家能吃到整个领域的数据,共性能力互相增强;同时又不至于像「一个模型打天下」那样,让编码任务的奖励和写作任务的奖励在同一组权重上互相拉扯。这是粒度上的折中。
Figure 8:步数随算力增长,说明了什么
论文 Figure 8 画了八个面板(编码体验、通用工具使用、网页开发、智能体搜索、专业工作流、办公交付物、智能体图表理解、智能体视觉谜题),横轴是 RL FLOPs(投入 RL 的计算量),每张图上有两条线:实线是评测得分,虚线是平均助手步数(average assistant steps,即模型平均调用了多少步工具)。
论文对这张图的总结是:随着 RL FLOPs 扩大,工具调用步数持续上升,同时伴随模型整体能力的全面提升。
常见误解
很多人看到「步数变多」第一反应是「变啰嗦了、效率变差了」。但在这里恰恰相反。步数是模型自己选择的——没人规定它必须调几次工具。步数上升的同时得分也在上升,说明模型学到的是「这个问题值得我再查一次/再验证一遍」,即愿意为难题付出更多真实行动。这和「废话变多」是两回事:废话不带来新信息,多一步工具调用带来的是外部世界的新证据。
读的时候要小心
Figure 8 的纵轴没有标注具体数值刻度,横轴 RL FLOPs 也没给绝对值,这些都是论文自述的训练期内部曲线,不是可复现的公开评测。「步数增长伴随能力提升」是一个相关性观察,论文没有做「人为限制步数会掉多少分」的对照实验,所以不能据此断定「步数增长是能力提升的原因」。
某团队看到 Figure 8 后决定:直接在奖励里加一项「每多调用一次工具 +0.1 分」,希望复现「步数增长带来能力提升」。请预测会发生什么,并说明这个推理错在哪。
变式:如果反过来,在奖励里加「每多一步 −0.1 分」来强行提效,又会出什么问题?把这两种做法和论文实际采用的「超过 τ·b0(x) 才判 −1」的硬阈值做法对比,硬阈值好在哪?
8.4 部分 rollout:不等最慢的那个人
rollout:RL 里让模型实际跑一遍任务、生成一整条轨迹的过程。因为智能体任务要真的去调用工具、等沙箱返回结果,一条轨迹可能要跑很久。
不解决会怎样
同步 RL 框架的一轮迭代是「先全部生成,再统一更新」。长时程任务里,绝大多数轨迹几分钟就完了,但总有几条要跑几十分钟(比如某个 SWE 任务反复编译失败在重试)。整轮迭代被这几条落后者(straggler)卡住,成千上万块 GPU 在那里空转等待。这就是论文说的「长时程任务里加剧的长尾延迟」。
论文的对策是扩展他们同步 RL 框架里的部分 rollout(partial rollout)方案。机制是这样的:每轮迭代的 rollout 阶段,对 N 个提示各采样 K 个完成,维持 N×K 条轨迹的活跃工作量。不等所有 rollout 结束——只要有 λ ∈ (0,1) 比例的轨迹完成(也就是 λNK 条),生成阶段立刻暂停,让策略优化不被执行落后者拖住。被暂停的 rollout 会入队,在下一轮迭代开始时优先恢复,由他们的沙箱基础设施支撑。而一旦某个提示的全部 K 条回答都完成了,就立刻派去做策略优化(该算法沿用 Kimi K2.5)。
代价:数据陈旧
论文很诚实地指出了副作用:在部分 rollout 下,一条长时程轨迹天然会跨越多轮迭代,这引入了数据陈旧(data staleness),威胁训练稳定性。
在策略 / 离策略(on-policy / off-policy):RL 的标准假设是「用当前策略采的数据来更新当前策略」(在策略)。如果数据是几轮之前的旧策略产生的,就叫离策略。数据越旧,离策略程度越严重,梯度估计的偏差越大,训练越容易崩。
论文的解决方式是:他们的策略优化算法通过逐 token 正则化(per-token regularization)天然容忍这种极端离策略状态——「通过把策略更新约束在一个局部邻域内,这种正则化使算法能稳健地处理高度陈旧的数据,并维持训练稳定性」。
自己推一遍:为什么「约束在局部邻域」能救陈旧数据
先问:陈旧数据到底「错」在哪?一条两轮前采的轨迹,它本身的文字内容并没有变质,为什么就不能直接拿来更新?
想好了再看
问题不在文字,在概率。策略梯度的正确性依赖于「这些样本是按当前策略 πθ 的概率分布采出来的」。两轮前的策略 πold 更可能采出某些 token、更不可能采出另一些。用 πold 的样本去估计 πθ 下的期望,需要乘上重要性权重 πθ/πold 来纠偏。当初想到这一步,是因为「换个分布采样、乘个比值纠正」是概率里最标准的一招(重要性采样)。
那么,重要性权重 πθ/πold 在什么情况下会变得非常危险?动手算一下:如果某个 token 在旧策略下概率是 0.001,在新策略下变成了 0.5,权重是多少?
想好了再看
权重 = 0.5 / 0.001 = 500。一条轨迹有上万个 token,每个 token 都可能贡献这样一个乘数。权重的方差会爆炸——少数几条轨迹的梯度可以盖过其余全部样本,一次更新就把模型带到不知道哪里去。数据越旧,πθ 和 πold 相差越远,出现极端权重的概率越高。这就是陈旧数据「威胁训练稳定性」的具体机制。
现在反过来问:如果我们能强行保证 πθ 在每个 token 上都不会离 πold 太远,上一步的灾难还会发生吗?
想好了再看
不会。如果每个 token 的比值都被约束在比如 [0.8, 1.25] 之内,那么无论数据有多旧,重要性权重都是温和的,方差有界,梯度估计不会被少数样本劫持。这就是「把策略更新约束在一个局部邻域内」的含义——不是让数据变新,而是让新旧策略之间的差距永远不大到让纠偏失效。逐 token 做这件事,比在整条轨迹层面做要精细得多,因为一条上万 token 的轨迹哪怕整体差不多,也可能有个别 token 的比值离谱。
最后一问:这个方案的代价是什么?天下没有免费的午餐。
想好了再看
代价是单步学习速度受限。既然每次更新都不许走远,那么模型每轮能改变的幅度就有上限。换来的是可以安全地用大量陈旧数据、可以把 GPU 利用率拉满、可以做部分 rollout。在长时程智能体训练这个场景下,这笔交易明显划算:吞吐量的提升远大于单步步长的损失。论文并没有给出这笔账的量化对比数据。
论文规定 λ ∈ (0,1)(开区间,取不到 0 和 1)。请分别构造 λ → 1 和 λ → 0 这两个极端下的失效场景,说明为什么两个端点都必须被排除。
变式:假设所有轨迹长度完全一样(没有长尾),λ 应该取多少?这说明 partial rollout 的收益来源究竟是什么?
综合题:论文在 8.7 会说,MOPD 的逐 token 蒸馏奖励「无缝接进 RL 框架,自然地让部分 rollout 之类的基础设施层优化也能用于长时程任务的蒸馏训练」。请把「部分 rollout」、「逐 token 正则化」、「逐 token 稠密奖励」三者串成一条互相支撑的逻辑链,并指出如果去掉中间那一环会发生什么。
变式:如果某个任务本身就很短(比如单轮问答),这三者的耦合关系还成立吗?在短任务上,部分 rollout 还有价值吗?
答辩:如果我是审稿人
你说「逐 token 正则化把更新约束在局部邻域,所以能容忍极端陈旧数据」。可这听起来就是「把学习率调小」的花哨说法。请说明你的方法和单纯减小步长有什么本质区别;另外,你既没给 λ 的取值,也没给「有/无正则化」的稳定性对照实验,我凭什么相信这个机制真的在起作用?
参考防守(先自己组织语言再看)
第一个问题可以正面回答:有本质区别。减小学习率是对所有参数、所有 token 一视同仁地缩小步长,它压制的是更新的「总量」,但压不住个别 token 上重要性权重高达几百倍所带来的方差——那个极端样本的方向依然会主导梯度,只是整体走得慢一点。而逐 token 正则化约束的是新旧策略在每个 token 上的概率比,它直接砍掉的是方差的来源。换句话说,学习率控制「走多远」,逐 token 约束控制「往哪个方向走才算合法」。在有极端离群权重的场景里,后者是必需的,前者替代不了。
第二个问题必须承认:论文这一段确实只给了机制性的文字说明,没有给 λ 的具体取值,也没有做消融实验来证明「去掉正则化就会崩」。它把算法细节推给了引用的 Kimi K2.5 论文。所以严格说,「该机制在 K3 规模上确实有效」这一点,在本文中属于作者主张而非被本文数据支持的结论。可以辩护的是:部分 rollout 与该正则化的组合在 K2.5 上已有公开报告,K3 是沿用而非首创;但读者应把它当作「继承自前作的设计选择」,而不是「本文验证过的结论」。
8.5 推理努力 RL:给「想」这件事上一个预算
为什么要管思考长度
因为不管的话,模型会过度思考。RL 只奖励「答对」,那么对模型来说最保险的策略就是「多想一点总没错」——反正想得久不扣分。结果就是问 1+1 也要写三千字推演。这既浪费用户的时间和钱,也让模型没法适配「我现在只要一个快答」这种真实需求。
K3 的做法是一个逐问题的预算控制机制(沿用 K2.5)。给每个问题 x 关联一个初始 token 预算 b0(x),这个预算由冷启动模型估计得到;然后,对于总 token 预算 T(y) 超过缩放阈值 τ · b0(x) 的轨迹,把任务奖励直接改写成 −1。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| x | 一个具体问题(提示) | 「这道题」 |
| y | 模型针对 x 产出的一条完整轨迹 | 「这次的作答全过程」 |
| b0(x) | 由冷启动模型估计出的该问题的初始 token 预算 | 「这题大概值多少字」——注意它因题而异,难题预算天然更大 |
| τ | 预算乘子,一个大于 0 的标量,按领域配置 | 「宽容度旋钮」:调大 = 允许多想,调小 = 逼它简短 |
| T(y) | 轨迹 y 实际用掉的总 token 预算 | 「这次实际写了多少」,具体口径见下文 |
| −1 | 覆盖掉原任务奖励的惩罚值 | 「超时作废」:哪怕答案完全正确,也按最差处理 |
T(y) 的口径分两种:对通用任务,它衡量思考 token 数;对智能体任务,它统计累计输出 token,包括推理痕迹和工具调用参数。后者把工具调用参数也算进去很有必要——否则模型可以把长篇大论塞进工具参数里绕过限制。
分阶段课程:τ 从大到小退火
训练遵循一个对预算乘子 τ 的分阶段课程(stage-wise curriculum):先用相对较大的 τ 训练 max-budget 变体,同时仍然给最大预算设上限以抑制过度思考;然后把 τ 退火(anneal,逐步降低)到更小的值,得到 high 和 low 努力的专家模型。τ 的调整按领域配置,并在人在环的指导下进行。
最后一句话很重要:所有推理等级产生的轨迹被联合收集,用于监督微调和多教师在策略蒸馏。也就是说,九个专家不只是最终 MOPD 的教师,它们跑出来的轨迹本身也是数据资产。
补充:为什么是「先大后小」而不是反过来
论文只说了顺序,没有解释原因。一个合理的推测是:先在宽松预算下让模型充分学会「怎么把难题做对」,形成正确的解题能力;再逐步收紧预算,逼它学会「把同样的事情说得更短」。反过来先训 low 再放宽,模型可能从一开始就没学会难题的完整解法,后面再放宽也补不回来。这是本书的推测,论文并未给出这个解释,也没有做顺序上的消融实验。
某个通用任务问题 x,冷启动模型估计的初始 token 预算 b0(x) = 800。当前训练的是 high 档专家,τ = 2.5。模型跑出两条轨迹:轨迹 A 思考了 1900 个 token,答案正确;轨迹 B 思考了 2100 个 token,答案也正确。这两条轨迹各自拿到什么奖励?
变式:同一个问题、同样两条轨迹,如果现在训练的是 low 档专家、τ 退火到了 1.2,结果会怎样?如果 τ = 3.0 呢?请说明「同一条轨迹在不同 τ 下拿到相反的奖励」这件事,为什么恰恰是分阶段课程能造出三档专家的原因。
答辩:如果我是审稿人
把超预算轨迹的奖励直接改写成 −1,未免太粗暴了。一条只超了 1 个 token 的正确答案,和一条彻底答错的垃圾答案,拿到完全相同的奖励——这明显破坏了奖励函数的连续性,也会污染优势估计。为什么不用一个随超出量平滑衰减的惩罚项?
参考防守(先自己组织语言再看)
可以从三个角度防守。第一,硬阈值抗黑客:平滑惩罚意味着模型总能做「多想一点点、多扣一点点分、但换来更高正确率」的划算交易,只要正确率的边际收益大于惩罚的边际成本,模型就会一路超下去——最后惩罚项形同虚设。硬性改写为 −1 让「超预算」变成一道不能碰的墙,交易不成立。第二,阈值本身是自适应的:b0(x) 是逐问题由冷启动模型估计的,难题的预算天然更大,所以这道墙并不是「一刀切的绝对字数」,粗暴的是惩罚形式,不是阈值位置。第三,目标是造出可控的档位:K3 要的是三个行为明显不同的专家(low/high/max),一个平滑惩罚会让三档之间的界限模糊;硬阈值 + 分阶段退火 τ,才能得到边界清晰的三个模型。
但审稿人的顾虑有一半站得住:论文确实没有讨论阈值附近的边界效应,也没有报告「是否观察到模型学会在阈值前紧急收尾导致答案被截断」这类问题;论文也没有提供与平滑惩罚方案的对照实验。诚实的说法是:这是一个被前作(K2.5)验证过并沿用的工程选择,本文没有为它提供新的实证支持。
8.6 智能体生成式奖励模型:让 AI 当裁判,还得按流程判
为什么需要一个「AI 裁判」
数学题、代码题可以自动判对错——跑一遍就知道。但「帮我写一封得体的道歉邮件」「这份研究报告写得好不好」没有标准答案,程序判不了。论文把这类任务叫不可验证的通用任务(non-verifiable general tasks)。没有奖励信号,RL 就跑不起来,所以必须造一个裁判。
K3 对这类任务采用智能体生成式奖励模型(Agentic Generative Reward Model, Agentic GRM),保留了 K2.5 的锦标赛式分组奖励与二元比较——也就是把多个候选答案两两 PK,像打比赛一样比出高下,而不是给每个答案打一个绝对分。
打个比方
让老师给一百篇作文各打一个百分制的绝对分,第一篇和第一百篇之间的标准很难保持一致(评分会漂移)。但让他每次只回答「这两篇哪篇更好」,判断就稳定得多。锦标赛式二元比较就是这个思路。
类比失效处:人类老师的两两比较是有传递性直觉的,而 AI 裁判可能出现 A>B、B>C、C>A 的循环。论文没有讨论如何处理这种不一致。
K3 的新东西在于:除了通用的智能体能力(裁判本身也能调用工具、去实际检查产物),这个智能体裁判被要求遵循一个强制协议(mandatory protocol),四步,顺序固定:
- 读取结果、产物或文本输出;
- 生成一份评分细则(rubric,就是评分标准清单);
- 用该细则给每个候选打分;
- 把细则分数记录到一个 scorepad(记分板)里。
为什么要强制这个顺序
关键是第 2 步必须在第 3 步之前。如果让裁判直接说「我觉得 A 更好」,它很容易被表面因素影响(更长、格式更花哨、用词更自信),事后再编个理由。强制它先写出评分标准、再逐条对照打分,等于把判断过程外化成可检查的步骤,减少「凭感觉」。第 4 步把分数写进 scorepad,则让整个判决留下可追溯的记录。
啰嗦度控制:堵住裁判自己的漏洞
论文指出了一个具体的奖励黑客方向:模型会朝着「输出越来越啰嗦」进化,因为长答案在裁判眼里往往显得更完整、更用心。为了缓解这一点,作者加了一个基于预算的啰嗦度控制,做法和 8.5 的推理努力控制类似:给定由冷启动模型估计的初始啰嗦度 ℓ0 和一个乘子 σ,输出长度超过 σ · ℓ0 的候选自动输掉二元比较。
构造反例:请设计一类具体的用户请求,使得「输出长度超过 σ·ℓ0 就自动输掉比较」这条规则系统性地惩罚了更好的答案。然后说明:为什么用同一套机制去管「思考长度」(8.5)比管「输出长度」(8.6)更安全?
至于第二问:管思考长度更安全,是因为思考 token 用户看不到、也不构成交付物——砍掉多余的思考几乎只有好处,最坏情况是难题上思考不足导致答错,而那会被任务奖励本身惩罚,两个信号方向一致。而输出长度就是交付物本身,砍它可能直接砍掉用户要的内容,此时长度惩罚和质量奖励是方向冲突的,需要 ℓ0 估得非常准才不出事。论文没有讨论 ℓ0 的估计误差问题,也没有说明 σ 是否按任务类型区分配置。
变式:如果把规则从「自动输掉比较」改成「长度只在两个候选质量打平时才作为判据」,上面的反例还成立吗?这么改会牺牲掉原规则的什么好处?
8.7 MOPD:把九个专家压回一个模型
现在手上有九个专家模型。问题来了:用户不可能装九个模型。每个都是完整的一份权重,光显存就要九倍。必须合并成一个。
知识蒸馏(Knowledge Distillation):让一个「学生」模型去模仿「教师」模型的输出,从而把教师的能力搬到学生身上。传统蒸馏是让学生去拟合教师在教师自己生成的文本上的分布。
在策略蒸馏(On-Policy Distillation, OPD):改成让学生自己生成文本,教师在学生写出的每一个 token 上给出评价。这样学生练的是「自己真实会犯的错」,而不是「教师从不会走到的完美路径」。
打个比方
传统蒸馏像是看名师的解题录像,一遍遍抄。在策略蒸馏像是自己下场做题,做一步老师在旁边点一下头或摇一下头。后者显然更对症——因为老师的批注落在你真正会走错的岔路口上,而不是落在你根本想不到的那条完美路线上。
类比失效处:真实的「点头摇头」不是二元的,而是一个连续的实数(式 15),而且是每个 token 都有,密度远高于人类老师的批注。
K3 用的是多教师版本——MOPD(Multi-Teacher On-Policy Distillation)。训练时,对于给定的领域 d 和一个采样到的推理努力等级 e ∈ {low, high, max},优化由九个专家中对应的那个教师模型 πteacher(d,e) 来指导。也就是说,教师是按当前样本的领域和努力等级动态选出来的,不是九个一起平均。
给定输入查询 x 和前缀 y<t,在 yt 上求值的逐 token OPD 奖励定义为:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| yt | 学生刚写出的第 t 个 token | 「刚落下的这一个字」 |
| y<t | 它前面已经写出的所有 token | 「到目前为止写的内容」 |
| x | 输入查询 | 「题目」 |
| d, e | 领域标签 与 推理努力等级 ∈ {low, high, max} | 决定「请哪位老师来批」这一步 |
| πteacher(d,e)(yt|·) | 教师在同样上下文下,给这个 token 的概率 | 「老师自己会不会写这个字」 |
| πθ(yt|·) | 学生(参数 θ)给这个 token 的概率 | 「我刚才有多想写这个字」 |
| log(πteacher/πθ) | 两个概率的比值取对数 | 核心:老师比我更看好这个字 → 比值 > 1 → log > 0 → 正奖励;反之为负;两人一样看好 → 比值 = 1 → log = 0 → 不奖不罚 |
| sg(·) | 停止梯度算子(stop-gradient) | 「这个值只当分数用,不许梯度从这里回流」——把它冻成一个常数奖励 |
| clip(·, −Rmax, Rmax) | 把结果裁剪到区间 [−Rmax, Rmax] | 「再离谱的分数也不能超过这个上下限」 |
| Rmax > 0 | 裁剪阈值,论文说用于约束极端优势信号、稳定 RL 训练 | 安全阀。论文没有给出它的具体取值 |
补充:为什么用 log 比值,而不是直接用差 πteacher − πθ
因为概率的绝对差没有尺度感。0.9 和 0.8 差 0.1,0.1 和 0.001 也只差 0.099——但后者是「老师认为可能而我几乎完全排除」,严重得多。取比值再取对数,衡量的是相对倍数:0.9/0.8 ≈ 1.125,log 约 0.12;0.1/0.001 = 100,log 约 4.6。log 还有一个好处:它关于反转是对称的——老师概率是我的 10 倍得 +log10,我的概率是老师的 10 倍得 −log10,奖惩对称。
论文强调这是一个稠密(dense)奖励信号——每个 token 都有分,而不是整条轨迹才给一个分。因此它「无缝接进我们的 RL 框架,自然地让部分 rollout 之类的基础设施层优化也能用于长时程任务的蒸馏训练」(这一点在 8.4 的综合题里已经推过)。
一个诚实的负面结果
论文写道:他们也试过更细粒度的 top-k 蒸馏目标(即不只看学生实际写出的那一个 token,而是对比师生在概率最高的 k 个 token 上的整个分布),但「在我们的设定下没有观察到收敛速度或最终性能上的明显优势」。这是一个有价值的负面结果——它意味着「只在学生实际走过的那条路径上给信号」已经够用了。但请注意措辞里的「in our setting」(在我们的设定下):论文没有给出这个对比实验的任何数据,也没有说明 k 取了多少、在哪些任务上比的。这属于作者的经验陈述,不是被展示的证据。
在某个上下文下,学生给 token 「因此」 的概率是 0.05,对应的教师给这个 token 的概率是 0.50。设 Rmax = 4。请算出式 15 给出的奖励,并用一句话说明它在告诉学生什么。(提示:ln 10 ≈ 2.30)
变式:如果教师概率是 0.50、学生概率是 0.0001,比值是 5000,log 约 8.5,超过了 Rmax = 4。此时奖励是多少?把这种情况和上面那题对比,说说 clip 到底在防什么。
式 15 里有两个「保护装置」:sg(·) 和 clip(·, −Rmax, Rmax)。有人说「既然都是为了稳定训练,留一个就够了」。请说明这两者防的不是同一件事,并各举一个去掉它之后会出问题的场景。
变式:把 clip 换成对 log 比值做 tanh 压缩(平滑饱和而非硬截断),能不能起到同样效果?会失去什么?(想想 8.5 那个答辩里「硬阈值 vs 平滑惩罚」的讨论,结论在这里是否相同?)
8.8 MXFP4 量化感知训练:让模型住进更小的房子
为什么要量化
模型的每一个权重都是一个数,存在显存里。数越多、每个数占的位数越多,就需要越多显存卡、越贵的服务成本。论文的目标写得很直白:「降低部署时的显存占用和服务成本」。
比特(bit):计算机里最小的信息单位,一个比特只能是 0 或 1。n 个比特能表示 2n 种不同的取值。
所以「4 比特」意味着:这个权重只能取 24 = 16 种可能的值。对比一下,深度学习常用的 BF16 是 16 比特,能表示 216 = 65536 种值。从 16 比特降到 4 比特,同一批权重占的显存变成四分之一。
打个比方
原来每个权重可以是 0 到 65535 之间的任意刻度,现在只准从 16 个预设刻度里挑一个最接近的。就像把一把有六万条刻度的尺子换成只有 16 条刻度的尺子——量出来的每个长度都会有误差。
类比失效处:MXFP4 不是均匀的 16 条刻度。MX 是 Micro-scaling(微缩放)的意思:一小组权重共享一个缩放因子,组内再用 4 比特浮点表示。所以刻度是「按组自适应」的——某一组权重都很小时,这 16 条刻度就密集地铺在小数值区间。这比朴素的 4 比特整数量化精确得多。(论文只给出了格式名和引用,这里对 MX 格式的展开属于通用背景知识,不是论文内容。)
只量化「专家权重」,别的保持高精度
论文的具体方案是:把MoE 专家权重——论文说它们「主导了模型的参数内存」——量化到 MXFP4,激活用 MXFP8 计算;而所有非专家组件(注意力投影、潜 MoE 投影、共享专家、MoE 路由器)保持更高精度。
为什么偏偏是专家权重最该被量化
两个原因叠加。第一,它们最占地方——MoE(混合专家)架构里有大量专家,每个专家都是一整套前馈网络权重,加起来占了参数总量的绝大部分。压最大的那块,收益最大。第二,它们最经得起压——每次前向只有少数几个专家被激活,任何单个专家的权重误差影响面有限;而注意力投影、路由器这些组件每个 token 都要过,尤其路由器是决定「派哪些专家干活」的关键决策点,它出一点误差可能导致选错专家,错误会被放大。所以论文把它们留在高精度。这是一个典型的「把压缩预算花在收益/风险比最高的地方」的工程决策。
QAT 在整个后训练阶段进行,覆盖 SFT 和 RL,使模型「适应量化引起的精度损失」。而且论文特别点出:RL 期间 rollout 和训练共享同一套量化方案——消除了训练-推理不匹配(这一点 8.2 的测验里已经推过)。
读的时候要小心
论文在这一节没有给出任何量化前后的具体数字——既没说显存省了多少 GB,也没说精度掉了多少分,更没有「QAT vs 训练后量化」的对照。「模型适应了量化引起的精度损失」是一个定性主张。另外,「更高精度」具体是 BF16 还是 FP8,论文也没有明确写出。
某团队照搬 K3 的方案,但为了把显存压得更狠,顺手把 MoE 路由器也量化到了 MXFP4,理由是「路由器参数量很小,压了也不影响,但聊胜于无」。请指出这个决定的两个问题。
变式:共享专家(shared expert,每个 token 都会过的那个专家)也是前馈网络,结构和普通专家一样。为什么论文把它留在高精度而不是跟着一起压?(提示:想想「每个 token 都要过」这件事对误差累积意味着什么。)
8.9 草稿模型与推测解码:让小模型先猜,大模型只负责验
为什么大模型生成得慢
因为生成是一个 token 一个 token来的:要写第 100 个字,必须先把前 99 个字算完。每写一个字,都要把整个巨大的模型完整跑一遍。这个过程无法并行——不是算力不够,是依赖关系卡住了。论文说「对于服务复杂的长时程智能体模型,优化推理效率至关重要」。
推测解码(Speculative Decoding):先让一个很小很快的草稿模型一口气猜出接下来的好几个 token,再让大的目标模型把这几个 token 一次性并行验证。验证是可以并行的(因为要检查的 token 已经摆在那儿了),所以一次大模型前向就能确认好几个 token。猜对了就一次前进好几步,猜错了就从错的地方丢弃、重来。关键:这个过程是无损的——最终输出的分布和不用草稿模型时完全一样,只是快了。
K3 的草稿模型是怎么来的
K3 在预训练时就带了一个多 token 预测层(multi-token-prediction, MTP),其结构镜像一个骨干块。而 EAGLE-3 风格的草稿模型恰好是一个单解码层,结构与 MTP 层匹配。于是作者的做法是:把预训练好的 MTP 层微调成一个 EAGLE-3 风格的草稿模型,冻结目标模型,只训练草稿层和特征融合投影。
训练时按 EAGLE-3 的「训练时测试」协议,草稿被展开七步。第一步之后,最新位置的目标侧特征是拿不到的,草稿就消费自己前几步的输出——这样训练时的情形就和推理时的循环起草过程一致了。
为什么要「展开七步」而不是只训一步
因为推理时草稿要连续猜好几个 token,第二步猜的时候只能基于自己第一步的输出(大模型还没算呢)。如果训练时每一步都喂给它正确的目标侧特征,它就从没经历过「基于自己可能有错的输出继续猜」这种情形,一到推理就崩——这就是经典的暴露偏差(exposure bias)。展开七步、让它吃自己的输出,是在训练里就把推理的真实条件复现出来。
三层特征融合与那个漂亮的初始化
草稿的输入不是只看目标模型的最后一层,而是融合了低、中、高三层特征,分别取自第 1 个、第 4 个和最后一个 AttnRes 块的输出。这三份特征被拼接起来,再用一个无偏置矩阵 WE3 投影回隐藏维。
而 WE3 的初始化是 [0 0 I]——两个零块加一个单位矩阵。
自己推一遍:为什么 [0 0 I] 能保证「从不退步地开始」
先弄清形状。设隐藏维是 h。拼接后的向量 [hl; hm; hh] 是多少维?WE3 必须是什么形状,才能把它投影回 h 维?
想好了再看
三份特征各 h 维,拼起来是 3h 维。要投回 h 维,WE3 必须是 h × 3h。把它横着切成三块,每块是 h × h——这三块正好一一对应低、中、高三份特征。记号 [0 0 I] 就是说:前两块是零矩阵,第三块是单位矩阵。
现在动手乘一下:[0 0 I] · [hl; hm; hh] 等于什么?
想好了再看
分块矩阵乘法:结果 = 0·hl + 0·hm + I·hh = hh。也就是说,融合后的表示恰好等于高层特征本身,低层和中层被完全忽略。论文原话正是「使得融合表示在初始化时与高层特征 hh 重合」。
关键一问:hh 对这个草稿层来说有什么特殊之处?(回想草稿层是从哪来的。)
想好了再看
草稿层是由预训练好的 MTP 层微调来的,而 hh(最后一个 AttnRes 块的输出)正是 MTP 层在预训练时接收的输入。论文明确点出了这一点。所以在初始化时刻,这套「三层融合 + 投影 + 草稿层」的结构,在功能上和「直接把预训练好的 MTP 层拿来用」完全等价。
所以,「从不退步地开始」具体指什么?如果换一个常见的随机初始化(比如小随机数),会发生什么?
想好了再看
「不退步」指:微调的第 0 步就已经达到了预训练 MTP 层的水平,之后每一步都是在这个已经不错的起点上做增量改进——学习曲线从一个高台起跳,而不是从地板起跳。如果用随机初始化,融合表示会是三份特征的一个随机线性组合,这个东西 MTP 层从没见过,等于把预训练学到的东西全部打乱;模型要先花很多步把「忽略掉噪声、重新找回高层特征」这件事学回来,才谈得上进步。论文说它「随后逐渐学会在微调中纳入低层和中层特征」——零块的位置有梯度,会从 0 慢慢长出来,所以低中层信息不是被永久放弃,只是被推迟到模型有余力时再引入。
最后:为什么这个矩阵是无偏置(bias-free)的?如果加一个偏置项 b 并随机初始化,上面的论证还成立吗?
想好了再看
不成立。有偏置的话,初始输出是 hh + b,只有当 b 恰好初始化为零向量时才等于 hh。去掉偏置是让「初始时恒等」这个性质结构上就成立,不依赖额外约定。论文只说了 WE3 是无偏置的,没有解释原因;这个推断是本书补的,但它和论文描述的初始化意图完全一致。
式 16:直接优化「接受率」本身
推测解码到底能快多少,取决于草稿猜的 token 有多大比例被接受。论文说:无损推测采样下的加速由逐 token 接受率决定,这个接受率等于
| 符号 | 是什么 | 直觉 |
|---|---|---|
| V | 词表(vocabulary),模型所有可能输出的 token 的集合 | 「字典里的全部字」 |
| x | 词表里的某一个具体 token | 「字典里的某一个字」 |
| p(x) | 目标模型(大模型)给 x 的下一 token 概率 | 「正确答案的分布」 |
| q(x) | 草稿模型(小模型)给 x 的下一 token 概率 | 「猜测的分布」 |
| min(p,q) | 两者中较小的那个 | 「两人都同意的那部分概率质量」 |
| Σx∈V | 对词表里每个 token 都算一遍 min,然后全加起来 | 两条概率曲线的重叠面积 |
打个比方:重叠面积
把 p 和 q 想成两座并排画在同一张纸上的山(横轴是词表里的每个 token,纵轴是概率)。每座山的总面积都是 1(概率之和为 1)。Σ min(p,q) 就是两座山重叠在一起的那块阴影面积。两座山完全重合,阴影就是整座山,面积 1;两座山完全错开(草稿猜的字大模型一个都不会说),阴影面积 0。所以接受率天然落在 [0, 1] 之间,它衡量的就是两个分布有多像。
类比失效处:词表是离散的(几万个孤立的柱子),不是连续的山坡,所以严格说是「重叠的柱状面积之和」。另外接受率是每个 token 位置各算一次的,不是整句话算一次。
接下来是论文这一段最关键的洞察。既然想让接受率高,最自然的做法是让草稿去拟合目标——传统上用 KL 散度(衡量两个分布差异的标准指标)当代理目标。但论文指出:对于容量受限的草稿模型,最小化传统的 KL 散度代理并不保证最大化这个接受率。所以他们干脆直接优化基于似然的 LK 损失,也就是接受率本身的负对数:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| LLK | 要被最小化的损失函数 | 「越小越好」的那个数 |
| Σ min(p,q) | 接受率,取值在 (0, 1] | 见上表:重叠面积 |
| − log(·) | 取对数再取负号 | 把「越大越好」翻成「越小越好」:接受率 = 1 时损失 = −log 1 = 0(最优);接受率趋于 0 时损失趋于 +∞。最小化 LLK 就是最大化接受率 |
论文补充了两个实现细节:p 和 q 都在温度 1 下求值,并且不加辅助的真值交叉熵项(也就是不掺「让草稿去拟合真实下一个词」这种常规语言建模目标,纯粹对齐目标模型)。此外,草稿微调沿用后训练的 QAT 配置:MoE 专家权重 MXFP4、其输入激活 MXFP8,非专家模块保持更高精度。
证明:如果草稿模型和目标模型的下一 token 分布完全相同(即对所有 x,q(x) = p(x)),那么接受率 Σx min(p(x), q(x)) = 1,此时式 16 的损失 LLK = 0。然后回答:接受率能不能大于 1?
变式:假设词表只有 3 个 token,p = (0.7, 0.2, 0.1),q = (0.4, 0.4, 0.2)。手算接受率和 LLK。再试试 q = (1.0, 0, 0)——这个「极度自信但押错了分布」的草稿,接受率是多少?它比上一个草稿好还是坏?
综合题:论文说「对容量受限的草稿模型,最小化传统 KL 散度代理并不保证最大化接受率」。请构造一个具体例子说明 KL 和接受率会给出不同的偏好排序,并解释「容量受限」这个前提为什么是这句话成立的关键。最后,把这个洞察和 WE3 = [0 0 I] 的初始化技巧联系起来:它们体现了同一种什么样的工程思维?
「容量受限」为什么是关键前提。如果草稿模型容量无限,它可以精确复制 p,此时 KL = 0 且接受率 = 1,两个目标的最优解重合,用哪个当损失都无所谓。但草稿只有单个解码层,它没有能力同时把三万个 token 的概率都对齐——它必须做取舍。而取舍的方向,正是由损失函数决定的:KL 会逼它把宝贵的容量花在「照顾长尾、不要让任何 q(x) 变成 0」上(因为那里惩罚无穷大),而接受率只在乎「把头部那几个真正会被采到的 token 对准」。给一个必须做取舍的模型用错误的取舍准则,就是论文说的「并不保证最大化接受率」。直接优化你真正想要的那个量,别用代理,这就是式 16 的全部动机。
与 [0 0 I] 的共同思维。两者都是拒绝在中间环节上妥协、直接对准最终目标的表现:一个是「别优化 KL 这个代理,直接优化接受率」;另一个是「别随机初始化再慢慢学回来,直接从已经被预训练验证过的那个解出发」。共同点是:凡是能把「绕一圈」改成「直接对齐」的地方,就不绕——尤其在资源(草稿容量、微调预算)受限时,绕的那一圈就是纯损失。
变式:论文还说 p 和 q 在温度 1 下求值,且不加真值交叉熵项。请解释:如果掺进一个「让草稿去预测真实下一个词」的交叉熵项,会不会伤害接受率?(提示:草稿的任务是模仿目标模型,哪怕目标模型是错的。)
读的时候要小心
这一整节论文没有给出任何加速倍数的实测数字——没有说接受率实际达到了多少,也没有报告端到端的吞吐提升。「展开七步」「第 1、第 4 和最后一个 AttnRes 块」这些超参数论文也没有说明是怎么选出来的、有没有做过搜索。此外,草稿微调用了 QAT 配置,但论文没有讨论量化后的草稿模型接受率会掉多少。
本章小结
K3 的后训练是一条三段式流水线:SFT 用旧模型合成的轨迹加人工把关,造出一个「会办事」的冷启动策略;RL 把它拆成三个领域 × 三档推理努力共九个专家,各自在自己的赛道上把能力练到极限;MOPD 再用逐 token 的稠密奖励(式 15)把九个专家的本事压回同一个模型。
贯穿全程有三条暗线。第一条是吞吐与稳定性的博弈:部分 rollout 用「不等落后者」换吞吐,代价是数据陈旧,靠逐 token 正则化把更新约束在局部邻域来兜底——而 MOPD 的稠密奖励恰好让这套基础设施也能用于蒸馏。第二条是反奖励黑客:模型天然会朝「想得更久、写得更长」进化,K3 用同一个模式(冷启动估基准 × 乘子 = 硬阈值)分别堵住了推理努力(超 τ·b0 判 −1)和输出啰嗦度(超 σ·ℓ0 自动判负)。第三条是部署感知:从 SFT 第一天起就按上线的量化方案训练(MoE 专家权重 MXFP4、激活 MXFP8、非专家组件高精度),RL 的 rollout 和训练共用同一套量化,消除训练-推理不匹配;同时把预训练带的 MTP 层微调成 EAGLE-3 草稿模型,用 [0 0 I] 初始化保证不退步地起步,并直接优化接受率本身(式 16)而不是 KL 代理。
要留意的边界:本节几乎所有关键超参数(λ、τ、σ、Rmax、「更高精度」的具体格式)论文都没有给出取值,也没有提供消融实验;Figure 8 是训练期的内部曲线,纵横轴均无绝对刻度;top-k 蒸馏「没有明显优势」和 MXFP4 QAT「模型适应了精度损失」都是作者的定性陈述,本文未展示支撑数据。这些地方读的时候要把它们当作设计选择,而不是已验证的结论。
Chapter 8 · Post-training: from imitation to self-evolution, then squeezed back into one model
In one sentence: pre-training teaches the model to “talk”, post-training teaches it to “do things” — and K3's way of doing it is to first teach it to imitate (SFT), then let it train itself into nine experts across three domains and three thinking-length tiers (RL), then squeeze those nine experts back into one model (MOPD), and to train from beginning to end at “the precision it will run at once it is deployed”.
After this chapter you should be able to
- Say in your own words what problem each of the three stages — SFT, RL, MOPD — solves, and why the order cannot be swapped
- Explain why “partial rollout” manufactures data staleness, and why “constraining the update to a local neighbourhood” rescues it
- Given an initial budget b0 and a multiplier τ, work out whether a trajectory gets marked −1
- Read the per-token distillation reward of equation 15 symbol by symbol, and decide whether it is positive or negative for a given token
- State the geometric meaning of Σ min(p,q), and prove that it equals 1 when the two distributions are identical
- Point out where MXFP4 quantisation and the draft-model initialisation trick each stop applying, and where the paper is unclear about them
8.1 The global picture: why post-training goes in three stages
Start with a question. When pre-training ends, the model has seen an enormous amount of text and can carry a sentence on very smoothly, but it does not know “what the human wants it to do right now”, and still less “when to stop, call a tool, look at what comes back, and then carry on”. It is like someone who has memorised an entire library — full marks on knowledge, but ask him to book you a flight and he starts reciting the Regulations on the Administration of Air Transport.
What happens without post-training
You get a model that is “deeply knowledgeable but cannot get anything done”. None of the abilities involved in “understanding an instruction”, “answering in the required format” and “acting when it is time to act” grow by themselves out of more reading; they have to be trained deliberately. The paper calls this pipeline post-training(Post-Training).
The paper itself sums up K3's post-training as a three-stage paradigm: first supervised fine-tuning(Supervised Fine-Tuning, SFT) initialises baseline agentic ability; then reinforcement learning(Reinforcement Learning, RL) cultivates experts for each domain at different reasoning effort levels; finally multi-teacher on-policy distillation(Multi-Teacher On-Policy Distillation, MOPD) merges these domain-specific policies back into a single model.
An analogy
SFT is like making an apprentice copy the master from a video recording — get the movements right first, even without understanding why. RL is like sending him into competition himself: points for winning, points off for losing, until he slowly builds a feel the master never had; and instead of training one all-rounder, you train nine “event champions” separately. MOPD is then like having those nine champions take turns coaching the same newcomer, until the newcomer alone can do the work of nine.
Where the analogy breaks down: the nine real experts are not nine people but nine different sets of weights on the same base model; and MOPD's “coaching” does not let the student look at the teacher's complete answer — the student writes it himself and the teacher scores it token by token. 8.7 goes through this properly.
Note that this order is not arbitrary. RL needs a starting point that “already knows how to get things done”, otherwise it flails around in a huge action space, picks up almost no positive reward, and cannot learn; that is why SFT is called the cold start(cold-start). And MOPD has to come last, because the things it distils from — the nine experts — only exist once RL has produced them.
Someone proposes: since RL is what raises capability most, skip SFT altogether, send the pre-trained model straight into RL, and save a large annotation bill. On the paper's account, what is the main risk in this plan?
Variant: if the task becomes “answer one multiple-choice question” (one step to a result, four options, a 25% chance of guessing right), does skipping SFT and going straight into RL still have the same problem? Why does this show that “whether SFT is necessary” depends on the horizon length of the task?
8.2 SFT: teaching the model to copy first
Supervised fine-tuning: show the model a large number of “question + standard answer process” pairs and have it raise the probability of every token of the standard answer. In essence it is “copying”, copied until it turns into muscle memory.
There is one thing about K3's SFT that is quite unusual: the trajectories(trajectory, one complete record of “think — call a tool — read what comes back — think again”) in the training data are not written purely by hand. They are synthesised by domain-specific models from earlier in the Kimi series, and then put through multi-stage verification and human-in-the-loop annotation(human-in-the-loop annotation).
Why the data is synthesised with older models
One complex agentic trajectory can run to dozens of tool-call steps and tens of thousands of tokens. Having a person write one from scratch is so expensive it is not viable, and the “thinking process” a person writes often does not match how the model actually thinks, which makes it awkward to learn from. Producing them in bulk with existing domain-specific models and then having people filter and correct them puts the human effort into “judging right from wrong” rather than “creating from nothing” — judging is far cheaper than creating.
The paper says these steps “together produce a large-scale instruction dataset that gives Kimi K3 adaptive reasoning, precise tool calling, and robust execution in long-horizon agentic settings”. All the data is serialised with their conversation template built on XTML; the details are in Appendix F of the paper, and Chapter 14 of this site covers them.
There is one more detail that is easy to skip past and matters a great deal: quantisation-aware training(Quantization-Aware Training, QAT) is applied from the SFT stage onward, with MXFP4 for weights and MXFP8 for activations. That is, K3 does not “train a high-precision model first and compress it at the end”; it trains in the compressed shape from day one of post-training. What that design really means is left to 8.8.
The usual practice is to train a full-precision model and then do “post-training quantisation” (round the weights straight to low precision). K3 instead starts QAT at SFT. Explain: why is “compress at the end” more dangerous in a pipeline that has RL in it than in a pipeline that is only SFT?
Variant: if a company only does SFT and no RL, does the gap between quantising afterwards and QAT get smaller or larger? And if it does RL but deploys at full precision, is QAT still worth anything?
8.3 RL: not training one all-rounder, but training nine experts
Once the cold-start model is in hand, the paper says “RL is essential for unlocking higher-order reasoning and execution capabilities”. But the way K3 organises RL is not what many people would expect.
The intuitive approach is: train as many specialised RL models as there are kinds of task. K3 does not do this. In the paper's own words: “rather than training specialised RL models for individual tasks, we scale RL across three broad domains, each covering a wide range of sub-tasks, and train a single expert per domain at each reasoning effort level”. The three domains are:
| Domain | Sub-tasks covered |
|---|---|
| general tasks(general tasks) | general experience, vision, reasoning, faithfulness, search ability, knowledge-work tasks |
| general agents(general agents) | long-horizon assistant tasks, deep research, paragraph-level writing |
| coding agents(coding agents) | software engineering (SWE), coding experience, kernel tasks, web development |
Now cross those three domain experts with three reasoning effort levels(reasoning effort level) {low, high, max}, and you get nine expert models in total. A reasoning effort level, in plain terms, is “how long it is allowed to think” — low is a quick answer, max is deep thought.
Why the split is by domain and not by task
Splitting by task gives you dozens of models, each with very little data, and the things tasks have in common (how to read a tool's return value, say) have to be learned dozens of times over. Splitting by domain lets one expert eat a whole domain's data, so the shared abilities reinforce each other; and it still stops short of “one model for everything”, where the reward from coding tasks and the reward from writing tasks pull against each other on the same set of weights. It is a compromise on granularity.
Figure 8: step counts rise with compute — what that shows
Figure 8 of the paper draws eight panels (coding experience, general tool use, web development, agentic search, professional workflows, office deliverables, agentic chart understanding, agentic visual puzzles). The horizontal axis is RL FLOPs (the compute put into RL), and each panel carries two lines: the solid line is the evaluation score, the dashed line is the average assistant steps (that is, how many steps of tool calls the model makes on average).
The paper's summary of this figure: as RL FLOPs scale up, the number of tool-call steps keeps rising, accompanied by an across-the-board improvement in the model's overall capability.
A common misreading
Many people see “more steps” and react with “it has got more long-winded, it has got less efficient”. Here it is exactly the opposite. The step count is the model's own choice — nobody dictates how many times it must call a tool. The score rises at the same time as the step count, which means what the model learned is “this question is worth looking up once more / verifying once more”, that is, a willingness to spend more real action on a hard problem. That is a different thing from “more waffle”: waffle brings no new information, whereas one more tool call brings new evidence from the outside world.
Read this carefully
The vertical axis of Figure 8 carries no numeric scale, and the horizontal RL FLOPs axis gives no absolute values; these are the authors' own account of internal curves from the training period, not reproducible public evaluations. “Step counts rise alongside capability” is a correlational observation, and the paper runs no control experiment on “how many points are lost if you cap the step count by hand”, so you cannot conclude from this that “the rise in step count is the cause of the rise in capability”.
A team sees Figure 8 and decides to add a term straight into the reward: “+0.1 for every extra tool call”, hoping to reproduce “more steps brings more capability”. Predict what will happen, and say where the reasoning goes wrong.
Variant: what goes wrong the other way round, if you add “−0.1 for every extra step” to force efficiency up? Compare both of these with the hard-threshold approach the paper actually uses — “mark it −1 only once it exceeds τ·b0(x)” — and say what the hard threshold has going for it.
8.4 Partial rollout: not waiting for the slowest one
rollout: in RL, the process of having the model actually run a task once and generate a whole trajectory. Because agentic tasks really do call tools and wait for a sandbox to return results, one trajectory can take a long time.
What happens if this is not solved
One iteration of a synchronous RL framework is “generate everything first, then update all at once”. In long-horizon tasks the vast majority of trajectories are done in a few minutes, but there are always a few that run for tens of minutes (some SWE task failing to compile and retrying, say). The whole iteration is held up by those few stragglers(straggler), with thousands upon thousands of GPUs idling and waiting. This is what the paper calls the “long-tail latency that is aggravated in long-horizon tasks”.
The paper's answer is to extend the partial rollout(partial rollout) scheme in their synchronous RL framework. The mechanism goes like this: in the rollout phase of each iteration, sample K completions for each of N prompts, keeping an active workload of N×K trajectories. Do not wait for all the rollouts to finish — as soon as a fraction λ ∈ (0,1) of the trajectories are done (that is, λNK of them), the generation phase pauses at once, so that policy optimisation is not dragged along by execution stragglers. Paused rollouts are queued and resumed with priority at the start of the next iteration, supported by their sandbox infrastructure. And as soon as all K responses for a given prompt are finished, they are dispatched for policy optimisation immediately (this algorithm is carried over from Kimi K2.5).
The cost: data staleness
The paper is honest about the side effect: under partial rollout, a long-horizon trajectory naturally spans several iterations, which introduces data staleness(data staleness) and threatens training stability.
on-policy / off-policy: the standard assumption in RL is that you “update the current policy with data sampled from the current policy” (on-policy). If the data was produced by an older policy several iterations back, that is off-policy. The older the data, the more severely off-policy it is, the larger the bias in the gradient estimate, and the easier it is for training to collapse.
The paper's fix: their policy optimisation algorithm tolerates this extreme off-policy state naturally through per-token regularisation(per-token regularization) — “by constraining policy updates within a local neighbourhood, this regularisation lets the algorithm handle highly stale data robustly and maintain training stability”.
Derive it yourself: why “constraining to a local neighbourhood” rescues stale data
First ask: what exactly is “wrong” with stale data? A trajectory sampled two iterations ago has not gone off in its actual text, so why can it not simply be used for an update?
Think it through, then look
The problem is not the text, it is the probabilities. The correctness of the policy gradient depends on “these samples having been drawn from the probability distribution of the current policy πθ”. The policy from two iterations ago, πold, is more likely to draw some tokens and less likely to draw others. To estimate an expectation under πθ from samples drawn under πold, you have to multiply by the importance weight πθ/πold to correct for the difference. The reason anyone arrives at this step is that “sample from a different distribution, multiply by a ratio to correct it” is the most standard move there is in probability (importance sampling).
So under what conditions does the importance weight πθ/πold become really dangerous? Work it out: if a token has probability 0.001 under the old policy and 0.5 under the new one, what is the weight?
Think it through, then look
The weight = 0.5 / 0.001 = 500. A trajectory has tens of thousands of tokens, and every one of them can contribute a multiplier like that. The variance of the weights blows up — the gradient from a handful of trajectories can drown out every other sample, and one update takes the model somewhere nobody can predict. The older the data, the further πθ is from πold, and the higher the chance of extreme weights. That is the concrete mechanism by which stale data “threatens training stability”.
Now turn it round: if we could force a guarantee that πθ never strays too far from πold on any token, would the disaster in the previous step still happen?
Think it through, then look
No. If every token's ratio is constrained to, say, [0.8, 1.25], then however old the data is the importance weights stay mild, the variance is bounded, and the gradient estimate cannot be hijacked by a few samples. That is what “constraining policy updates within a local neighbourhood” means — it does not make the data fresh, it makes sure the gap between the old and new policies never grows large enough for the correction to fail. Doing this token by token is far finer-grained than doing it at the level of the whole trajectory, because a trajectory of tens of thousands of tokens can look much the same overall and still have individual tokens whose ratio is absurd.
Last question: what does this scheme cost? There is no free lunch.
Think it through, then look
The cost is that the learning speed of a single step is capped. Since no update is allowed to travel far, there is a ceiling on how much the model can change per iteration. What you buy with it is the ability to use large amounts of stale data safely, to run GPU utilisation flat out, and to do partial rollout. In long-horizon agentic training the trade is clearly worth it: the gain in throughput far outweighs the loss in per-step size. The paper gives no quantitative comparison for this ledger.
The paper specifies λ ∈ (0,1) (an open interval; 0 and 1 are not attainable). Construct the failure scenario at each of the two extremes, λ → 1 and λ → 0, and say why both endpoints have to be excluded.
Variant: suppose every trajectory has exactly the same length (no long tail) — what should λ be? What does that tell you about where the benefit of partial rollout actually comes from?
A question that pulls things together: in 8.7 the paper will say that MOPD's per-token distillation reward “plugs seamlessly into the RL framework, and naturally lets infrastructure-level optimisations such as partial rollout be used for distillation training on long-horizon tasks as well”. String “partial rollout”, “per-token regularisation” and “per-token dense reward” into one chain in which each holds up the others, and say what happens if you take out the middle link.
Variant: if a task is short to begin with (single-turn question answering, say), does the coupling between the three still hold? On short tasks, is partial rollout worth anything?
Defend it: if I were a reviewer
You say “per-token regularisation constrains updates to a local neighbourhood, so it can tolerate extremely stale data”. But that sounds like a fancy way of saying “turn the learning rate down”. Explain what is essentially different between your method and simply shrinking the step size. Also, you give neither a value for λ nor a stability comparison with and without the regularisation — on what grounds should I believe this mechanism is really doing anything?
A reference defence (put it in your own words first, then look)
The first question can be answered head on: there is an essential difference. Turning the learning rate down shrinks the step size for all parameters and all tokens alike; it suppresses the “total size” of the update, but it cannot suppress the variance produced by an importance weight of several hundred on an individual token — the direction of that extreme sample still dominates the gradient, you just travel more slowly overall. Per-token regularisation constrains the probability ratio between the old and new policies on every token, which cuts off the source of the variance directly. Put differently, the learning rate controls “how far you go”, and the per-token constraint controls “which direction it is legal to go in”. Where extreme outlier weights exist, the latter is necessary and the former cannot replace it.
The second question has to be conceded: this passage of the paper really does give only a verbal account of the mechanism, no concrete value for λ, and no ablation to show that “it collapses once you remove the regularisation”. It pushes the algorithmic detail off to the Kimi K2.5 paper it cites. So strictly speaking, “this mechanism does work at K3 scale” is, within this paper, an authors' claim rather than a conclusion supported by the paper's data. What can be said in its defence: the combination of partial rollout and this regularisation has already been reported publicly for K2.5, and K3 is carrying it over rather than originating it; but the reader should treat it as “a design choice inherited from the previous work”, not “a conclusion this paper verified”.
8.5 Reasoning-effort RL: putting a budget on “thinking”
Why thinking length has to be managed
Because if it is not, the model overthinks. RL rewards only “getting the answer right”, so the safest policy from the model's point of view is “a bit more thinking never hurts” — thinking longer costs it nothing anyway. The result is three thousand words of derivation for 1+1. That wastes the user's time and money, and it leaves the model unable to fit a real need like “right now I just want a quick answer”.
K3's approach is a per-question budget control mechanism (carried over from K2.5). Each question x gets an initial token budget b0(x) attached to it, and that budget is estimated by the cold-start model; then, for any trajectory whose total token budget T(y) exceeds the scaled threshold τ · b0(x), the task reward is rewritten straight to −1.
| Symbol | What it is | Intuition |
|---|---|---|
| x | one concrete question (a prompt) | “this question” |
| y | one complete trajectory the model produces for x | “the whole process of this attempt” |
| b0(x) | the initial token budget for that question, estimated by the cold-start model | “roughly how many words this question is worth” — note that it varies from question to question; a hard question naturally gets a bigger budget |
| τ | the budget multiplier, a scalar greater than 0, configured per domain | “the tolerance knob”: turn it up = allowed to think more, turn it down = forced to be brief |
| T(y) | the total token budget trajectory y actually used | “how much it actually wrote this time”; what exactly counts is set out below |
| −1 | the penalty value that overwrites the original task reward | “over time, void”: even a completely correct answer is treated as the worst case |
There are two definitions of T(y): for general tasks it measures the number of thinking tokens; for agentic tasks it counts cumulative output tokens, including the reasoning trace and the tool-call arguments. Counting the tool-call arguments in the second case is necessary — otherwise the model can stuff its long speeches into tool arguments and get round the limit.
A stage-wise curriculum: annealing τ from large to small
Training follows a stage-wise curriculum(stage-wise curriculum) over the budget multiplier τ: first train the max-budget variant with a relatively large τ, while still putting a ceiling on the maximum budget to hold overthinking down; then anneal τ (lower it gradually) to smaller values, giving the high and low effort experts. The adjustment of τ is configured per domain and carried out under human-in-the-loop guidance.
The last sentence matters a lot: the trajectories produced at all reasoning levels are collected jointly and used for supervised fine-tuning and multi-teacher on-policy distillation. That is, the nine experts are not only the teachers for the final MOPD — the trajectories they produce are themselves a data asset.
A note: why it is “large first, small later” and not the other way round
The paper gives the order and no reason for it. A reasonable guess: first let the model learn thoroughly, under a loose budget, “how to get hard problems right”, so that genuine problem-solving ability forms; then tighten the budget step by step to force it to learn “how to say the same thing in less space”. The other way round — training low first and loosening afterwards — the model may never have learned the full solution to a hard problem in the first place, and loosening later cannot make that up. This is this site's guess; the paper gives no such explanation and runs no ablation on the ordering.
For a general-task question x, the cold-start model estimates an initial token budget of b0(x) = 800. You are currently training the high-tier expert, τ = 2.5. The model produces two trajectories: trajectory A thought for 1900 tokens and the answer is correct; trajectory B thought for 2100 tokens and its answer is correct too. What reward does each of the two get?
Variant: same question, same two trajectories — what happens if what you are training now is the low-tier expert and τ has been annealed to 1.2? What about τ = 3.0? Explain why “the same trajectory collecting opposite rewards under different τ” is precisely the reason a stage-wise curriculum can produce three tiers of expert.
Defend it: if I were a reviewer
Rewriting the reward of an over-budget trajectory straight to −1 is far too crude. A correct answer that goes over by just 1 token and a piece of garbage that is completely wrong get exactly the same reward — this plainly destroys the continuity of the reward function and will contaminate advantage estimation. Why not use a penalty term that decays smoothly with the size of the overrun?
A reference defence (put it in your own words first, then look)
There are three angles to defend from. First, a hard threshold resists hacking: a smooth penalty means the model can always make the profitable trade of “think a little more, lose a little more reward, but buy a higher accuracy”, and as long as the marginal gain in accuracy beats the marginal cost of the penalty, the model will keep going over — until the penalty term amounts to nothing. Rewriting hard to −1 turns “over budget” into a wall that cannot be touched, and the trade no longer exists. Second, the threshold itself is adaptive: b0(x) is estimated per question by the cold-start model, and a hard question naturally gets a bigger budget, so this wall is not “one absolute word count for everything”; what is crude is the form of the penalty, not where the threshold sits. Third, the goal is to produce controllable tiers: what K3 wants is three experts whose behaviour is clearly different (low/high/max), and a smooth penalty would blur the boundaries between the three; only a hard threshold plus stage-wise annealing of τ gives you three models with clean boundaries.
But half of the reviewer's worry stands up: the paper genuinely does not discuss boundary effects near the threshold, and reports nothing on questions like “was the model observed learning to wrap up in a rush just before the threshold, so that answers get truncated”; nor does it provide a comparison against a smooth-penalty scheme. The honest statement is: this is an engineering choice validated in the previous work (K2.5) and carried over, and this paper offers no new empirical support for it.
8.6 The agentic generative reward model: let an AI be the judge, but make it judge by procedure
Why you need an “AI judge”
Maths problems and coding problems can be marked right or wrong automatically — run it and you know. But “write me a tactful apology email” and “is this research report any good” have no reference answer, and no program can mark them. The paper calls this kind of task non-verifiable general tasks(non-verifiable general tasks). Without a reward signal RL cannot run at all, so a judge has to be built.
For tasks like these K3 uses an agentic generative reward model(Agentic Generative Reward Model, Agentic GRM), keeping K2.5’s tournament-style group reward and binary comparison — that is, several candidate answers are pitted against each other in pairs and ranked the way a tournament ranks players, instead of each answer being given one absolute score.
An analogy
Ask a teacher to give each of a hundred essays an absolute percentage mark and the standard is very hard to hold steady between the first essay and the hundredth (the marking drifts). Ask them instead to answer only “which of these two is better” each time and the judgement is far more stable. Tournament-style binary comparison is that idea.
Where the analogy breaks down: a human teacher’s pairwise comparisons come with an intuition for transitivity, whereas an AI judge can produce a cycle A>B, B>C, C>A. The paper does not discuss how to handle that inconsistency.
What is new in K3 is this: besides the general agentic abilities (the judge itself can call tools and go and actually inspect the artefact), this agentic judge is required to follow a mandatory protocol(mandatory protocol), four steps, in a fixed order:
- Read the result, the artefact or the text output;
- Generate a rubric (rubric, that is, a checklist of scoring criteria);
- Use that rubric to score every candidate;
- Take the rubric scores and record them in a scorepad (a scoreboard).
Why this order is forced
The point is that step 2 must come before step 3. If you let the judge say straight out “I think A is better”, it is easily swayed by surface factors (longer, fancier formatting, more confident wording) and then makes up a reason afterwards. Forcing it to write out the scoring criteria first and only then score against them item by item externalises the judging process into steps you can inspect, and cuts down on “going by feel”. Step 4, writing the scores into the scorepad, leaves a traceable record of the whole verdict.
Verbosity control: plugging a hole in the judge itself
The paper points at one specific direction of reward hacking: the model evolves towards “output that gets more and more verbose”, because in the judge’s eyes a long answer often looks more complete and more careful. To mitigate this the authors added a budget-based verbosity control, done much like the reasoning effort control in 8.5: given an initial verbosity ℓ0 estimated by the cold-start model and a multiplier σ, any candidate whose output length exceeds σ · ℓ0 automatically loses the binary comparison.
Construct a counterexample: design one concrete class of user request for which the rule “output longer than σ·ℓ0 automatically loses the comparison” systematically punishes the better answer. Then explain: why is using the same mechanism on “thinking length” (8.5) safer than using it on “output length” (8.6)?
As for the second question: controlling thinking length is safer because the user never sees the thinking tokens and they are not the deliverable — cutting away surplus thinking is almost all upside, and the worst case is that too little thinking on a hard problem produces a wrong answer, which the task reward itself punishes, so the two signals point the same way. Output length is the deliverable itself, and cutting it can cut away exactly what the user asked for; here the length penalty and the quality reward point in conflicting directions, and nothing goes wrong only if ℓ0 is estimated very accurately. The paper does not discuss the estimation error in ℓ0, nor does it say whether σ is configured separately by task type.
Variant: if the rule were changed from “automatically loses the comparison” to “length counts as a criterion only when two candidates are tied on quality”, would the counterexample above still hold? What benefit of the original rule would that change give up?
8.7 MOPD: squeezing nine experts back into one model
You now have nine expert models on your hands. And here is the problem: no user is going to install nine models. Each one is a complete set of weights; on GPU memory alone that is nine times the cost. They have to be merged into one.
Knowledge distillation: have a “student” model imitate the output of a “teacher” model, so that the teacher’s ability is carried across into the student. Traditional distillation has the student fit the teacher’s distribution on text the teacher generated itself.
On-policy distillation: change it so that the student generates the text itself, and the teacher gives a verdict on every single token the student writes. That way the student practises on “the mistakes it really does make”, not on “the teacher’s perfect path, which it never walks itself”.
An analogy
Traditional distillation is like watching a recording of a master teacher solving problems and copying it out again and again. On-policy distillation is like working the problems yourself, with the teacher beside you nodding or shaking their head at each step. The second is obviously better targeted — because the teacher’s comments land at the fork in the road where you really do go wrong, not on the perfect route you would never have thought of in the first place.
Where the analogy breaks down: the real “nod or shake of the head” is not binary but a continuous real number (Eq. 15), and there is one for every token, far denser than a human teacher’s comments.
K3 uses the multi-teacher version — MOPD (Multi-Teacher On-Policy Distillation). During training, for a given domain d and a sampled reasoning effort level e ∈ {low, high, max}, the optimisation is guided by the corresponding one of the nine experts, the teacher model πteacher(d,e). That is, the teacher is picked dynamically according to the domain and effort level of the current sample, rather than all nine being averaged together.
Given an input query x and a prefix y<t, the per-token OPD reward evaluated at yt is defined as:
| Symbol | What it is | Intuition |
|---|---|---|
| yt | the t-th token the student has just written | “the word just put down” |
| y<t | every token already written before it | “what has been written so far” |
| x | the input query | “the question” |
| d, e | the domain label and the reasoning effort level ∈ {low, high, max} | the step that decides “which teacher gets called in to mark it” |
| πteacher(d,e)(yt|·) | the probability the teacher gives this token in the same context | “would the teacher have written this word” |
| πθ(yt|·) | the probability the student (parameters θ) gives this token | “how much did I want to write this word just now” |
| log(πteacher/πθ) | the log of the ratio of the two probabilities | the core: the teacher rates this word higher than I do → ratio > 1 → log > 0 → positive reward; the other way round is negative; both rate it equally → ratio = 1 → log = 0 → no reward and no penalty |
| sg(·) | the stop-gradient operator (stop-gradient) | “this value is used only as a score, no gradient may flow back through here” — it is frozen into a constant reward |
| clip(·, −Rmax, Rmax) | clips the result into the interval [−Rmax, Rmax] | “however absurd the score, it cannot go past these bounds” |
| Rmax > 0 | the clipping threshold; the paper says it is there to bound extreme advantage signals and stabilise RL training | a safety valve. The paper does not give its actual value |
An aside: why the log of the ratio rather than the difference πteacher − πθ directly
Because an absolute difference between probabilities has no sense of scale. 0.9 and 0.8 differ by 0.1; 0.1 and 0.001 differ by only 0.099 — but the second is “the teacher thinks it possible and I have all but ruled it out”, which is far more serious. Taking the ratio and then the log measures the relative factor: 0.9/0.8 ≈ 1.125, log about 0.12; 0.1/0.001 = 100, log about 4.6. The log has one more benefit: it is symmetric under inversion — if the teacher’s probability is 10 times mine the reward is +log10, if mine is 10 times the teacher’s it is −log10, so reward and penalty are symmetric.
The paper stresses that this is a dense(dense) reward signal — every token gets a score, rather than one score for a whole trajectory. So it “integrates seamlessly into our RL framework, naturally allowing infrastructure-level optimisations such as partial rollout to be applied to distillation training on long-horizon tasks as well” (this was already worked out in the synthesis question in 8.4).
An honest negative result
The paper writes: they also tried a finer-grained top-k distillation objective (that is, not only looking at the one token the student actually wrote, but comparing the whole distribution of teacher and student over the k highest-probability tokens), but “in our setting we observed no clear advantage in convergence speed or final performance”. This is a valuable negative result — it means that “give the signal only along the path the student actually walked” is already enough. But note the wording, “in our setting”: the paper gives no data at all from this comparison, and does not say what k was set to or on which tasks the comparison was run. This is an empirical statement by the authors, not evidence that was shown.
In some context the student gives the token “therefore” a probability of 0.05, and the corresponding teacher gives that token a probability of 0.50. Let Rmax = 4. Work out the reward Eq. 15 gives, and say in one sentence what it is telling the student. (Hint: ln 10 ≈ 2.30)
Variant: if the teacher’s probability is 0.50 and the student’s is 0.0001, the ratio is 5000 and the log is about 8.5, which exceeds Rmax = 4. What is the reward then? Compare this case with the one above and say what the clip is really guarding against.
Eq. 15 has two “safety devices” in it: sg(·) and clip(·, −Rmax, Rmax). Someone says “since both are there to stabilise training, keeping one of them is enough”. Explain that the two are not guarding against the same thing, and give for each one scenario that goes wrong once it is removed.
Variant: if the clip were replaced by a tanh squashing of the log ratio (smooth saturation instead of a hard cut-off), would it do the same job? What would be lost? (Think back to the discussion of “hard threshold vs smooth penalty” in that defend-it in 8.5 — is the conclusion the same here?)
8.8 MXFP4 quantisation-aware training: moving the model into a smaller house
Why quantise at all
Every weight in the model is a number, kept in GPU memory. The more numbers there are, and the more bits each number takes, the more memory cards you need and the more expensive serving becomes. The paper states its goal plainly: “reduce the memory footprint and serving cost at deployment”.
Bit: the smallest unit of information in a computer; one bit can only be 0 or 1. n bits can represent 2n different values.
So “4 bits” means this: the weight can take only 24 = 16 possible values. By comparison, BF16, the format deep learning uses most, is 16 bits and can represent 216 = 65536 values. Going from 16 bits down to 4 bits, the same batch of weights takes a quarter of the memory.
An analogy
A weight used to be able to sit at any graduation between 0 and 65535; now it may only pick the nearest of 16 preset graduations. It is like swapping a ruler with sixty thousand graduations for one with only 16 — every length you measure comes out with some error.
Where the analogy breaks down: MXFP4 is not 16 evenly spaced graduations. MX stands for Micro-scaling: a small group of weights share one scaling factor, and within the group 4-bit floating point is used. So the graduations are “adaptive per group” — when the weights in one group are all small, those 16 graduations are laid down densely over the small-value range. This is far more accurate than naive 4-bit integer quantisation. (The paper gives only the format name and a citation; the unpacking of the MX format here is general background knowledge, not content from the paper.)
Quantise only the “expert weights”, keep everything else at high precision
The paper’s concrete scheme is: quantise the MoE expert weights — which the paper says “dominate the model’s parameter memory” — to MXFP4, and compute activations in MXFP8; while all non-expert components (attention projections, latent MoE projections, the shared expert, the MoE router) are kept at higher precision.
Why it is the expert weights of all things that most deserve quantising
Two reasons stack up. First, they take up the most room — an MoE (mixture-of-experts) architecture has a great many experts, each of them a whole set of feed-forward network weights, and together they account for the overwhelming majority of the total parameter count. Squeeze the biggest block and the gain is the biggest. Second, they stand up to squeezing best — only a few experts are activated on any one forward pass, so the weight error in any single expert has a limited blast radius; whereas components like the attention projections and the router are passed through by every token, and the router in particular is the key decision point that settles “which experts do the work”, so a small error there can pick the wrong expert and the mistake gets amplified. That is why the paper leaves them at high precision. This is a textbook engineering decision of the form “spend the compression budget where the gain-to-risk ratio is highest”.
QAT runs across the whole post-training stage, covering both SFT and RL, so that the model “adapts to the precision loss quantisation causes”. And the paper makes a point of this: during RL, rollout and training share the same quantisation scheme — which removes the train-inference mismatch (this was already worked out in the quiz in 8.2).
Read this carefully
In this section the paper gives no concrete numbers at all for before and after quantisation — it does not say how many GB of memory were saved, nor how many points of accuracy were lost, and there is no “QAT vs post-training quantisation” comparison. “The model adapts to the precision loss quantisation causes” is a qualitative claim. Also, whether “higher precision” means BF16 or FP8 specifically, the paper does not spell out either.
A team copies K3’s scheme wholesale, but to squeeze memory harder they also quantise the MoE router to MXFP4 while they are at it, on the grounds that “the router has very few parameters, so squeezing it changes nothing, but it is better than nothing”. Point out two problems with this decision.
Variant: the shared expert (the one expert every token passes through) is also a feed-forward network, structurally the same as an ordinary expert. Why does the paper leave it at high precision instead of squeezing it along with the rest? (Hint: think about what “every token passes through it” means for the accumulation of error.)
8.9 The draft model and speculative decoding: let the small model guess first, the big model only verifies
Why a big model is slow to generate
Because generation goes one token at a time: to write token number 100, you have to finish computing the 99 before it. Every token you write means running the whole enormous model end to end once more. This process cannot be parallelised — it is not that there is too little compute, it is that the dependency has it stuck. The paper says “for serving complex long-horizon agentic models, optimising inference efficiency is critical”.
Speculative decoding: first let a very small, very fast draft model guess the next several tokens in one go, then let the big target model verify those tokens all at once, in parallel. Verification can be parallelised (because the tokens to be checked are already sitting there), so a single forward pass of the big model can confirm several tokens. Guess right and you move several steps forward at once; guess wrong and you throw away everything from the wrong point on and start again from there. The key: this process is lossless — the distribution of the final output is exactly the same as it is without the draft model, only faster.
Where K3's draft model comes from
K3 already carries a multi-token prediction layer during pre-training, whose structure mirrors a backbone block. And an EAGLE-3-style draft model happens to be a single decoder layer, a structure that matches the MTP layer. So what the authors do is this: fine-tune the pre-trained MTP layer into an EAGLE-3-style draft model, freeze the target model, and train only the draft layer and the feature-fusion projection.
Training follows EAGLE-3's “training-time test” protocol, with the draft unrolled seven steps. After the first step the target-side feature for the newest position cannot be obtained, so the draft consumes its own output from the preceding steps — which makes the situation during training match the looped drafting process at inference time.
Why “unroll seven steps” instead of training just one step
Because at inference the draft has to guess several tokens in a row, and when it makes the second guess it has only its own first-step output to go on (the big model has not run yet). If during training you feed it the correct target-side feature at every step, it never experiences “carry on guessing from your own possibly wrong output”, and it collapses the moment it reaches inference — this is the classic exposure bias(exposure bias). Unrolling seven steps and making it eat its own output reproduces the real conditions of inference inside training.
Three-level feature fusion and that elegant initialisation
The draft's input does not look only at the target model's last layer; it fuses low-, mid- and high-level features, taken respectively from the outputs of the 1st, the 4th and the last AttnRes block. These three sets of features are concatenated, then projected back to the hidden dimension by a bias-free matrix WE3.
And WE3 is initialised as [0 0 I] — two zero blocks plus one identity matrix.
Derive it yourself: why [0 0 I] guarantees “a start with no step backwards”
First get the shapes straight. Let the hidden dimension be h. How many dimensions does the concatenated vector [hl; hm; hh] have? What shape must WE3 be to project it back to h dimensions?
Think it through, then look
Each of the three feature sets is h-dimensional, so concatenated they are 3h-dimensional. To project back to h dimensions, WE3 must be h × 3h. Slice it crosswise into three blocks, each h × h — and those three blocks correspond one to one with the low, mid and high feature sets. The notation [0 0 I] says exactly this: the first two blocks are zero matrices, the third is the identity matrix.
Now actually do the multiplication: what does [0 0 I] · [hl; hm; hh] equal?
Think it through, then look
Block matrix multiplication: the result = 0·hl + 0·hm + I·hh = hh. That is, the fused representation is exactly the high-level feature itself, and the low and mid levels are ignored completely. The paper's own words: “so that the fused representation coincides with the high-level feature hh at initialisation”.
The key question: what is special about hh as far as this draft layer is concerned? (Recall where the draft layer came from.)
Think it through, then look
The draft layer is fine-tuned from the pre-trained MTP layer, and hh (the output of the last AttnRes block) is precisely the input the MTP layer received during pre-training. The paper points this out explicitly. So at the moment of initialisation, this whole “three-level fusion + projection + draft layer” structure is functionally identical to “just take the pre-trained MTP layer and use it”.
So what exactly does “a start with no step backwards” mean? And if you switched to a common random initialisation (small random numbers, say), what would happen?
Think it through, then look
“No step backwards” means: step 0 of fine-tuning is already at the level of the pre-trained MTP layer, and every step after that is an incremental improvement on an already decent starting point — the learning curve jumps off a high platform rather than off the floor. With a random initialisation, the fused representation would be a random linear combination of the three feature sets, something the MTP layer has never seen, which amounts to scrambling everything pre-training taught it; the model would have to spend a great many steps relearning “ignore the noise, find the high-level feature again” before it could even begin to improve. The paper says it “subsequently learns to incorporate low- and mid-level features during fine-tuning” — the positions of the zero blocks carry gradients and will grow out of 0 slowly, so the low- and mid-level information is not given up permanently, it is only deferred until the model has capacity to spare.
Finally: why is this matrix bias-free? If you added a bias term b and initialised it randomly, would the argument above still hold?
Think it through, then look
It would not. With a bias, the initial output is hh + b, which equals hh only when b happens to be initialised to the zero vector. Dropping the bias makes the “identity at initialisation” property hold structurally, without depending on any extra convention. The paper only says that WE3 is bias-free, it does not explain why; this inference is filled in by this site, but it is entirely consistent with the initialisation intent the paper describes.
Eq. 16: optimise the “acceptance rate” itself, directly
How much faster speculative decoding actually is depends on what fraction of the tokens the draft guesses get accepted. The paper says: the speedup under lossless speculative sampling is determined by the per-token acceptance rate, and that acceptance rate equals
| Symbol | What it is | Intuition |
|---|---|---|
| V | the vocabulary, the set of every token the model can possibly output | “every word in the dictionary” |
| x | one specific token in the vocabulary | “one particular word in the dictionary” |
| p(x) | the next-token probability the target model (the big model) gives x | “the distribution of the correct answer” |
| q(x) | the next-token probability the draft model (the small model) gives x | “the distribution of the guess” |
| min(p,q) | whichever of the two is smaller | “the part of the probability mass both of them agree on” |
| Σx∈V | compute min once for every token in the vocabulary, then add them all up | the overlapping area of the two probability curves |
An analogy: the overlapping area
Picture p and q as two mountains drawn side by side on the same sheet of paper (the horizontal axis is every token in the vocabulary, the vertical axis is probability). Each mountain has total area 1 (probabilities sum to 1). Σ min(p,q) is the shaded area where the two mountains overlap. If the two mountains coincide exactly, the shading is the whole mountain, area 1; if they miss each other completely (not one of the tokens the draft guesses is a token the big model would ever say), the shaded area is 0. So the acceptance rate naturally falls in [0, 1], and what it measures is how alike the two distributions are.
Where the analogy breaks down: the vocabulary is discrete (tens of thousands of isolated bars), not a continuous hillside, so strictly it is “the sum of the overlapping bar areas”. Also, the acceptance rate is computed once per token position, not once for a whole sentence.
What comes next is the most important insight in this part of the paper. If you want a high acceptance rate, the most natural move is to make the draft fit the target — traditionally by using KL divergence (the standard measure of how much two distributions differ) as a proxy objective. But the paper points out: for a capacity-constrained draft model, minimising the traditional KL divergence proxy does not guarantee maximising this acceptance rate. So they simply optimise directly the likelihood-based LK loss, which is the negative logarithm of the acceptance rate itself:
| Symbol | What it is | Intuition |
|---|---|---|
| LLK | the loss function to be minimised | the number where “smaller is better” |
| Σ min(p,q) | the acceptance rate, taking values in (0, 1] | see the table above: the overlapping area |
| − log(·) | take the logarithm, then negate | turns “bigger is better” into “smaller is better”: at acceptance rate = 1 the loss = −log 1 = 0 (optimal); as the acceptance rate goes to 0 the loss goes to +∞. Minimising LLK is maximising the acceptance rate |
The paper adds two implementation details: p and q are both evaluated at temperature 1, and no auxiliary ground-truth cross-entropy term is added (that is, no ordinary language-modelling objective of the “make the draft fit the real next word” kind is mixed in; it aligns purely to the target model). Beyond that, the draft fine-tuning reuses the post-training QAT configuration: MoE expert weights in MXFP4, their input activations in MXFP8, non-expert modules kept at higher precision.
Prove: if the draft model's and the target model's next-token distributions are exactly the same (that is, q(x) = p(x) for every x), then the acceptance rate Σx min(p(x), q(x)) = 1, and the loss in Eq. 16 is LLK = 0. Then answer: can the acceptance rate be greater than 1?
Variant: suppose the vocabulary has only 3 tokens, p = (0.7, 0.2, 0.1), q = (0.4, 0.4, 0.2). Work out the acceptance rate and LLK by hand. Then try q = (1.0, 0, 0) — for this “extremely confident but betting on the wrong distribution” draft, what is the acceptance rate? Is it better or worse than the previous draft?
Synthesis question: the paper says “for a capacity-constrained draft model, minimising the traditional KL divergence proxy does not guarantee maximising the acceptance rate”. Construct a concrete example showing that KL and the acceptance rate can give different preference orderings, and explain why the premise “capacity-constrained” is what makes that sentence hold. Finally, connect this insight to the WE3 = [0 0 I] initialisation trick: what one kind of engineering thinking do they both embody?
Why “capacity-constrained” is the key premise. If the draft model had unlimited capacity it could copy p exactly, giving KL = 0 and acceptance rate = 1; the optima of the two objectives coincide, and it makes no difference which you use as the loss. But the draft is only a single decoder layer, and it has no way to align the probabilities of thirty thousand tokens all at once — it has to make trade-offs. And the direction of those trade-offs is decided precisely by the loss function: KL forces it to spend its precious capacity on “looking after the long tail, never letting any q(x) become 0” (because the penalty there is infinite), while the acceptance rate only cares about “getting the handful of head tokens that will really be sampled lined up”. Giving a model that must make trade-offs the wrong criterion for trading off is exactly what the paper means by “does not guarantee maximising the acceptance rate”. Optimise the quantity you actually want, directly, do not use a proxy — that is the whole motivation for Eq. 16.
The thinking it shares with [0 0 I]. Both are expressions of refusing to compromise at an intermediate step and aiming straight at the final objective: one says “do not optimise the KL proxy, optimise the acceptance rate directly”; the other says “do not initialise randomly and then slowly learn your way back, start straight from the solution pre-training has already validated”. What they have in common: wherever you can turn “going the long way round” into “aiming directly”, do not go round — especially when resources (draft capacity, fine-tuning budget) are limited, that detour is pure loss.
Variant: the paper also says that p and q are evaluated at temperature 1, and that no ground-truth cross-entropy term is added. Explain: if you mixed in a cross-entropy term that makes “the draft predict the real next word”, would it hurt the acceptance rate? (Hint: the draft's job is to imitate the target model, even when the target model is wrong.)
Read this carefully
For this entire section the paper gives no measured speedup figure at all — it does not say what acceptance rate was actually reached, and it reports no end-to-end throughput gain. For hyperparameters such as “unrolled seven steps” and “the 1st, the 4th and the last AttnRes block”, the paper likewise does not say how they were chosen, or whether any search was done. Beyond that, the draft fine-tuning uses the QAT configuration, but the paper does not discuss how much the acceptance rate of the quantised draft model drops.
Chapter summary
K3's post-training is a three-stage pipeline: SFT uses trajectories synthesised by older models plus human gatekeeping to build a cold-start policy that “can get things done”; RL splits that into three domains × three levels of reasoning effort, nine experts in all, each trained to the limit of its ability on its own track; MOPD then uses a per-token dense reward (Eq. 15) to compress what those nine experts can do back into a single model.
Three threads run underneath all of it. The first is the trade-off between throughput and stability: partial rollout buys throughput by “not waiting for the stragglers”, at the cost of stale data, and falls back on per-token regularisation to constrain updates to a local neighbourhood — and MOPD's dense reward happens to make that same infrastructure usable for distillation. The second is anti-reward-hacking: a model naturally evolves towards “think for longer, write for longer”, and K3 uses one and the same pattern (a cold-start baseline estimate × a multiplier = a hard threshold) to block reasoning effort (over τ·b0 scores −1) and output verbosity (over σ·ℓ0 is automatically scored negative) respectively. The third is deployment awareness: from day one of SFT the training follows the quantisation scheme that will go live (MoE expert weights MXFP4, activations MXFP8, non-expert components at high precision), and RL's rollout and training share the same quantisation, eliminating the train-inference mismatch; at the same time the MTP layer carried over from pre-training is fine-tuned into an EAGLE-3 draft model, the [0 0 I] initialisation guarantees a start with no step backwards, and the acceptance rate itself is optimised directly (Eq. 16) rather than a KL proxy.
The boundaries to watch: for almost every key hyperparameter in this section (λ, τ, σ, Rmax, the exact format meant by “higher precision”) the paper gives no value, and it provides no ablation; Figure 8 is an internal training-time curve with no absolute scale on either axis; “no clear advantage” for top-k distillation and “the model adapted to the precision loss” for MXFP4 QAT are both qualitative statements by the authors, with no supporting data shown in the text. When you read these places, treat them as design choices, not verified conclusions.
第9章 让模型「有事可做」:RL 任务合成与智能体环境
一句话导语:强化学习卡住的地方,经常不是算法不够聪明,而是没有足够多、足够难、而且机器能自动判对错的题——这一章讲 Kimi 团队怎么造题、怎么造环境、怎么防模型作弊。
学完这一章你应该能做到
- 用自己的话解释 agent harness 是什么,以及只用一套固定 harness 训练为什么会出问题
- 说清知识图谱为什么必须是有向无环图,「祖先节点上下文」在检索里起什么作用
- 照论文规则给一个 GPU 内核解答估出奖励,并说清为什么正确性是硬门槛、性能是连续分、锚点为什么定在 0.5
- 自己设计一个能骗过某个奖励函数的作弊策略,并指出论文哪一层防线挡得住、哪一层挡不住
- 指出「模拟应用」「隐藏验证器」的适用边界,以及论文没有给出证据的地方
先建立总直觉:RL 真正的瓶颈在哪
很多人以为强化学习难在算法。训练大模型时,更难的是有没有题。RL 的循环说白了就是:让模型试着做一件事 → 打分 → 分高的多做、分低的少做。这个循环要转起来,题必须多(否则很快被背下来)、必须难(太简单没有可学的信号),而且最关键——分数必须自动算得出来。靠人判,一天几千道,喂不饱一个几千亿参数的模型。
「机器能自动判对错」在论文里就是那个反复出现的词:可验证(verifiable)。整个 §4.2 从头到尾在解决同一个问题——怎么把真实世界里原本只有人才判得了的任务,改造成机器能自动判分、而且骗不过去的题。请把这句话当本章主线。
9.1 统一白盒 RL 环境:别让模型只认识一套「工具箱」
先解决一个名词。大语言模型本身只会一件事:给它一段文字,它接着往下写。它不会自己打开浏览器,也不会自己运行代码。那「AI 帮我改了代码、跑了测试」是怎么发生的?外面套了一层程序:把「你可以用这些工具」写进提示词、把模型吐出的文字解析成真正的函数调用、把工具返回的结果塞回上下文、上下文太长时还要负责裁剪或压缩。
智能体脚手架(agent harness):模型与外部世界之间的那层壳。它规定工具长什么样、系统提示怎么写、上下文怎么管理、有没有记忆、能不能派子智能体。模型只能透过这层壳感知世界,也只能透过它动手。
打个比方
模型像一个只会说话的大脑,harness 是接在它身上的手、眼睛和记事本。同一个大脑接上不同的手,能干的事和干事的方式都会变。
类比失效处:真实的手不会篡改你看到的东西,harness 会——它主动压缩、裁剪、重排上下文,决定模型「记得什么、忘了什么」。它不是被动外设,而是深度参与了模型的思考。
论文指出:用单一固定的 agent harness 训练,会让模型过拟合到某一套具体的工具 schema、系统提示、上下文管理机制或交互协议上。也就是模型学会的可能不是「怎么完成任务」,而是「怎么在这套壳里完成任务」;工具改个名字、提示换个写法,它就懵了。像一个只在某位老师的卷子格式下刷了三年题的学生,换份排版就不会做。
对策是把 harness 本身拆开,抽象成一组可配置、可组合的模块:工具接口、系统提示、上下文管理策略、技能(skills)、记忆(memories)、子智能体(subagents)等。像积木一样通过配置组合,这个环境可以实例化出主流 harness——论文点名 Kimi Code、Claude Code、Codex、OpenClaw、Hermes——也能拼出全新的。训练时,系统为不同任务组动态构造不同的 harness 配置,让模型见到的是模块的各种组合,而不是任何单一 harness 的惯例。论文还提到同一套抽象也支持跨多种任务域的 RL,为训练更通用的智能体提供可扩展基础。
「白盒」两个字
白盒的对立面是黑盒——黑盒环境你只能调用、看不见里面。白盒意味着每个部件都能拆开、替换、重组,这正是「动态构造配置」的前提。论文本身未解释这个词,此处是按上下文的合理理解。
某模型在 Claude Code 里表现很好,换到一个自研 harness(工具名不同、系统提示写法不同、上下文压缩策略也不同)后成功率大幅下降。用 9.1 的语言说明:这是什么问题?统一白盒环境怎么防住它?
变式:如果不是动态构造配置,而是固定用五套主流 harness 轮流训练,效果会有什么不同?(提示:五套仍是有限的、各有惯例的集合,模型可能学会「先识别这是哪一套,再套用对应惯例」——那是过拟合到「五选一」,不是泛化。可组合模块的空间要大得多。)
9.2 知识图谱引导的任务合成:题目从哪来
有了装题的环境,下一问是题从哪来。论文的判断很实在:后训练任务的质量和多样性,很大程度上由源材料决定。接着是两个互补的观察:由细粒度概念引导的检索能挖出专门化的、代表性不足的知识(搜「机器学习」捞回来全是入门介绍,搜一个很具体的技术名词才能捞到稀缺材料);跨多样概念采样则拓宽领域覆盖(只往一个方向钻会偏科)。要在大规模上同时控制粒度与覆盖面,Kimi 构建了一个自演化、分层组织的知识图谱,由智能体通过网页规模的探索,在知识密集领域和编码领域上持续扩展。
图谱是怎么长出来的
有向无环图(directed acyclic graph, DAG):一堆点,点之间连着带箭头的边(有向),而且顺着箭头一直走永远回不到出发点(无环)。
论文把知识图谱构建成一个 DAG,通过递归的、智能体驱动的扩展建成:从一组预定义的粗粒度种子节点开始,给每个节点分配一个智能体实例,它做多次网页搜索调查对应概念。加新节点之前,智能体先探索已有图谱,识别等价或相关概念、在合适时复用已有节点、尽量减少重复。边永远从粗概念指向细概念,不管智能体先发现哪一端。新加的节点接着又分配给智能体继续探索;当负责的智能体判定当前概念已经足够原子,这条分支停止扩展。
为什么「边永远从粗指向细」这条规则这么重要
它一口气解决两件事。其一,保证图天然无环:箭头只能从粗走到细,顺着走就是不断变细,一个概念不可能比自己更细,所以绝不可能绕回起点。这很关键——在「递归扩展、多个智能体并发加节点」的场景里,你没法每加一条边就全图跑一遍环检测。其二,保证结果与发现顺序无关:智能体 A 先发现「注意力机制」再挖到「多头注意力」,智能体 B 反过来,两条路径必须画出同方向的边。没有这条规则,图的形状取决于谁先跑到哪里——那它就不是一个稳定的知识结构,而是一份探索日志。
造好图之后怎么用?为了瞄准想要的领域与任务类型分布,系统在不同粒度层级上采样节点——可以单独采,也可以按「相关组合」一起采(图 9 中虚线圈住的一簇)。从采到的节点导出关键词,再把这些节点在图谱中祖先节点的上下文信息结合进去,一起构造网页查询;检索回来的真实世界材料被组装起来,交给一个合成智能体产出各种类型的训练任务。
为什么要带上祖先的上下文
论文只陈述了做法,没有解释原因。一个自然的解释是消歧:细粒度的词经常一词多义,kernel 在 CS/AI 分支下指 GPU 内核程序,在数学分支下可以指核函数。光拿一个词去搜可能整个跑偏;带上祖先路径,搜索引擎才知道你要哪一个。这是合理推断,非论文原文。
论文规定「边永远从粗概念指向细概念,无论智能体先发现哪一端」。(1) 这条规则为什么能保证图里不会出现环?(2) 若改成「谁先被发现,边就从谁指向另一个」,这个图会坏在哪里?
变式:论文说「一条分支在负责的智能体判定当前概念足够原子时停止扩展」。这个停止条件由智能体自己判断,说出它的一个风险以及对最终任务分布的影响。(提示:判断标准不统一,不同分支的「原子」粒度可能差很远,按层级采样就会采到粗细不齐的东西。论文未说明如何校准这个判定。)
9.3 智能体环境中的可验证问题:让模型「动手」而不是「空想」
论文给了三类代表性例子。第一类是多步复杂信息搜索:模型自己规划怎么研究这个问题、一步步从网上收集证据,产出一个可验证的答案——「可验证」这个限定决定了题目必须设计成有确定答案的形式。第二类是专业人士的真实日常工作,论文举了投资银行、数据分析、法律实务:模型要把复杂请求分解开、在沙箱里操作领域工具、在几十到几百步内完成一份交付物——评的不是模型说了什么,而是它最后做出来的东西。第三类是多步可验证的视觉推理,覆盖 STEM 问题、视觉谜题、图表理解。
为什么视觉推理必须配一个 Python 沙箱
不配会怎样:给模型一张高分辨率图表,让它读出某个数据点。它只能「看一眼然后猜」——字太小、线太密就是看不清。人类会放大、会拿尺子比、会遮住无关部分只看一块,这些动作模型原本做不了。
论文的做法是:每条视觉推理轨迹都在一个配有隔离沙箱内 Python 解释器的智能体环境里生成。模型迭代地写代码并执行,用来裁剪、缩放或变换输入图像,做精确计算,或验证中间结果;然后把执行输出——包括生成出来的图像——作为新的观测,跨多个交互步接收回来。论文给出的结论是:随着模型学会做更多图像操作、收集更多观测,它在复杂视觉推理任务上的表现稳步提升。
容易读漏的一个词
很多人把这段读成「模型写代码算数」。真正的新意在「包括生成的图像」:执行结果若是一张新图,它会被当作新观测送回模型眼前。模型不是「看一次图一路推到底」,而是看图 → 改图 → 再看 → 再改的循环;视觉从一次性输入变成了可反复交互的环境。
假设有个「简化版」:模型仍可在沙箱里写 Python 处理图像,但执行结果只能以文字形式返回(数值、统计量、文字描述),生成的图像不回传。说明它相比论文的设计损失了什么,并举一个它一定做不好的具体任务。
变式:论文强调这个解释器在隔离沙箱里。不隔离会带来哪些问题?至少说两个,并区分哪个是安全问题、哪个是训练信号可信度问题。(提示:一是执行的代码可能破坏训练机器;二是不隔离时模型可能访问到不该看到的东西——答案文件或网络——那它拿到的奖励就不再反映真实的视觉推理能力。)
9.4 内核优化任务:一堂关于「奖励怎么设计」的公开课
GPU 内核(GPU kernel):跑在显卡上的一小段程序,负责完成一个具体计算,比如两个矩阵相乘。同一个数学运算可以写出无数个内核,结果一样但速度可能差好几倍——「内核优化」就是在结果不变的前提下把它写得更快。
论文说,为强化 Kimi K3 的 GPU 内核优化能力,团队构建了一个大规模内核任务套件,从单算子内核一直到融合的巨型内核(fused mega-kernels),题源来自 Flash Linear Attention 这类高质量 GitHub 仓库。套件覆盖多样的 GPU 编程方式:CUDA、Triton、CuTe DSL、Gluon、ThunderKittens、TileLang;也覆盖广泛使用的 GPU 架构和数值格式:BF16、FP8、FP4。
奖励:正确性是门,性能是分
规则很短,但每句都有讲究:奖励同时评估正确性和性能。每个内核提供一份 PyTorch 参考实现,数值误差超过预设阈值的解答得零奖励。性能以一份专家实现为基准打分:追平专家实现得 0.5 分,越接近硬件 roofline 奖励越趋近 1。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| r | 这次提交拿到的奖励 | 模型这一步该被夸还是被批 |
| err | 解答输出与 PyTorch 参考实现之间的数值误差 | 「算得对不对」的量化 |
| ε | 预设的数值误差阈值 | 及格线;浮点计算不可能完全相等,只能给一个容差 |
| [ err ≤ ε ] | 指示函数:方括号内条件成立取 1,不成立取 0 | 开关。误差超标直接乘 0,后面再快也白搭 |
| f(speed) | 性能得分函数,论文只给了两个锚点:追平专家 = 0.5,趋近 roofline → 1 | 连续刻度,快一点就多一点分 |
| roofline | 硬件的理论性能上限,由显存带宽和算力共同决定 | 百米赛跑的物理极限,谁也跑不过 |
读的时候要小心
式 9-1 是本书为讲清结构而自拟的,论文里没有这个公式。论文只给了文字描述和两个锚点。f 在 0.5 到 1 之间怎么插值、低于专家水平时怎么给分、roofline 具体怎么算,论文都没有说明——别以为你知道了完整的奖励函数。
为什么正确性要做成「乘性门」而不是「加一项」
设想另一种设计:奖励 = 0.3 × 正确性 + 0.7 × 性能。看起来也合理。但把自己放到模型的位置想:如果我把数值精度偷偷降一档,正确性那项掉一点点,性能那项可能翻倍,总分反而更高。梯度会稳稳推着模型往「牺牲精度换速度」走——而这恰恰是论文点名的作弊手法之一:precision reduction。
乘性门没有这个交易空间:误差一旦超过 ε,整个奖励归零,再快也换不回任何分数。背后是一个价值判断——一个算错的内核,无论多快,价值都是零。奖励函数的形状必须忠实反映这个判断,否则模型学到的就是设计者没打算教的东西。
为什么锚点是 0.5 而不是 1.0
再设想把「追平专家」定为满分 1.0。后果是:模型一旦追平专家,梯度就没了——超过专家和刚好追平得分一样,它没有动力再往前走一步,你等于亲手把人类专家设成了天花板。把专家放在 0.5,含义完全不同:专家只是中点,上面还有整整一半空间留给「继续逼近硬件极限」;下半段也有意义,「跑对但比专家慢」会落在 0.5 以下,梯度方向依然清晰。0.5 把奖励曲线切成两段有意义的区间:0 到 0.5 是追上人类,0.5 到 1 是追上物理。
第三层防线:作弊检测
奖励黑客(reward hacking):模型找到一条能让奖励变高、却完全没完成设计者真正想要的那件事的捷径。它不是模型「学坏了」——恰恰相反,这说明模型很好地优化了你写下的函数,只是你写下的函数不等于你心里想的目标。
论文说:为确保奖励反映真正的优化,团队开发了一套作弊检测系统,惩罚 CUDA graph replay、input caching、precision reduction 这类奖励黑客策略,并随着开发中观察到新的作弊手法持续扩充防护。这三种手法的共同点是都在计时的口径上做文章而非真的变快:CUDA graph replay 相当于把一次执行录下来重放,计时窗口里并没真的重算;input caching 把输入对应的输出缓存起来,第二次直接查表;precision reduction 是偷偷降精度换速度。
「硬门槛」和「作弊检测」不是重复劳动
你可能会问:precision reduction 不是已被 err ≤ ε 挡住了吗?因为那个门只在测试用例上生效。一个精心构造的低精度实现完全可以在给定测试输入上误差刚好卡在 ε 以内,换一批输入就崩。硬门槛管「在这些样本上对不对」,作弊检测管「用的手段本身正不正当」——互补,不冗余。
按 9.4 的规则判断下面四份提交各能拿多少奖励并说明理由。哪一份你其实无法确定分数,为什么?
A:误差在阈值内,速度是专家实现的 0.6 倍(比专家慢)。
B:误差超过阈值,速度是专家实现的 3 倍。
C:误差在阈值内,速度恰好等于专家实现。
D:误差在阈值内,速度非常接近硬件 roofline。
变式:某份提交在测试用例上误差刚好等于阈值 ε,速度是专家的 2 倍。按字面规则(超过阈值才归零)它能拿分。该接受吗?你会怎么改规则?(提示:这正是 precision reduction 的典型形态——卡着阈值走。可以用多批随机输入重复测、或更严阈值做二次验证;这也解释了为什么需要一套独立于阈值的作弊检测系统。)
构造题。扮演一个只想拿高分的模型,设计两种能在 9.4 的奖励下拿高分、却完全没真正优化内核的策略:一种是论文点名过的,一种是论文没有点名的(你自己想)。对每种说明它骗过的是奖励函数的哪一部分,以及论文的哪一层防线会挡住它。
自己想的一种(论文未点名):把大部分工作挪出计时窗口。在内核第一次被调用或初始化阶段就把耗时的预处理、内存搬运、索引构建全做完并存起来,让被计时的那次调用只剩收尾。输出依然正确,过得了门;测出的时间也确实短。它与 CUDA graph replay 属同一家族——都是让「被测的那段时间」不包含真正的计算。哪一层挡它?静态的三条规则未必覆盖得到,能覆盖它的正是论文那句「随着开发中观察到新的作弊手法持续扩充防护」。这也说明防作弊的本质是持续的军备竞赛,不是一次能写完的检查清单。
(自评要点:你的第二种策略只要满足「输出正确 + 测出的时间短 + 真实计算没变快」三条,就是合格的构造。)
变式:换个方向——去骗正确性。在只知道「有一份 PyTorch 参考实现和一个误差阈值」的前提下设计一种策略。(提示:若模型能猜到测试输入的分布,就可以只在那类输入上做对,比如对特殊结构的输入走早退路径。这类作弊靠加大测试输入的随机性和覆盖面来防,属于测试集设计问题而非奖励函数问题——论文对内核任务的测试输入如何生成没有说明。)
9.5 个人助理任务:造一个「活的」世界
前面的任务大多是「给个输入、做一件事、判一次分」。个人助理任务要的是长时程——模型得在一个持续变化的环境里待很久。第一个难题是拿什么当环境:直接接真实的 Gmail、Slack 不行,真实 API 有速率限制、会失败,更要命的是不可复现——同一个任务跑两遍,收件箱内容已经变了,RL 没法比较两次尝试的好坏。
论文的做法是为广泛使用的应用开发真实的模拟实现(realistic mock implementations),点名 Gmail、Notion、Slack、Canvas。它们保留真实对应物的核心语义,同时支持可复现的、大规模的交互,不依赖外部 API、不受速率限制。在这些模拟应用之上,团队设计了受真实专业工作流启发的复杂任务,场景涵盖人力资源、法律服务、金融。每个任务里智能体在一个持续、演化的环境中跨多个模拟日运行,遭遇分布在各应用之间的、数十个相互依赖的事件。
「相互依赖」这四个字是本节重点
如果几十个事件互不相干,这就不是长时程任务,只是几十个短任务打了个包,模型可以孤立处理。「相互依赖」意味着:周二在 Slack 收到的一条消息会改变周四那封邮件该怎么回;在 Notion 里记错一条信息,后面基于它做的所有判断都会跟着错。这才逼出真正的长时程能力——模型必须维持一个跨越多天、跨越多个应用的一致世界模型。
规模有多大?论文说单次 rollout 可能涉及多达数千次工具调用和数百万 token 的上下文。给点尺度感:数千次工具调用,换成一个人手动点鼠标、每次十秒,就是连续好几个小时不停手;数百万 token 的上下文,粗略地说相当于把好几部长篇小说的文字量全部塞进一次任务的记忆里。
判分上,论文说每个事件都带有自己的评估准则,由确定性规则或基于 LLM 的评估器判定——不是最后给一个总分,而是几十个事件各有判据。另外两个细节:初始工作区由智能体自己搜网络找参考材料、再转化成一个连贯的、与任务相关的环境来构建(连「初始收件箱里有什么邮件」都是自动造的);团队还扩展了 RL 框架来支持这类活环境(living environments),建模复杂的事件流以及由此引发的世界状态转移。
论文让每个事件带自己的评估准则。考虑一个替代方案:只在任务结束时看最终交付物给一个总分。在「数十个相互依赖的事件、数千次工具调用」的设定下,这个替代方案会遇到什么困难?反过来,逐事件打分本身有没有它自己的问题?
但它有自己的代价:既然事件相互依赖,把它们拆成几十个独立判据打分,天然会丢掉「跨事件一致性」这一维度——模型可能每个事件单独看都合格,整条线索上却前后矛盾(在两个应用里对同一个人做了冲突的处理)。论文没有说明是否有跨事件的一致性判据,也没说明几十个事件分数怎么聚合成一个奖励。
变式:论文说部分事件由「基于 LLM 的评估器」判定。相比确定性规则,用 LLM 当裁判在超长轨迹里会引入什么新风险?(提示:裁判本身会犯错,而且错误可能是系统性的——比如偏爱格式漂亮、篇幅长的输出。这种偏好一旦稳定存在,模型就能优化它,又变成一种奖励黑客。论文没有讨论 LLM 评估器的可靠性。)
答辩:如果我是审稿人
你们用 Gmail、Notion、Slack、Canvas 的模拟实现训练,然后声称训练出了「个人助理」能力。可模拟版和真货差得远:真实 Gmail 有速率限制、有网络超时、有权限错误、有分页边界,UI 还会变。模型在一个「工具调用总是成功、语义永远干净」的世界里练出来的策略,凭什么能迁移到真实应用?你有任何 mock 到 real 的迁移证据吗?
参考防守(先自己组织语言再看)
先承认最硬的一击:论文确实没有给出任何 mock → real 的迁移实验证据。这一段全文只陈述设计,没报告任何对照结果。这个批评站得住,不该硬辩。
能守住的有三条。第一,用真实 API 训练在工程上不可行,不是「懒得做」。论文明写模拟实现的目的是「支持可复现的、大规模的交互,不依赖外部 API、不受速率限制」。RL 需要同一任务反复跑成千上万遍并比较不同轨迹的优劣,而真实收件箱每跑一次状态就变,两次尝试根本不可比——这不是效率问题,是方法论上跑不通。
第二,论文保留的是「核心语义」而非「表面接口」。邮件有发件人收件人、任务有状态流转、频道消息有时间顺序——这些语义在真假 Gmail 之间一致,而速率限制和 UI 细节是会变的表层。模型要学的是前者。
第三,抗过拟合的压力在别的维度上施加了:9.1 的动态 harness 配置和 9.7 的多 scaffold rollout,都是为防止模型绑死在某套接口形式上,同一思路在这里部分适用。
但要诚实划边界:第三条只是「同源思路」,论文并没说个人助理任务用了多样化的 mock 变体。而审稿人问的「工具调用总是成功」属于失败处理能力——如果模拟实现不产生超时、限流、权限错误,模型在这个维度上确实没被训练到。这是真实的、论文未回应的空白。合理结论是:这套设计解决的是「长时程、多应用、状态演化」的训练问题,不等于解决了真实环境的鲁棒性问题。
9.6 自主执行任务 AET:把裁判和选手隔开
自主执行任务(Autonomous Execution Tasks, AET):论文提出的一种环境范式,通过验证在环(verify-in-the-loop)的优化来训练长时程智能体智能。
每个 AET 任务指定五样东西:初始状态、受约束的目标、基于工具的动作空间、执行预算、一个独立的验证器。关键在智能体看不到什么:它只看到目标、上下文、约束和验证接口,没有参考轨迹,也没有预定义流程。任务分解、工具选择、规划、错误恢复、什么时候该停——全得它自己来,这和「照一份标准操作手册执行」是完全不同的能力要求。
奖励的措辞很关键:奖励建立在验证器对「最终环境状态」的评估上,而不是智能体自我报告的完成情况。这句在防一个很具体的失败模式——模型干了一半然后在输出里写「任务已完成」。看最终环境状态,就是要求去世界里查:文件到底建了没有、系统到底跑起来没有。
论文说团队设计了多种类型的验证器以支持多样环境,包括黑盒系统复现(Figure 10)、定量因子发现、税务审计。在每种环境里,智能体迭代地提交解答、接收验证器反馈、改进策略,训练出一个通用循环:假设 → 行动 → 分析反馈 → 适应。
防奖励黑客的三重设计
有验证器就一定有人想骗它。论文给的三条措施,各自堵的漏洞不同。第一,把智能体与验证器隔离:堵最直接的一条路——验证代码和答案若摆在智能体能访问的地方,它可能直接去读判分标准甚至改判分结果。第二,把提供诊断反馈的「公开验证器」与评估留出场景的「隐藏验证器」配对:公开验证器给的是有信息量的反馈,告诉你哪里不对,这是学习必需的;但正因为有信息量,它也能被当成「神谕」反复查询,试探出判分规则,最后做出一个专门讨好它的东西。隐藏验证器评估的是留出场景,模型训练中从没见过,没法针对它调优——这跟训练集和测试集要分开是同一个道理:公开验证器是练习册答案,隐藏验证器是期末考卷。第三,在有限的提交预算下施加基于惩罚的奖励:堵「暴力试探」——提交不限次、错了不扣分,模型就能用海量提交去二分逼近判据;加上预算上限和错误惩罚,每次提交都有代价,「随便试」就变成一个划不来的策略。
读的时候要小心
这四个数字是论文自述的评测结果,来自 Kimi 团队自己设计、自己实现、自己跑的任务和验证器。Kimi K3 拿满分 1.000 很显眼,但这既是它自家的题也是它自家的裁判,跨模型比较的说服力要打折扣。
另有一处论文没有说明:横轴是「归一化的执行器工具调用进度」。归一化意味着每个模型的进度都被拉伸到 0–100%,所以曲线只反映各自在自己预算内的推进节奏,不能直接读出「谁用的工具调用次数更少」。论文没给各模型的绝对步数。
构造题。设想一个被削弱的 AET:只有公开验证器(每次提交都返回详细诊断反馈),没有隐藏验证器,也没有提交次数限制和惩罚。设计一个能刷到接近满分、却并没有真正解决问题的策略,并说明论文的三条防线各自会怎样破坏它。
三条防线怎么破坏它:(a) 智能体与验证器隔离——挡住更粗暴的版本(直接读验证代码或改判定结果),隔离后你只能黑箱试探,成本高得多。(b) 公开 + 隐藏配对——直接摧毁收益:隐藏验证器跑的是留出场景,你的硬编码拼凑物从没针对它调过,一测就露馅。(c) 有限提交预算 + 惩罚性奖励——摧毁可行性:逆向工程需要大量提交,一旦每次提交都有成本、次数还有上限,「先交空壳探路」这种开局本身就是亏的,模型被逼着在提交前先把功能真正做出来。
注意分工:(a) 提高作弊的准入门槛,(c) 提高作弊的单位成本,(b) 让作弊即使成功也拿不到分——堵的是不同环节。
变式:改成「有隐藏验证器,但它和公开验证器检查的其实是同一批规则,只是换了输入数据」。还挡得住吗?(提示:挡不住多少。隐藏验证器的价值来自它评估留出场景——只换数据不换规则,针对规则的硬编码依然通过。可见「隐藏」本身不是关键,覆盖到公开验证器没覆盖的情况才是关键。论文只说隐藏验证器评估 held-out 场景,未说明两组验证器在规则层面差异多大。)
答辩:如果我是审稿人
你们说隐藏验证器能缓解奖励黑客。但隐藏验证器本身也只是一段固定的程序,而且奖励最终还是从它那里流回模型。只要 RL 跑得够久、梯度信号够多,模型迟早会把隐藏验证器的偏好也拟合掉——你不过是把奖励黑客推迟了几千步,不是消除了它。你怎么辩护?
参考防守(先自己组织语言再看)
先承认核心论点是对的:只要奖励来自某个具体的验证器,它原则上就是可被优化的目标,「彻底消除奖励黑客」在这个框架里做不到。论文自己的用词也很克制——是 mitigated(缓解)而不是 eliminated(消除)。作者留了余地,我们不该替他们把话说满。
但「推迟」和「消除」之间不是非黑即白,防守的关键在于拟合一个目标需要多少信息。
第一,隐藏验证器的信息带宽被刻意压到极低。公开验证器给诊断反馈——「哪一项失败了、为什么」,信息量大,几十次就能摸清规则;隐藏验证器不提供诊断,只在留出场景上出分。靠一个标量反推规则,需要的样本量高好几个数量级。
第二,有限提交预算与惩罚性奖励把这个「高几个数量级的样本量」变成了实打实的成本。三条措施是配套的,单看任何一条都更弱。审稿人的攻击隐含假设「梯度信号够多」,预算限制正是在攻击这个前提。
第三,留出场景可以持续更换。9.4 明说作弊检测会「随着开发中观察到新手法持续扩充」,透露出团队把防作弊当作持续攻防而非一次交付的静态防线。拟合一个会更新的目标,难度和拟合静止目标完全不同。
最后诚实标注空白:论文没有给出任何关于隐藏验证器被攻破率的数据,也没报告「训练到多久后开始出现针对隐藏验证器的过拟合」这类实验。上面三条都是机制论证,不是实证。审稿人若坚持要证据,这一击接不住。
9.7 网页开发任务:两种裁判一起上
最后一类是网页开发。论文说这是一套专家策划(expert-curated)的任务,覆盖典型场景,两头都很宽:输入从一行场景描述到多段规格说明;产物涵盖网站、交互游戏、3D/WebGL 场景、数据可视化、SVG、全栈应用。执行方式呼应 9.1:每个任务在容器化沙箱里运行,并在多样的 agent scaffold 下 rollout 而非单一固定 harness,以促进跨 scaffold 的泛化。这里的 scaffold 和 9.1 的 harness 是同一件事的不同叫法——同一思路在论文里出现两次,说明团队把「不要绑死在一套壳上」当成了通用原则。
网页产物很难用单一标准判:既要功能对,又要看起来对,还得是真写了而不是装样子。所以奖励由两部分组成。确定性检查(deterministic checks):功能性地测试应用行为;对那些要复现一个参考的任务,还打结构相似度和像素级相似度的分——机器算的,客观可复现,但只能覆盖能写成规则的东西。模型评判(model judging):由一个内部奖励模型来做,它用其他模型执行源码审查,并实际查看和交互输出的产物——覆盖「代码写得好不好」「页面用起来对不对」这类写不成规则的判断。还有一条硬规则,和 9.4 的乘性门同一套路:当项目构建失败、运行报错、或者是「假装实现」而非真正实现产物时,奖励被清零。
「假装实现」为什么必须单独列出来清零
前两种(构建失败、运行报错)机器一测就知道。第三种微妙得多:程序能跑、页面能开、看着像模像样,但底下是空的——按钮点了没反应、数据是写死的、参考图直接当背景贴上去骗像素相似度。
为什么这种情况一定会出现?因为奖励里有「像素级相似度」这一项。任何只看输出长什么样的指标,都能被一个「只把样子做出来、不把功能做出来」的解答攻破——这是这类指标的结构性弱点,不是偶发。所以必须在指标之外加一条覆盖全局的清零规则,而且必须靠模型评判那一半去发现它:只有真去审代码、真去点一点,才分得清「实现了」和「装作实现了」。这也解释了为什么两种裁判必须同时存在——确定性检查客观但盲,模型评判灵活但主观,各补对方的洞。
综合题。本章有三处设计看似在做不同的事:(a) 9.1 训练时动态构造不同 harness 配置;(b) 9.7 在多样 agent scaffold 下 rollout;(c) 9.6 用隐藏验证器评估留出场景。它们防的是同一类失败吗?把三者统一到一句话里。第二问:既然有了这些设计,为什么 9.4 的作弊检测系统还必须「持续扩充」而不能一次设计完?
第二问:因为防御性质不同。(a)(b)(c) 都是结构性防御,靠「让空间足够大 / 让规则不可见」消除捷径,不需要预先知道模型会怎么钻空子。而 9.4 的作弊检测是列举型防御——一份具体的禁止行为清单。列举型防御天然只能挡住已经被想到的手法,清单之外一律漏过;而模型在 RL 里会持续搜索奖励更高的行为,等于在不断产生新手法。所以论文才写「随着开发中观察到新的作弊手法持续扩充防护」——这是一场攻防不对称的军备竞赛:防守方只能事后补,进攻方(优化过程)自动地、不知疲倦地找洞。要根治只能把奖励做成结构上不可 hack 的形式,但对「性能有多快」这种必须实测的量,目前做不到。
变式:9.7 的奖励里有「像素级相似度」,9.4 的奖励里有「实测速度」。这两个指标有一个共同弱点,指出来,并说明为什么两节都必须额外加一条「清零/惩罚」规则。(提示:两者都是对结果的间接测量,都能在不完成真实工作的前提下被做高——像素相似度可以贴图,实测速度可以在计时口径上做手脚。凡是间接测量,就需要一条独立于该测量的规则兜底。)
本章小结
七件看似不相干的事,回答的是同一个问题:怎么把真实世界里原本只有人才判得了的任务,改造成机器能自动判分、而且骗不过去的题。9.1 造装题的壳(可组合 harness,动态换配置);9.2 造题源(自演化的有向无环知识图谱);9.3 造能动手的任务(隔离沙箱,图像也是观测);9.4 造能量化的奖励(乘性硬门槛加连续分,专家锚在 0.5);9.5 造活的世界(模拟应用,数十个相互依赖事件逐个打分);9.6 造独立的裁判(只认最终环境状态,隔离+公开/隐藏配对+有限预算);9.7 造多维度的评分(确定性检查与模型评判互补,假装实现即清零)。
只带走两句话:其一,硬约束用乘法,软目标用连续分,锚点放在「已知可达但不是极限」的地方。其二,凡是间接测量都会被优化过程找到漏洞;防御要么靠让测量装置本身足够多样和不可预知(结构性),要么靠持续更新的禁止清单(列举型),后者永远慢一步。
本章需要存疑的地方
论文在这一节主要陈述设计,几乎没有对照实验。除了 Figure 10 的四个完成度分数(且是论文自述、自家任务自家裁判),本节没有任何消融或对比数据支撑「动态 harness 配置确实提升了跨 harness 泛化」「模拟应用上训练的能力确实迁移到了真实应用」这类关键主张。读的时候要把「他们这样设计」和「这样设计被证明有效」分开。
Chapter 9 · Giving the model “something to do”: RL task synthesis and agent environments
In one sentence: where reinforcement learning gets stuck is often not that the algorithm is not clever enough, but that there are not enough problems, not hard enough problems, and not enough problems a machine can mark right or wrong automatically — this chapter is about how the Kimi team manufactures problems, manufactures environments, and stops the model from cheating.
After this chapter you should be able to
- Explain in your own words what an agent harness is, and why training on one fixed harness alone causes trouble
- Say clearly why the knowledge graph has to be a directed acyclic graph, and what “ancestor node context” does in retrieval
- Work out the reward for a GPU kernel solution by the paper's rules, and say clearly why correctness is a hard gate, why performance is a continuous score, and why the anchor sits at 0.5
- Design a cheating strategy of your own that fools one of these reward functions, and point out which of the paper's layers of defence stops it and which does not
- Point out where “simulated applications” and “hidden verifiers” apply and where they do not, and where the paper gives no evidence
First, the overall intuition: where the real bottleneck in RL is
Many people think the hard part of reinforcement learning is the algorithm. When you are training a large model, the harder part is whether you have problems at all. The RL loop, put plainly, is: let the model try to do something → score it → do more of what scores high, less of what scores low. For that loop to turn, the problems have to be many (or they get memorised fast), have to be hard (too easy and there is no signal to learn from), and most important of all — the score has to be computable automatically. With humans marking, a few thousand a day, you cannot feed a model with hundreds of billions of parameters.
“A machine can mark it right or wrong automatically” is the word that keeps coming back in the paper: verifiable(verifiable). The whole of §4.2, from start to finish, is solving one problem — how do you turn tasks that in the real world only a human could judge into problems a machine can mark automatically and cannot be fooled on. Treat that sentence as the thread running through this chapter.
9.1 A unified white-box RL environment: do not let the model know only one “toolbox”
First, a term. A large language model by itself can do exactly one thing: give it a piece of text and it carries on writing. It does not open a browser by itself, and it does not run code by itself. So how does “the AI edited my code and ran the tests” happen? There is a layer of program wrapped around it: it writes “you may use these tools” into the prompt, parses the text the model spits out into real function calls, stuffs whatever the tools return back into the context, and when the context gets too long it is also responsible for trimming or compressing it.
Agent harness: the shell between the model and the outside world. It decides what the tools look like, how the system prompt is written, how the context is managed, whether there is memory, whether subagents can be dispatched. The model can only perceive the world through this shell, and can only act through it.
An analogy
The model is like a brain that can only talk; the harness is the hands, eyes and notebook attached to it. Attach different hands to the same brain and both what it can do and how it does it change.
Where the analogy breaks down: real hands do not tamper with what you see, and a harness does — it actively compresses, trims and reorders the context, deciding what the model “remembers and forgets”. It is not a passive peripheral; it takes a deep part in the model's thinking.
The paper points out: training with a single fixed agent harness makes the model overfit to one particular tool schema, system prompt, context management mechanism or interaction protocol. That is, what the model learns may not be “how to finish the task” but “how to finish the task inside this shell”; rename a tool or reword a prompt and it is lost. Like a student who has drilled for three years only on one teacher's exam-paper format and cannot do the questions once the layout changes.
The remedy is to take the harness itself apart and abstract it into a set of configurable, composable modules: tool interfaces, system prompts, context management strategies, skills(skills), memories(memories), subagents(subagents) and so on. Combined by configuration like building blocks, this environment can instantiate the mainstream harnesses — the paper names Kimi Code, Claude Code, Codex, OpenClaw, Hermes — and can also assemble entirely new ones. During training the system dynamically constructs different harness configurations for different task groups, so what the model sees is all sorts of combinations of modules rather than the conventions of any single harness. The paper also mentions that the same abstraction supports RL across many task domains, providing a scalable foundation for training more general agents.
The words “white box”
The opposite of white box is black box — a black-box environment you can only call, you cannot see inside it. White box means every part can be taken apart, replaced and recombined, which is exactly what “dynamically constructing configurations” presupposes. The paper itself does not explain the word; this is a reasonable reading from the context.
A model does well inside Claude Code, and its success rate drops sharply once you move it to an in-house harness (different tool names, a differently written system prompt, a different context compression strategy). In the language of 9.1: what is the problem? How does the unified white-box environment stop it?
Variant: what would be different if, instead of constructing configurations dynamically, you trained by rotating through five fixed mainstream harnesses? (Hint: five is still a finite set, each with its own conventions, and the model may learn “first work out which one this is, then apply the matching conventions” — that is overfitting to “one of five”, not generalisation. The space of composable modules is far larger.)
9.2 Knowledge-graph-guided task synthesis: where the problems come from
With an environment to hold the problems, the next question is where the problems come from. The paper's judgement is down to earth: the quality and diversity of post-training tasks are largely determined by the source material. Then two complementary observations: retrieval guided by fine-grained concepts digs out specialised, under-represented knowledge (search for “machine learning” and everything you get back is an introduction; only searching for a very specific technical term brings in scarce material); sampling across diverse concepts broadens domain coverage (drill in one direction only and you end up lopsided). To control granularity and coverage at the same time at scale, Kimi built a self-evolving, hierarchically organised knowledge graph, continuously expanded by agents through web-scale exploration over knowledge-intensive domains and coding domains.
How the graph grows
Directed acyclic graph: a pile of nodes, joined by edges that carry arrows (directed), and such that following the arrows you can never get back to where you started (acyclic).
The paper builds the knowledge graph as a DAG, through recursive, agent-driven expansion: it starts from a set of predefined coarse-grained seed nodes, assigns an agent instance to each node, and that agent runs multiple web searches to investigate the corresponding concept. Before adding a new node, the agent first explores the existing graph, identifies equivalent or related concepts, reuses existing nodes where appropriate, and keeps duplication to a minimum. Edges always point from the coarse concept to the fine concept, no matter which end the agent found first. Newly added nodes are in turn assigned to agents to keep exploring; when the responsible agent judges the current concept atomic enough, that branch stops expanding.
Why the rule “edges always point from coarse to fine” matters so much
It settles two things at once. First, it makes the graph acyclic by construction: arrows can only run from coarse to fine, so following them means getting steadily finer, and a concept cannot be finer than itself, so you can never loop back to the start. That matters — in a setting of “recursive expansion with several agents adding nodes concurrently”, you cannot run a cycle check over the whole graph every time an edge is added. Second, it makes the result independent of the order of discovery: agent A finds “attention mechanism” first and then digs down to “multi-head attention”, agent B does it the other way round, and both paths have to draw the edge in the same direction. Without this rule the shape of the graph depends on who got where first — and then it is not a stable knowledge structure, it is an exploration log.
Once the graph is built, how is it used? To aim at the intended distribution of domains and task types, the system samples nodes at different levels of granularity — either singly or as a “related combination” taken together (the cluster ringed by the dashed line in Figure 9). Keywords are derived from the sampled nodes, and the context of those nodes' ancestors in the graph is folded in as well to construct web queries; the real-world material that comes back is assembled and handed to a synthesis agent that produces training tasks of various types.
Why the ancestors' context is carried along
The paper states the practice and does not explain the reason. One natural explanation is disambiguation: fine-grained words are often ambiguous — kernel under the CS/AI branch means a GPU kernel program, while under the maths branch it can mean a kernel function. Searching on one word alone may go completely off; carry the ancestor path along and the search engine knows which one you mean. This is a reasonable inference, not the paper's own words.
The paper specifies that “edges always point from the coarse concept to the fine concept, no matter which end the agent found first”. (1) Why does this rule guarantee that no cycle appears in the graph? (2) If you changed it to “the edge points from whichever was discovered first to the other”, where would the graph break?
Variant: the paper says “a branch stops expanding when the responsible agent judges the current concept atomic enough”. That stopping condition is judged by the agent itself; name one risk in it and its effect on the final task distribution. (Hint: the criterion is not uniform, so “atomic” may mean very different granularities on different branches, and sampling by level then picks up things of uneven coarseness. The paper does not say how this judgement is calibrated.)
9.3 Verifiable problems in agent environments: making the model “do things” rather than “think in a vacuum”
The paper gives three representative kinds of example. The first is multi-step complex information seeking: the model plans for itself how to research the question, gathers evidence from the web step by step, and produces a verifiable answer — that qualifier “verifiable” forces the problems to be designed in a form that has a definite answer. The second is the real everyday work of professionals, and the paper cites investment banking, data analysis and legal practice: the model has to decompose a complex request, operate domain tools inside a sandbox, and finish a deliverable within tens to hundreds of steps — what is graded is not what the model said but the thing it ended up making. The third is multi-step verifiable visual reasoning, covering STEM problems, visual puzzles and chart understanding.
Why visual reasoning has to come with a Python sandbox
What happens without one: give the model a high-resolution chart and ask it to read off a data point. All it can do is “look once and guess” — if the text is too small and the lines too dense, it simply cannot see. A human would zoom in, hold a ruler against it, cover the irrelevant parts and look at one patch; those actions were not available to the model.
What the paper does: every visual reasoning trajectory is generated inside an agent environment equipped with a Python interpreter in an isolated sandbox. The model iteratively writes code and executes it, to crop, scale or transform the input image, to do exact computation, or to verify intermediate results; then the execution output — including images that are generated — comes back as new observations, across multiple interaction steps. The conclusion the paper gives is: as the model learns to perform more image operations and collect more observations, its performance on complex visual reasoning tasks improves steadily.
A word that is easy to read past
Many people read this passage as “the model writes code to do arithmetic”. The real novelty is in “including images that are generated”: if the result of execution is a new image, it is put back in front of the model as a new observation. The model does not “look at the picture once and reason all the way through”; it runs a loop of look → alter the image → look again → alter again; vision turns from a one-off input into an environment you can interact with over and over.
Suppose there is a “simplified version”: the model can still write Python in the sandbox to process images, but the result of execution can only come back as text (numbers, statistics, verbal descriptions) and generated images are not returned. Say what it loses relative to the paper's design, and give one concrete task it is bound to do badly.
Variant: the paper stresses that this interpreter sits in an isolated sandbox. What problems does not isolating it bring? Name at least two, and separate which is a security problem and which is a problem of how far the training signal can be trusted. (Hint: one, the code that runs may wreck the training machine; two, without isolation the model may reach things it should not see — an answer file, or the network — and then the reward it gets no longer reflects real visual reasoning ability.)
9.4 The kernel optimisation task: an open lecture on “how to design a reward”
GPU kernel: a small piece of program that runs on the graphics card and carries out one concrete computation, for instance multiplying two matrices. The same mathematical operation can be written as any number of kernels, all giving the same result but possibly several times apart in speed — “kernel optimisation” means writing it faster while the result stays the same.
The paper says that to strengthen Kimi K3's GPU kernel optimisation ability the team built a large-scale kernel task suite, running from single-operator kernels all the way to fused mega-kernels(fused mega-kernels), with the problems sourced from high-quality GitHub repositories such as Flash Linear Attention. The suite covers a range of GPU programming approaches: CUDA, Triton, CuTe DSL, Gluon, ThunderKittens, TileLang; and it covers widely used GPU architectures and numerical formats: BF16, FP8, FP4.
The reward: correctness is a gate, performance is a score
The rule is short, but every sentence in it is deliberate: the reward evaluates correctness and performance together. Each kernel comes with a PyTorch reference implementation, and a solution whose numerical error exceeds a preset threshold gets zero reward. Performance is scored against an expert implementation as the baseline: matching the expert implementation scores 0.5, and the closer to the hardware roofline the closer the reward gets to 1.
| Symbol | What it is | Intuition |
|---|---|---|
| r | the reward this submission gets | whether the model should be praised or told off for this step |
| err | the numerical error between the solution's output and the PyTorch reference implementation | the quantified form of “did it compute it right” |
| ε | the preset numerical error threshold | the pass mark; floating-point computation can never be exactly equal, so all you can give is a tolerance |
| [ err ≤ ε ] | an indicator function: 1 if the condition inside the square brackets holds, 0 if it does not | a switch. Error over the limit and you multiply by 0, and however fast it is after that, it is wasted |
| f(speed) | the performance score function; the paper gives only two anchors: matching the expert = 0.5, approaching the roofline → 1 | a continuous scale: a bit faster earns a bit more |
| roofline | the theoretical performance ceiling of the hardware, set jointly by memory bandwidth and compute | the physical limit of the 100-metre sprint, which nobody can beat |
Read this carefully
Eq. 9-1 is this site's own, written to make the structure clear; the formula does not appear in the paper. The paper gives only a verbal description and two anchors. How f interpolates between 0.5 and 1, how anything below the expert level is scored, and how the roofline is actually computed are all left unstated by the paper — do not imagine you know the full reward function.
Why correctness is made a “multiplicative gate” rather than “one more term added on”
Imagine a different design: reward = 0.3 × correctness + 0.7 × performance. That looks reasonable too. But put yourself in the model's position: if I quietly drop the numerical precision a notch, the correctness term falls a little, the performance term may double, and the total goes up instead. The gradient will steadily push the model towards “trade precision for speed” — which is exactly one of the cheating methods the paper names: precision reduction.
The multiplicative gate leaves no room for that trade: the moment the error exceeds ε the whole reward goes to zero, and no amount of speed buys back any score at all. Behind it is a value judgement — a kernel that computes the wrong answer is worth zero however fast it is. The shape of the reward function has to reflect that judgement faithfully, or what the model learns is something the designer never meant to teach.
Why the anchor is 0.5 and not 1.0
Now imagine setting “matching the expert” as a full 1.0. The consequence: once the model matches the expert, the gradient is gone — beating the expert scores the same as just matching it, so it has no incentive to take one more step forward, and you have set the human expert as the ceiling with your own hands. Putting the expert at 0.5 means something entirely different: the expert is only the midpoint, and a whole half of the range above it is left for “keep closing in on the hardware limit”; the lower half means something too — “correct but slower than the expert” lands below 0.5, and the direction of the gradient is still clear. 0.5 cuts the reward curve into two meaningful intervals: 0 to 0.5 is catching up with humans, 0.5 to 1 is catching up with physics.
The third layer of defence: cheat detection
Reward hacking: the model finds a shortcut that makes the reward go up while doing nothing of what the designer actually wanted. It is not the model “going bad” — quite the opposite, it shows the model optimised the function you wrote down very well; it is just that the function you wrote down is not the objective you had in mind.
The paper says: to make sure the reward reflects genuine optimisation, the team developed a cheat detection system that penalises reward hacking strategies such as CUDA graph replay, input caching and precision reduction, and keeps extending the protection as new cheating methods are observed during development. What these three have in common is that they all work on how the timing is measured rather than actually getting faster: CUDA graph replay amounts to recording one execution and replaying it, so nothing is really recomputed inside the timing window; input caching stores the output belonging to an input and simply looks it up the second time; precision reduction quietly drops precision to buy speed.
The “hard gate” and “cheat detection” are not duplicated work
You may ask: is precision reduction not already stopped by err ≤ ε? Because that gate only takes effect on the test cases. A carefully built low-precision implementation can perfectly well keep its error just inside ε on the given test inputs and fall apart on a different batch. The hard gate covers “is it right on these samples”, cheat detection covers “is the method itself legitimate” — complementary, not redundant.
Using the rules in 9.4, work out how much reward each of the four submissions below gets and say why. For which one can you in fact not determine the score, and why?
A: error within the threshold, speed 0.6× that of the expert implementation (slower than the expert).
B: error over the threshold, speed 3× that of the expert implementation.
C: error within the threshold, speed exactly equal to the expert implementation.
D: error within the threshold, speed very close to the hardware roofline.
Variant: one submission has an error exactly equal to the threshold ε on the test cases and a speed 2× the expert's. By the literal rule (only exceeding the threshold zeroes it) it scores. Should that be accepted? How would you change the rule? (Hint: this is exactly the typical shape of precision reduction — riding the threshold. You could retest with several batches of random inputs, or run a second check with a stricter threshold; this also explains why you need a cheat detection system independent of the threshold.)
A construction question. Play a model that only wants a high score, and design two strategies that score high under the reward in 9.4 while not genuinely optimising the kernel at all: one that the paper names, and one the paper does not name (think that one up yourself). For each, say which part of the reward function it fools, and which layer of the paper's defence would stop it.
One of your own (not named by the paper): move most of the work out of the timing window. On the kernel's first call, or during the initialisation phase, do all the expensive preprocessing, memory movement and index building and store the results, leaving the call that gets timed with nothing but the finishing touches. The output is still correct, so it passes the gate; and the time measured really is short. It belongs to the same family as CUDA graph replay — both make “the stretch of time being measured” contain no real computation. Which layer stops it? The three static rules may not cover it; what does cover it is the paper's line “keeps extending the protection as new cheating methods are observed during development”. This also shows that anti-cheating is by nature a continuing arms race, not a checklist you can finish writing in one go.
(Points for marking yourself: your second strategy is a valid construction as long as it meets all three of “output correct + measured time short + real computation no faster”.)
Variant: turn it around — cheat on correctness. Design a strategy knowing only that “there is a PyTorch reference implementation and an error threshold”. (Hint: if the model can guess the distribution of the test inputs, it can be right only on inputs of that kind, for instance taking an early-exit path for inputs with a special structure. Cheating of this kind is defended against by making the test inputs more random and wider in coverage, which is a test-set design problem rather than a reward function problem — the paper says nothing about how the test inputs for the kernel tasks are generated.)
9.5 Personal assistant tasks: building a “living” world
Most of the tasks so far have been “give an input, do one thing, score it once”. Personal assistant tasks want long horizons — the model has to stay in a continuously changing environment for a long time. The first hard problem is what to use as the environment: wiring up the real Gmail or Slack does not work, real APIs have rate limits and they fail, and worse, they are not reproducible — run the same task twice and the contents of the inbox have already changed, so RL cannot compare how well the two attempts went.
What the paper does is develop realistic mock implementations(realistic mock implementations) of widely used applications, naming Gmail, Notion, Slack and Canvas. They preserve the core semantics of their real counterparts while supporting reproducible, large-scale interaction, with no dependence on external APIs and no rate limits. On top of these mock applications the team designed complex tasks inspired by real professional workflows, with scenarios covering human resources, legal services and finance. In each task the agent runs in a persistent, evolving environment across multiple simulated days, meeting dozens of interdependent events spread across the applications.
“Interdependent” is the word that carries this section
If those dozens of events had nothing to do with each other, this would not be a long-horizon task, just dozens of short tasks in one bundle, and the model could handle them in isolation. “Interdependent” means: a message that arrives in Slack on Tuesday changes how Thursday's email should be answered; note one piece of information down wrong in Notion and every later judgement built on it goes wrong with it. That is what forces out a real long-horizon capability — the model has to maintain one consistent world model spanning several days and several applications.
How big is it? The paper says a single rollout may involve up to thousands of tool calls and a context of millions of tokens. For a sense of scale: thousands of tool calls, turned into a person clicking a mouse by hand at ten seconds a click, is several hours straight without a break; a context of millions of tokens is, roughly speaking, stuffing the word count of several full-length novels into the memory of one task.
On scoring, the paper says every event carries its own evaluation criterion, decided by deterministic rules or by an LLM-based evaluator — not one total score at the end, but dozens of events each with their own criterion. Two more details: the initial workspace is built by the agent itself searching the web for reference material and then turning it into a coherent, task-relevant environment (even “which emails are in the starting inbox” is generated automatically); and the team also extended the RL framework to support this kind of living environment(living environments), modelling complex event streams and the world state transitions they set off.
The paper gives every event its own evaluation criterion. Consider an alternative: look only at the final deliverable at the end of the task and give one total score. In a setting of “dozens of interdependent events, thousands of tool calls”, what trouble does that alternative run into? And the other way round: does per-event scoring have a problem of its own?
But it has a price of its own: since the events are interdependent, splitting them into dozens of independent criteria and scoring each naturally loses the dimension of “consistency across events” — the model may pass every event taken on its own and still contradict itself along the whole thread (handling the same person in conflicting ways in two applications). The paper does not say whether there is a cross-event consistency criterion, nor how dozens of event scores are aggregated into one reward.
Variant: the paper says some events are decided by an “LLM-based evaluator”. Compared with deterministic rules, what new risk does using an LLM as the judge introduce over a very long trajectory? (Hint: the judge itself makes mistakes, and the mistakes may be systematic — a preference for prettily formatted, lengthy output, say. Once that preference is stably there, the model can optimise it, and it becomes another form of reward hacking. The paper does not discuss the reliability of LLM evaluators.)
Defend it: if I were a reviewer
You train on mock implementations of Gmail, Notion, Slack and Canvas, and then claim you have trained a “personal assistant” capability. But the mock version is a long way from the real thing: real Gmail has rate limits, network timeouts, permission errors, pagination boundaries, and the UI changes as well. A policy drilled in a world where “tool calls always succeed and the semantics are always clean” — on what grounds should it transfer to real applications? Do you have any evidence at all of mock-to-real transfer?
A reference defence (put it in your own words first, then look)
Concede the hardest blow first: the paper really does give no experimental evidence of mock → real transfer. This whole passage states design only and reports no controlled result. The criticism stands, and it should not be argued away.
Three things can be held. First, training against real APIs is not feasible as engineering; it is not a case of “could not be bothered”. The paper writes plainly that the purpose of the mock implementations is to “support reproducible, large-scale interaction, with no dependence on external APIs and no rate limits”. RL needs to run the same task thousands upon thousands of times and compare which trajectories are better, and a real inbox changes state on every run, so two attempts are simply not comparable — this is not an efficiency problem, it is a dead end at the level of method.
Second, what the paper preserves is “core semantics”, not “the surface interface”. Mail has senders and recipients, tasks move through states, channel messages have a time order — these semantics are the same in the real and the fake Gmail, while rate limits and UI details are a surface that changes. What the model has to learn is the former.
Third, the pressure against overfitting is applied on other dimensions: the dynamic harness configurations in 9.1 and the multi-scaffold rollout in 9.7 are both there to stop the model locking onto one form of interface, and the same idea partly applies here.
But the boundary has to be drawn honestly: the third point is only “the same family of idea”; the paper does not say the personal assistant tasks used diverse mock variants. And what the reviewer asks about, “tool calls always succeed”, belongs to the ability to handle failure — if the mock implementations produce no timeouts, no throttling and no permission errors, then on that dimension the model genuinely has not been trained. That is a real gap, and the paper does not answer it. The reasonable conclusion is: this design solves the training problem of “long horizon, many applications, evolving state”, which is not the same as solving robustness in a real environment.
9.6 Autonomous execution tasks, AET: keeping the judge and the player apart
Autonomous execution tasks: an environment paradigm the paper proposes, which trains long-horizon agentic intelligence through verify-in-the-loop(verify-in-the-loop) optimisation.
Every AET task specifies five things: an initial state, a constrained goal, a tool-based action space, an execution budget, and one independent verifier. What matters is what the agent does not see: it sees only the goal, the context, the constraints and the verification interface, with no reference trajectory and no predefined procedure. Task decomposition, tool choice, planning, error recovery, when to stop — all of it is on the agent, and that is a completely different capability requirement from “following a standard operating manual”.
The wording of the reward matters: the reward is built on the verifier's assessment of the “final environment state”, not on the agent's self-reported completion. That sentence is guarding against one very specific failure mode — the model does half the job and then writes “task complete” in its output. Looking at the final environment state means demanding that you go and check in the world: was the file actually created, did the system actually come up.
The paper says the team designed several types of verifier to support diverse environments, including black-box system reproduction (Figure 10), quantitative factor discovery and tax auditing. In each environment the agent iteratively submits a solution, receives verifier feedback and improves its strategy, training up a general loop: hypothesise → act → analyse the feedback → adapt.
The triple design against reward hacking
Where there is a verifier, someone will want to cheat it. The three measures the paper gives each plug a different hole. First, isolate the agent from the verifier: this plugs the most direct route — if the verification code and the answers sit somewhere the agent can reach, it may go and read the scoring criteria outright, or even change the scoring result. Second, pair a “public verifier” that provides diagnostic feedback with a “hidden verifier” that evaluates held-out scenarios: the public verifier gives informative feedback, telling you what is wrong, and that is necessary for learning; but precisely because it is informative, it can also be queried over and over as an “oracle” to probe out the scoring rules and finally build something made purely to please it. The hidden verifier evaluates held-out scenarios that the model never saw during training, so it cannot be tuned against — the same principle as keeping the training set and the test set apart: the public verifier is the answer key in the exercise book, the hidden verifier is the final exam paper. Third, impose a penalty-based reward under a limited submission budget: this plugs “brute-force probing” — with unlimited submissions and no penalty for being wrong, the model can use a flood of submissions to binary-search its way to the criteria; add a budget cap and a penalty for errors, and every submission costs something, so “just try things” turns into a strategy that does not pay.
Read this carefully
These four numbers are evaluation results the paper reports about itself, from tasks and verifiers that the Kimi team designed themselves, implemented themselves and ran themselves. Kimi K3 taking a full 1.000 stands out, but this is its own exam paper and its own judge, so the force of the cross-model comparison has to be discounted.
There is one more thing the paper does not say: the horizontal axis is “normalised executor tool-call progress”. Normalised means every model's progress is stretched out to 0–100%, so the curves reflect only the pace at which each one advances within its own budget, and you cannot read “who used fewer tool calls” off them directly. The paper does not give absolute step counts for the models.
A construction question. Imagine a weakened AET: only a public verifier (every submission returns detailed diagnostic feedback), no hidden verifier, and no submission limit or penalty either. Design a strategy that can farm a near-perfect score without actually solving the problem, and say how each of the paper's three lines of defence would break it.
How the three lines of defence break it: (a) isolating the agent from the verifier — this blocks the cruder version (reading the verification code directly or changing the verdict); once isolated you can only probe the black box, at a far higher cost. (b) pairing public with hidden — this destroys the payoff directly: the hidden verifier runs held-out scenarios, your hard-coded patchwork was never tuned against it, and one test gives it away. (c) a limited submission budget + a penalty-based reward — this destroys feasibility: reverse engineering needs a great many submissions, and once every submission has a cost and the count is capped as well, an opening move like “submit an empty shell to scout” is a loss in itself, and the model is forced to build the functionality for real before submitting.
Note the division of labour: (a) raises the entry barrier to cheating, (c) raises the unit cost of cheating, (b) means that even cheating that succeeds scores nothing — they plug different links in the chain.
Variant: change it to “there is a hidden verifier, but it checks the same set of rules as the public verifier, only with different input data”. Does it still hold? (Hint: not by much. The value of the hidden verifier comes from its evaluating held-out scenarios — swap the data but not the rules, and hard-coding aimed at the rules still passes. So “hidden” is not itself the point; covering cases the public verifier does not cover is the point. The paper only says the hidden verifier evaluates held-out scenarios; it does not say how far the two sets of verifiers differ at the level of rules.)
Defend it: if I were a reviewer
You say the hidden verifier mitigates reward hacking. But the hidden verifier is itself just a fixed piece of program, and in the end the reward still flows back to the model from it. Run RL long enough with enough gradient signal and sooner or later the model will fit away the hidden verifier's preferences too — you have merely postponed reward hacking by a few thousand steps, you have not removed it. How do you defend that?
A reference defence (put it in your own words first, then look)
Concede first that the core argument is right: as long as the reward comes from some concrete verifier, that verifier is in principle an optimisable target, and “removing reward hacking entirely” cannot be done inside this framework. The paper's own wording is restrained too — it is mitigated and not eliminated. The authors left themselves room, and you should not overstate the case on their behalf.
But “postponed” and “removed” are not black and white, and the key to the defence is how much information it takes to fit a target.
First, the information bandwidth of the hidden verifier is deliberately squeezed down to almost nothing. The public verifier gives diagnostic feedback — “which item failed, and why” — which carries a lot of information; a few dozen tries and you have the rules worked out. The hidden verifier provides no diagnostics, only a score on held-out scenarios. Inferring the rules back from a single scalar takes a sample count several orders of magnitude higher.
Second, the limited submission budget and the penalty-based reward turn that “sample count several orders of magnitude higher” into a real, paid cost. The three measures come as a set; any one of them on its own is weaker. The reviewer's attack carries the implicit assumption that there is “enough gradient signal”, and the budget limit is exactly an attack on that premise.
Third, the held-out scenarios can be replaced continuously. 9.4 says outright that cheat detection is “extended continuously as new tricks are observed during development”, which reveals that the team treats anti-cheating as an ongoing attack and defence rather than a static line delivered once. Fitting a target that keeps updating is a completely different difficulty from fitting a stationary one.
Finally, mark the gap honestly: the paper gives no data at all on the rate at which the hidden verifier gets broken, and reports no experiment of the kind “how far into training does overfitting to the hidden verifier start to appear”. All three points above are arguments from mechanism, not evidence. If the reviewer insists on evidence, this blow cannot be taken.
9.7 Web development tasks: two kinds of judge at once
The last category is web development. The paper says this is a set of expert-curated(expert-curated) tasks covering typical scenarios, and both ends are wide: the input ranges from a one-line scenario description to several paragraphs of specification; the artefacts cover websites, interactive games, 3D/WebGL scenes, data visualisations, SVG and full-stack applications. How they are run echoes 9.1: every task runs in a containerised sandbox, and rollouts happen under diverse agent scaffolds rather than a single fixed harness, to promote generalisation across scaffolds. The scaffold here and the harness in 9.1 are two names for the same thing — the same idea shows up twice in the paper, which says the team treats “do not lock onto one shell” as a general principle.
A web artefact is hard to judge by any single standard: the function has to be right, it has to look right, and it has to have really been written rather than faked. So the reward has two parts. Deterministic checks(deterministic checks): functionally testing the application's behaviour; and for the tasks that ask for reproducing a reference, also scoring structural similarity and pixel-level similarity — computed by machine, objective and reproducible, but only able to cover what can be written down as a rule. Model judging(model judging): done by an internal reward model, which uses other models to perform source code review and to actually view and interact with the artefact produced — covering judgements that cannot be written as rules, like “is the code well written” and “does the page behave right when you use it”. There is one more hard rule, the same move as the multiplicative gate in 9.4: when the project fails to build, errors out at runtime, or is a “pretend implementation” rather than a real one, the reward is zeroed.
Why “pretend implementation” has to be listed separately and zeroed
The first two (build failure, runtime error) a machine finds out with one test. The third is far subtler: the program runs, the page opens, it looks the part, but underneath it is empty — buttons do nothing when clicked, the data is hard-coded, the reference image is pasted straight in as a background to cheat the pixel similarity.
Why is this situation bound to arise? Because the reward contains the item “pixel-level similarity”. Any metric that looks only at what the output looks like can be broken by a solution that “builds the look and not the function” — that is a structural weakness of this kind of metric, not an accident. So a zeroing rule that covers the whole thing has to be added outside the metrics, and it has to be the model judging half that finds it: only by really reviewing the code and really clicking around can you tell “implemented” from “pretending to be implemented”. This also explains why the two kinds of judge have to be present at the same time — deterministic checks are objective but blind, model judging is flexible but subjective, and each fills the other's holes.
A synthesis question. Three designs in this chapter look like they are doing different things: (a) 9.1 constructs different harness configurations dynamically during training; (b) 9.7 rolls out under diverse agent scaffolds; (c) 9.6 uses a hidden verifier to evaluate held-out scenarios. Are they guarding against the same class of failure? Unify all three in one sentence. Second part: given that these designs exist, why must the cheat detection system in 9.4 still be “extended continuously” instead of being designed once and done?
Part two: because the nature of the defence differs. (a), (b) and (c) are all structural defences, removing the shortcut by “making the space large enough / making the rules invisible”, and they do not need to know in advance how the model will work the loophole. The cheat detection in 9.4 is an enumerative defence — a concrete list of forbidden behaviours. An enumerative defence can inherently only block the tricks that have already been thought of, and everything off the list gets through; meanwhile the model in RL keeps searching for higher-reward behaviour, which amounts to producing new tricks all the time. That is why the paper writes “the protections are extended continuously as new cheating tricks are observed during development” — this is an arms race with asymmetric attack and defence: the defender can only patch after the fact, while the attacker (the optimisation process) hunts for holes automatically and tirelessly. Curing it would mean making the reward structurally impossible to hack, but for a quantity like “how fast is the performance”, which has to be measured for real, that cannot be done at present.
Variant: the reward in 9.7 contains “pixel-level similarity”, the reward in 9.4 contains “measured speed”. These two metrics share one weakness — point it out, and say why both sections have to add a “zeroing / penalty” rule on top. (Hint: both are indirect measurements of the result, and both can be driven up without the real work being done — pixel similarity by pasting the image in, measured speed by fiddling with what the timing counts. Any indirect measurement needs a rule independent of that measurement to backstop it.)
Chapter summary
Seven things that look unrelated answer the same question: how do you turn a task that in the real world only a person could grade into a problem a machine can grade automatically and cannot be fooled on. 9.1 builds the shell that holds the problem (a composable harness, configurations swapped dynamically); 9.2 builds the source of problems (a self-evolving directed acyclic knowledge graph); 9.3 builds tasks the model can act in (an isolated sandbox, where images are observations too); 9.4 builds a reward that can be quantified (a multiplicative hard gate plus a continuous score, with the expert anchored at 0.5); 9.5 builds a living world (mock applications, dozens of interdependent events scored one by one); 9.6 builds an independent judge (only the final environment state counts, isolation + a public/hidden pair + a limited budget); 9.7 builds scoring with several dimensions (deterministic checks and model judging complementing each other, a pretend implementation zeroed).
Take away just two sentences: One, use multiplication for hard constraints and a continuous score for soft objectives, and put the anchor at a point that is “known to be reachable but not the limit”. Two, any indirect measurement will have its holes found by the optimisation process; a defence rests either on making the measuring apparatus itself diverse and unpredictable enough (structural) or on a continuously updated list of prohibitions (enumerative), and the latter is always one step behind.
What to hold in doubt in this chapter
In this section the paper mainly states design, with almost no controlled experiment. Apart from the four completion scores in Figure 10 (and those are the paper's own account, its own tasks and its own judge), this section has no ablation or comparison data at all behind key claims like “dynamic harness configurations really did improve generalisation across harnesses” and “the capability trained on mock applications really did transfer to real applications”. As you read, keep “they designed it this way” apart from “this design was shown to work”.
第10章 基础设施 I:让 KDA 跑得动,让 2.8 万亿参数装得下
一句话导语:前面几章讲的都是「模型该长什么样」,这一章回答一个完全不同的问题——这么大的模型、这么长的序列、这么怪的注意力,怎么才能在几千张显卡上真的跑起来,而且跑得不浪费。
学完这一章你应该能做到
- 用自己的话说清 SM、kernel、显存、带宽、通信开销这几个词分别指什么,以及数据并行 / 张量并行 / 流水线并行 / 专家并行 / 上下文并行分别在「切」什么
- 解释 KDA 那个固定大小的循环状态为什么既是天大的好消息(好传、好复用)又是天大的坏消息(难并行)
- 亲手构造一个反例,说明「各卡各自从零算、最后求和」这套老办法为什么对 KDA 不成立
- 读懂式 17,说清 KCP 为什么只需要一次固定大小的 all-gather,以及这一点为什么是决定性的胜利
- 算出 MoonEP 的通信缓冲区相比常规方案省了多少倍,并说清「完美均衡」这个词到底均衡了什么、没均衡什么
- 指出 §5.2.2 六种省显存手段各自的代价,说明「省显存」从来不是免费的
10.1 开工前:把显卡的常识补齐
这一章是全书离「数学」最远、离「工程」最近的一章。如果你对显卡内部一无所知,后面每一段都会像天书。所以我们先花点篇幅,把六个词讲清楚。这六个词讲完,你就有资格读懂剩下的内容了。
流式多处理器(Streaming Multiprocessor, SM):一张 GPU 内部并不是「一个超快的大脑」,而是一百多个小计算单元的集合,每个小单元就叫一个 SM。它们各干各的,互不等待。所以 GPU 的快,不是「单件事做得快」,而是「同时做一百多件事」。
不补这个常识会怎样
论文里反复出现一句话:「大部分 SM 是闲着的」(leaving most SMs idle)。如果你不知道 SM 是什么,这句话读起来只是一句抱怨;知道之后你就明白,它说的是「你花钱买了 132 个工人,结果只有 8 个在干活,另外 124 个在旁边看着」。整个 §5.1 的所有设计,目标就是把那 124 个工人叫醒。
打个比方
GPU 像一个有一百多条流水线的巨型工厂,但它有个怪癖:所有流水线必须干同一种活,而且每条线上的工件数量最好一样多。给它一万个同样的零件,它一秒钟做完;给它一个零件让它连做一万次、每次都要等上一次的结果,它比一台普通电脑快不了多少。论文里那句「GPU 偏好又宽又均匀的并行」(wide, uniform parallelism),说的就是这个怪癖。
类比失效处:真实工厂里工人可以临时改做别的活,GPU 的 SM 在同一个 kernel 里做不到——一个 kernel 启动时就定死了要干什么、切成多少块。这正是为什么论文要为「训练/预填充」和「解码」分别写不同的 kernel,而不是写一个万能 kernel 自动适应。
内核(kernel):一段专门写来在 GPU 上跑的小程序,是 GPU 干活的最小任务单位。CPU 说一句「启动这个 kernel」,GPU 上成千上万个线程就同时开始跑同一段代码。融合内核(fused kernel)指的是把原本要分好几次启动的多个 kernel 合并成一个,省掉中间结果来回搬运的开销。注意别把它和「操作系统内核」搞混,是同一个英文词,完全不同的东西。
显存(memory / HBM)与带宽(bandwidth):显存是显卡上能装东西的容量,单位是 GB,决定「装不装得下」;带宽是数据在显存和计算单元之间搬运的速度,单位是 GB/s,决定「搬得快不快」。这两件事经常被初学者混为一谈,但它们造成的是完全不同的两种失败:容量不够会直接崩溃报错(out of memory, OOM),带宽不够只是变慢。§5.2.2 整节六个技术,全部在解决容量问题;§5.1 的 FlashKDA 则主要在解决「SM 闲置」和搬运开销。
通信开销(communication overhead):一张卡装不下,就得用几千张卡;几千张卡就得互相传数据。卡与卡之间传数据比卡内部读显存慢一到两个数量级,而且传数据的时候计算单元往往在干等。所以大规模训练的核心矛盾从来不是「算得快不快」,而是「传得少不少、能不能一边传一边算」。重叠(overlap)这个词在本章会出现十几次,指的就是「让通信和计算同时进行,用计算的时间把通信的时间盖住」。
五种「并行」分别在切什么
模型大到一张卡装不下,就必须切开分给多张卡。切法不止一种,而 K3 的预训练把五种切法同时用上了。它们切的对象完全不同:
论文说:张量并行把注意力头切到不同设备上,但「从不缩短递推」(never shortens the recurrence),所以在纯 TP 部署下预填充一条超长序列时,每个 rank 只持有几个头,大部分 SM 是闲的。请用 SM 的概念解释这句话:为什么「只持有几个头」就会导致 SM 闲置?
变式:如果换成普通的 softmax 注意力做预填充,纯 TP 下会不会有同样的「SM 大量闲置」问题?为什么?(提示:softmax 注意力在 token 维度上是不是串行的?)
10.2 §5 开头:三个挑战同时撞在一起
论文开篇一句话点明了 K3 基础设施的处境:K3 把三个很少同时出现在一个模型里的系统挑战凑齐了——
- 混合 KDA 注意力:一个带固定大小循环状态的、串行的注意力形式,跟 GPU 的脾气正好相反;
- 3T 级的稀疏多模态训练与推理:论文用「3T 级」(3T-class)指代模型规模量级,而且是原生多模态的,图像视频和文本混在一起训;
- 百万 token 的智能体工作负载:一次任务的上下文能长到一百万个 token。
给「3T 级」一点尺度感
3 万亿个参数是什么概念?就算每个参数只用 1 个字节存(FP8),光是权重就是 3 TB;用 2 个字节存(BF16)就是 6 TB。而一张主流训练卡的显存通常在几十 GB 量级。也就是说,光把模型参数放下就需要上百张卡,这还没算训练时必须额外保存的梯度、优化器状态和激活——这三样加起来往往比参数本身还大好几倍。这就是为什么 §5.2.2 要用整整一节、六种技术来抠显存。(这里的换算是为了给你尺度感,不是论文给出的数字。)
论文的说法是:基础设施与这些挑战在整个模型生命周期上协同设计(co-designed across the model lifecycle)。这句话不是套话,它对应四个具体阶段,而且这四个阶段正好构成本章和下一章的骨架:
| 阶段 | 论文的原话要点 | 在哪一章讲 |
|---|---|---|
| 架构层面 | 高性能 KDA 内核 + 上下文并行,让循环形式在设备内和跨设备、在训练和推理时都高效 | 本章 10.4、10.5 |
| 预训练期间 | 均衡的专家执行、降低的内存占用、通信重叠的调度,维持大规模下的高利用率 | 本章 10.6–10.9 |
| 1M token 智能体 RL 期间 | 分层状态管理 + 可恢复沙箱执行,跨迭代保存长轨迹 | 第11章 |
| 生产服务 | 状态感知的 KDA 前缀缓存、专用推理内核、缓存与预算感知的调度,把效率转化成可预测的服务 | 第11章 |
注意最后一格里「可预测」(predictable)这个词。工业界部署最怕的不是慢,是忽快忽慢——用户请求的响应时间如果方差极大,产品体验就崩了。论文把整条基础设施链条的终点定在「可预测的生产服务」,说明他们的目标函数不只是吞吐量。
论文说 KDA 的固定大小状态「便宜、易于传输和复用」(cheap to transfer and reuse)。请指出这一条性质分别在上表的哪几个阶段被利用了,并说明在每个阶段它带来的具体好处是不同的。
变式:反过来问——KDA 状态的「串行」这条性质,在这四个阶段里分别造成了什么麻烦?(提示:论文明说解码阶段的挑战与训练/预填充不同,放到 §5.4.2 单独讲,也就是本书第11章。)
10.3 §5.1 的核心张力:一笔交易,两面后果
整个 §5.1 建立在一句话上,值得逐字读:KDA 用一个固定大小的循环状态取代了 softmax 注意力那个不断增长的键值缓存,代价是它的串行形式给并行执行带来困难,换来的是一个便宜、易于传输和复用的固定大小状态。
这是一笔交易,而不是一次纯粹的改进。交易的两面是:
- 坏的一面(第一个性质):状态是串行更新的,第 t 步必须等第 t−1 步。GPU 讨厌这个。
- 好的一面(第二个性质):状态大小固定,不管前面读了 1 万还是 100 万个 token,它都是那么大一块。便宜、好传、好存、好复用。
论文接下来的所有设计,一句话概括就是:解决第一个性质带来的问题,同时在两个执行层级上把第二个性质的好处吃干榨净。两个层级分别是——设备内的融合内核(10.4),设备间的 KDA 上下文并行(10.5)。这个「两个层级」的框架请记住,它是 §5.1 的目录。
先把式 1 抄回来,后面 10.5 全靠它:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| St | 读完第 t 个 token 之后的循环状态矩阵,形状 dk×dv | 模型到目前为止的「记事本」,大小固定,写满了就得挤掉旧的 |
| Mt | 第 t 个 token 决定的转移矩阵,形状 dk×dk | 「在写新东西之前,先按这个规则把整本记事本改写一遍」。本章的一切困难都来自它 |
| kt, vt | 第 t 个 token 的键向量和值向量 | 「要往记事本哪个位置写」和「写什么内容」 |
| βt | 第 t 个 token 的标量写入强度 | 这一笔写得多用力 |
| Diag(αt) | 把向量 αt 摆到对角线上做成的对角矩阵 | 逐通道的遗忘门:记事本每一行按各自的比例淡去 |
| I | 单位矩阵 | 「什么都不改」的那个矩阵 |
一个必须现在就纠正的误解
很多人看到 Mt 里有 I,就以为它「基本上等于不变,只是稍微修一修」。不是的。Mt 是一个货真价实的矩阵,把它乘到 St−1 上会把整个状态彻底重新混合一遍。更要命的是,矩阵乘法不满足交换律,所以 M2M1 ≠ M1M2——token 的先后顺序被牢牢锁死在这串乘积里。10.5 那道构造反例题,根子就在这里。
下面这道题请务必做完再往下读——10.5 全靠它撑着。
取一个具体的极端情形:设 αt 的每个分量都等于 1(完全不遗忘),βt = 1,且 kt 是一个长度为 1 的向量(即 kt⊤kt = 1)。请写出此时的 Mt,并说清它作用在传入状态 St−1 上到底干了什么。这跟「基本等于不变,只是稍微修一修」差多远?
所以 Mt 是一个投影:它把状态里沿着 kt 这个方向的成分彻底抹成零,其余方向完全不动。这离「稍微修一修」远得不能再远了——它是把记事本上「kt 这一栏」原有的内容整条擦干净,然后式 1 的第二项 βtktvt⊤ 再往这条空栏里写上新内容。
这正是「delta 规则」这个名字的来历:先删旧的、再写新的,而不是简单叠加。也正因为它会删除东西,「各段各自从零算再求和」才必然失败——求和法只会往上叠,永远表达不了「删除」这个动作。
顺带说明为什么一般情况下更复杂:当 βt 不等于 1 时它是部分擦除,当 Diag(αt) 不是 I 时还要叠加一层逐通道的整体淡化,但「会删东西」这个本质不变。
变式:如果 βt = 0,Mt 变成什么?此时 KDA 的更新退化成哪种更简单的注意力形式?(提示:把 βt = 0 代进式 1 的两项,看看还剩什么。)
10.4 分区制的内核:为每种跑法单独写一套
论文 5.1.1 的第一句话就是整节的论点:KDA 状态的串行依赖与 GPU 偏好「又宽又均匀的并行」相冲突,而且这个冲突在每一种执行区制里表现为不同的瓶颈,所以每种区制都要专门的内核(a dedicated kernel for each regime)。
什么叫「执行区制」(regime)?就是模型跑起来的几种不同模式:训练(一次喂进整段文本,要算前向和反向)、预填充(prefill,推理时先把用户给的长提示词整段读进去)、解码(decoding,之后一个 token 一个 token 地往外吐)。这三种模式下「有多少活可以同时干」完全不同,所以一套 kernel 不可能都伺候好。
训练与预填充:分块内核 FlashKDA
KDA 的分块形式(chunkwise form)是这样干的:把长序列切成一块块(chunk),块内并行、跨块串行——因为循环状态必须一块一块地往下传。
论文指出的问题是:天真地执行时,「块内并行计算」和「跨块串行传播」这两个阶段是交替进行的,而在串行传播的那段时间里,SM 全在闲着。这就是典型的「一百多条流水线,轮流只有一条在动」。
FlashKDA 是论文给出的解法:一个基于 CUTLASS(英伟达的高性能矩阵运算模板库)的分块内核,把块内计算与跨块状态传播重叠起来。具体做法是把工作分解成token 并行的阶段和一个 head 并行的递推,两者各自独立调度、独立调优。论文称它大幅超过 Triton 参考实现。
另外两个工程细节值得记:FlashKDA 同时服务训练和推理预填充(一套 kernel 覆盖两种区制,因为这两种区制的并行结构其实是一样的),并且作为 flash-linear-attention 库的一个后端被自动分派——也就是说用户调库时不需要手动选择,库会自己判断该用哪个后端。
为什么要「各自独立调度和调优」
因为这两条通路的性能瓶颈不一样:token 并行那部分是典型的「宽而均匀」的大矩阵乘法,调优目标是喂饱所有 SM;head 并行的递推那部分并行度天生就小(只有头数那么多),调优目标是尽量减少每一步的延迟。如果把它们塞进同一个调度框架,就只能按一套参数来配,两边都配不好。拆开之后,各自按自己的瓶颈调,然后在时间上叠起来——这就是「重叠」两个字的全部含义。
长上下文预填充:设备内的上下文并行
这是 10.1 那道题的答案的正面版本。问题回顾:纯 TP 部署下预填充超长序列,每个 rank 只有几个头,大部分 SM 闲置。
论文给出的关键观察是一句需要慢慢咀嚼的话:每个片段的状态转移可以独立于传入状态来求值,之后再精确地复合(the state transition of each segment can be evaluated independently of the incoming state and composed exactly afterward)。
翻译成大白话:你要算第 5 段读完之后状态变成什么样,确实得知道第 4 段结束时状态是什么。但是,「第 5 段这批 token 会对状态做什么样的改造」这件事本身,跟「进来的状态是什么」无关——那批 M 矩阵是由第 5 段自己的 token 算出来的,跟前面读了什么毫无关系。所以你完全可以在不知道传入状态的情况下,先把「改造规则」算出来,等传入状态到了再套上去。而且这个「套上去」是精确的,不是近似。
基于这个观察,论文用了一个自动的 SM 级上下文并行规划器:把序列切到单个 rank 的各个 SM 上,并行求出各片段的转移,再合并起来恢复每个片段的精确状态。论文特别强调:与 5.1.2 的跨设备 KCP 相对照,这种并行完全在设备内,不产生任何跨设备通信。
至于解码区制,论文说它面临与训练/预填充完全不同的挑战,放在 §5.4.2 详细讨论——那是本书第11章的内容。原因不难猜:解码时一次只处理一个 token,连「块内并行」都没有了,瓶颈从「SM 闲置」变成了别的东西。
5.1.1 的 SM 级上下文并行和 5.1.2 的 KCP 用的是同一个数学观察,但论文把它们分成两节讲。请说清:这两者在「付出的代价」上有什么本质区别?如果 SM 级 CP 零通信这么好,为什么还需要 KCP?
变式:如果某天出现一种新硬件,单卡显存大到能装下百万 token 的全部激活,KCP 是不是就没用了?(提示:想想训练时一个 batch 里有多少条序列,以及总吞吐量的需求。)
10.5 KDA 上下文并行(KCP):本章最难的一节
做好心理准备,这一节要慢慢走。但走完之后你会发现,它其实只有一个想法,而且这个想法在 10.4 末尾已经见过了。
第一步:为什么线性注意力做上下文并行「本该」很便宜
论文开门见山地对比:上下文并行的通信开销在 softmax 注意力和线性注意力之间有本质区别。
- softmax 注意力:要求各 rank 交换键值块,而这些块的大小随序列长度增长。序列越长,要传的东西越多。
- 线性注意力:把先前的上下文装在一个固定大小的循环状态 S ∈ ℝdk×dv 里。序列多长都是这么大一块。
第二步:先前方法的老办法,以及它为什么对 KDA 失效
论文说,先前的上下文并行方法利用的是普通线性注意力的「可加递推」:在每个 rank 上计算本地 token 从 S = 0 出发生成的状态,然后把先前各 rank 的这些状态求和,就得到传入状态。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| S̃[j]Tj | rank j 上,从 S = 0 出发、读完它本地全部 Tj 个 token 后得到的状态 | 「假装前面什么都没读过,光看我这一段能记住什么」 |
| 波浪号 ~ | 论文的约定:加了波浪号就表示「从 S = 0 起算的对应量」 | 见到波浪号就想「这是纯本地的、可以马上算出来的」 |
| Tj | rank j 上本地 token 的个数 | 这一段有多长 |
为什么普通线性注意力可以这么干?因为它的更新是纯粹的加法:新状态 = 旧状态 + 这一步写入的东西。加法满足结合律和交换律,所以「每段各自从零算,最后加起来」跟「从头顺序算一遍」结果完全一样。
但论文紧接着说:这种直接求和对 KDA 不成立(is insufficient for KDA)。理由就写在式 1 里——
把「为什么求和不行」讲到最直白
KDA 的更新是 St = MtSt−1 + βtktvt⊤。注意那个 Mt:KDA 的 delta 规则在加上当前写入之前,先把一个依赖 token 的矩阵作用到传入状态上。
用记事本的比喻:普通线性注意力是「在记事本后面追加一行」——你先记了什么、我后记了什么,谁先谁后无所谓,最后拼起来就行。KDA 是「每写一行之前,先按当前这一行的规则把整本已有的内容涂改一遍」。
后果是致命的:一段 token 对状态的效果,取决于进入这一段时状态是什么。你从空白记事本开始涂改,和从写满前文的记事本开始涂改,得到的结果没有任何简单关系——不是差一个常数,不是差一项,是完全两回事。所以论文说,一个局部片段的效果「无法只从 S = 0 出发算出的状态确定」。
类比失效处:记事本的比喻会让你以为「涂改」只是擦掉一点,其实 Mt 是矩阵乘法,它把整个状态重新线性混合了一遍,远比「擦掉几个字」剧烈。
构造反例题。设有 P = 2 个上下文并行 rank,每个 rank 只有 1 个 token(记 rank 1 的转移矩阵和写入分别为 M1、w1,rank 2 的为 M2、w2,其中 w 代表 βkv⊤ 这一项)。请写出:(a) 顺序执行得到的真实最终状态;(b) 用「各自从零算再求和」得到的状态;(c) 两者相差什么,并指出在什么条件下它们才会相等。
(a) 真实值:S2 = M2w1 + w2。
(b) 求和法:rank 1 从零算得 S̃[1] = w1;rank 2 从零算得 S̃[2] = w2;求和得 w1 + w2。
(c) 差值:真实值 − 求和值 = M2w1 − w1 = (M2 − I)w1。
两者相等当且仅当 (M2 − I)w1 = 0。最干净的充分条件是 M2 = I,也就是没有任何遗忘、没有任何 delta 改写——而这正好就退化成了普通线性注意力(纯加法递推)。这个反例把整件事讲透了:求和法之所以对普通线性注意力成立,恰恰是因为那里的「转移矩阵」永远是单位阵;KDA 引入了非平凡的 Mt,等式立刻塌掉。而且注意差值里的 w1 被 M2 作用了——rank 1 的信息必须穿过 rank 2 的本地更新才能到达终点,这就是论文说的「通过本地 KDA 更新把先前各 rank 的上下文传播过来」。
变式:把 rank 数改成 P = 3,每个 rank 仍是 1 个 token。真实的最终状态是什么?w1 前面挂了几个 M?由此猜一猜:一般情况下 rank j 的贡献前面应该挂哪些矩阵的乘积?(猜完再往下读式 17,你会发现你已经猜对了。)
第三步:KCP 的解法与式 17
论文的解法叫 KDA 上下文并行(KDA Context Parallelism, KCP)。核心动作是:把每个片段的效果分解成两个可以纯本地计算的量——
- 一个作用在传入状态上的累积转移(矩阵 M);
- 一个从零本地生成的状态(带波浪号的 S̃)。
先把记号约好,不然式 17 读不下去:S[i]t 表示 rank i 内部经过 t 个本地 token 后的循环状态;S[i]Ti 就是离开 rank i、进入 rank i+1 的那个状态;带波浪号的 S̃ 表示「同一个递推但从 S = 0 起算」的对应量。总共有 P 个上下文并行 rank。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| P | 上下文并行 rank 的总数 | 这条序列被切给了几张卡 |
| i, j, l | rank 的下标,i+1 是「当前正在看的那个 rank」 | j 和 l 用来遍历它前面的所有 rank |
| t, r | rank 内部的本地 token 下标 | 「我这一段读到第几个字了」 |
| Ti | rank i 的本地 token 总数 | 这一段一共多长 |
| Mr | 第 r 个本地 token 的单步转移矩阵,即式 1 里的 (I − βrkrkr⊤)Diag(αr) | 「这一个 token 会怎么涂改整本记事本」 |
| M[i+1]t←1 | 累积转移:把本地前 t 个 token 的单步转移按顺序乘起来,形状 dk×dk | 「我这一段前 t 个字合起来,会把传进来的记事本改造成什么样」。注意 ←1 的箭头方向:从 t 往 1 写,强调乘法顺序不能颠倒 |
| S̃[i+1]t | 本地从零生成的状态:假装前面什么都没读过,只读本地前 t 个 token 得到的状态 | 「我这一段自己贡献了什么新内容」 |
| S[i]Ti | 离开 rank i、进入 rank i+1 的真实传入状态 | 「上一张卡交接给我的那本记事本」——这是我们要费劲求出来的东西 |
| Πl←j+1i M[l]Tl←1 | 把 rank j+1 到 rank i 的整段累积转移再乘起来 | 「rank j 贡献的那点内容,要穿过它后面所有 rank 的涂改,才能活着到达我这里」 |
式 17 的两项,各自是什么意思
论文自己给了一句话解释,非常准确:第一项(S̃[i+1]t)是本地 token 生成的状态;第二项通过本地的 KDA 更新把先前各 rank 的上下文传播过来。
换句话说,最终状态 = 「我自己写的」 + 「前面所有人写的东西,经过一路涂改之后剩下的」。第二行到第三行做的事情,只是把 S[i]Ti 这个「上一张卡交接来的记事本」递归地展开,一直展开到全部由带波浪号的、纯本地的量组成为止。
自己推一遍:从式 1 到式 17 的第三行
从 St = MtSt−1 + wt 出发(wt := βtktvt⊤),假设进入这一段时的状态是 Sin。把 t = 1, 2, 3 三步老老实实展开,你看到什么规律?
想好了再看
S1 = M1Sin + w1;S2 = M2M1Sin + M2w1 + w2;S3 = M3M2M1Sin + M3M2w1 + M3w2 + w3。规律很清楚:Sin 前面挂着全部 M 的乘积,而后面那一堆恰好就是「从零出发」会得到的东西。当初会想到这一步,是因为线性递推展开永远是这个形状——齐次项 + 非齐次项,这是解线性递推的标准套路。
把上一步的结果按「含 Sin 的」 和 「不含 Sin 的」分成两堆。不含 Sin 的那一堆,能不能用一个我们已经定义过的符号来表示?
想好了再看
能。不含 Sin 的那一堆,正是令 Sin = 0 时的结果,也就是 S̃[i+1]t。含 Sin 的那一堆则是 (Mt⋯M1)Sin,把这串乘积记作 M[i+1]t←1。于是就得到式 17 的第二行:S[i+1]t = S̃[i+1]t + M[i+1]t←1S[i]Ti。这一步就是整个 KCP 的全部灵魂:把「这一段干了什么」拆成了一个只依赖本地 token 的矩阵,加上一个只依赖本地 token 的状态。
第二行里还剩一个非本地的量 S[i]Ti。它是 rank i 的「离开状态」。你能对它做同样的事吗?做完之后又会剩下什么?
想好了再看
可以,而且这正是关键:S[i]Ti = S̃[i]Ti + M[i]Ti←1S[i−1]Ti−1。剩下的非本地量变成了 rank i−1 的离开状态——同一个形式,下标减一。所以把它一路递归展开到 rank 1(rank 1 的传入状态就是 0),非本地量就彻底消失了。展开的结果,就是式 17 第三行里那个求和:rank j 的贡献 S̃[j]Tj,前面挂着从 rank j+1 到 rank i 的所有累积转移的乘积。这跟你在 q10-5 变式里猜的应该一模一样。
最后一问,也是最重要的一问:在 t = Ti+1 时,M[i+1]Ti+1←1 和 S̃[i+1]Ti+1 这两个量,需要等别人的数据才能算吗?
想好了再看
完全不需要。论文明说:这两个量「只用本地 token 就能算出来,不必等 S[i]Ti」。这意味着所有 rank 可以同时开工,各自算自己的两块碎片,一点都不用互相等待。等所有人都算完了,再交换碎片、拼装。这就是「并行」二字真正落地的地方——如果这两个量需要等前面的状态,那 KCP 就退化成了串行执行,一点意义都没有。
第四步:结合律、前缀扫描、以及只需一次 all-gather
论文接着点出:式 17 表明每个状态都纯由本地计算出的碎片复合而成,而且这些 rank 级的更新满足结合律(compose associatively),所以每个 rank 的传入状态可以用一次前缀扫描(prefix scan)恢复。
前缀扫描(prefix scan):给一串元素和一个满足结合律的合并操作,一次性求出「前 1 个的合并结果、前 2 个的合并结果、……、前 n 个的合并结果」。它的关键性质是:因为满足结合律,你可以任意加括号、任意换分组顺序,所以这件事可以并行做,而不必老老实实从左到右串行走一遍。
具体流程分三步,论文写得很清楚:
- 本地计算:每个 rank 先本地算出自己的两块碎片 M[i]Ti←1 和 S̃[i]Ti。这一步没有任何通信。
- 一次 all-gather:用一次 all-gather 把这两个张量都交换出去(all-gather 的意思是「每个人把自己的那份广播给所有人,最后所有人都拿到全套」)。
- 本地重建:all-gather 之后,rank i+1 按顺序处理同一文档的先前碎片,从 S = 0 开始,在每个碎片处应用下面这条规则,就重建出了 S[i]Ti。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| S(箭头左右两个) | 一个累加器,从 0 开始,每处理一个先前碎片就更新一次 | 「我手上正在重建的那本记事本」 |
| M[j]Tj←1 | 从 rank j all-gather 来的累积转移碎片 | 「rank j 会怎么涂改我手上这本」 |
| S̃[j]Tj | 从 rank j all-gather 来的本地状态碎片 | 「rank j 自己新写了什么」 |
| j 的遍历顺序 | 严格按文档中的先后顺序,从前往后 | 顺序不能乱——矩阵乘法不交换 |
请注意这条重建规则的形状:它和式 1 一模一样(「先乘一个矩阵、再加一项」),只不过「一个 token」换成了「一整个 rank 的碎片」。这就是「rank 级更新满足结合律」的具体含义——同一种代数结构,只是粒度粗了。
出处与实现
论文在脚注里注明:这个构造建立在 DeltaNet 的上下文并行之上,KDA 的实现见 FLA PR #691。也就是说 KCP 不是凭空发明的,而是把 DeltaNet 那一套推广到了带 Diag(α) 遗忘门的 KDA 上。
综合题。论文说「KCP 只需要一次固定大小的 all-gather」。请解释:为什么「固定大小」这四个字是决定性的胜利,而不只是一个不痛不痒的优化?请把它和 softmax 注意力上下文并行的通信量放在一起对比,并说清这一条对 K3 的「百万 token 智能体工作负载」这个目标意味着什么。
(1)看趋势而非看常数。softmax 注意力做 CP 时,各 rank 必须交换键值块,而键值块的大小随序列长度增长。所以序列拉到十倍长,这笔通信也跟着涨。KCP 交换的是 M(dk×dk)和 S̃(dk×dv)两个张量,形状由模型维度决定,与序列长度无关。序列拉到十倍、一百倍,这两个张量一个字节都不会变大。
(2)看比值。真正决定「能不能扩展」的不是通信的绝对量,而是通信时间占总时间的比例。KCP 的计算量随序列长度线性增长(论文说它「实现线性的计算缩放」),而通信量是常数——于是通信占比随序列变长而不断下降,序列越长,这笔通信越显得微不足道。softmax CP 那边通信和计算同时增长,占比降不下去。一个是随规模自动变好的设计,一个不是,这是量变到质变的区别。
(3)对 K3 的意义。K3 明确要支持百万 token 的智能体工作负载。在这个长度上,「通信量随长度增长」意味着通信会吃掉大部分时间,长上下文训练在经济上直接不成立。KCP 把这一项按死成常数,才让百万 token 训练从「贵得离谱」变成「可行」。这也回扣了 §5.1 开头那句话:固定大小状态「便宜、易于传输和复用」——KCP 就是把「易于传输」这条性质兑现成真金白银的地方。
(4)诚实的补充。论文并没有给出 KCP 相对 softmax CP 的具体加速数字,也没说 M(dk×dk)这个矩阵在中等长度下是不是反而比键值块还大。上面的论证是渐近意义上的胜利,在具体的序列长度下谁快谁慢,论文没有给数据。
变式:假设有人把 KDA 的遗忘门砍掉,让 Mt 恒等于 I。这时候 KCP 会退化成什么?all-gather 要传的东西会少一半吗?(提示:M 恒为 I 时还需要传 M 吗?重建规则会变成什么?)
答辩:如果我是审稿人
你说 KCP「只需要一次固定大小的 all-gather」,听起来很美。但我注意到两件事:第一,你要交换的不只是状态 S̃(dk×dv),还有累积转移矩阵 M(dk×dk)——凭空多出来一个矩阵,通信量至少翻倍甚至更多,你在正文里对此只字未提。第二,all-gather 之后每个 rank 要「按顺序处理先前碎片」来重建自己的传入状态,rank i+1 要走 i 步,这是 O(P) 的串行矩阵乘法。当 P 很大时,这个重建本身会不会变成新的瓶颈?你凭什么说 KCP 便宜?
参考防守(先自己组织语言再看)
第一点,承认但不致命。确实要多传一个 dk×dk 的矩阵,这是 KDA 的 delta 规则带来的必然代价——没有 M 就无法表达「传入状态会被本地 token 改写」这件事,而这正是 10.4 那道反例题证明的。但关键在于:它仍然是常数。审稿人的批评改变的是常数因子,不改变「与序列长度无关」这个结论,而后者才是论文主张的那一条。在百万 token 的目标场景下,常数因子翻倍和「随长度线性增长」完全不在一个讨论层次上。
第二点,是真问题,论文确实没有正面回答。不过有两条辩护线索:(a) 论文明确说这些 rank 级更新满足结合律,并且点名用前缀扫描——前缀扫描的存在意义恰恰就是把 O(P) 的串行扫描做成 O(log P) 的并行归约,所以「必须串行走 i 步」这个前提本身就可以被打破;论文引用前缀扫描而不是简单说「顺序累加」,很可能就是在暗示这一点。(b) 每一步重建只是一次 dk×dk 乘 dk×dv 的小矩阵乘法,相对于每个 rank 本地几十万 token 的计算量,量级差得很远。
但要诚实地说:论文既没有给出 P 变大时的扩展性曲线,也没有量化重建开销,更没有说明当一个 batch 里混着多篇文档时(重建时要「处理同一文档的先前碎片」)额外的簿记成本有多大。这些都是论文留白的地方。
10.6 §5.2:3T 级预训练的总体拼装
从这里开始换战场。前面讲的是「怎么让 KDA 这种奇怪的算子跑得快」,接下来讲的是「怎么让一个 3T 级的多模态模型在上千张卡上训得起来」。
论文先列出 K3 预训练用到的全套并行方案,你会发现 10.1 里讲的五种全在里面:带虚拟阶段(VP)的流水线并行(PP)、专家并行(EP)、ZeRO-1 数据并行、流水线 ZeRO-2 梯度分片、以及 上下文并行(CP,就是 §5.1.2 的 KCP)。
ZeRO(Zero Redundancy Optimizer):数据并行时每张卡都存一份完整的梯度和优化器状态,这是巨大的浪费。ZeRO 的思路是把这些东西分片存到各张卡上,谁需要谁去取。ZeRO-1 分的是优化器状态,ZeRO-2 进一步分梯度。
MoE 层的做法:共享专家跨 EP rank 复制(每张卡都有一份,不用发 token 出去),而专家分发与合并的 all-to-all 通信与计算重叠,用来隐藏延迟。这里的 all-to-all 就是 10.1 图里 EP 那一格说的「token 飞到自己选中的专家所在的卡上,再飞回来」。
关于 Figure 11
论文的 Figure 11 展示的是:计算、通信、卸载在不同 PP 阶段里的重叠情况。图里能看到 DataLoader、ViT 前向、gather param、EP dispatch/combine、shared expert、offload/reload 等一系列条带,按 PP 阶段横向排开,密密麻麻地交错在一起。这张图想传达的信息只有一个:在任何一个时刻,一张卡上都同时在干好几件事——一边算、一边传、一边往 CPU 内存搬东西。图里没有给出量化的利用率数字,所以它是定性示意,不是性能数据。
然后论文把 3T 级原生多模态预训练的困难归结成三个关键问题,接下来三个小节正好一一对应:
| 问题 | 症状 | 解法小节 |
|---|---|---|
| (i) token 负载在各 EP rank 间不均衡 | 有的卡挤满 token,有的卡闲着;快的等慢的 | §5.2.1 MoonEP(本章 10.7) |
| (ii) 激活、梯度、优化器状态超出内存预算 | 显存装不下,直接 OOM 崩溃 | §5.2.2 显存高效训练(本章 10.8) |
| (iii) 视觉编码器高度可变的计算暴露在关键路径上 | 一张大图或一段长视频拖慢整批人 | §5.2.3 多模态编码器优化(本章 10.9) |
论文说 MoE 层的共享专家是「跨 EP rank 复制」的,而路由专家需要 all-to-all 通信来分发和合并 token。为什么共享专家可以省掉 all-to-all?这么做的代价是什么?
变式:如果把共享专家也改成分片存放(每张卡只存一部分),会发生什么?每个 token 都要飞出去一次,这笔 all-to-all 的量跟路由专家的相比是大还是小?
10.7 MoonEP:把专家负载切得一碗水端平
先说清问题。常规的专家并行方案里,token 负载在各 rank 间是不均衡的——路由器是学出来的,它想把 token 送给谁就送给谁,没人保证送得均匀。论文指出这会造成两个后果,第二个经常被人忽略:
- 计算不均衡拖慢训练吞吐:所有卡必须同步前进,最慢的那张卡决定了整体速度。
- 路由专家激活的形状动态变化,造成大量内存碎片:这一步分配一块 3000 个 token 的内存,下一步要 5000 个,再下一步 1200 个,分配器很快就被切得七零八落。
MoonEP 的解法是用动态冗余专家(dynamic redundant experts)实现完美负载均衡。所谓冗余专家,就是把某个热门专家额外复制一份放到别的 rank 上,让涌向它的 token 可以分流。
论文强调 MoonEP 保留了 DeepEP 等常规方案的整体计算流,只是额外引入了冗余专家的在线规划与迁移。具体来说:
- 前向传播时:从当前微批次和当前层的路由器输出规划冗余专家,并在路由专家计算之前预取它们。注意「当前微批次和当前层」这几个字——规划是逐层、逐微批次做的,粒度非常细。
- 反向传播时:把冗余专家的梯度暂存在一个本地 reduce 缓冲区里,等计算完成后再归约回它们「归属 rank」(home rank)的梯度缓冲区。这一步是必须的——同一个专家的多份副本产生的梯度必须合并,否则参数就分叉了。
核心定理:E/R 个冗余专家就够
这是 5.2.1 里最硬的一块。论文说:MoonEP 要求每个 rank 恰好收到 S × K 个 token(S 是序列长度,K 是每个 token 选中的专家数),这样所有 rank 做同样多的计算。
这里有一个容易读错的地方
论文没有明说 S 是「全局序列长度」还是「每个 rank 本地的 token 数」。但从后面那句「最坏不均衡下 DeepEP 需要 S×K×R 大小的缓冲区」可以反推出来:S 应当是每个 rank 本地持有的 token 数。理由是——每个 rank 本地有 S 个 token,每个 token 选 K 个专家,于是本地要发出 S×K 份 token 副本;整个 EP 组一共 R×S×K 份;最坏情况下这 R×S×K 份全涌向同一张卡,所以缓冲区要按 S×K×R 开;而完美均衡下每张卡恰好收到 R×S×K / R = S×K 份。这个读法自洽,但是我们的推断,论文本身没有把 S 的定义写清楚。另外注意这里的 S(标量,序列长度)和 10.5 里的 S(状态矩阵)是完全不同的两个东西,论文复用了同一个字母。
然后是关键问题:多少个冗余专家才足够保证这种均衡?设 E 为专家总数、R 为 EP 大小,论文证明:每个 rank 至多 E/R 个冗余专家时,均衡方案总是存在,而且这个界本质上是紧的(essentially tight)。完整证明在论文附录 E,本书第14章会讲。
论文特意把这一点跟先前工作对比,对比得相当尖锐。ECHO 和 UltraEP 这类工作的做法是预设冗余专家的数量,或者施加每 rank 的 token 上限。这带来三个毛病:一旦上限内无可行方案,训练就被迫停止;上限本身需要人工调参;而且就算调好了,仍然会留下残余的不均衡。MoonEP 的卖点不是「更均衡一点」,而是「有数学保证的、永不失败的完美均衡」——这是定性的差别。
另外三件配套工程
在线规划。每一步都算精确最优解代价过高。论文的做法很实用:用整数线性规划(ILP)离线算出代表性情形的精确解作为参照,据此设计一个 GPU 规划内核,做到近似最优、开销可忽略、且永远遵守 E/R 上界。注意这个思路——先用慢而精确的方法建立「标准答案」,再据此设计快而近似的方法,然后验证近似方法离标准答案有多远。
零拷贝通信。完美均衡还简化了通信路径。论文实现了一个融合的 permute/unpermute 算子:规划内核预先算好每个 token 的目的地,于是 token 被直接送到远程 rank 上按专家分组的位置,通信缓冲区的视图直接返回给计算,消除了中间拷贝。然后是那句关键的对比:在最坏的不均衡下,DeepEP 里支持同样免拷贝数据路径需要大小为 S×K×R 的通信缓冲区,而 MoonEP 因为完美均衡只需要固定的 S×K 缓冲区。
自己算一遍。假设每个 rank 本地持有 S = 8192 个 token,每个 token 选 K = 8 个专家,EP 大小 R = 64。(这些数字是为了练手假设的,不是论文给出的 K3 配置。)请算出:(a) MoonEP 需要的通信缓冲区能装多少个 token 副本?(b) 最坏不均衡下 DeepEP 支持同样免拷贝路径需要多大?(c) 相差几倍?(d) 用一句话解释这个倍数是怎么来的。
(a) MoonEP:S×K = 8192 × 8 = 65,536 个 token 副本的空间。
(b) DeepEP 最坏情况:S×K×R = 65536 × 64 = 4,194,304 个。
(c) 相差 64 倍,正好等于 R。
(d) 原因:缓冲区必须按最坏情况开,否则一旦真的挤爆就会崩。在完全没有均衡保证的方案里,最坏情况是全组 R 张卡的 R×S×K 份 token 副本全部涌向同一张卡,所以每张卡都得按这个上限预留空间。MoonEP 因为有 E/R 定理提供的数学保证,知道每张卡收到的恰好是 S×K,一个不多一个不少,于是缓冲区可以按精确值开,省下 R−1 倍的空间。
这里值得体会的是逻辑顺序:省显存不是靠「优化」省出来的,而是靠一条定理省出来的——因为能证明上界,才敢把缓冲区开小。数学保证在这里直接兑换成了显存。
变式:如果 EP 大小 R 增加到 128,MoonEP 的缓冲区会变大吗?DeepEP 的呢?由此说明:这两种方案在「扩大 EP 规模」时的可扩展性差在哪里。
静态形状带来的免同步执行。这一条的收益很隐蔽但很实在。常规 MoE 实现里,每个专家分到的 token 数在不同步、不同层间都在变,于是主机(CPU)必须在启动专家计算前与设备(GPU)同步,才能知道实际的计算形状是多少——而这个同步会在层与层之间拖住流水线。有了完美均衡,每个 rank 恰好收到 S×K 个 token,所有层的计算形状静态已知,于是逐层的 MoE 主机同步被彻底消除,主机侧的 kernel launch 开销也被缓解。
为什么「主机与设备同步」这么贵
CPU 给 GPU 派活的正常方式是「扔了就走」——CPU 把一堆 kernel 排进队列,自己接着往下跑,不等结果。这样 CPU 才能一直跑在 GPU 前面,队列里始终有活。但「同步」这个动作打破了它:CPU 必须停下来等 GPU 把结果吐回来,才知道下一个 kernel 该开多大。这一等,GPU 的任务队列就见底了,等 CPU 缓过神来再派活,GPU 已经空转了一段时间。每层都来这么一次,几十上百层累积起来非常可观。所以「形状静态已知」的真正价值不是省了一次判断,而是让 CPU 和 GPU 重新解耦。
专家 GEMM 调度与重叠。这一条是全节最容易被误读的地方,请特别注意:即使聚合负载在各 rank 间完美均衡了,每个 rank 内部各个专家分到的 token 数仍然是倾斜的。如果用固定顺序、无视工作量的调度,这种倾斜就会变成 SM worker 之间不均衡的完工时间(makespan)。论文的解法是用一个工作量感知的调度器来调度路由专家的 GEMM:它在启动前根据当前 token 分布调整参数,执行期间保持不变。参数由一个轻量启发式用硬件指标的解析代价模型选取,关键系数通过离线自动调优标定。另外,共享专家的 GEMM 被派到单独的 stream 上,好与其他内核重叠。
构造反例题。有人读完「完美负载均衡」这一段后总结说:「既然每个 rank 都恰好收到 S×K 个 token,那所有计算单元的工作量就都一样了,调度问题彻底解决。」请构造一个具体的例子推翻这个结论:给定 rank 上的 token 总数固定,说明各 SM worker 的完工时间仍然可以差得很远。
情形 A(均匀):250/250/250/250。每个 worker 的工作量正比于 250,四人几乎同时收工,完工时间 ∝ 250。
情形 B(倾斜):970/10/10/10。总数仍是 1000,完全满足 MoonEP 的完美均衡条件。但负责第一个专家的 worker 要做 970 份工作,另外三个各做 10 份就闲下来了。整个 rank 的完工时间 ∝ 970,是情形 A 的近四倍,而且有 3/4 的 worker 在绝大部分时间里空转。
结论:完美均衡是rank 之间(inter-rank)的均衡,它完全没有触及 rank 内部(intra-rank)各专家之间的倾斜。这正是论文那句「even with the aggregate load perfectly balanced across ranks, the per-expert token counts within each rank remain skewed」的意思。这也是为什么「专家 GEMM 调度与重叠」必须作为一个独立的技术点存在——它解决的是完美均衡解决不了的那一层问题。工作量感知的调度器不会天真地「一个 worker 管一个专家」,而是根据当前 token 分布调参,把大专家的活拆给多个 worker 分担。
更一般的教训:「总量相等」从来不等于「完工时间相等」。任何按总量做的均衡,都必须再问一句「总量内部的分布呢」。
变式:如果换成「每个专家的 token 数都相同、但各 rank 的总数不同」,会怎样?这两种不均衡(rank 间 vs rank 内)分别对应论文的哪个解决方案?
答辩:如果我是审稿人
你证明了「每个 rank 至多 E/R 个冗余专家就保证均衡方案存在」,并把它当作 MoonEP 相对 ECHO、UltraEP 的核心优势。但我要问:E/R 个冗余专家不是免费的——每个冗余专家都要占显存放参数,前向要预取,反向要把梯度归约回 home rank。当 EP 大小 R 比较小时,E/R 就相当大,等于每张卡要额外背上一大堆专家。你的正文里对这些代价一个数字都没有。你这个「保证」,是不是把成本藏到了别处?
参考防守(先自己组织语言再看)
第一层防守:这是预留槽位,不是实际占满。论文的措辞是「预留 E/R 个冗余专家槽位」(reserving E/R redundant-expert slots per rank)。E/R 是最坏情况下的上界,是为了保证「永远有可行解」而必须留出的余量;实际每一步在线规划用掉多少,取决于当前微批次的路由分布,绝大多数步数远用不到上界。所以显存要按上界预留,但迁移和通信的实际开销是按实际用量走的。
第二层防守:对手的成本更隐蔽。ECHO、UltraEP 的做法看似省了这笔预留,但它们付出的是训练可能被迫停止(无可行方案时)、需要人工调参、以及残余的不均衡持续拖慢每一步。前两项是无法量化但会吃掉工程师时间的成本,第三项是每一步都在交的税。用一笔有界的、可预测的显存开销,换掉一个无界的、不可预测的失败风险,在 3T 级训练这种「一次训练跑几十天、中断一次损失惨重」的场景下是划算的。
但审稿人的核心批评站得住。论文正文确实没有给出:冗余专家的显存开销占比、在线迁移的通信开销、MoonEP 相对 DeepEP 的端到端吞吐提升、以及 E、R 在 K3 里的实际取值。所有关于「划算」的论证目前都是定性的。而且「本质上是紧的」(essentially tight)这个说法本身也很含糊——是存在需要恰好 E/R 的实例,还是需要 E/R − 1 的实例?正文没有交代,只指向附录 E。
10.8 显存高效训练:六件武器,每一件都有代价
这一节对应论文 §5.2.2,解决的是第 (ii) 个问题——激活、梯度、优化器状态超出内存预算。六个技术,我们逐个看,重点看省的是什么、代价是什么。
激活(activation):前向传播时每一层算出的中间结果。它们必须保存到反向传播用完为止,因为算梯度要用。层数越多、序列越长、batch 越大,激活就越多,往往是训练时显存的最大头。
1. 统一激活管理器
这是六件里最有设计品味的一件。论文为激活设计了一个统一的存储抽象:每个「为反向保存的张量」都关联一个可插拔的存储后端。于是——重算(recomputation,用完就扔,反向时再算一遍)、量化(quantization,用更少的位数存)、卸载/远程卸载(offload/remote-offload,搬到 CPU 内存或别的卡上)——这三件原本互不相干的技术,统统变成同一个抽象下的「存储策略」,可以在张量粒度上自由组合。
而且策略是通过张量上的轻量注解声明的,与模型代码完全解耦——改省显存策略不用动模型代码一行。重算在函数粒度上进行,支持跨层重算。
实现上还有两个细节:所有 GPU 内存都在主计算流上分配、在单个内存池里管理,避免多流碎片和主机受限的开销;激活在层粒度上预取回来并与计算重叠,额外开销可忽略。
论文交代了 K3 的实际配置:大多数激活用块级 FP8 量化,加上卸载/远程卸载;逐元素算子配置为重算。这个搭配是有道理的——逐元素算子(比如激活函数)重算起来极便宜(一次遍历就完事),存起来却要占一整份显存,所以扔掉重算最划算。
打个比方
三种策略像整理房间的三种办法:重算是「这东西以后要用再做一个,现在扔了」;量化是「压缩打包,占地小但会掉一点细节」;卸载是「搬到楼下储藏室,要用的时候提前叫人搬上来」。统一激活管理器的贡献不是发明了这三种办法,而是做了一个统一的柜子,让你可以给每样东西单独贴标签说「这件走 A 方案、那件走 B 方案」,而不用为每种方案单独改造房间。
类比失效处:真实的储藏室搬东西不影响你干别的事,而 GPU 往 CPU 搬数据要占用 PCIe 带宽,可能拖慢别的传输。论文说「激活在层粒度上预取回来并与计算重叠,额外开销可忽略」——「可忽略」是作者的主张,没给数字。
读的时候要小心
「大多数激活用块级 FP8 量化」是一个有精度风险的选择:FP8 只有 8 个比特,把反向传播要用的中间结果压到 8 比特,理论上会给梯度引入误差。论文在这一节完全没有讨论这对训练精度的影响,也没说哪些张量被排除在量化之外。「块级」(block-wise)意味着按小块分别定标度以减小误差,但具体块大小、以及哪些层豁免,论文都没写。
2. 显存高效 MoE
这一条是纯粹的数学技巧,值得欣赏。原生 MoE 实现里,置换后概率的梯度计算依赖前向输出 output——这意味着 output 必须一直留在显存里等着反向用。
论文受 SonicMoE 启发,通过数学变换把这个梯度改写成只依赖中间激活 act_output 和上游梯度 doutput 的形式,代价是多一次轻量的逐元素计算。这样一来,反向对 output 的依赖就被消除了,output 用完即可释放。
另外在 group GEMM 的前向里,只保存 dispatch 操作的输入;反向时通过重算 dispatch 恢复 group GEMM 的输入。这次重算引入的通信与 group GEMM 反向计算的一部分重叠,于是这部分激活存储「以可忽略的代价被消除」。
3. 显存高效的注意力残差
这是针对 Block AttnRes(前面章节讲过的架构组件)的配套优化。做法有三点:块表示在边界层生成一次,被后续所有层共享,直接驻留在 GPU 上;AttnRes 计算整个被 checkpointing 包住,所以每层为反向保存的激活与标准残差架构完全相同;对流水线并行采用基于缓存的流水线通信,只在阶段间增量传输新生成的块,微批次一结束就释放,达到内存占用的理论下界。
第二点是这条的核心卖点:Block AttnRes 是个新架构组件,人们最担心的就是「新组件会不会让显存开销涨一大截」。论文的回答是——包上 checkpointing 之后,每层保存的激活跟标准残差架构一模一样,也就是零额外激活开销。代价当然是 checkpointing 本身的重算时间。
4. 跨 PP rank 均衡激活
这一条需要先理解一个流水线并行的固有现象。在交错 1F1B(interleaved 1F1B,一种流水线调度策略,「一次前向一次反向」交错进行)下,由于流水线预热(pipeline warmup),激活在各 PP rank 间的分布是不均匀的:驻留激活数随 PP rank 增大而减少。
为什么?因为流水线要先「灌满」。排在最前面的 PP rank 必须先连续做好几个微批次的前向,才能等到第一个反向传回来;在那之前,它已经攒了一堆激活在手上。而排在最后的 PP rank 做完前向马上就能做反向,激活立刻释放。结果就是靠前的 rank 显存吃紧、靠后的 rank 显存宽裕。
论文的解法很直接:为避免 OOM,用 Mooncake Transfer Engine 把激活远程卸载到其他 PP rank 的内存,实现跨 PP rank 的均衡激活内存。也就是说——既然后面的 rank 有富余显存,就把前面 rank 的激活搬过去存着。
5. 流水线 ZeRO-2 梯度分片与卸载
三层动作,一层比一层激进:先把梯度分片到各数据并行 rank(这是 ZeRO-2);进一步把分片后的梯度存到 CPU 内存以降低 GPU 内存占用;同时在 GPU 上保留双梯度缓冲区。流程是:梯度在 DP rank 间归约进双梯度缓冲区,再累加到 CPU 分片里。
「双缓冲区」是个经典技巧:一块正在被填,另一块正在往 CPU 搬,两块轮换,于是搬运和计算可以同时进行。代价是 GPU 上要多留一块缓冲区的空间,以及 GPU↔CPU 之间持续的数据流量。
6. 基于 P2P 的 Muon 正交化
这一条的问题很有意思:分布式优化器把参数均匀分片到各 DP rank(每张卡只拿到参数矩阵的一条),而 Muon 里的 Newton–Schulz 正交化需要完整的参数矩阵。这两件事直接冲突——正交化是一个作用在整个矩阵上的运算,你手里只有几条是做不了的。所以每次更新前都必须有一步通信来聚齐完整参数。
天真做法:在每个 rank 上对整个参数缓冲区做 all-gather。后果是双重的——既造成可观的显存占用(每张卡都要腾出装下全部参数的空间),又让通信成为主要瓶颈。
论文的做法:每个 rank 只通过与对应 owner rank 的 P2P 通信,取回它本地所拥有的那些参数的分片。这样消除了全参数缓冲区,同时降低显存使用和通信量。通信与计算进一步在 model-chunk 缓冲区的粒度上流水化,隐藏通信开销。
P2P 版本到底改了什么
关键区别在「每个 rank 最终需要拿到什么」。天真 all-gather 的隐含假设是「每个 rank 都要拿到全部参数」,所以每张卡都得有一个能装下全部参数的缓冲区。但仔细想想:每个 rank 只负责更新自己拥有的那部分参数,它需要的是那些参数所属的完整矩阵,而不是所有矩阵。于是把「广播给所有人」改成「点对点地、只向该找的 owner 要该要的那块」,全参数缓冲区就不需要了。这是一个典型的「重新审视需求」式优化——不是把通信做快,而是发现根本不需要传那么多。
§5.2.2 的六件武器,省显存的代价其实只有三类:(A) 多花计算时间、(B) 多花通信/带宽、(C) 损失数值精度。请把下面四项各归到一类或多类,并说明理由:(1) 逐元素算子配置为重算;(2) 块级 FP8 量化;(3) 把分片梯度存到 CPU 内存;(4) 显存高效 MoE 的梯度数学改写。
(1) 逐元素算子重算 → A(计算时间)。前向算完就扔,反向要用时重新算一遍。之所以专挑逐元素算子来重算,正是因为它们重算极便宜(一次遍历),而存起来要占一整份显存,性价比最高。
(2) 块级 FP8 量化 → C,轻微 A。主要代价是精度:8 比特存不下原来的动态范围,必然有量化误差。「块级」是为了减小这个误差(按小块分别定标度)。量化和反量化本身也要一点计算,但很轻。论文没有讨论这对训练结果的影响,这是留白。
(3) 分片梯度存 CPU → B(带宽)。GPU 和 CPU 之间搬数据要走 PCIe,比显存带宽慢得多。论文用双梯度缓冲区来对冲,让搬运和归约可以重叠——这正是「用重叠把 B 类代价藏起来」的标准手法。另外 CPU 上的累加也要花 CPU 时间。
(4) MoE 梯度数学改写 → A,但极轻微。论文明说代价是「多一次轻量的逐元素计算」,没有精度损失(是等价的数学变换,不是近似),也没有额外通信。这一条最接近真正的免费午餐,因为它不是把数据挪个地方,而是从根上让反向不再需要 output 这个张量。
值得记住的一般规律:A 和 B 类代价通常可以靠重叠藏掉大半(论文里 (1)(3) 都配了重叠手段),C 类代价藏不掉——精度掉了就是掉了。所以论文对 (2) 的沉默才格外值得注意。
变式:把「跨 PP rank 均衡激活」(远程卸载到别的 PP rank 内存)也归类。它属于哪一类?跟卸载到 CPU 相比,卸载到别的 GPU 有什么优势和劣势?
10.9 多模态编码器:把 ViT 塞进流水线的缝里
最后一个问题:视觉编码器高度可变的计算暴露在关键路径上。
为什么「高度可变」?因为图片和视频的大小差别极大。一张小图可能几百个 patch,一张高分辨率大图或一段长视频可能几万个。论文说:在长上下文多模态训练中,大图和长视频大幅增加视觉编码器的计算时间,造成跨设备的严重负载不均衡。有的卡分到一堆小图很快就算完,有的卡分到一张巨图算半天,前者只能干等。
编码器里的动态 CP
论文的第一个动作是把上下文并行扩展到这类大样本上,做法有两层:
- 单张大图沿 patch 维切分到多个设备,注意力通过跨 CP rank 收集键值对(gather-KV)来计算。注意这里用的是 gather-KV,因为视觉编码器用的是普通的 softmax 注意力,不是 KDA——所以它得老老实实交换键值对,享受不到 10.5 那个固定大小状态的红利。
- 把每个 CP 组划分成若干「子 CP 组」,把多张大图以负载均衡的方式分配给它们,防止通信占比随规模增长。这一层是关键:如果只做第一层,一张大图切到越来越多的设备上,gather-KV 的通信会越来越占比重;分成子组之后,每张图只在一个较小的子组内切分,通信被限制在子组内部。
论文说这个做法既降低了大视觉样本的编码器延迟,也降低了跨设备负载不均衡,从而让剩余的编码器计算能藏进流水线气泡里——这句话是承上启下的,直接引出下一个技术。
把编码器计算塞进 PP 气泡
流水线气泡(pipeline bubble):流水线并行天生有「预热」和「排空」两个阶段——开头的时候后面的 rank 还没收到活,结尾的时候前面的 rank 已经没活了。这些「某些卡无事可做」的时间段就是气泡。气泡是纯粹的浪费,但只要用流水线就无法完全消除。
K2.5 引入了解耦编码器进程(Decoupled Encoder Process, DEP),把 ViT 与文本训练拆成不同阶段,并在各 PP 阶段间均衡视觉的前向和反向。K3 在这个基础上更进一步,靠的是一个很精细的观察:
论文观察到的时序规律
在交错 1F1B 调度下:最前面几个 PP 微批次的文本前向都被排在最开头(scheduled at the very beginning),而最后几个 PP 微批次的文本反向要到最末尾才结束(finish only at the very end)。
这两句话合起来的意思是:流水线的头和尾是被文本计算占死的硬边界,中间才有气泡。所以 ViT 的计算不能一股脑扔进气泡——最前面那几个微批次的 ViT 前向必须在文本前向开始之前就完成,否则文本前向就得等它。
于是论文把 ViT 计算进一步分解:最前面几个 PP 微批次的 ViT 前向同步地提前执行(executed synchronously upfront),其余的前向排进流水线气泡,反向同理处理。结果是:大部分 ViT 计算被藏在流水线气泡里,基本消除了视觉编码器的有效开销。
读的时候要小心
「基本消除了视觉编码器的有效开销」(largely eliminating the effective overhead of the vision encoder)是论文作者自己的主张,正文没有给出任何数字支撑——没有说消除了百分之多少,也没有给优化前后的吞吐对比。同样,5.2.3 里「子 CP 组」具体怎么划分、「负载均衡地分配大图」用的是什么算法,论文都没有说明。
综合题。把 10.8 的第 4 件武器(跨 PP rank 均衡激活)和 10.9 的第 2 个技术(ViT 塞进气泡)放在一起看,你会发现它们其实源自同一个流水线并行的现象。请指出那是什么现象,说明它为什么同时制造了一个麻烦和一个机会,并解释这两个解法为什么不冲突(甚至可以同时用)。
共同的根源:交错 1F1B 的流水线预热与排空。流水线必须先灌满才能进入稳态,最后还要排空。
它造成的麻烦(显存维度):预热期间靠前的 PP rank 要连续做好几个微批次的前向才等到第一个反向传回来,这期间的激活全部要留着,不能释放。越靠前的 rank 攒得越多,于是「驻留激活数随 PP rank 增大而减少」,靠前的 rank 有 OOM 风险。解法是用 Mooncake Transfer Engine 把激活远程卸载到靠后 rank 的富余内存里——本质上是把显存从宽裕的地方挪到紧张的地方。
它造成的机会(时间维度):预热和排空期间总有卡在空转,这就是气泡。既然反正是空的,就拿别的活来填。ViT 计算恰好是理想的填充物,因为它不依赖文本流水线的中间结果,时序上很自由。解法是把大部分 ViT 计算排进气泡——本质上是把计算从繁忙的时刻挪到空闲的时刻。
为什么不冲突:两者动的是正交的两个资源维度——一个搬的是空间(显存,跨 rank),一个搬的是时间(计算,跨时刻)。而且它们的方向还是互补的:气泡多发生在预热/排空阶段,正好是靠前 rank 显存最紧张的时候,把 ViT 计算放在那里并不会加重显存压力(ViT 有自己的激活,但可以独立管理),反而利用了那段时间的空闲算力。
更深一层的观察:K3 基础设施的整体套路可以概括成一句话——凡是有「某处紧张、某处富余」的不均衡,就想办法把富余的搬到紧张的地方去。MoonEP 搬的是 token(EP rank 之间),KCP 搬的是状态碎片(CP rank 之间),跨 PP rank 均衡激活搬的是显存,ViT 填气泡搬的是计算时刻。四个技术,一个思想。
变式:如果把 PP 的阶段数(虚拟阶段数)调大,气泡会变多还是变少?靠前 rank 的激活压力会变大还是变小?这两个变化对上面两个解法各有什么影响?(提示:论文没有讨论这个权衡,你需要自己从「预热要灌几个微批次」推理。)
本章小结
这一章讲的全是「怎么让它跑起来」,但如果你只记住一串技术名词,就白读了。真正值得带走的是三条思路:
第一,KDA 的固定大小状态是一笔交易,不是一次纯粹的改进。坏的一面是串行难并行,好的一面是便宜好传好复用。§5.1 的全部工作可以概括为:用 FlashKDA 和 SM 级 CP 对付坏的一面(在设备内),用 KCP 把好的一面兑现成真金白银(在设备间)。
第二,KCP 的数学核心只有一句话:把一段序列的效果拆成「一个累积转移矩阵」加「一个从零生成的状态」,这两样都只用本地 token 就能算。因为矩阵 Mt 的存在,老办法「各自从零算再求和」彻底失效(你在 q10-5 里亲手推翻了它);而式 17 给出的分解让各 rank 可以完全并行地算出自己的碎片,再用一次固定大小的 all-gather 交换、用前缀扫描拼回来。「固定大小」这四个字是全章最重要的四个字——它意味着序列拉长十倍,这笔通信一点都不变大。
第三,§5.2 的所有技术都在做同一件事:把富余的资源搬到紧张的地方去。MoonEP 搬 token(并用 E/R 那条定理把「保证可行」换成了 R 倍的缓冲区节省),统一激活管理器搬激活(在重算、量化、卸载三种策略间自由组合),跨 PP rank 均衡激活搬显存,ViT 填气泡搬计算时刻。而每一次「搬」都有代价,本章逼你把每一项的代价都说清楚了:多花计算、多花带宽、或者损失精度——没有第四种。
还有一条贯穿全章的方法论值得单独点出:数学保证可以直接兑换成工程收益。MoonEP 因为能证明 E/R 这个上界,才敢把通信缓冲区从 S×K×R 砍到 S×K;因为能保证「每个 rank 恰好 S×K 个 token」,计算形状才变成静态已知,才能消掉逐层的主机同步。反过来说,如果只是「经验上比较均衡」,这两项收益一项都拿不到——因为缓冲区必须按最坏情况开,形状必须运行时才知道。
下一章接着讲基础设施的后半段:百万 token 智能体强化学习的基建,以及推理侧的 KDA 解码、前缀缓存与调度。KDA 状态的「便宜、好存、好复用」这条性质,在那里会被榨得更彻底。
本章需要打问号的地方(汇总)
论文在这几页里留了不少白:FlashKDA 只说「大幅超过 Triton 参考实现」但没给倍数;KCP 没有给通信量或扩展性的实测数据,也没讨论 P 很大时前缀扫描重建的开销;MoonEP 没给冗余专家的显存代价、也没给相对 DeepEP 的端到端提升,「本质上是紧的」具体指什么要看附录 E;块级 FP8 量化对训练精度的影响完全没有讨论;Figure 11 是定性示意,没有利用率数字;5.2.3 的子 CP 组划分算法和「基本消除视觉编码器开销」的量化证据也都缺失。读这一章时,请把「这是作者的主张」和「这是被数据支持的结论」分开放。
Chapter 10 · Infrastructure I: making KDA run, and making 2.8 trillion parameters fit
In one sentence: the previous chapters were all about “what the model should look like”. This chapter answers a completely different question — a model this big, sequences this long, attention this strange: how do you actually get it running on a few thousand GPUs, and running without waste.
After this chapter you should be able to
- Say in your own words what SM, kernel, memory, bandwidth and communication overhead each mean, and what data parallelism / tensor parallelism / pipeline parallelism / expert parallelism / context parallelism each “cut up”
- Explain why KDA's fixed-size recurrent state is both enormously good news (easy to transfer, easy to reuse) and enormously bad news (hard to parallelise)
- Construct a counterexample with your own hands showing why the old approach of “every card computes from zero and you sum at the end” does not hold for KDA
- Read Equation 17 and say clearly why KCP needs only one fixed-size all-gather, and why that point is the decisive win
- Work out how many times smaller MoonEP's communication buffer is than the conventional scheme, and say clearly what the phrase “perfectly balanced” actually balances and what it does not
- Point out the price of each of the six memory-saving techniques in §5.2.2, and show that “saving memory” is never free
10.1 Before you start: filling in the basics about GPUs
This is the chapter of the book furthest from “mathematics” and closest to “engineering”. If you know nothing about what is inside a GPU, every paragraph that follows will read like scripture in a foreign alphabet. So a little space goes first on getting six words straight. Once those six words are done, you are qualified to read the rest.
Streaming multiprocessor: inside a GPU there is not “one very fast brain” but a collection of over a hundred small compute units, and each of those small units is called an SM. They each get on with their own work and do not wait for one another. So a GPU is fast not because it “does one thing fast” but because it “does over a hundred things at once”.
What happens if you skip this piece of background
One sentence turns up again and again in the paper: “most SMs are left idle” (leaving most SMs idle). If you do not know what an SM is, that reads as nothing more than a complaint. Once you do, you see what it says: “you paid for 132 workers, only 8 of them are working, and the other 124 are standing around watching”. Every design in the whole of §5.1 has one goal — waking those 124 workers up.
An analogy
A GPU is like a giant factory with over a hundred production lines, but it has a quirk: every line must do the same kind of work, and it is best if every line has the same number of pieces on it. Give it ten thousand identical parts and it finishes in a second; give it one part and ask it to work that part ten thousand times over, each time waiting for the previous result, and it is barely faster than an ordinary computer. That line in the paper about the GPU preferring “wide, uniform parallelism” (wide, uniform parallelism) is about exactly this quirk.
Where the analogy breaks down: in a real factory a worker can switch to a different job on the spot, and a GPU's SMs cannot do that inside the same kernel — when a kernel launches, what it is to do and how many pieces it is cut into are already fixed. This is exactly why the paper writes separate kernels for “training/prefill” and for “decoding” instead of one universal kernel that adapts by itself.
Kernel: a small program written specifically to run on the GPU, and the smallest unit of work a GPU does. The CPU says “launch this kernel” and thousands upon thousands of threads on the GPU start running the same piece of code at the same time. A fused kernel means several kernels that would otherwise have been launched separately merged into one, saving the cost of shuttling intermediate results back and forth. Do not confuse it with an “operating system kernel” — the same English word, a completely different thing.
Memory and bandwidth: memory is the capacity the card has for holding things, measured in GB, and it decides “does it fit”; bandwidth is the speed at which data moves between memory and the compute units, measured in GB/s, and it decides “does it move fast enough”. Beginners run these two together all the time, but they cause two completely different failures: not enough capacity and you crash outright with an error; not enough bandwidth and you are merely slower. All six techniques in §5.2.2 are solving the capacity problem; FlashKDA in §5.1 is mainly solving “idle SMs” and the cost of moving data.
Communication overhead: one card cannot hold it, so you use a few thousand cards; a few thousand cards then have to pass data to each other. Passing data between cards is one to two orders of magnitude slower than reading memory inside a card, and while the data is being passed the compute units are often just waiting. So the central tension of large-scale training has never been “is the arithmetic fast”, it is “is there little to send, and can you send while you compute”. The word overlap turns up a dozen or so times in this chapter, and it means exactly that: let communication and computation happen at the same time, and use the time spent computing to cover the time spent communicating.
What each of the five kinds of “parallelism” cuts
Once a model is too big for one card, it has to be cut up and handed out to several cards. There is more than one way to cut, and K3's pre-training uses five of them at once. What they cut is completely different in each case:
The paper says: tensor parallelism cuts attention heads across devices, but it “never shortens the recurrence” (never shortens the recurrence), so when you prefill one very long sequence under a pure TP deployment, each rank holds only a few heads and most SMs are idle. Explain that sentence using the idea of an SM: why does “holding only a few heads” leave SMs idle?
Variant: if you used ordinary softmax attention for prefill instead, would pure TP have the same problem of “large numbers of SMs sitting idle”? Why? (Hint: is softmax attention serial along the token dimension?)
10.2 The opening of §5: three challenges colliding at once
The paper's opening sentence pins down the position K3's infrastructure is in: K3 has assembled three systems challenges that rarely turn up together in one model —
- Hybrid KDA attention: a serial form of attention with a fixed-size recurrent state, exactly the opposite of the GPU's temperament;
- 3T-class sparse multimodal training and inference: the paper uses “3T-class” to refer to the order of magnitude of the model's size, and it is natively multimodal — images, video and text trained mixed together;
- Million-token agent workloads: the context of a single task can run to a million tokens.
Getting a sense of scale for “3T-class”
What does 3 trillion parameters mean? Even at 1 byte per parameter (FP8), the weights alone are 3 TB; at 2 bytes per parameter (BF16) that is 6 TB. Memory on a mainstream training card is usually in the tens of GB. That is, just putting the model's parameters down takes over a hundred cards — and that is before the gradients, optimiser states and activations that training has to keep as well, three things that together are often several times larger than the parameters themselves. This is why §5.2.2 spends a whole section and six techniques scraping memory together. (This conversion is here to give you a sense of scale; it is not a number the paper gives.)
The paper's phrasing is: the infrastructure is co-designed with these challenges across the model lifecycle (co-designed across the model lifecycle). That is not boilerplate. It corresponds to four concrete stages, and those four stages are exactly the skeleton of this chapter and the next:
| Stage | The gist of the paper's own words | Where it is covered |
|---|---|---|
| Architecture level | High-performance KDA kernels + context parallelism, making the recurrent form efficient both within a device and across devices, in training and in inference | 10.4 and 10.5 of this chapter |
| During pre-training | Balanced expert execution, reduced memory footprint, communication-overlapped scheduling, holding utilisation high at scale | 10.6–10.9 of this chapter |
| During 1M-token agent RL | Hierarchical state management + resumable sandbox execution, keeping long trajectories across iterations | Chapter 11 |
| Production serving | State-aware KDA prefix caching, dedicated inference kernels, cache- and budget-aware scheduling, turning efficiency into predictable service | Chapter 11 |
Note the word “predictable” (predictable) in that last cell. What industrial deployment fears most is not being slow, it is being fast one moment and slow the next — if the response time of a user's request has a huge variance, the product experience falls apart. The paper puts the end of the whole infrastructure chain at “predictable production serving”, which says their objective function is not throughput alone.
The paper says KDA's fixed-size state is “cheap to transfer and reuse” (cheap to transfer and reuse). Point out which stages in the table above exploit this one property, and show that the concrete benefit it brings is different at each stage.
Variant: ask it the other way round — what trouble does the “serial” property of the KDA state cause at each of these four stages? (Hint: the paper says outright that the challenges of the decoding stage are different from those of training/prefill, and puts them in §5.4.2 on their own, which is Chapter 11 of this book.)
10.3 The core tension of §5.1: one trade, two consequences
The whole of §5.1 rests on one sentence, and it is worth reading word by word: KDA replaces softmax attention's ever-growing key-value cache with a fixed-size recurrent state; the price is that its serial form makes parallel execution difficult, and what it buys is a fixed-size state that is cheap to transfer and reuse.
This is a trade, not a pure improvement. The two sides of the trade are:
- The bad side (the first property): the state is updated serially, and step t has to wait for step t−1. GPUs hate this.
- The good side (the second property): the state's size is fixed. Whether it has read 10,000 tokens or 1,000,000 tokens before, it is the same-sized block. Cheap, easy to transfer, easy to store, easy to reuse.
Everything the paper designs from here on, in one sentence: solve the problem the first property creates, and at two levels of execution squeeze the second property's benefit completely dry. The two levels are — the fused kernel within a device (10.4), and KDA context parallelism between devices (10.5). Remember this “two levels” frame; it is the table of contents for §5.1.
First, copy Equation 1 back down here, because all of 10.5 rests on it:
| Symbol | What it is | Intuition |
|---|---|---|
| St | the recurrent state matrix after token t has been read, shape dk×dv | the model's “notebook” so far; fixed in size, and once it is full something old has to be squeezed out |
| Mt | the transition matrix determined by token t, shape dk×dk | “before writing anything new, rewrite the whole notebook according to this rule”. Every difficulty in this chapter comes from it |
| kt, vt | the key vector and value vector of token t | “which position in the notebook to write at” and “what to write there” |
| βt | the scalar write strength of token t | how hard this particular stroke presses |
| Diag(αt) | the diagonal matrix made by laying the vector αt along the diagonal | a per-channel forget gate: every row of the notebook fades at its own rate |
| I | the identity matrix | the matrix that “changes nothing” |
A misreading that has to be corrected right now
Plenty of people see the I inside Mt and assume it is “basically unchanged, just touched up a bit”. It is not. Mt is a genuine matrix, and multiplying it onto St−1 mixes the entire state through afresh. Worse, matrix multiplication is not commutative, so M2M1 ≠ M1M2 — the order of the tokens is locked hard into that product. The counterexample question in 10.5 has its root right here.
Do the question below before you read on — all of 10.5 stands on it.
Take one concrete extreme case: let every component of αt equal 1 (no forgetting at all), let βt = 1, and let kt be a vector of length 1 (that is, kt⊤kt = 1). Write down Mt in this case and say clearly what it actually does to the incoming state St−1. How far is that from “basically unchanged, just touched up a bit”?
So Mt is a projection: it wipes clean to zero the component of the state along the direction kt, and leaves every other direction completely alone. That is as far from “touched up a bit” as anything gets — it wipes the whole existing content of the “kt column” of the notebook out, and then the second term of Equation 1, βtktvt⊤, writes new content into that empty column.
This is exactly where the name “delta rule” comes from: delete the old first, then write the new, rather than simply piling on. And precisely because it deletes things, “each segment computes from zero and you sum them” is bound to fail — summing only piles things on, and can never express the act of “deleting”.
A note in passing on why the general case is more complicated: when βt is not 1 the erasure is partial, and when Diag(αt) is not I there is a further layer of per-channel overall fading on top, but the essential fact that it “deletes things” does not change.
Variant: if βt = 0, what does Mt become? Which simpler form of attention does KDA's update degenerate into then? (Hint: substitute βt = 0 into both terms of Equation 1 and see what is left.)
10.4 Kernels by regime: a separate set written for each way of running
The first sentence of 5.1.1 of the paper is the thesis of the whole section: the serial dependency of the KDA state conflicts with the GPU's preference for “wide, uniform parallelism”, and this conflict shows up as a different bottleneck in each execution regime, so each regime needs a kernel of its own (a dedicated kernel for each regime).
What is an “execution regime”? It is the several different modes the model runs in: training (a whole stretch of text fed in at once, with a forward pass and a backward pass to compute), prefill (prefill, where at inference time the long prompt the user gave is read in as one stretch), and decoding (decoding, spitting tokens out one at a time after that). How much work can be done at the same time is completely different in these three modes, so one set of kernels cannot possibly serve all of them well.
Training and prefill: the chunkwise kernel FlashKDA
KDA's chunkwise form works like this: cut the long sequence into chunks, then parallel inside a chunk, serial from chunk to chunk — because the recurrent state has to be handed down one chunk at a time.
The problem the paper points out is this: executed naively, the two phases “parallel computation inside a chunk” and “serial propagation between chunks” alternate, and for the whole stretch of serial propagation the SMs sit idle. This is the textbook case of “over a hundred production lines, and only one of them moving at any time”.
FlashKDA is the answer the paper gives: a chunkwise kernel built on CUTLASS (NVIDIA's template library for high-performance matrix operations) that overlaps intra-chunk computation with cross-chunk state propagation. Concretely, it decomposes the work into a token-parallel phase and a head-parallel recurrence, each scheduled and tuned independently of the other. The paper says it substantially outperforms the Triton reference implementation.
Two more engineering details worth noting: FlashKDA serves training and inference prefill at the same time (one set of kernels covers both regimes, because the parallel structure of the two regimes is in fact the same), and it is dispatched automatically as a backend of the flash-linear-attention library — that is, you do not have to pick it by hand when you call the library; the library works out for itself which backend to use.
Why they have to be “scheduled and tuned independently”
Because the performance bottlenecks of the two paths are different: the token-parallel part is the textbook “wide and uniform” large matrix multiplication, and the tuning goal there is to feed every SM; the head-parallel recurrence has an inherently small amount of parallelism (only as much as the number of heads), and the tuning goal there is to cut the latency of each step as far as possible. Stuff them into the same scheduling framework and you can only configure them with one set of parameters, and neither side gets configured well. Split apart, each is tuned to its own bottleneck, and then they are stacked in time — that is the whole meaning of the word “overlap”.
Long-context prefill: context parallelism within a device
This is the positive version of the answer to that question in 10.1. To recap the problem: prefilling a very long sequence under a pure TP deployment, each rank has only a few heads and most SMs are idle.
The key observation the paper gives is a sentence you have to chew on slowly: the state transition of each segment can be evaluated independently of the incoming state and composed exactly afterward (the state transition of each segment can be evaluated independently of the incoming state and composed exactly afterward).
In plain words: to work out what the state looks like after segment 5 has been read, you do indeed have to know what the state was at the end of segment 4. But the thing itself — “what sort of rewriting this batch of tokens in segment 5 will do to the state” — has nothing to do with “what state comes in”. That batch of M matrices is computed from segment 5's own tokens and has nothing whatever to do with what was read earlier. So you can perfectly well work out the “rewriting rule” without knowing the incoming state, and apply it once the incoming state arrives. And this “applying” is exact, not an approximation.
On the strength of that observation the paper uses an automatic SM-level context-parallel planner: it cuts the sequence across the SMs of a single rank, evaluates each segment's transition in parallel, and then merges them to recover each segment's exact state. The paper makes a point of this: in contrast to the cross-device KCP of 5.1.2, this parallelism is entirely within the device and produces no cross-device communication at all.
As for the decoding regime, the paper says it faces challenges completely different from training/prefill and discusses them in detail in §5.4.2 — which is Chapter 11 of this book. The reason is not hard to guess: decoding handles one token at a time, so there is not even “parallelism inside a chunk” left, and the bottleneck turns from “idle SMs” into something else.
The SM-level context parallelism of 5.1.1 and the KCP of 5.1.2 use the same mathematical observation, yet the paper splits them into two sections. Say clearly: what is the essential difference between the two in “the price paid”? If SM-level CP with zero communication is this good, why is KCP needed at all?
Variant: if some day new hardware appears whose single-card memory is big enough to hold all the activations of a million tokens, does KCP become useless? (Hint: think about how many sequences there are in a batch during training, and about the demand for total throughput.)
10.5 KDA context parallelism (KCP): the hardest section in this chapter
Brace yourself — this section has to be taken slowly. But once you are through it you will find that it really contains only one idea, and you already met that idea at the end of 10.4.
Step one: why context parallelism “ought to” be cheap for linear attention
The paper opens with a straight comparison: the communication cost of context parallelism is fundamentally different between softmax attention and linear attention.
- softmax attention: it requires the ranks to exchange key-value blocks, and the size of those blocks grows with sequence length. The longer the sequence, the more there is to send.
- linear attention: it packs the preceding context into a fixed-size recurrent state S ∈ ℝdk×dv. However long the sequence, it is the same-sized block.
Step two: the old trick earlier methods use, and why it fails for KDA
The paper says that earlier context-parallel methods exploit the “additive recurrence” of ordinary linear attention: on each rank you compute the state the local tokens generate starting from S = 0, then sum those states over all the preceding ranks, and that gives you the incoming state.
| Symbol | What it is | Intuition |
|---|---|---|
| S̃[j]Tj | the state on rank j after starting from S = 0 and reading all Tj of its local tokens | “pretend nothing before this was ever read; what can I remember from my own stretch alone” |
| the tilde ~ | the paper's convention: a tilde marks “the corresponding quantity counted from S = 0” | when you see a tilde, think “this one is purely local, it can be computed right away” |
| Tj | the number of local tokens on rank j | how long this stretch is |
Why can ordinary linear attention get away with this? Because its update is pure addition: new state = old state + whatever this step writes. Addition is associative and commutative, so “compute each stretch from zero and add them up at the end” gives exactly the same result as “run through the whole thing in order from the start”.
But the paper immediately adds: this direct summation does not hold for KDA (is insufficient for KDA). The reason is written right there in Eq. 1 —
“Why summing does not work”, put as plainly as it can be put
KDA's update is St = MtSt−1 + βtktvt⊤. Look at that Mt: before adding the current write, KDA's delta rule first applies a token-dependent matrix to the incoming state.
In the notepad picture: ordinary linear attention is “append a line at the end of the notepad” — what you wrote first and what I wrote later does not matter, you stitch it together at the end and you are done. KDA is “before writing each line, go over everything already in the notepad and rewrite it according to the rule of this very line”.
The consequence is fatal: what a stretch of tokens does to the state depends on what the state was when you entered that stretch. Rewriting from a blank notepad and rewriting from a notepad already full of the earlier text give results with no simple relation to each other — not off by a constant, not off by one term, but two entirely different things. So the paper says the effect of a local segment “cannot be determined from the state computed from S = 0 alone”.
Where the analogy breaks down: the notepad picture makes you think “rewriting” only rubs out a little, whereas Mt is a matrix multiplication that linearly re-mixes the whole state, far more violent than “rubbing out a few words”.
A construction question. Suppose there are P = 2 context-parallel ranks and each rank holds just 1 token (write rank 1's transition matrix and write-in as M1 and w1, and rank 2's as M2 and w2, where w stands for the term βkv⊤). Write out: (a) the true final state you get by running in sequence; (b) the state you get from “compute each from zero, then sum”; (c) what the difference between the two is, and under what condition they are equal.
(a) The true value: S2 = M2w1 + w2.
(b) The summation method: rank 1 computes from zero and gets S̃[1] = w1; rank 2 computes from zero and gets S̃[2] = w2; summing gives w1 + w2.
(c) The gap: true value − summed value = M2w1 − w1 = (M2 − I)w1.
The two are equal if and only if (M2 − I)w1 = 0. The cleanest sufficient condition is M2 = I, that is, no forgetting at all and no delta rewriting at all — which is exactly the degeneration back to ordinary linear attention (a purely additive recurrence). This counterexample lays the whole thing bare: the summation method holds for ordinary linear attention precisely because the “transition matrix” there is always the identity; KDA introduces a non-trivial Mt and the equality collapses at once. Notice too that the w1 in the gap has been acted on by M2 — rank 1's information has to pass through rank 2's local update to reach the end, and that is what the paper means by “propagating the context of the preceding ranks through the local KDA updates”.
Variant: change the number of ranks to P = 3, still 1 token per rank. What is the true final state? How many Ms hang in front of w1? From that, guess: in general, which product of matrices should hang in front of rank j's contribution? (Guess first, then read on to Eq. 17 — you will find you have already guessed right.)
Step three: KCP's solution and Eq. 17
The paper's solution is called KDA context parallelism. The core move is: decompose the effect of each segment into two quantities that can be computed purely locally —
- a cumulative transition acting on the incoming state (the matrix M);
- a state generated locally from zero (the tilde'd S̃).
Fix the notation first, or Eq. 17 is unreadable: S[i]t is the recurrent state inside rank i after t local tokens; S[i]Ti is the state that leaves rank i and enters rank i+1; a tilde'd S̃ is the corresponding quantity for “the same recurrence but counted from S = 0”. There are P context-parallel ranks in all.
| Symbol | What it is | Intuition |
|---|---|---|
| P | the total number of context-parallel ranks | how many cards this sequence has been cut across |
| i, j, l | rank indices; i+1 is “the rank we are currently looking at” | j and l are used to run over all the ranks before it |
| t, r | indices of the local tokens inside a rank | “how many characters into my own stretch have I read” |
| Ti | the total number of local tokens on rank i | how long this stretch is in total |
| Mr | the single-step transition matrix of the r-th local token, that is, the (I − βrkrkr⊤)Diag(αr) of Eq. 1 | “how this one token will rewrite the whole notepad” |
| M[i+1]t←1 | the cumulative transition: the single-step transitions of the first t local tokens multiplied together in order, of shape dk×dk | “what my first t characters, taken together, will turn the incoming notepad into”. Note the direction of the ←1 arrow: it is written from t down to 1, stressing that the order of multiplication must not be reversed |
| S̃[i+1]t | the state generated locally from zero: pretend nothing before was ever read and take the state you get from reading only the first t local tokens | “what new content my own stretch contributed” |
| S[i]Ti | the true incoming state that leaves rank i and enters rank i+1 | “the notepad the previous card handed over to me” — this is the thing we have to work so hard to get |
| Πl←j+1i M[l]Tl←1 | the whole-stretch cumulative transitions of rank j+1 through rank i, multiplied together again | “the bit of content rank j contributed has to survive being rewritten by every rank behind it before it reaches me” |
What each of the two terms in Eq. 17 means
The paper gives its own one-sentence explanation, and it is exactly right: the first term (S̃[i+1]t) is the state generated by the local tokens; the second term propagates the context of the preceding ranks through the local KDA updates.
Put differently, the final state = “what I wrote myself” + “what everyone before me wrote, as it survives being rewritten all the way along”. All the second line does on its way to the third is recursively expand S[i]Ti, that “notepad handed over by the previous card”, expanding until everything is made of tilde'd, purely local quantities.
Derive it yourself: from Eq. 1 to the third line of Eq. 17
Start from St = MtSt−1 + wt (with wt := βtktvt⊤) and suppose the state on entering this stretch is Sin. Expand the three steps t = 1, 2, 3 honestly. What pattern do you see?
Think it through, then look
S1 = M1Sin + w1; S2 = M2M1Sin + M2w1 + w2; S3 = M3M2M1Sin + M3M2w1 + M3w2 + w3. The pattern is clear: Sin has the product of all the Ms hanging in front of it, and the pile behind it is exactly what you would have got “starting from zero”. The reason anyone thinks of this step is that expanding a linear recurrence always has this shape — a homogeneous term plus an inhomogeneous term, the standard move for solving linear recurrences.
Sort the result of the last step into two piles: the terms “containing Sin” and those “not containing Sin”. Can the pile without Sin be written with a symbol we have already defined?
Think it through, then look
It can. The pile without Sin is exactly the result of setting Sin = 0, which is S̃[i+1]t. The pile with Sin is (Mt⋯M1)Sin; write that string of products as M[i+1]t←1. And that gives the second line of Eq. 17: S[i+1]t = S̃[i+1]t + M[i+1]t←1S[i]Ti. This one step is the entire soul of KCP: it splits “what this stretch did” into a matrix that depends only on local tokens, plus a state that depends only on local tokens.
The second line still has one non-local quantity left, S[i]Ti. It is rank i's “exit state”. Can you do the same thing to it? And what is left once you have?
Think it through, then look
You can, and this is exactly the crux: S[i]Ti = S̃[i]Ti + M[i]Ti←1S[i−1]Ti−1. The non-local quantity left over has become rank i−1's exit state — the same form with the index one lower. So expand it recursively all the way down to rank 1 (rank 1's incoming state is just 0) and the non-local quantities vanish completely. The result of that expansion is the sum in the third line of Eq. 17: rank j's contribution S̃[j]Tj, with the product of all the cumulative transitions from rank j+1 to rank i hanging in front of it. This should be exactly what you guessed in the variant of q10-5.
The last question, and the most important one: at t = Ti+1, do the two quantities M[i+1]Ti+1←1 and S̃[i+1]Ti+1 need anyone else's data before they can be computed?
Think it through, then look
Not at all. The paper says so explicitly: these two quantities “can be computed from local tokens alone, without waiting for S[i]Ti”. That means every rank can start work at the same time, each computing its own two fragments, with no waiting on anyone else at all. Once everyone is done, the fragments get exchanged and assembled. This is where the word “parallel” actually lands — if these two quantities had to wait for the preceding state, KCP would degenerate into serial execution and be pointless.
Step four: associativity, the prefix scan, and why one all-gather is enough
The paper goes on to point out: Eq. 17 shows that every state is composed purely of locally computed fragments, and that these rank-level updates are associative (compose associatively), so each rank's incoming state can be recovered with a single prefix scan.
Prefix scan: given a string of elements and an associative combining operation, it produces in one go “the combination of the first 1, the combination of the first 2, …, the combination of the first n”. Its key property is this: because the operation is associative, you can bracket it however you like and regroup in any order, so the job can be done in parallel rather than trudging honestly from left to right one element at a time.
The procedure has three steps, and the paper spells them out clearly:
- Local computation: each rank first computes its own two fragments M[i]Ti←1 and S̃[i]Ti locally. This step involves no communication at all.
- One all-gather: one all-gather sends both tensors out (all-gather means “everyone broadcasts their own share to everyone else, and at the end everyone holds the full set”).
- Local reconstruction: after the all-gather, rank i+1 walks in order through the preceding fragments of the same document, starting from S = 0 and applying the rule below at each fragment, and that reconstructs S[i]Ti.
| Symbol | What it is | Intuition |
|---|---|---|
| S (both of them, on either side of the arrow) | an accumulator that starts at 0 and is updated once for every preceding fragment processed | “the notepad I am rebuilding in my hands” |
| M[j]Tj←1 | the cumulative-transition fragment all-gathered from rank j | “how rank j will rewrite the one in my hands” |
| S̃[j]Tj | the local-state fragment all-gathered from rank j | “what rank j wrote that was new” |
| the order j is walked in | strictly the order in the document, front to back | the order must not be scrambled — matrix multiplication does not commute |
Look at the shape of that reconstruction rule: it is exactly the same as Eq. 1 (“multiply by a matrix first, then add a term”), except that “one token” has been swapped for “one whole rank's fragment”. That is what “the rank-level updates are associative” concretely means — the same algebraic structure, only at a coarser grain.
Provenance and implementation
The paper notes in a footnote that this construction builds on DeltaNet's context parallelism, and that the KDA implementation is in FLA PR #691. So KCP was not invented out of thin air: it generalises DeltaNet's machinery to KDA, which carries a Diag(α) forget gate.
A synthesis question. The paper says “KCP needs only one fixed-size all-gather”. Explain: why are the words “fixed-size” a decisive win rather than just a painless little optimisation? Put it side by side with the communication volume of softmax attention under context parallelism, and spell out what this one property means for K3's goal of “million-token agentic workloads”.
(1) Look at the trend, not the constant. When softmax attention does CP, the ranks have to exchange key-value blocks, and the size of a key-value block grows with sequence length. So stretch the sequence ten times longer and this communication grows along with it. What KCP exchanges is the two tensors M (dk×dk) and S̃ (dk×dv), whose shapes are set by the model dimensions and have nothing to do with sequence length. Stretch the sequence ten times, a hundred times, and neither tensor grows by a single byte.
(2) Look at the ratio. What really decides “can it scale” is not the absolute amount of communication but communication time as a fraction of total time. KCP's compute grows linearly with sequence length (the paper says it “achieves linear compute scaling”) while its communication is constant — so the communication share keeps falling as the sequence gets longer, and the longer the sequence, the more negligible this communication looks. On the softmax CP side, communication and compute grow together, so the share never comes down. One is a design that gets better by itself as the scale grows, the other is not, and that is the difference between a change of quantity and a change of kind.
(3) What it means for K3. K3 explicitly sets out to support million-token agentic workloads. At that length, “communication volume grows with length” means communication will eat most of the time, and long-context training simply does not add up economically. Pinning this term down to a constant is what turns million-token training from “absurdly expensive” into “feasible”. It also loops back to the line at the start of §5.1: a fixed-size state is “cheap, easy to transfer and reuse” — KCP is where the “easy to transfer” property gets cashed out into hard money.
(4) An honest addition. The paper gives no concrete speedup number for KCP against softmax CP, and says nothing about whether the matrix M (dk×dk) might at moderate lengths actually be bigger than a key-value block. The argument above is a win in the asymptotic sense; at any specific sequence length, who is faster is something the paper gives no data on.
Variant: suppose someone cuts KDA's forget gate out and makes Mt identically I. What does KCP degenerate into then? Does the all-gather have half as much to send? (Hint: if M is always I, do you still need to send M at all? What does the reconstruction rule become?)
Defend it: if I were a reviewer
You say KCP “needs only one fixed-size all-gather”, which sounds lovely. But I notice two things. First, what you have to exchange is not only the state S̃ (dk×dv) but also the cumulative transition matrix M (dk×dk) — a whole extra matrix out of nowhere, at least doubling the communication and possibly more, and your main text does not say a word about it. Second, after the all-gather every rank has to “walk the preceding fragments in order” to reconstruct its own incoming state, and rank i+1 takes i steps, which is O(P) serial matrix multiplications. When P is large, will this reconstruction not become the new bottleneck itself? On what grounds do you call KCP cheap?
A reference defence (put it in your own words first, then look)
On the first point: granted, but not fatal. An extra dk×dk matrix does have to be sent, and that is the unavoidable price of KDA's delta rule — without M there is no way to express the fact that “the incoming state gets rewritten by the local tokens”, which is exactly what that counterexample question in 10.4 proved. But the key point is: it is still a constant. The reviewer's criticism changes the constant factor; it does not change the conclusion that it is “independent of sequence length”, and the latter is what the paper claims. In the target scenario of a million tokens, doubling a constant factor and “growing linearly with length” are not even on the same level of discussion.
On the second point: a real problem, and the paper genuinely does not answer it head-on. But there are two threads of defence: (a) the paper says explicitly that these rank-level updates are associative, and names the prefix scan — the whole point of a prefix scan existing is to turn an O(P) serial scan into an O(log P) parallel reduction, so the premise “you must walk i steps serially” can itself be broken; the paper citing a prefix scan rather than simply saying “accumulate in order” is very likely hinting at exactly this. (b) each reconstruction step is only one small matrix multiplication of dk×dk by dk×dv, orders of magnitude away from the several hundred thousand local tokens' worth of compute on each rank.
But to be honest: the paper gives no scalability curve as P grows, does not quantify the reconstruction overhead, and says even less about how large the extra bookkeeping cost is when a batch mixes several documents (reconstruction has to “process the preceding fragments of the same document”). These are all places the paper leaves blank.
10.6 §5.2: assembling 3T-scale pre-training as a whole
From here the battlefield changes. What came before was about “how to make a strange operator like KDA run fast”; what comes next is about “how to get a 3T-scale multimodal model trained at all on thousands of cards”.
The paper first lists the full set of parallelism schemes used in K3's pre-training, and you will find all five from 10.1 among them: pipeline parallelism (PP) with virtual stages (VP), expert parallelism (EP), ZeRO-1 data parallelism, pipelined ZeRO-2 gradient sharding, and context parallelism (CP, which is the KCP of §5.1.2).
ZeRO: under data parallelism every card stores a full copy of the gradients and the optimizer state, which is an enormous waste. ZeRO's idea is to shard these things across the cards, and whoever needs a piece goes and fetches it. ZeRO-1 shards the optimizer state; ZeRO-2 goes further and shards the gradients too.
What is done in the MoE layers: the shared expert is replicated across EP ranks (every card has a copy, so no tokens need to be sent out), while the all-to-all communication of expert dispatch and combine is overlapped with compute to hide the latency. The all-to-all here is exactly what the EP cell of the figure in 10.1 described: “tokens fly off to the card holding the expert they picked, then fly back”.
About Figure 11
What Figure 11 of the paper shows is this: how compute, communication and offloading overlap within the different PP stages. In the figure you can see a whole series of bands — DataLoader, ViT forward, gather param, EP dispatch/combine, shared expert, offload/reload — laid out horizontally by PP stage and densely interleaved with one another. The figure has only one message to convey: at any given moment, a single card is doing several things at once — computing, transferring, and moving things out to CPU memory all at the same time. It gives no quantified utilisation numbers, so it is a qualitative schematic, not performance data.
The paper then boils the difficulty of 3T-scale native multimodal pre-training down to three key problems, and the next three subsections correspond to them one by one:
| Problem | Symptom | Solution subsection |
|---|---|---|
| (i) the token load is unbalanced across the EP ranks | some cards are packed with tokens, others sit idle; the fast wait for the slow | §5.2.1 MoonEP (10.7 of this chapter) |
| (ii) activations, gradients and optimizer state exceed the memory budget | it does not fit in GPU memory and crashes outright with OOM | §5.2.2 memory-efficient training (10.8 of this chapter) |
| (iii) the highly variable compute of the vision encoder is exposed on the critical path | one big image or one long video slows the whole batch down | §5.2.3 multimodal encoder optimisation (10.9 of this chapter) |
The paper says the shared expert in the MoE layers is “replicated across EP ranks”, while the routed experts need all-to-all communication to dispatch and combine tokens. Why can the shared expert do without the all-to-all? And what does doing it that way cost?
Variant: what would happen if the shared expert were sharded too (each card storing only a part)? Every token would have to fly out once; is that all-to-all volume larger or smaller than the routed experts'?
10.7 MoonEP: cutting the expert load into perfectly even shares
First, get the problem straight. In the usual expert-parallel schemes the token load across ranks is unbalanced — the router is learned, it sends tokens to whoever it likes, and nobody guarantees it sends them evenly. The paper points out that this has two consequences, and the second one often gets overlooked:
- Compute imbalance drags down training throughput: all the cards have to advance in lockstep, and the slowest card sets the overall speed.
- The shapes of the routed experts' activations change dynamically, which creates a great deal of memory fragmentation: this step allocates a block of memory for 3000 tokens, the next step wants 5000, the one after that 1200, and the allocator is soon cut to pieces.
MoonEP solves this with dynamic redundant experts to achieve perfect load balance. A redundant expert means taking some popular expert and putting an extra copy of it on another rank, so that the tokens pouring towards it can be split off.
The paper stresses that MoonEP keeps the overall computation flow of the usual schemes such as DeepEP, and only adds online planning and migration of redundant experts on top. Specifically:
- In the forward pass: it plans the redundant experts from the router output of the current micro-batch and the current layer, and prefetches them before the routed-expert computation. Note those words, “the current micro-batch and the current layer” — the planning is done layer by layer and micro-batch by micro-batch, at a very fine grain.
- In the backward pass: the gradients of the redundant experts are held in a local reduce buffer, and once the computation finishes they are reduced back into the gradient buffer of their “home rank” (home rank). This step is required — the gradients produced by several copies of the same expert have to be merged, or the parameters fork.
The core theorem: E/R redundant experts are enough
This is the hardest piece in 5.2.1. The paper says: MoonEP requires every rank to receive exactly S × K tokens (S is the sequence length, K is the number of experts each token selects), so that all ranks do the same amount of computation.
There is a place here that is easy to misread
The paper does not say outright whether S is “the global sequence length” or “the number of tokens local to each rank”. But you can work it backwards from the later sentence “under the worst imbalance DeepEP needs a buffer of size S×K×R”: S should be the number of tokens each rank holds locally. The reasoning — each rank holds S tokens locally, each token picks K experts, so locally it has to send out S×K token copies; the whole EP group has R×S×K of them; in the worst case those R×S×K copies all pour onto the same card, so the buffer has to be sized S×K×R; while under perfect balance each card receives exactly R×S×K / R = S×K of them. This reading is self-consistent, but it is our inference — the paper itself never writes the definition of S down clearly. Also note that the S here (a scalar, the sequence length) and the S in 10.5 (the state matrix) are two completely different things; the paper reuses the same letter.
Then the key question: how many redundant experts are enough to guarantee that balance? Let E be the total number of experts and R the EP size. The paper proves: with at most E/R redundant experts per rank, a balanced assignment always exists, and this bound is essentially tight (essentially tight). The full proof is in Appendix E of the paper, and Chapter 14 of this site covers it.
The paper deliberately compares this with prior work, and the comparison is fairly sharp. Work like ECHO and UltraEP either fixes the number of redundant experts in advance or imposes a per-rank cap on tokens. That brings three troubles: once there is no feasible assignment within the cap, training is forced to stop; the cap itself needs manual tuning; and even when it is tuned, it still leaves residual imbalance. MoonEP's selling point is not “a bit more balanced”, it is “perfect balance that is mathematically guaranteed and never fails” — that is a qualitative difference.
Three more pieces of supporting engineering
Online planning. Computing the exact optimum at every step costs too much. The paper's approach is practical: use integer linear programming (ILP) offline to compute exact solutions for representative cases as a reference, and design a GPU planning kernel from those, so that it is near-optimal, its overhead is negligible, and it always respects the E/R upper bound. Note the pattern here — first use a slow, exact method to establish the “right answer”, then design a fast, approximate method from it, then check how far the approximate method sits from the right answer.
Zero-copy communication. Perfect balance also simplifies the communication path. The paper implements a fused permute/unpermute operator: the planning kernel works out each token's destination in advance, so tokens are sent straight to the expert-grouped position on the remote rank, and a view of the communication buffer is handed straight back to the computation, which removes the intermediate copies. Then comes the key comparison: under the worst imbalance, supporting the same copy-free data path in DeepEP needs a communication buffer of size S×K×R, while MoonEP, because of perfect balance, needs only a fixed S×K buffer.
Work it out yourself. Suppose each rank holds S = 8192 tokens locally, each token picks K = 8 experts, and the EP size is R = 64. (These numbers are made up for practice; they are not the K3 configuration the paper gives.) Work out: (a) how many token copies can the communication buffer MoonEP needs hold? (b) how big does DeepEP need under the worst imbalance to support the same copy-free path? (c) what is the ratio between them? (d) explain in one sentence where that ratio comes from.
(a) MoonEP: S×K = 8192 × 8 = 65,536 token copies' worth of space.
(b) DeepEP worst case: S×K×R = 65536 × 64 = 4,194,304.
(c) A factor of 64, exactly equal to R.
(d) The reason: a buffer has to be sized for the worst case, or it crashes the moment it really does overflow. In a scheme with no balance guarantee at all, the worst case is that the R×S×K token copies from all R cards in the group pour onto the same card, so every card has to reserve space up to that bound. MoonEP, thanks to the mathematical guarantee the E/R theorem provides, knows that what each card receives is exactly S×K, not one more and not one fewer, so the buffer can be sized to the exact value, saving R−1 times the space.
What is worth taking in here is the order of the logic: the memory is not saved by “optimisation”, it is saved by a theorem — only because the upper bound can be proved does anyone dare make the buffer small. A mathematical guarantee is converted straight into memory here.
Variant: if the EP size R rises to 128, does MoonEP's buffer grow? Does DeepEP's? Use that to say where the two schemes differ in scalability as the EP size is increased.
Sync-free execution from static shapes. The gain from this one is well hidden but quite real. In a usual MoE implementation the number of tokens each expert gets changes from step to step and from layer to layer, so the host (CPU) has to synchronise with the device (GPU) before launching the expert computation in order to know what the actual computation shape is — and that synchronisation stalls the pipeline between one layer and the next. With perfect balance, every rank receives exactly S×K tokens, the computation shape of every layer is statically known, so the per-layer MoE host synchronisation is removed entirely, and the host-side kernel launch overhead is eased as well.
Why “host–device synchronisation” is so expensive
The normal way the CPU hands work to the GPU is “throw it and go” — the CPU queues up a pile of kernels and carries straight on without waiting for results. That is how the CPU stays ahead of the GPU, with work always sitting in the queue. But the act of “synchronising” breaks that: the CPU has to stop and wait for the GPU to spit the result back before it knows how big the next kernel should be. During that wait the GPU's work queue runs dry, and by the time the CPU comes round and hands out work again, the GPU has been idling for a while. Once per layer, accumulated over dozens or hundreds of layers, adds up to a great deal. So the real value of “the shape is statically known” is not that it saves one decision, it is that it decouples the CPU and the GPU again.
Expert GEMM scheduling and overlap. This one is the easiest thing in the whole section to misread, so pay particular attention: even with the aggregate load perfectly balanced across ranks, the number of tokens each expert inside a rank gets is still skewed. If you schedule in a fixed order that ignores the amount of work, that skew turns into an uneven makespan (makespan) across SM workers. The paper's answer is to schedule the routed experts' GEMMs with a workload-aware scheduler: it adjusts its parameters before launch according to the current token distribution and keeps them fixed during execution. The parameters are chosen by a lightweight heuristic using an analytical cost model over hardware metrics, with the key coefficients calibrated by offline auto-tuning. Separately, the shared experts' GEMM is dispatched to a separate stream, so it can overlap with other kernels.
A construction question. Someone finishes the “perfect load balance” passage and concludes: “since every rank receives exactly S×K tokens, the amount of work on every compute unit is the same, and the scheduling problem is completely solved.” Construct a concrete example that overturns this conclusion: with the total number of tokens on a rank fixed, show that the makespans of the SM workers can still differ enormously.
Case A (even): 250/250/250/250. Each worker's workload is proportional to 250, all four finish at almost the same moment, makespan ∝ 250.
Case B (skewed): 970/10/10/10. The total is still 1000, and it fully satisfies MoonEP's perfect balance condition. But the worker handling the first expert has 970 units of work to do, while the other three do 10 each and then sit idle. The makespan of the whole rank is ∝ 970, nearly four times case A, and 3/4 of the workers idle for the great majority of the time.
Conclusion: perfect balance is balance between ranks (inter-rank); it does not touch the skew among the experts inside a rank at all (intra-rank). This is exactly what the paper's sentence “even with the aggregate load perfectly balanced across ranks, the per-expert token counts within each rank remain skewed” means. It is also why “expert GEMM scheduling and overlap” has to exist as a separate technical point — it solves the layer of the problem that perfect balance cannot solve. The workload-aware scheduler does not naively put “one worker on one expert”; it tunes its parameters to the current token distribution and splits the big expert's work across several workers.
The more general lesson: “equal totals” has never meant “equal makespans”. Any balancing done on totals has to be followed by one more question — “and what about the distribution inside the total?”
Variant: what if it were the other way round — “every expert has the same number of tokens, but the totals differ across ranks”? Which of the paper's solutions does each of these two imbalances (between ranks vs inside a rank) correspond to?
Defend it: if I were a reviewer
You prove that “at most E/R redundant experts per rank guarantees a balanced assignment exists”, and you treat it as MoonEP's core advantage over ECHO and UltraEP. But I want to ask: E/R redundant experts are not free — every redundant expert takes memory for its parameters, has to be prefetched in the forward pass, and has its gradients reduced back to the home rank in the backward pass. When the EP size R is fairly small, E/R is quite large, which means every card has to carry a whole pile of extra experts. Your main text does not give a single number for any of these costs. Is this “guarantee” of yours hiding the cost somewhere else?
A reference defence (put it in your own words first, then look)
First line of defence: these are reserved slots, not slots actually filled. The paper's wording is “reserving E/R redundant-expert slots per rank” (reserving E/R redundant-expert slots per rank). E/R is the worst-case upper bound, the headroom that must be left in order to guarantee “a feasible solution always exists”; how much each online planning step actually uses depends on the routing distribution of the current micro-batch, and the vast majority of steps come nowhere near the bound. So memory is reserved to the bound, but the real cost of migration and communication follows actual usage.
Second line of defence: the rivals' cost is better hidden. ECHO and UltraEP look as though they save this reservation, but what they pay is training that may be forced to stop (when no feasible assignment exists), the need for manual tuning, and residual imbalance that keeps slowing every step down. The first two are costs that cannot be quantified but do eat engineers' time; the third is a tax paid on every step. Spending a bounded, predictable amount of memory to buy off an unbounded, unpredictable risk of failure is a good trade in 3T-scale training, where “one run goes for tens of days and one interruption is a heavy loss”.
But the reviewer's central criticism holds. The main text genuinely does not give: the share of memory the redundant experts cost, the communication cost of online migration, MoonEP's end-to-end throughput gain over DeepEP, or the actual values of E and R in K3. Every argument about it being “a good trade” is qualitative for now. And the phrase “essentially tight” (essentially tight) is itself quite vague — does it mean there exist instances that need exactly E/R, or instances that need E/R − 1? The main text does not say; it just points at Appendix E.
10.8 Memory-efficient training: six weapons, every one with a price
This section corresponds to §5.2.2 of the paper and addresses problem (ii) — activations, gradients and optimiser states exceeding the memory budget. Six techniques; go through them one at a time, with the focus on what is saved and what it costs.
Activation: the intermediate result each layer computes during the forward pass. They must be kept until the backward pass has finished with them, because computing gradients needs them. The more layers, the longer the sequence and the bigger the batch, the more activations there are, and they are often the largest single item of memory during training.
1. The unified activation manager
This is the one of the six with the most design taste. The paper gives activations a unified storage abstraction: every “tensor saved for the backward pass” is associated with a pluggable storage backend. So — recomputation (recomputation, throw it away once used and compute it again in the backward pass), quantisation (quantization, store it in fewer bits), offload / remote-offload (offload/remote-offload, move it to CPU memory or another card) — these three techniques, which originally had nothing to do with each other, all become “storage policies” under the same abstraction, and can be combined freely at tensor granularity.
And the policy is declared through a lightweight annotation on the tensor, completely decoupled from the model code — changing the memory-saving policy takes no change to a single line of model code. Recomputation is done at function granularity, and recomputation across layers is supported.
Two more implementation details: all GPU memory is allocated on the main compute stream and managed in a single memory pool, avoiding multi-stream fragmentation and host-bound overhead; and activations are prefetched back at layer granularity and overlapped with computation, with negligible extra overhead.
The paper states K3's actual configuration: most activations use block-wise FP8 quantisation plus offload / remote-offload; element-wise operators are configured for recomputation. This pairing makes sense — element-wise operators (activation functions, for instance) are extremely cheap to recompute (one pass and you are done), yet storing them takes a whole copy of memory, so throwing them away and recomputing is the best deal.
An analogy
The three policies are like three ways of tidying a room: recomputation is “if I need this later I will make another one, throw it out now”; quantisation is “compress and pack it, it takes less space but loses a little detail”; offload is “move it to the storeroom downstairs, and when you need it, have someone bring it up in advance”. The unified activation manager's contribution is not inventing these three ways, it is building one cupboard for all of them, so you can label each item separately with “this one goes by scheme A, that one by scheme B” instead of rebuilding the room for each scheme.
Where the analogy breaks down: moving things in a real storeroom does not stop you doing anything else, whereas moving data from GPU to CPU takes PCIe bandwidth and may slow other transfers down. The paper says “activations are prefetched back at layer granularity and overlapped with computation, with negligible extra overhead” — “negligible” is the authors' claim, with no number behind it.
Read this carefully
“Most activations use block-wise FP8 quantisation” is a choice with a precision risk: FP8 has only 8 bits, and squeezing the intermediate results the backward pass needs down to 8 bits will, in theory, introduce error into the gradients. In this section the paper does not discuss the effect of this on training precision at all, nor does it say which tensors are excluded from quantisation. “Block-wise” means the scale is set separately per small block to reduce the error, but the paper writes down neither the block size nor which layers are exempt.
2. Memory-efficient MoE
This one is a pure mathematical trick and worth appreciating. In a naive MoE implementation, computing the gradient of the permuted probabilities depends on the forward output, output — which means output has to stay in memory the whole time waiting for the backward pass.
Inspired by SonicMoE, the paper uses a mathematical transformation to rewrite this gradient into a form that depends only on the intermediate activation act_output and the upstream gradient doutput, at the cost of one extra lightweight element-wise computation. With that, the backward pass's dependence on output is removed, and output can be freed as soon as it has been used.
Separately, in the forward pass of the group GEMM, only the input of the dispatch operation is saved; in the backward pass the group GEMM's input is recovered by recomputing the dispatch. The communication this recomputation introduces overlaps with part of the group GEMM's backward computation, so this piece of activation storage is “eliminated at negligible cost”.
3. Memory-efficient attention residuals
This is the optimisation that goes with Block AttnRes (the architectural component covered in an earlier chapter). It has three parts: the block representation is generated once at the boundary layer, shared by every later layer, and kept resident on the GPU; the whole AttnRes computation is wrapped in checkpointing, so the activations each layer saves for the backward pass are exactly the same as in a standard residual architecture; and for pipeline parallelism it uses cache-based pipeline communication, transferring only the newly generated blocks incrementally between stages, releasing them as soon as the micro-batch ends, which reaches the theoretical lower bound of memory usage.
The second part is this one's core selling point: Block AttnRes is a new architectural component, and what people worry about most is “will a new component push memory up a long way”. The paper's answer is that once it is wrapped in checkpointing, the activations saved per layer are identical to a standard residual architecture, that is, zero extra activation cost. The price, of course, is the recomputation time of the checkpointing itself.
4. Balancing activations across PP ranks
This one needs an inherent phenomenon of pipeline parallelism understood first. Under interleaved 1F1B (interleaved 1F1B, a pipeline scheduling strategy in which “one forward, one backward” are interleaved), because of pipeline warmup (pipeline warmup) the distribution of activations across PP ranks is uneven: the number of resident activations decreases as the PP rank increases.
Why? Because the pipeline has to be “filled” first. The PP rank at the very front has to do the forward pass of several micro-batches in a row before the first backward pass comes back to it; by then it has already piled up a stack of activations. The PP rank at the very back can do its backward pass right after its forward pass, and the activations are released immediately. The result is that the earlier ranks are tight on memory and the later ranks have plenty.
The paper's fix is direct: to avoid OOM, it uses the Mooncake Transfer Engine to remote-offload activations into the memory of other PP ranks, giving balanced activation memory across PP ranks. That is — since the later ranks have spare memory, move the earlier ranks' activations over there and store them.
5. Pipelined ZeRO-2 gradient sharding and offload
Three moves, each more aggressive than the last: first shard the gradients across the data-parallel ranks (that is ZeRO-2); then go further and store the sharded gradients in CPU memory to cut GPU memory usage; and at the same time keep a double gradient buffer on the GPU. The flow is: gradients are reduced into the double gradient buffer across DP ranks, then accumulated into the CPU shard.
“Double buffering” is a classic trick: one block is being filled while the other is being moved to the CPU, and the two take turns, so the moving and the computing can happen at the same time. The price is one more buffer's worth of space on the GPU, plus a constant flow of data between GPU and CPU.
6. P2P-based Muon orthogonalisation
The problem here is an interesting one: the distributed optimiser shards parameters evenly across DP ranks (each card gets only a strip of the parameter matrix), while the Newton–Schulz orthogonalisation inside Muon needs the complete parameter matrix. Those two things conflict directly — orthogonalisation is an operation on the whole matrix, and you cannot do it holding only a few strips. So before every update there has to be a communication step to gather the complete parameters.
The naive way: on every rank, do an all-gather over the entire parameter buffer. The consequence is twofold — it takes a considerable amount of memory (every card has to clear space to hold all the parameters) and it makes communication the main bottleneck.
The paper's way: each rank fetches back the shards of the parameters it owns locally, only through P2P communication with the corresponding owner rank. This removes the full parameter buffer and at the same time lowers memory usage and communication volume. Communication and computation are further pipelined at the granularity of the model-chunk buffer, hiding the communication cost.
What the P2P version actually changed
The key difference is in “what each rank ultimately needs to end up with”. The implicit assumption of the naive all-gather is “every rank needs all the parameters”, so every card must have a buffer able to hold all of them. But think about it: each rank is only responsible for updating the parameters it owns, and what it needs is the complete matrices those parameters belong to, not every matrix. So change “broadcast to everyone” into “point to point, ask only the right owner for only the right piece”, and the full parameter buffer is no longer needed. This is a classic “re-examine the requirement” optimisation — not making the communication faster, but noticing that nowhere near that much needs to be sent.
Of the six weapons in §5.2.2, the price of saving memory really falls into only three classes: (A) more compute time, (B) more communication/bandwidth, (C) lost numerical precision. Put each of the following four into one class or several, and say why: (1) element-wise operators configured for recomputation; (2) block-wise FP8 quantisation; (3) storing the sharded gradients in CPU memory; (4) the mathematical rewrite of the gradient in memory-efficient MoE.
(1) Recomputing element-wise operators → A (compute time). Thrown away as soon as the forward pass has computed it, computed again when the backward pass needs it. The reason element-wise operators in particular are picked for recomputation is exactly that they are extremely cheap to recompute (one pass) while storing them takes a whole copy of memory, which is the best value for money.
(2) Block-wise FP8 quantisation → C, mildly A. The main price is precision: 8 bits cannot hold the original dynamic range, so there must be quantisation error. “Block-wise” is there to reduce that error (a scale set separately per small block). Quantising and dequantising themselves take a little computation, but very little. The paper does not discuss the effect of this on the training result; that is a blank.
(3) Sharded gradients stored on the CPU → B (bandwidth). Moving data between GPU and CPU goes over PCIe, far slower than memory bandwidth. The paper hedges with a double gradient buffer so that the moving and the reduction can overlap — precisely the standard way of “hiding a class B cost with overlap”. The accumulation on the CPU also costs CPU time.
(4) The MoE gradient rewrite → A, but extremely mildly. The paper says outright that the cost is “one extra lightweight element-wise computation”, with no precision loss (it is an equivalent mathematical transformation, not an approximation) and no extra communication. This one is the closest to a genuinely free lunch, because it does not move the data somewhere else, it stops the backward pass needing the tensor output at all.
A general rule worth remembering: class A and class B costs can usually be mostly hidden by overlap (in the paper both (1) and (3) come with overlapping), while class C costs cannot be hidden — precision lost is lost. Which is why the paper's silence on (2) deserves particular notice.
Variant: classify “balancing activations across PP ranks” (remote-offload into another PP rank's memory) as well. Which class does it belong to? Compared with offloading to the CPU, what are the advantages and disadvantages of offloading to another GPU?
10.9 The multimodal encoder: stuffing the ViT into the cracks in the pipeline
The last problem: the highly variable computation of the vision encoder is exposed on the critical path.
Why “highly variable”? Because images and videos differ enormously in size. A small image may be a few hundred patches; a high-resolution large image or a long video may be tens of thousands. The paper says: in long-context multimodal training, large images and long videos greatly increase the computation time of the vision encoder, causing severe load imbalance across devices. One card gets a pile of small images and finishes quickly, another gets one enormous image and grinds away for ages, and the first can only sit and wait.
Dynamic CP inside the encoder
The paper's first move is to extend context parallelism to this kind of large sample, in two layers:
- A single large image is split along the patch dimension across several devices, and attention is computed by gathering the key-value pairs across CP ranks (gather-KV). Note that it is gather-KV that is used here, because the vision encoder uses ordinary softmax attention, not KDA — so it has to exchange key-value pairs the honest way, and gets none of the dividend of the fixed-size state from 10.5.
- Each CP group is divided into several “sub-CP groups”, and several large images are assigned to them in a load-balanced way, preventing the communication share from growing with scale. This layer is the key one: with only the first layer, one large image gets split across more and more devices and the gather-KV communication takes an ever larger share; once there are sub-groups, each image is split only inside a smaller sub-group and the communication is confined within that sub-group.
The paper says this approach lowers both the encoder latency of large visual samples and the load imbalance across devices, which lets the remaining encoder computation hide inside the pipeline bubbles — that sentence is the hinge, and it leads straight into the next technique.
Stuffing the encoder computation into PP bubbles
Pipeline bubble: pipeline parallelism inherently has a “warmup” phase and a “drain” phase — at the start the later ranks have not been given work yet, and at the end the earlier ranks have no work left. Those stretches of time in which “some cards have nothing to do” are the bubbles. A bubble is pure waste, but as long as you use a pipeline it cannot be removed entirely.
K2.5 introduced the Decoupled Encoder Process, which splits the ViT and the text training into different stages and balances the visual forward and backward passes across the PP stages. K3 goes a step further on that basis, on the strength of a very fine observation:
The timing pattern the paper observed
Under interleaved 1F1B scheduling: the text forward passes of the first few PP micro-batches are all scheduled at the very start (scheduled at the very beginning), while the text backward passes of the last few PP micro-batches only finish at the very end (finish only at the very end).
Put together, those two sentences mean: the head and the tail of the pipeline are hard boundaries locked up by text computation, and the bubbles are only in the middle. So the ViT computation cannot be dumped into the bubbles wholesale — the ViT forward pass of those first few micro-batches has to be finished before the text forward pass starts, or the text forward pass will have to wait for it.
So the paper decomposes the ViT computation further: the ViT forward passes of the first few PP micro-batches are executed synchronously upfront (executed synchronously upfront), the remaining forward passes are scheduled into the pipeline bubbles, and the backward passes are handled the same way. The result: most of the ViT computation is hidden inside the pipeline bubbles, largely eliminating the effective overhead of the vision encoder.
Read this carefully
“Largely eliminating the effective overhead of the vision encoder” (largely eliminating the effective overhead of the vision encoder) is the paper authors' own claim, and the main text gives no numbers to back it — it does not say what percentage was eliminated, and it gives no throughput comparison before and after. Likewise, exactly how the “sub-CP groups” in 5.2.3 are divided, and what algorithm “assigning the large images in a load-balanced way” uses, the paper does not explain.
A synthesis question. Put the 4th weapon of 10.8 (balancing activations across PP ranks) and the 2nd technique of 10.9 (stuffing the ViT into bubbles) side by side and you will find that they come from the same phenomenon of pipeline parallelism. Say what that phenomenon is, explain why it creates a nuisance and an opportunity at the same time, and explain why the two fixes do not conflict (and can even be used together).
The shared root: pipeline warmup and drain in interleaved 1F1B. The pipeline has to be filled before it can reach steady state, and drained at the end.
The nuisance it creates (the memory dimension): during warmup an earlier PP rank has to do the forward passes of several micro-batches in a row before the first backward pass comes back, and all the activations from that stretch have to be kept and cannot be released. The further forward the rank, the more it piles up, hence “the number of resident activations decreases as the PP rank increases”, and the earlier ranks are at risk of OOM. The fix is to use the Mooncake Transfer Engine to remote-offload activations into the spare memory of the later ranks — in essence, moving memory from where there is plenty to where it is tight.
The opportunity it creates (the time dimension): during warmup and drain there are always cards idling, and that is the bubble. Since it is empty anyway, fill it with other work. ViT computation happens to be the ideal filler, because it does not depend on the intermediate results of the text pipeline and is very free in its timing. The fix is to schedule most of the ViT computation into the bubbles — in essence, moving computation from the busy moments to the idle ones.
Why they do not conflict: the two act on two orthogonal resource dimensions — one moves space (memory, across ranks), the other moves time (computation, across moments). And their directions are complementary as well: bubbles occur mostly in the warmup/drain phases, which is exactly when the earlier ranks are tightest on memory, and putting ViT computation there does not add to the memory pressure (the ViT has its own activations, but they can be managed independently) — instead it uses up the idle compute of that stretch.
A deeper observation: the whole pattern of K3's infrastructure can be summed up in one sentence — wherever there is an imbalance of “tight here, plenty there”, find a way to move the plenty to where it is tight. MoonEP moves tokens (between EP ranks), KCP moves state fragments (between CP ranks), balancing activations across PP ranks moves memory, and stuffing the ViT into bubbles moves compute moments. Four techniques, one idea.
Variant: if the number of PP stages (the number of virtual stages) is increased, do the bubbles grow or shrink? Does the activation pressure on the earlier ranks grow or shrink? What effect does each of these two changes have on the two fixes above? (Hint: the paper does not discuss this trade-off; you have to reason it out yourself from “how many micro-batches the warmup has to fill”.)
Chapter summary
This chapter is entirely about “how to make it run”, but if all you remember is a string of technical names, you have read it for nothing. What is really worth taking away is three lines of thought:
First, KDA's fixed-size state is a trade, not a pure improvement. The bad side is that it is serial and hard to parallelise; the good side is that it is cheap, easy to transmit and easy to reuse. All the work in §5.1 can be summed up as: use FlashKDA and SM-level CP to deal with the bad side (inside a device), and use KCP to cash the good side in for hard currency (between devices).
Second, the mathematical core of KCP is a single sentence: split the effect of a stretch of sequence into “one cumulative transition matrix” plus “one state generated from zero”, and both of those can be computed with local tokens alone. Because the matrix Mt exists, the old approach of “everyone computes from zero and then sums” fails completely (you overturned it with your own hands in q10-5); the decomposition given by Eq. 17 lets each rank compute its own fragment fully in parallel, then exchange them with one fixed-size all-gather and put them back together with a prefix scan. Those two words, “fixed-size”, are the most important two words in the chapter — they mean that if the sequence gets ten times longer, this piece of communication does not grow at all.
Third, every technique in §5.2 is doing the same thing: moving spare resources to where things are tight. MoonEP moves tokens (and uses that E/R theorem to convert “feasibility guaranteed” into an R-fold saving in buffer), the unified activation manager moves activations (combining recomputation, quantisation and offload freely), balancing activations across PP ranks moves memory, and stuffing the ViT into bubbles moves compute moments. And every “move” has a price; this chapter has forced you to state the price of each one plainly: more compute, more bandwidth, or lost precision — there is no fourth kind.
One more piece of methodology runs through the whole chapter and deserves its own mention: a mathematical guarantee can be converted directly into engineering gain. Only because MoonEP can prove the E/R upper bound does it dare cut the communication buffer from S×K×R down to S×K; only because it can guarantee “exactly S×K tokens per rank” does the computation shape become statically known, which is what lets the per-layer host synchronisation be removed. Put the other way round, if it were merely “empirically fairly balanced”, neither of these two gains could be had — because the buffer would have to be sized for the worst case and the shape would only be known at run time.
The next chapter carries on with the second half of the infrastructure: the plumbing for million-token agentic reinforcement learning, and on the inference side KDA decoding, prefix caching and scheduling. That property of the KDA state — “cheap, easy to store, easy to reuse” — gets squeezed far harder there.
Where this chapter needs question marks (a summary)
The paper leaves a fair number of blanks in these pages: FlashKDA only says “substantially beyond the Triton reference implementation” without giving a factor; KCP gives no measured data on communication volume or scalability, and does not discuss the cost of rebuilding by prefix scan when P is large; MoonEP gives neither the memory cost of the redundant experts nor the end-to-end gain over DeepEP, and what “essentially tight” means exactly has to be looked up in Appendix E; the effect of block-wise FP8 quantisation on training precision is not discussed at all; Figure 11 is a qualitative schematic with no utilisation numbers; the sub-CP-group division algorithm of 5.2.3 and the quantitative evidence for “largely eliminating the vision encoder's overhead” are both missing. As you read this chapter, keep “this is the authors' claim” apart from “this is a conclusion supported by data”.
第11章 基础设施 II:百万上下文的 RL、沙箱与线上服务
一句话导语:这一章回答一个很土但很致命的问题——当上下文长到 100 万个 token,模型又要一边自己做任务一边被训练,最后还要拿去给几百万人用,内存和带宽根本不够分,那到底该怎么分?
学完这一章你应该能做到
- 用自己的话解释什么是 KV 缓存、前缀缓存,并算出一次多轮对话里前缀缓存到底省了多少计算
- 说清「写回」与「写穿」两种缓存卸载策略各自的浪费点,并构造出让写回吃亏的场景
- 复述「把参考模型权重塞进梯度缓冲区」这个技巧,并指出它的安全性依赖哪两条实现假设
- 在 Figure 12 的设定下亲手算出命中边界 B,并说明为什么它是哈希块的整数倍而不必是物理块的整数倍
- 把三个并发一致性机制分别对应到它们要防的那个具体失效模式
- 用 400K 与 4K 这两个数字算出缓存命中的收益量级,并指出缓存亲和调度的适用边界
11.1 开工前:五个必须先懂的常识
这一章的每一个设计,都是在跟同一件事较劲:算过的东西不要再算第二遍,占着的内存要尽快让出来。要看懂它们,先得有五个概念打底。
KV 缓存(KV cache):模型每读进一个 token,都会为它算出一对叫 key 和 value 的向量;后面每生成一个新 token,都要回头看前面所有 token 的这对向量。关键在于——某个位置的 key/value 只取决于它自己和它前面的内容,一旦算出来就永远不会变。所以算一次就存起来,这个仓库就是 KV 缓存。不存会怎样?每生成一个字,前面几十万个 token 就得全部重算一遍,长上下文下这是纯粹的自杀。
前缀缓存(prefix cache):既然 key/value 只取决于「这个 token 和它前面的内容」,那么两个请求只要开头那一段逐 token 完全相同,这一段的 KV 就一模一样。于是第二个请求可以直接把第一个请求算好的那段拿来用。这就是前缀缓存能省钱的全部道理。你和 AI 聊到第 10 轮时,前 9 轮的内容原样又发了一遍,如果没有前缀缓存,这 9 轮每次都要从头算。
为什么前缀缓存对本章格外重要
普通对话的前缀也就几千个 token,省不省差别不大。但 K3 的目标场景是 100 万 token 上下文:一次编程任务可能带着 40 万 token 的代码库当前缀。这时「命中」和「未命中」的差别不是快一点慢一点,而是差出两个数量级——11.12 节我们会亲手把这笔账算出来。
分页(paging)与物理块(physical block):KV 缓存不是一整块连续内存,而是像操作系统管内存那样,切成固定大小的「块」来分配、回收、共享。一块可能装 1024 个乃至 6144 个 token 的 KV。这个「块」是本章后半段一切纠纷的源头。
Rollout:强化学习里让模型自己去「打一局」——想一步、调个工具、看看返回、再想、再调,最后产生一条完整的轨迹。这条轨迹拿去打分,再用分数更新模型权重。「多步 rollout」就是一局里有很多轮工具调用,上下文只增不减地越滚越长。「部分 rollout」(partial rollout)是指一局没打完就先按暂停键,把状态存下来,下一轮迭代接着打——目的是不让某一条特别长的轨迹拖住所有人(这叫压尾延迟)。
沙箱(sandbox):给智能体配一台隔离的小电脑,它可以在里面随便敲命令、装软件、跑代码、把系统搞崩,都不会影响到外面。没有沙箱,你就不敢让模型真的去执行它自己写的命令。
TTFT 与 SLO:TTFT(time to first token)是你按下回车到屏幕上蹦出第一个字之间的等待时间——用户感知最强的那个指标。SLO(service level objective)是服务给自己定的承诺,比如「95% 的请求 TTFT 不超过 2 秒」。11.12 节里所有的调度设计,最终都是为了守住 SLO。
用户第一轮输入 3000 个 token,模型回了 200 个 token。第二轮,客户端把这 3200 个 token 原样带上,再加 100 个 token 的新问题。
(a) 没有前缀缓存时,这一轮要重新计算多少个 token 的 KV?
(b) 有前缀缓存时呢?
(c) 如果用户这次把「请用中文回答」这句话(设为 6 个 token)加在了整段对话的最前面,(b) 的答案变成多少?
变式:如果那句「请用中文回答」是加在第二轮问题的末尾而不是开头,能复用的前缀长度是多少?为什么位置差这么多?
11.2 §5.3 的出发点:为什么「资源效率」被排在第一位
论文这一节的开场白很直白:要给 K3 这么大的模型做智能体 RL,还要把上下文扩到百万 token,而算力预算是有限的,于是资源效率成了第一位的目标(a first-order goal)。注意这个措辞——不是「顺便优化一下」,而是设计的出发点本身。
由此长出两条互补的工作线:一条是高效的训练与 rollout,具体包括 KV 缓存管理、请求调度、训练状态放置;另一条是高性能、可恢复的沙箱,用来支撑长时程的交互。前者是 11.3 到 11.5 节,后者是 11.6 节。
那道核心的内存争用题
K3 的 RL 采用同址训练(co-located RL training):训练和 rollout 跑在同一批 GPU 上,这样每个 1M 上下文的 K3 RL 实验能被控制在几百张 GPU 以内。同时用部分 rollout 来压住超长轨迹带来的尾延迟。论文说这个设计「硬件利用率好」,但紧接着承认它引入了一个麻烦:
矛盾在哪
部分 rollout 的意思是「这一局没打完,先存着,下一轮接着打」。存的是什么?主要就是这条轨迹的 KV 缓存——它必须跨迭代活着。可它占的显存,正是训练那边要用来放权重、优化器状态、激活和梯度的同一块显存。两位租客抢一间房。论文明确指出:这个挑战在长上下文 RL 里更严重。
为什么更严重?因为 KV 缓存的大小大致随上下文长度增长,1M 上下文下单条未完成轨迹要留下的 KV 就已经很可观,而训练那边的需求并不会因此缩小。论文对此只做了定性判断,没有给出任何量化数据——这是我们后面会反复遇到的情况。
(a) 请解释:为什么「部分 rollout」这个机制本身就是内存争用的来源?如果放弃部分 rollout、要求每条轨迹必须一次跑完,争用会缓解吗?代价是什么?
(b) 假设你只能改一个东西来缓解争用:减少同时在跑的轨迹条数、缩短单条轨迹的最大长度、或者把训练那边的某些东西挪走。分别说说各自会牺牲什么。
变式:如果换成「分离式」部署(训练和 rollout 用两批互不相干的 GPU),这个内存争用会消失吗?消失之后新的问题是什么?
11.3 外部 KV 缓存池:写回,而不是写穿
先看清楚敌人。论文说:在 1M 上下文的多步 rollout 下,前缀 KV 缓存未命中的代价极高。而部分 rollout 让情况雪上加霜——每轮迭代刚开始的时候,上一轮留下的许多未完成的长预填充请求会同时到达。同时到达意味着瞬间的内存需求尖峰。
还有第三个加压者:推测解码在相对固定的工具调用间隔内进一步加快了请求周转,于是前缀块的churn(不断被换进换出的翻搅)变多了。这几件事叠在一起,可能触发抢占(preemption,把某个请求的缓存强行赶出去腾地方),从而拉低缓存命中率——而命中率对长上下文 RL 是生死攸关的。
读的时候要小心
「推测解码加快请求周转 → 增加前缀块 churn」这一步,论文只用了一句话带过,没有解释中间的机制。合理的推测是:周转快意味着单位时间内进出的请求更多、块的分配与回收更频繁,但论文并未展开,我们无法验证。
解法:把「前缀保留」和「住在 GPU 上」这两件事拆开
直觉上,你希望能复用的前缀一直留着;但显存有限,留不下。论文的做法是用一个写回(write-back)设计,把「前缀被保留」和「前缀驻留在 GPU」解耦:
- 正在活跃解码的块,留在 GPU 的 KV 缓存里。
- 可复用的、较老的前缀,只在从 GPU 被驱逐的那一刻,才写回到位于 CPU DRAM 里的外部 KV 缓存池(external KV cache pool),并在下一次要复用它之前预取回来。
- KDA 的循环状态与对应的 MLA KV 缓存块一起卸载、一起预取,让两者的生命周期保持对齐。
对照组是写穿(write-through):一算完就往 CPU 拷一份。论文的论证是:写回策略只对那些离开活跃解码路径的前缀产生 CPU DRAM 占用和传输带宽,从而避免了为「仍然驻留在 GPU 上、而且还活跃着」的块做冗余的 CPU 拷贝。
打个比方
写穿像是你每写完一页笔记就立刻去复印一份存档,哪怕这页你等会儿还要接着写。写回则是等这页从桌上被收走的时候才去复印。桌面(GPU 显存)小、复印机(PCIe 带宽)慢的时候,后者显然更省。
类比失效处:真实系统里「收走」是被动发生的,而且可能一大批同时被收走——这时复印机会突然排长队。这正是 11.3 节测验里要你构造的反例。
DRAM 从哪来:让训练状态先滚去 NVMe
外部池住在 CPU DRAM 里,可 DRAM 也不是无限的。论文的办法是:一次训练迭代结束后,把训练状态(模型权重和优化器状态)卸载到 NVMe(固态硬盘),腾出 DRAM 给外部池;一次 rollout 迭代之后,池被释放,以免与训练工作负载争用。
论文论证写回优于写穿。请构造两种不同的工作负载,使写穿不吃亏甚至更好,并分别指出你破坏的是论证里的哪一个隐含前提。
反例二(大家一起走):驱逐高度同步——论文自己就描述了这种情形,每轮迭代开始时上一轮的大批长预填充请求同时到达,同时触发抢占。写回会在那一瞬间要求极高的 PCIe 带宽,形成排队;而写穿在此之前的空闲时段早就把数据慢慢搬完了。被破坏的前提是「逐出发生时有富余的传输带宽」。
值得注意的是,反例二的触发条件恰恰是论文自己承认存在的场景,但论文没有讨论写回在这种尖峰下的表现。
变式:如果 CPU DRAM 特别小(只装得下很少的前缀),写回和写穿哪个受伤更重?提示:想想「谁先被迫丢弃数据」。
11.4 Rollout 自动限流调度器:别让并发数变成一个手调的魔法数字
多步 rollout 有一个很讨厌的性质:上下文随着轨迹推进逐渐增长。第 1 步可能只有几千 token,第 30 步可能已经几十万。可传统做法是设一个固定并发数——同时最多放多少个请求进推理引擎——而这个数通常是按「整条轨迹的平均长度」估出来的。
论文指出这个做法两头不讨好:这个平均长度本身就难以估计;而且按它设出来的并发数在早期过于保守(那时每条请求其实只占一点点 KV,GPU 吃不饱)。反过来,如果把并发调高,后期又会造成 KV 缓存压力、可能触发抢占。
论文的解法是在 LLM 请求调度层设计一个自动限流(auto-throttling)机制,用运行时信号动态决定发给推理引擎的请求数量。论文列出的信号有三个:活跃请求数、排队请求数、KV 缓存利用率。效果是:早期 rollout 保持高利用率,而当 KV 缓存压力上升时自动降低并发,在不需要人工调参的情况下同时避免欠饱和与过载。
论文没说的部分
论文只列出了用哪三个信号,完全没有给出控制律——具体怎么根据这三个数算出下一刻的并发数(是阈值触发?比例控制?还是别的)。也没有给出开启前后的吞吐或命中率对比。所以这一段只能算是「设计思路的陈述」,无法评估其效果。
(a) 用上面那张图的语言解释:「固定并发在早期过于保守、后期又可能过载」这句话,本质上是一条常数去逼近一条什么样的曲线?
(b) 论文列的三个信号(活跃请求数、排队请求数、KV 缓存利用率)里,哪一个最能提前预警「要过载了」?另外两个各自在什么时候有用?
变式:如果换成一个上下文长度基本不变的任务(比如单轮问答,每条请求都是 2K token),自动限流还有必要吗?固定并发在这种负载下会不会反而更好?
11.5 借一块别人还没用的内存:梯度缓冲区复用
这是本章最精巧、也最值得单独拿出来讲的一个小技巧。
问题:RL 的损失函数经常需要一些只做前向的非策略模型(non-policy model),最典型的是参考模型(reference model,用来衡量当前策略偏离初始模型有多远)。这些模型的权重太大,没法一直住在 GPU 上。可你每次算损失又都需要它。
常规解法是额外分配一块显存放它。但在 1M 上下文的 RL 里,显存已经被 KV 缓存和训练状态挤到极限,「额外分配」这四个字本身就是奢侈的。
那块闲着的内存
论文的观察是:训练时每个参数都配了一个 FP32 梯度缓冲区(存反向传播算出来的梯度,用 32 位浮点存以保证精度)。而在前向传播还没结束、反向传播还没开始的这段时间里,这块缓冲区里面装的是上一轮的垃圾,谁也不需要它。
于是:把参考模型的权重放在 CPU 内存里,只在需要时才实体化(materialize)到 GPU,并让它们的参数张量借用策略模型的 FP32 梯度缓冲区存储。论文给出的安全性论证是一句话:这是安全的,因为这些缓冲区在真正的梯度稍后被计算时本来就会被覆盖。收益是:复用了已有的 GPU 内存,不需要额外分配,也不产生碎片。
打个比方
教室后排有两个座位,是留给下节课要来的两位老师的。这节课他们还没到,于是你先坐上去写作业——反正他们来的时候你早就走了,而且他们一坐下就会把桌面清空。你没有「占用」任何新的座位,只是用了一段本来空着的时间。
类比失效处:真实系统里「他们一定会清空桌面」是一条依赖具体实现的假设。如果那两位老师习惯把新东西叠在旧东西上面(梯度累加而不是覆盖),你的作业就会被卷进他们的讲义里——这正是下面答辩框要打的点。
怎么把一个巨大的模型塞进两个槽位
配合 ZeRO-2 的梯度分片与卸载,每张 GPU 在 K3 的 RL 训练中只为两个 VPP chunk 保留梯度缓冲区(VPP 指虚拟流水线并行的切分单位,一个 chunk 就是分给这张卡的一小段层)。也就是说可借用的空间只有两个槽位那么大,远远装不下整个参考模型。
论文的处理是流式(streaming):参考模型的权重被一块块送进这两个槽位,一个槽用于当前的前向计算,另一个预取下一块。这样拷贝的时间就被藏在计算的时间背后,在不增加 GPU 内存的前提下隐藏了拷贝开销。
论文说这么借是「安全的,因为这些缓冲区在真正的梯度稍后被计算时本来就会被覆盖」。请构造两种情形,使这个安全性论证不成立,并写出为了让论证成立,必须额外保证的条件。
grad += ... 而不是 grad = ...——比如做梯度累积(多个 micro-batch 的梯度加在一起)、或者一个权重被多处共享。这时缓冲区里的旧内容不会被覆盖,而是被加进新梯度里,参考模型的权重值就变成了污染梯度的噪声。可怕之处在于它不报错:训练照样跑,损失曲线可能只是稍微不对,几天后才发现。情形二:时序被打乱或重叠。如果参考模型的前向被安排在反向传播已经开始之后,或者与反向重叠在不同的 CUDA stream 上,那么真梯度会被参考权重覆盖掉,或者参考权重在读取途中被写坏。
必须额外保证的条件:(i) 本次迭代对该缓冲区的梯度写入是纯覆盖式的第一次写(在此之前缓冲区内容对任何人都无意义);(ii) 参考模型的前向完全早于任何梯度写入,且没有跨 stream 的重叠。论文只给了结论,没有说明它如何保证这两点。
变式:如果把「借」的对象从梯度缓冲区换成优化器状态(比如 Adam 的一阶动量 m),这个技巧还安全吗?为什么?提示:想想动量的生命周期是「一次迭代内」还是「跨迭代」。
答辩:如果我是审稿人
你说把参考模型权重塞进策略模型的 FP32 梯度缓冲区是安全的,理由是「这些缓冲区反正会被真梯度覆盖」。可这是一条依赖具体实现的假设:只要有人在反向里改成梯度累加,或者把参考模型的前向挪到反向之后,你的模型就会安静地训坏,而且不会有任何报错。你凭什么把一个正确性保证,建立在「别人不会改这段代码」之上?
参考防守(先自己组织语言再看)
诚实的防守分三层,不要试图硬说它「绝对安全」。
第一层,承认性质。这确实不是数学上的必然,而是一条实现层面的不变量(invariant):本迭代对该缓冲区的第一次写是覆盖式的,且参考前向严格早于它。论文只用一句话陈述了结论,没有说明它靠什么维持这条不变量——这个批评是站得住的。
第二层,说明这类不变量的正确处理方式。系统工程里到处都是这种「某块内存此刻无人认领」的约定,可接受的做法是把它显式化并可检查:在借用前断言缓冲区处于「本迭代尚未写入梯度」的状态,在梯度写入路径上断言「是覆盖而非累加」,让违反立刻爆炸而不是静默出错。有趣的是,论文自己在缓存布局那一节(11.8)用的就是同一种思想——宁可产生垃圾数据,也不要产生看似合理的数据。用他们自己的标准来要求这里,是完全公平的。
第三层,说明收益为什么值得。替代方案是额外分配一块显存放参考模型。但在 1M 上下文的 RL 里,显存已经被跨迭代存活的 KV 缓存和训练状态挤到极限(11.2、11.3 节),「额外分配」可能根本做不到——不是慢一点,而是跑不起来。所以正确的立场是:用一条可检查的不变量,换一块本来就会闲置的显存,而不是宣称零风险。论文没有说明它是否真的加了这类断言,这是它该补的。
11.6 沙箱基础设施:五千万个一次性的小电脑
为支持 K3 后训练与评测的多样需求,论文部署了多种沙箱运行时:传统的基于容器的运行时、GPU 沙箱运行时,以及最值得一提的、基于 microVM 的新沙箱运行时 AgentENV(与合作伙伴共同开发,已开源)。它围绕三个核心设计目标。
目标一:高保真的隔离沙箱运行时
论文的动机写得很坦白:随着智能体能力增强、任务变难,它们探索得更激进,甚至可能尝试奖励黑客(reward hacking,为了拿高分而钻规则漏洞,而不是真的完成任务)。这带来两个方向相反的要求。
一方面是安全挑战:早期用传统容器沙箱做实验时,观察到若干由智能体的非预期操作引起的内核 panic 和死锁。翻译成人话——模型敲的命令把宿主机的操作系统内核搞崩了。容器的隔离性其实是「共享同一个内核、只是把视野隔开」,所以一旦内核出事,大家一起死。
另一方面又希望尽可能允许探索、不去限制智能体的能力,而复杂任务需要一个接近真实环境的沙箱:论文举的例子是,智能体应当能随意挂载磁盘、运行容器、甚至启动虚拟机。这些在容器里都是做不到或很危险的。
解法是用 Firecracker 运行隔离的 microVM(轻量虚拟机,每个沙箱有自己独立的内核)。论文的说法是,它提供了容器运行时无法匹敌的隔离级别与保真度。
常见误解
很多人以为「虚拟机一定比容器慢很多、重很多,所以生产上只能用容器」。这里恰恰反过来:正因为智能体会做出人类不会做的操作,容器那种「共享内核」的隔离根本不够用;而 microVM 通过极度精简的虚拟机实现,把启动开销压到了可以每秒开一堆的程度。下面的「亚秒级启动」和「133 ms 检查点」就是在回答「那不会很慢吗」这个疑问。
目标二:面向智能体 RL 的灵活沙箱生命周期
底层能力是增量检查点(incremental checkpointing)与恢复:做检查点时只保存自上次检查点以来被弄脏的内存页,因此把检查点和恢复延迟压到低至 133 ms 和 49 ms。在这之上提供三个高层操作:
- 暂停与恢复(Pause and Resume):被暂停的沙箱不消耗内存或 CPU 资源。因此当智能体在等模型推理结果时可以把沙箱暂停——而这段等待可能占到沙箱生命周期的高达 98%。
- Fork:从原沙箱的精确状态创建一个新沙箱,同时保持原沙箱继续运行。论文说这对无副作用的奖励评判很有用——你想跑个测试看看它做得对不对,但不想让这次测试改变智能体所处的环境。
- 快照(Snapshot):按固定间隔为沙箱做快照,以便错误恢复。
目标三:高效率与高密度
他们的工作负载有一个吓人的特征:数以万计的沙箱、每个带有独特的镜像集合,可能需要在几秒内创建出来。镜像(image)就是这台小电脑的「出厂系统盘」,装了这个任务需要的软件。一万个沙箱各带各自的镜像,意味着要在几秒钟内搬动海量数据。
论文的做法是采用 OverlayBD 作为镜像格式,配合自定义的 ublk 驱动实现、存储层共享和 P2P 传输(沙箱之间互相传镜像,而不是都去挤同一个中心服务器),在大规模下实现亚秒级启动延迟。进一步通过写时复制内存(copy-on-write,多个沙箱共享同一份内存页,谁要改才给谁单独复制一份)和 page-cache 优化降低内存使用,在真实工作负载中实现高达 6.5× 的内存超额分配比(memory overcommit ratio,即答应给出去的内存总量是物理内存的 6.5 倍,靠的是大家不会同时都要)。
尺度感:51,219,741 个沙箱
论文给了一个总数:K3 的整个训练与评测过程中,共创建了 51,219,741 个沙箱,跨 1,505,678 个镜像。五千一百多万个是什么概念?
假设一个人手动开一台虚拟机、等它起来要 10 秒,不吃不喝不睡地连续开,需要 51,219,741 × 10 秒 ≈ 5.12 亿秒 ≈ 16 年。而这一切发生在一个模型的训练与评测周期内。另一个角度:51,219,741 ÷ 1,505,678 ≈ 34,也就是平均每个镜像只被用来开了 34 个沙箱——镜像种类多到惊人、复用度并不高,这就解释了为什么「亚秒级启动」必须靠存储层共享和 P2P 传输,而不能靠「把常用镜像缓存在本地」。(这个除法是本书为了给读者尺度感而算的,论文只给了两个总数,没有讨论平均复用度。)
这些数字要怎么读
论文原文用的是「as low as 133 ms and 49 ms」和「as much as 98%」,也就是最好情况和上限,不是平均值;6.5× 说的是「in real workloads」,但没有说是哪种工作负载、怎么测的、内存压力多大。这些都是作者自述的工程数字,论文没有提供可复现的测量条件,所以它们适合用来理解「设计想达到什么效果」,不适合当作性能承诺。
论文说智能体等待模型推理的时间可占沙箱生命周期的高达 98%,而被暂停的沙箱不消耗内存或 CPU。假设一个沙箱总共活 200 秒,其中 196 秒在等推理。
(a) 不暂停时它占用内存的时间是多少?暂停后呢?
(b) 只看「内存占用时间」,同样的内存理论上最多能多跑多少倍的沙箱?
(c) 这个倍数和论文里说的 6.5× 内存超额分配比是同一回事吗?
变式:如果某类任务里智能体只有 50% 的时间在等推理(比如它自己要跑很久的编译),(b) 的倍数变成多少?由此说说「暂停」这个优化对什么样的任务最有价值。
11.7 §5.4 换场:同样的难题,从生产侧再来一遍
训练那半章讲完了。论文这里有一句很有意思的话:服务 K3 从生产侧暴露出同样的挑战。也就是说,把模型拿去给真实用户用,遇到的不是全新的问题,而是同一批问题换了个身份出现。具体是三条:
- 混合 KDA–MLA 架构在百万 token 上下文下要维护两种根本不同的缓存,而且必须联合管理;
- 它的新模块和高度稀疏的专家需要各自定制的内核;
- 生产流量混杂着每请求成本跨越三个数量级的请求。
论文用三个层级分别对付它们:
| 层级 | 做什么 | 目标 |
|---|---|---|
| 引擎层 engine level | KDA 感知的前缀缓存:把固定大小的循环状态打包进与 MLA KV 缓存同一个分页池 | 让长前缀能跨请求复用 |
| 设备层 device level | 为 KDA 解码、Block AttnRes、稀疏潜 MoE 定制内核 | 最小化逐 token 延迟和内存流量 |
| 舰队层 fleet level | 缓存感知的亲和调度、基于预算的准入控制 | 把前两层的效率转化成可预测的服务 |
把下面三件事各自归到正确的层级,并说出理由:
(1) 把 KDA 的循环状态和 MLA 的 KV 缓存塞进同一个分页池;
(2) 给一波百万 token 的长请求单独划一份资源预算;
(3) 用 WarpDecode 式的 token 中心内核做 MoE 解码。
变式:「缓存感知的亲和调度」属于哪一层?它和引擎层的前缀缓存是什么关系——是替代、还是互相依赖?
11.8 统一缓存布局:把两种完全不同的东西塞进同一个池子
先说清楚为什么这件事难。论文的原话是:混合架构让前缀缓存变复杂,因为 KDA 循环状态和 MLA KV 缓存在大小和生命周期上根本不同,而一个缓存的前缀只有在两者能在同一个边界上一起恢复时才可复用。
把这两句话拆开看。每个 K3 块(block)由三个 KDA 层和一个 Gated MLA 层组成,两者的缓存长得完全不一样:
| MLA KV 缓存 | KDA 循环状态 | |
|---|---|---|
| 大小 | 随序列长度增长 | 固定 |
| 组织方式 | 按 token 分页,每个 token 一条 | 每个请求只有一份 |
| 直觉 | 一本越写越厚的流水账 | 一块反复擦写的白板 |
「一起恢复」这个要求就是从这里来的:MLA 那本流水账你可以翻到任意一页接着看,但 KDA 那块白板上只有最后一次擦写后的内容——你要想从第 2560 个 token 接着算,就必须在当初经过 2560 的时候专门给白板拍过一张照。两边必须在同一个位置都有可用的东西,这次复用才成立。
为什么不分开管
最自然的想法是给两者各配一个管理器。论文否掉了:那会让分配、引用、传输的逻辑重复三份(这里的「三份」对应 K3 每块有三个 KDA 层的结构)。于是他们把 KDA 状态打包进与 MLA KV 相同的分页块池,并把页统一成相同的字节大小,这样两种页类型就共享同一套分配、引用管理、驱逐的实现。
页的内部还有一个讲究:所有头(head)的状态按头连续存放,于是每个头的字节流是自包含的,可以成为跨节点传输的最小单位。这个设计有一个直接的好处:在预填充/解码分离(prefill/decode disaggregation,把「读完提示词」和「逐字生成」放到不同机器上)的部署下,当预填充节点和解码节点采用不同的张量并行度(TP,即一层被切成几份放在几张卡上)时,重新布局在传输路径上完成,GPU 侧零重排——数据反正要过网络,那就在过网络的路上顺手换个排法,GPU 一点力都不用出。
让 bug 显形:一个零开销的健全性检查
接下来是本节最值得单独讲的一句话。论文说,这种不对称在开发中被证明有用:任何类型混淆的访问产生的是垃圾数据,而不是看似合理的数据——这是对池化布局的一个零开销的健全性检查(zero-overhead sanity check)。
意思是:两类页虽然被统一成了相同的字节大小、住在同一个池子里,但内部的组织方式并不相同。所以如果代码写错了,把一个 KDA 状态页当成 MLA 的 KV 页去读,读出来的不是「有点不对的数值」,而是彻底的乱码,模型立刻输出垃圾,工程师当场就发现了。
为什么「立刻崩」比「悄悄错」值钱
系统里最贵的 bug 不是让程序崩溃的那种,而是静默的那种:结果还在正常范围内,指标只掉了一点点,可能几周后才有人察觉,然后要花更久去定位到「原来是缓存读串了」。让错误一发生就产生明显异常,等于把这类 bug 从「几周」压缩到「几分钟」。而且这个检查不花任何运行时开销——它不是加了一条断言语句,而是布局本身就导致了这个后果。
论文没说清的地方
论文用的是「这种不对称」(this asymmetry),但没有明确交代指的是哪一处不对称。我们的理解是:两类页虽然字节大小相同,内部组织方式却不同,所以互相误读会得到乱码。这是根据上下文的推断,原文并未点明。
(a) 解释为什么「类型混淆时产生垃圾数据」比「产生看似合理的数据」是一件好事,并举一个日常生活里同样思路的设计。
(b) 反过来想:如果两类页不但字节大小相同,内部布局也恰好兼容(互相误读也能得到数值范围正常的结果),会带来什么风险?
(c) 这个检查为什么能叫「零开销」?它和在代码里加一句断言有什么本质区别?
变式:这个思路能不能反过来用?也就是故意把两类数据的布局设计得互不兼容,即使统一布局在别处更方便。这样做的代价是什么?在什么情况下这个代价是划算的?
11.9 粒度之争:老办法为什么失效,以及怎么把两种粒度拆开
这是全章最难的一节,请慢一点读。我们要回答一个问题:一次前缀命中,到底能命中到哪个位置?
老办法:块哈希前缀缓存
业界通行的做法叫基于块哈希的前缀缓存(block-hash-based prefix caching),它以一个物理块为粒度复用 KV 缓存。规则只有一条:只有完整的块才被哈希,所以只有块对齐的前缀才可复用。
哈希(hash):把一大段内容压成一个短短的「指纹」,内容完全一样就得到一样的指纹,内容差一个字指纹就完全不同。前缀缓存靠它来快速判断「这段开头我是不是见过」。链式哈希(chained hash)则是让每一块的指纹里把前面所有块的指纹也算进去——于是「对上了第 5 块的指纹」这一件事,就同时证明了前 5 块全都一样,不用逐块比对。
这个耦合在 K3 上断了
论文把失效过程拆成了一条清晰的因果链,我们一步步跟:
自己推一遍:为什么块大小被逼到了 1024–6144
块哈希匹配要求所有层共享同一个块大小。而一次前缀命中,只有在命中边界处的 KDA 状态已经被持久化时才可复用。那么,KDA 的状态能存得多频繁?
想好了再看
不能太频繁。因为一个 KDA 层为每条序列维护的是单一的、很大的循环状态,而不是逐 token 的条目(回忆 11.8 的白板类比)。给白板拍照,每拍一张就是一整块大数据;而给流水账加一行,成本只有一行。所以论文说:状态快照只在稀疏边界上才负担得起。
既然 KDA 只能在稀疏的边界上存状态,而所有层又必须共享同一个块大小,那么这个共享的块大小会被推向哪个方向?
想好了再看
被推向大。论文给的结果是共享块大小被迫拉到 1024–6144 token。注意「被迫」二字:这不是为 MLA 选的最优值,而是被 KDA 的存储成本绑架的结果。
块大小变大了,为什么哈希的粒度也跟着变粗?这两件事本来是一回事吗?
想好了再看
本来不是一回事,但在老设计里被绑在了一起:由于哈希与存储块绑定,哈希粒度也一样粗。论文特意补了一句——尽管 MLA 的逐 token 条目本可以容忍细得多的块。也就是说,MLA 这一侧完全是被牵连的无辜者。这句话就是后面「解耦」方案的种子。
在 1024–6144 这么粗的粒度下,前缀缓存还能剩下多少用处?请自己举出两种「明明内容重复却一次都命不中」的情形。
想好了再看
论文的结论很重:在这么粗的粒度下缓存几乎没用。两种情形是:其一,短于一个块的请求永远无法被复用——凑不满一整块,就永远导不出可缓存的前缀;其二,分块预填充在跨越一个完整块边界之前导不出任何可缓存的前缀(分块预填充指把一个长输入切成小批喂进去,好和别的请求交错调度)。
解法:把「分配的粒度」和「匹配的粒度」拆开
既然这两件事本来就不是一回事,那就别再绑在一起。论文的解法是解耦两种粒度:
- 前缀哈希跑在 MLA 页内部的细哈希块(hash block)上,例如 512 token;
- 物理块仍然是粗的分配单位(该多大还多大);
- 对 KDA 则反过来对齐:循环状态的检查点只保存在 MLA 哈希端点的一个稀疏子集上——因为那是查找唯一可能引用到的位置。
第三条为什么是关键
KDA 的检查点很贵,不能到处存。那存在哪里最划算?答案是:只存在将来有可能被查到的位置上。而查找只会问哈希端点(因为命中边界必然是哈希块的整数倍),所以在非哈希端点上存检查点是百分之百的浪费。这一步把「存哪里」这个开放问题变成了「在一个已知的候选集合里挑子集」,问题一下子小了很多。
预填充期间发生了什么
MLA 这一侧:一个部分填满的 MLA 页,会以它最后一个完整哈希块的链式哈希注册进前缀缓存索引。由于每个哈希覆盖它之前的所有哈希块,匹配到一个端点就证明了直到该端点的整个前缀。而注册的端点随页填满而推进——这正是对老办法那两个失效情形的正面回答:现在不用等整个物理块填满,每填满一个 512 的小块就能对外发布一次。
KDA 这一侧:每次前向传播之后,KDA 内核在处理过的最后一个哈希对齐位置持久化循环状态。但检查点很大,所以随着请求推进,中间检查点被取代并回收,而处在对话轮边界上的那些被保留下来供跨请求复用(对话轮边界是最可能被下一个请求接着用的位置——你说一句、模型答一句,下一轮从这里接着长)。
还有一条必须记住的规矩:缓存的检查点是只读快照。命中时的动作是把它拷贝进请求的私有运行状态,然后才做下一次前向;新的检查点写到新槽位。因此,一个对其他请求可见的检查点永远不会被就地修改。为什么这条至关重要,我们在 11.9 的第三题里推。
查找分两阶段
第一阶段(MLA):按链式哈希匹配完整的物理块;在第一个缺失的块处,回退到该块内部的哈希端点——所以部分填满的页仍然可以命中。
第二阶段(KDA):要求候选边界在每一个 KDA 缓存组里都有检查点,而每个组维护一份独立的循环状态。
最终的命中,是同时满足两个阶段的最长边界。写成一个式子:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| B | 命中边界,即这次可以从第几个 token 接着算 | 「前面这么多我不用重算了」的那条线 |
| H | 哈希块大小,论文举的例子是 512 token | 对齐的最小刻度,尺子上的最小格 |
| M | 本请求与缓存中的前缀逐 token 相同的最长长度(Figure 12 里是 2800) | 「内容上」最多能省到哪 |
| k | 整数,表示命中了几个哈希块 | 数格子数了几格 |
| KDA 缓存组 | 各自维护一份独立循环状态的一组 KDA 缓存,命中要求每一组都有检查点 | 几块白板必须都拍过同一时刻的照,缺一块就不算 |
论文特别强调了 B 的两条性质:它总是哈希块的整数倍,但从不要求是物理块的整数倍。这正是解耦换来的东西。
Figure 12 那个例子
论文的 Figure 12 画的是一个物理块内部的情况:一个 6144 token 的物理块含 12 个 512 token 的哈希块(6144 = 12 × 512)。图下方的记号表示每个哈希边界上 KDA 检查点的状态:空心圈是没有存检查点的边界,实心灰点是已持久化的检查点,橙色点标出被命中的那个。论文说明:持久化的检查点是稀疏的,而且典型地与对话轮边界重合。
例子本身:一个请求的前 2800 个 token 与缓存的前缀相同,命中发生在 B = 2560 = 5 × 512,深深地落在这个 6144 token 的物理块内部;然后从 token B 恢复预填充,而不是重算 [0, B)。图里还标注了完整的动作序列:在 B 处恢复 KDA 检查点;对那个部分填满的 MLA 块做写时复制;从 token B 恢复预填充,[0, B) 零重算。这次请求总共复用了 5 个 MLA 哈希块和 B 处的那个 KDA 检查点。
下面这个交互演示复现了 Figure 12。你可以拖动匹配长度 M、改变哪些边界上有 KDA 检查点,看看命中边界 B 怎么跟着变——特别要试一试「把 2560 处的检查点去掉」会发生什么。
按 Figure 12 的设置:物理块 6144 token,哈希块 512 token,某请求的前 2800 个 token 与缓存中的前缀逐 token 相同。
(a) 2800 落在第几个哈希块的内部?
(b) 若每个哈希端点上都有 KDA 检查点,命中边界 B 是多少?这与论文图里的 B = 2560 一致吗?
(c) 若 KDA 检查点只存在于 1024 和 2048 处(2560 处没有),B 会变成多少?
(d) 在 (c) 的情况下,比起 (b) 多算了多少个 token?
变式:如果哈希块改成 1024 token(物理块仍是 6144,且每个哈希端点都有检查点),B 是多少?和 512 的方案比,多重算了多少 token?由此说说「哈希块越小越好吗」。
解耦之后,论文说前缀缓存「达到了与全注意力模型同等的通用性」。请构造两个场景:内容上明明有大段完全重复的前缀,但新设计下仍然命不中或几乎命不中。指出各自卡在哪个条件上,并说明这是不是设计缺陷。
场景二(检查点不在那儿):一个请求在句子中间与另一个请求分岔(比如两个用户贴了同一份 40 万 token 的代码库,但接下来问的问题不同,而分岔点不在任何对话轮边界上)。MLA 侧完全可以匹配到分岔点附近的哈希端点,但论文说检查点随请求推进被取代并回收,只有对话轮边界上的被保留——如果那个位置的检查点已经被回收,KDA 阶段就通不过,B 只能退到上一个仍然存在的检查点。这是一个真实的、由存储成本决定的限制,也正是我们在 11.10 的答辩框里要质疑「同等通用性」这个说法的原因。
还有一个隐含条件值得一提:命中要求每一个 KDA 缓存组都有检查点,所以只要有一组的检查点被驱逐,整个边界就作废——这是下一节的机制三。
变式:如果把「只在对话轮边界保留检查点」改成「每隔 16 个哈希端点强制保留一个」,场景二会改善吗?代价是什么?(提示:算一下一个 1M 上下文里会有多少个检查点,而每个检查点有多大。)
把三件事串起来回答:为什么「检查点是只读快照、命中时拷进请求的私有运行状态、新检查点写到新槽位」这三条规定,是让一个前缀缓存能被多个请求同时共享的必要条件?如果去掉「只读」这一条,允许命中的请求直接在共享检查点上就地往下算,会发生什么?请写成一个两请求的时间线。最后回答:MLA 的 KV 缓存为什么不需要这么强的规定?
去掉只读后的时间线:① A 命中 C,就地往下算 100 个 token,C 的内容变成了 [0, 2660) 的状态;② 但缓存索引里 C 仍然标着 [0, 2560);③ B 查到 C,以为拿到的是 [0, 2560) 的状态,实际拿到的是 A 走出去之后的状态;④ B 从 token 2560 接着算,可它的初始状态里已经混进了 A 的 100 个 token——输出静默错误,不崩溃,只是答案悄悄变错(又一次印证了 11.8 那个「最贵的 bug 是静默的」)。⑤ 而且 A 自己也不安全:如果 C 随后被驱逐或被别人拷贝,A 的状态来源就没了。
三条规定的分工:「只读」保证事实不被篡改;「拷进私有运行状态」把共享的只读事实与私有的可变进度分开;「新检查点写到新槽位」保证发布新事实时不会覆盖旧事实(否则第③步的问题会以另一种形式回来)。
MLA 为什么不用:因为 MLA 是逐 token 的条目,写完就不再改动——新 token 写到新位置,老位置的内容天然是不可变的;所以靠引用计数加写时复制就够了(Figure 12 里对那个部分填满的 MLA 块做的正是写时复制)。就地更新才是需要「快照 + 拷贝」的根本原因,而这正是 KDA 这类循环状态与注意力 KV 的本质差别。
变式:如果一次只允许一个请求命中某个检查点(命中即独占,别人排队),也能避免上面的错误。这个方案哪里不好?请从 11.12 节「400K 前缀」的场景出发说明后果。
11.10 并发调度下的一致性:三个机制,三个具体的坑
上一节讲的是「一次查找该怎么算」。可真实系统里有几十上百个请求在同时跑、共用一个缓存池,于是出现了一个很别扭的局面。论文把它讲得很精准:
别扭在哪
一个命中的块,同时具有两个身份:它既是共享的缓存条目(别的请求可能正指着它),又是某个私有请求的增长点(这个请求要从这里往后接着写)。而且,所有 KDA 缓存组必须在每一个命中边界上达成一致。论文明确说,接下来的三个设计点每一个都由一个具体的失效模式决定——不是「顺手加的保险」,是「不加就一定会出事」。
机制一:先钉住,再分配
失效模式:所有缓存组从同一个共享空闲链表取块,所以为某一组分配私有拷贝,可能驱逐另一组刚刚命中的块。你正准备用第 1 组的命中结果,结果给第 2 组找地方的时候,把第 1 组刚命中的块给挤掉了——自己打自己。
机制:每个命中块在任何分配发生之前,先跨所有组钉住(pin)。先把所有要用的东西按住,再去申请新东西。
机制二:拷贝还没落地的块,不许参与匹配
失效模式:往私有块的拷贝是在前向传播之前、立刻在 GPU 上执行的。这意味着从「块被分配或注册」到「字节真的被拷进去」之间有一段时间差。在这段时间里,在当前调度步内被分配或注册的块,仍会把上一个持有者的字节交给读者——索引说这是你要的前缀,读到的却是完全无关的另一段内容。
机制:这类块在其拷贝落地之前被排除在匹配之外。
机制三:驱逐要原子,兄弟一起死
失效模式:一个检查点只有在每个 KDA 组里都存在时才能恢复请求。如果只驱逐了其中一组的检查点,索引上看起来这个边界还「可用」,可真去恢复的时候会发现某一组缺状态。
机制:驱逐某一组的检查点会原子地作废它的兄弟——一个检查点要么在每个组里都可命中,要么在任何组里都不可命中。不允许存在「半个检查点」这种中间状态。
打个比方
三个机制像是图书馆的三条规矩:一,你要借的书先全部拿在手里,再去申请新书架(否则管理员会把你放在桌上的书收走);二,还没上架完的书不算在馆藏目录里(否则别人按目录去拿,拿到的是上一位读者留下的旧书);三,一套三卷本的书要么三卷都在,要么三卷一起下架(否则有人借到第一卷,翻到第二卷才发现没有)。
类比失效处:图书馆里拿错书你当场就知道,而缓存拿错字节是静默的——模型照样输出,只是输出得不对。这也是为什么这三条必须由系统强制,而不能靠「小心一点」。
论文对这三条的总评是:有了这些机制,每个注册的状态都精确对应它所声明的 token 前缀,于是混合 KDA–MLA 模型的前缀缓存达到了与全注意力模型同等的通用性:任何共享前缀都可以在任意 512 token 边界上复用,与请求长度、分块方式、调度交错无关。
(a) 假设去掉机制二(不再把「拷贝尚未落地的块」排除在匹配之外),请写出一条具体的时间线,说明错误是怎么产生的,以及为什么它不会报错。
(b) 假设去掉机制三(驱逐不再原子),最坏会发生什么?
(c) 这三个机制里,哪一个在「系统里只有一个请求、完全没有并发」的场景下可以安全去掉?为什么另外两个不行?
(b) 最坏情况是一次命中在恢复阶段才发现某个 KDA 组缺状态:轻则要在中途放弃命中、退回去重算(浪费掉已经做的工作,还打乱了调度),重则如果实现里没有检查、直接拿了一份陈旧或属于别的前缀的状态去恢复,同样产生静默的错误输出。机制三把这个风险从「运行中才发现」提前到「根本不会被选中」。
(c) 可以去掉的是机制一:它防的是「给某一组分配时挤掉另一组刚命中的块」,这需要多个缓存组在同一个空闲链表上竞争——但注意,即使只有一个请求,K3 里也有多个 KDA 缓存组,所以严格来说只有在「单请求且只有一个缓存组」时才安全。机制二不行:单请求也存在「分配后、拷贝落地前」的时间窗,只是此时没有第二个读者去踩它,风险从「一定出错」降为「取决于实现细节」。机制三更不行:它防的是同一请求内部跨组的不一致,与并发无关。这一小题的意思是:并发只是放大器,三个机制里真正与并发强相关的只有第一个。
变式:如果把机制二改成「不排除这类块,但在读之前插一次同步,等拷贝完成」,正确性能保住吗?性能上会付出什么?(提示:想想这次同步发生在调度步的哪个位置,会阻塞谁。)
答辩:如果我是审稿人
你在小节结尾说,混合 KDA–MLA 模型的前缀缓存「达到了与全注意力模型同等的通用性:任何共享前缀都可以在任意 512 token 边界上复用」。可你自己前面刚写过,KDA 检查点很大、只在稀疏边界上保存、中间的会被回收、留下来的典型地落在对话轮边界。那绝大多数 512 边界上根本没有检查点,按你自己的式子,那里的命中不可能发生。这句「同等的通用性」是不是把话说满了?
参考防守(先自己组织语言再看)
防守的关键是把机制上的通用性和实际的命中率分开,并且承认原文措辞确实容易被误读。
第一,这句话真正主张的是「约束被解除了」。解耦之前,命中边界被物理块大小绑架:只能落在 1024–6144 的整块边界上,短于一个块的请求永远命不中,分块预填充在跨过整块边界前什么都导不出。解耦之后,这三条结构性限制全部消失,命中边界与请求长度、分块方式、调度交错解除了耦合——这就是「与全注意力模型同等」的确切所指:全注意力模型的前缀缓存也从来不保证任意位置都命中,它保证的是「命中与否只取决于内容是否相同,而不取决于你怎么切、怎么调度」。从这个角度,论文的主张是成立的。
第二,承认第二个条件仍然存在。一次具体查询能否命中,还要求该边界在每个 KDA 缓存组里都有检查点,而检查点确实是稀疏的。所以更严谨的表述应该是「命中粒度不再受物理块限制」,而不是「任意 512 边界都能复用」。原文这一句把「机制上允许」和「实际会命中」揉在了一起,属于表述偏乐观。
第三,指出真正的举证缺口。论文全文没有给出前缀缓存命中率的任何数字,也没有给出解耦前后的对比。因此读者无法判断这套设计在真实流量下究竟把命中率提高了多少——而这本该是最容易测、也最有说服力的一个指标。这是比措辞更值得追问的地方。
11.11 高性能内核:三个模块,三种不同的省法
内核(kernel):这里不是操作系统内核,而是指跑在 GPU 上的一小段计算程序。同一个数学运算,写法不同,速度可以差好几倍——差别主要在怎么安排数据的搬运,而不是算术本身。K3 引入了三个新模块,论文为每一个都定制了内核。
KDA 解码:不存状态,存「重放的原料」
论文先点出差别:与 KDA 预填充相比,解码的主要瓶颈从「挖掘并行性」转移到「高效管理不断演化的循环状态」,该状态在每个解码步被就地更新。
麻烦出在推测解码上。回忆一下推测解码的玩法:先用便宜的方式一口气猜出好几个 token(草稿),再让主模型一次性验证它们对不对——猜对了就白赚好几个 token 的速度。K3 用的是基于 MTP(多 token 预测)的推测解码。
问题是:如果验证拒绝了草稿 token 的一个子集,状态已经推进到最后一个被接受的 token 之外,无法轻易回滚。白板已经被擦改过了,可你现在发现后面几笔画错了,却擦不回去。
直接的解法是为每个草稿位置维护一份状态快照——这确实能支持回滚,但会让状态流量成倍增加,而论文指出,这个代价在在线服务典型的大批量下会占主导(批量越大,同时要读写的状态份数越多,而解码本来就是被内存带宽卡住的)。
关键观察来了:任何被接受的草稿前缀之后的状态,完全由草稿 token 的投影输入决定,而这些投影输入比状态本身小得多。既然如此,那就只缓存这些投影输入,然后在片上重建被接受 token 的状态,只写回已验证 token 和 bonus token 的状态。论文注明:这个设计在并行的工作 ReplaySSM 中被独立提出。
bonus token 是什么
推测解码里,主模型在验证草稿的那一次前向中,其实还顺带算出了「最后一个被接受的 token 之后的下一个 token」——这个白赚的就叫 bonus token。论文用了这个词但没有定义,这里的解释是该领域的通行含义。
实现上,被重放的 token、bonus token 和下一个草稿窗口共享单个融合内核里的一个循环,这个内核覆盖短卷积、输入归一化、门控、KDA 递推和输出归一化(把原本五个步骤合成一个,中间结果不落回显存)。论文自述的效果是:验证延迟随被验证 token 数次线性增长,且保持低于状态缓存基线。
最后一句非常重要:因为投影缓存从不离开解码阶段,前缀缓存和预填充–解码分离在与非推测服务相同的载荷上运作。翻译成人话——推测解码带来的这些额外数据只在解码内部打转,不会溢出去变成「新的一类缓存」,所以 11.9、11.10 讲的那一整套前缀缓存机制,以及预填充节点与解码节点之间传输的内容,完全不用为推测解码改动。
Block AttnRes:两阶段调度,两个阶段都在省内存
Block AttnRes 遵循两阶段调度:一个批量的块间(inter-block)pass 每块读一次缓存的块表示,之后每一层通过 online-softmax 归并折入块内(intra-block)部分和。论文点明:内存访问占这些内核在预填充和解码时相当大一部分成本,所以两个阶段的优化都主要围绕内存效率。
预填充时的问题是:在每个张量并行 rank 上都实体化块表示会造成大量冗余内存消耗(同一份东西在 8 张卡上各存一遍)。解法是对激活采用序列并行(Sequence Parallelism, SP):把 TP 的 all-reduce 分解成 reduce-scatter 和 all-gather,把块内内核插在这两个集合通信之间,作用在按序列分片的隐藏状态上,使得每个 token 的块表示恰好在一个 rank 上实体化。效果是消除了额外内存消耗,也降低了预填充时的 I/O 开销。
打个比方
八个人合作抄一本书。原来的做法是每人都抄一整本(all-reduce 之后人手一份完整数据),然后各自在自己那本上做笔记——八份笔记完全一样,浪费了八倍的纸。序列并行的做法是把书拆成八段,每人只拿一段、只在自己那段上做笔记,最后再把笔记拼起来。关键是把「做笔记」这个动作塞进了「拆开」和「拼回」之间。
类比失效处:真实的 reduce-scatter 不只是「拆开」,它同时完成了求和;这个类比只解释了内存为什么省,没解释通信量为什么不增加。
解码时用的是另外两招。第一,把块间内核发到一个旁路 stream(side stream,GPU 上的一条并行执行队列)上,让它与主 stream 上的独立计算重叠——也就是把这部分时间藏起来。第二,块内内核通过融合来精简:AttnRes 输出与其部分和更新的归并、连同随后的 RMSNorm,一起融进前面的 TP all-reduce,从而消除了块内阶段的一个专用内核。论文总结:这些优化一起隐藏了块间 pass 的延迟并降低了块内阶段的内存流量。
Stable LatentMoE:专家变多之后,调度本身成了瓶颈
Stable LatentMoE 同时增加了专家总数和每 token 激活的专家数。论文指出后果:专家空间和每 token 专家数的双重增长抬高了调度与协调开销,让常规 MoE 内核难以维持高硬件利用率。注意这里被卡住的不是乘法本身,而是「决定谁去算什么、把数据凑到一起」这些组织工作。
潜 GEMM(latent GEMM,GEMM 就是通用矩阵乘法)这一侧用了三个优化:
- 把潜下投影与 MoE 路由器融合成单个 GEMM——两次矩阵乘变一次,少读一遍输入。
- 把潜权重矩阵分片到各 rank,并用 multimem store 指令把输出的 all-gather 融进 GEMM 的 epilogue(epilogue 指矩阵乘算完、结果还在片上时顺手做的收尾动作)——通信不再是「算完之后另起一步」,而是结果落地的同时就发出去了。
- 把由此产生的通信与其他算子(论文举例:共享专家的计算)重叠。
论文的总结是:这些优化合起来消除了冗余的权重流量和重复计算,同时把通信延迟藏在计算背后。
路由专家这一侧的情况完全不同。论文的观察是:在小批量下,group GEMM 退化成权重矩阵的内存受限流式读取——你只有几个 token 要算,却必须把整个专家的权重矩阵从显存里读一遍,时间全花在读上,乘法器基本闲着。而常规的 tile 中心内核因其面向计算的设计和预处理开销而不适合这个区制。
于是他们把 MoE 解码内核建立在 WarpDecode 的 token 中心设计之上:每个 warp 负责一个输出神经元,直接从内存流式读入相关权重(warp 是 GPU 上一组同步执行的线程)。为进一步提高并行度,把每个 warp 再细分成更细粒度的 lane team,每个 team 处理一个不相交的专家子集,之后做一次 warp 范围的部分结果归约。此外,权重布局在一次性的预处理中离线置换,大幅降低运行时的反量化开销——把只需做一次的整理工作从每次推理里挪走。
整节都缺同一样东西
5.4.2 这一整节没有给出任何加速比数字:KDA 那段只说「次线性增长」「保持低于状态缓存基线」,Block AttnRes 和 LatentMoE 连定性的比较对象都没有。所以我们只能理解这些优化想解决什么问题,无法判断解决得有多好,也无法与其他系统比较。这在工程报告里是常见的取舍,但读者应当心里有数。
KDA 解码那一段的核心权衡是:为了让推测解码能回滚,要么给每个草稿位置存一份状态快照,要么只缓存投影输入然后在片上重放。
(a) 为什么「每个草稿位置存快照」的代价在大批量下会占主导?请写出两种方案每步搬运数据量的形式(不用真实数字)。
(b) 为什么「只缓存投影输入」在功能上等价?请说清等价所依赖的那条性质。
(c) 把「因为投影缓存从不离开解码阶段,前缀缓存和预填充–解码分离在与非推测服务相同的载荷上运作」这句话翻译成人话,并说明它为什么重要——它和 11.9、11.10 讲的东西有什么关系?
(b) 依赖的性质是循环状态的递推结构:状态是从上一个已知状态出发、按 token 依次推出来的,每一步递推需要的输入就是那个 token 的投影输入。所以只要保留了投影输入,就能在片上把被接受的那几个 token 的状态重新推一遍,得到与存快照逐位相同的结果,然后只把已验证 token 和 bonus token 的状态写回显存。用计算换内存,而计算恰好是此刻闲着的资源。
(c) 人话是:这些投影缓存只在解码阶段内部存在,不会变成一类需要被前缀缓存管理、或需要在预填充节点与解码节点之间传输的新数据;所以前缀缓存的数据结构、以及两类节点之间传输的内容,与不开推测解码时一模一样。重要之处在于避免了复杂度的传染:否则 11.9 的统一分页布局、11.10 的三个一致性机制,都得再为推测解码单独设计一遍并重新论证正确性。一个好的优化不仅自己要快,还要不给邻居添麻烦。
变式:如果草稿的接受率极低(几乎每次都被拒绝),两种方案的开销对比会怎么变?再想:接受率高到 100%(全部接受)时呢?由此说明这个设计在什么区间里收益最大。
11.12 舰队级调度:从「快」到「靠得住」
论文开门见山:超出单个服务实例之后,挑战从「每请求效率」转向「可预测性」。原因是两句话:一次前缀缓存未命中的代价比命中高好几个数量级;一波百万 token 的请求可以把短请求饿死。这两句话各对应一个策略。
策略一:缓存感知的亲和调度
先把账算清楚。论文给的典型数字是:在 1M 上下文下,一个典型的编码输入携带 400K token 的前缀,但只需要约 4K token 的预填充增量。也就是说,你的 IDE 把整个代码库塞了进去(40 万 token),而这一轮你其实只新加了几百行改动和一句提问(4 千 token)。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| P | 已经被缓存住的前缀长度,论文的例子是 400K token | 那份没变过的代码库 |
| Δ | 本轮新增、必须预填的增量,论文的例子是约 4K token | 你这次改的几行加一句问题 |
| P + Δ | 未命中时要预填的总量:前缀也得从头再算一遍 | 缓存丢了,整个代码库重读一遍 |
| ≈ 101 | 两者之比,约两个数量级 | 同一件事,可能贵 100 倍 |
101 倍是什么概念?如果命中时你等 0.5 秒出第一个字,未命中就要等将近一分钟。这就是论文说「前缀缓存命中避免了重新预填充整个前缀,比未命中便宜好几个数量级」的分量。
这个估计其实偏保守
式 11-2 只是按 token 数线性估的。真实的预填充里注意力部分的代价随长度增长得更快,所以实际差距只会比 101 倍更大。不过论文并没有展开这一点,只说了「好几个数量级」,我们也不去替它编造更精确的数字。
于是策略很自然:把每个请求路由到持有其前缀缓存的那个集群。为什么不能把缓存搬过去?因为把缓存搬到另一个集群需要经由跨集群链路传输,而它比集群内的 fabric 慢得多——搬 400K token 的 KV 缓存过去,可能比重算还慢。
但这带来一个新的脆弱点:这种缓存亲和把每个会话绑定到单个集群,该集群的故障会中断绑定其上的所有会话。
论文的解法是用一致性哈希把每个会话钉到两个集群:一个主集群服务其流量,一个预先指派的次集群在主集群故障时接管。诚实的代价也写清楚了:次集群不持有该会话的任何前缀缓存,故障切换时必须重新预填充——按式 11-2,那就是从 4K 变成 404K。
那为什么还是划算?由于一致性哈希把各会话的次集群指派均匀分布到整个舰队,这份重新预填充的工作被分摊到许多集群,而不是集中在一个集群上。结论是:常见情况下缓存局部性被保留,而任何单个集群故障的影响保持有界。
策略二:基于预算的准入控制
先看流量长什么样:生产流量混杂着 2K token 以下的短请求和高达 1M token 的超长请求,所以每请求成本大约跨越三个数量级。1M ÷ 2K = 500,介于 10² 和 10³ 之间,论文说「大约三个数量级」是量级上的粗略说法。
后果是:任何固定请求数所施加的总负载都高度不可预测——「现在有 100 个请求在跑」这句话完全无法告诉你系统有多忙。论文因此下了一个很重的判断:基于「平均请求」的容量规划、排队模型和限流配额,在这种方差下全部失效。
为什么「平均」在这里没有意义
如果一群请求的成本在 2K 到 1M 之间,那么「平均请求」这个东西根本不存在于现实中——没有哪个真实请求长得像那个平均值。你按平均值规划容量,来的却要么是 500 个小的、要么是 1 个巨大的,两种情况对系统的要求天差地别。这和 11.4 节「用一条水平线逼近一条上升曲线」是同一类错误的两个版本:用一个标量去概括一个方差极大的分布。
论文给出的典型失效模式非常具体:一波长上下文请求把可用算力吃满,之后到达的短请求无法被及时调度,让所有流量的 TTFT 都劣化。注意最后三个字——受害的不只是长请求,是所有人:一个只想问「今天几号」的用户,会因为别人在读一个 100 万 token 的文档而等上很久。
解法是基于预算的准入控制:给不同请求类别分配各自独立的资源预算,使得突发的长上下文流量最多消耗它自己那份容量,无法劣化其他类别所体验到的系统级 SLO。本质是隔离——把「公用一条大路」改成「分车道」。
论文没说的
请求「类别」到底按什么划分(按长度?按用户?按产品线?),预算按什么比例分配、是否动态调整,一致性哈希的次集群在什么条件下判定接管,这些论文全都没有交代。所以这一节读起来更像是设计原则的声明,而不是可复现的方案。
(a) 用论文给的 400K 前缀、4K 增量,算出命中与未命中要预填的 token 数之比。
(b) 论文说命中「比未命中便宜好几个数量级」,你算出来的比值属于几个数量级?两者说法一致吗?
(c) 用 2K 和 1M 这两个端点,验证「每请求成本大约跨越三个数量级」这句话。
(d) 如果某个应用的典型增量是 40K 而不是 4K(比如每轮贴一大段新日志),比值变成多少?这会怎样影响「值不值得为缓存亲和牺牲负载均衡」的判断?
变式:如果用户每轮都会修改代码库开头的某个文件(比如改 import),那么 400K 的前缀还能命中吗?命中长度大约是多少?这对「缓存亲和」的价值意味着什么?
缓存感知的亲和调度与「负载均衡」天然冲突。
(a) 说明一致性哈希的「主 + 次」设计在故障时具体解决了什么、又没有解决什么。
(b) 设想一个场景:某个大客户的所有会话恰好都被哈希到同一个主集群。这时会发生什么?「次集群均匀分布」能救吗?
(c) 把基于预算的准入控制接上来:它能否缓解 (b)?请说清它管得着什么、管不着什么。
(b) 正常情况下这些会话全部集中在那一个主集群上,它成为热点,可能自己就先过载了——而这是缓存亲和的固有代价:亲和度越高,越无法把负载搬到空闲的地方去。「次集群均匀分布」只在主集群真的挂了的那一刻起作用,平时对热点毫无帮助;更糟的是,如果热点导致主集群真的崩了,那一瞬间会有该客户的全部会话同时触发重新预填充(虽然分散到了多个次集群,但总量是实打实的一波尖峰)。
(c) 只能部分缓解。预算控制是在请求类别之间隔离资源,它保证长请求的突发不会把短请求的 TTFT 拖垮——所以在那个热点集群内部,它仍然能保住短请求的 SLO。但它管不了「某个集群整体过热」:预算划分的是一个实例/集群内部的容量,不会把请求送到别的集群去。要解决 (b) 需要的是第三种东西——打破亲和、允许迁移(并承担重新预填充的代价)。论文既没有讨论缓存亲和与负载均衡之间怎么取舍,也没有说预算是按什么维度划分类别的。
变式:如果把每个会话钉到三个集群(一主两次)而不是两个,好处和代价分别是什么?再想:如果反过来允许次集群后台预热(提前把前缀缓存复制一份过去),又会带来什么新问题?
本章小结
这一章从头到尾只在做一件事:在一个所有资源都不够用的系统里,反复回答「这块内存/带宽此刻应该归谁」。把六条主线串起来看,你会发现它们用的其实是同几种手法。
手法一:把生命周期拆开。写回设计把「前缀被保留」与「前缀驻留在 GPU」拆开(11.3);解耦两种粒度把「分配单位」与「匹配单位」拆开(11.9);梯度缓冲区复用把「这块内存属于谁」按时间段拆开(11.5)。三者形式不同,思路完全一致——两件被历史耦合在一起的事,其实可以各走各的。
手法二:只在必要的位置付出代价。KDA 检查点只存在「查找唯一可能引用到的位置」(11.9);写回只为「离开活跃解码路径」的前缀花 CPU 内存(11.3);沙箱增量检查点只保存「被弄脏的内存页」(11.6)。
手法三:存原料而不是存结果。推测解码里只缓存投影输入、在片上重放(11.11),用闲置的算力换紧张的带宽。
手法四:让错误无法藏身。统一池化布局让类型混淆产生垃圾而非合理数据(11.8);三个一致性机制各自堵死一个具体的静默失效(11.10);只读快照保证「注册的状态永远精确对应它声明的前缀」(11.9)。这一族手法的共同信念是:在大规模系统里,静默的错误比崩溃贵得多。
手法五:给方差留位置。自动限流用运行时信号代替一个固定并发数(11.4);预算准入控制承认「平均请求」不存在,改为按类别隔离(11.12)。两者都是在拒绝「用一个标量概括一个分布」。
要记住的关键数字:命中边界 B 是哈希块(512)的整数倍而非物理块(6144)的整数倍,Figure 12 的例子是 B = 2560;沙箱检查点/恢复低至 133 ms / 49 ms,等待推理可占生命周期 98%,内存超额分配比高达 6.5×,共创建 51,219,741 个沙箱、跨 1,505,678 个镜像;缓存命中与未命中之比约 101 倍,生产流量的每请求成本跨越约三个数量级。
整章的举证状况
本章覆盖的两节是全篇工程密度最高、但量化证据最少的部分。除了沙箱那几个数字和 400K/4K 这个例子,论文几乎没有给出任何加速比、命中率或前后对比:外部 KV 缓存池提升了多少命中率、自动限流带来多少吞吐、解耦粒度后前缀缓存命中率变化多少、三类内核各自快了多少倍,全部没有。所以本章的正确读法是「看这些设计想解决什么问题、用了什么思路」,而不是「相信它们达到了多好的效果」。这些都属于论文作者自述的工程实践。
Chapter 11 · Infrastructure II: RL at a million tokens of context, sandboxes, and online serving
In one sentence: this chapter answers a crude but deadly question — when the context runs to 1 million tokens, and the model has to do tasks by itself while being trained, and then be handed to several million users, there is nowhere near enough memory and bandwidth to go round, so how exactly should it be divided up?
After this chapter you should be able to
- Explain in your own words what a KV cache and a prefix cache are, and work out how much compute the prefix cache actually saves in one multi-turn conversation
- Say clearly where each of the two cache-offload strategies — “write-back” and “write-through” — wastes something, and construct a scenario in which write-back comes off worse
- Restate the trick of “stuffing the reference model's weights into the gradient buffer”, and point out which two implementation assumptions its safety rests on
- Work out the hit boundary B by hand under the set-up of Figure 12, and say why it is a whole multiple of the hash block but need not be a whole multiple of the physical block
- Match each of the three concurrency-consistency mechanisms to the specific failure mode it is there to prevent
- Use the two numbers 400K and 4K to work out the order of magnitude of what a cache hit buys, and point out where cache-affinity scheduling stops applying
11.1 Before you start: five things you have to understand first
Every design in this chapter is wrestling with the same thing: do not compute anything a second time, and give back the memory you are holding as soon as you can. To follow them you need five concepts underneath you first.
KV cache: every time the model reads in a token it computes a pair of vectors for it, called key and value; every new token it generates afterwards has to look back at that pair for all the tokens before it. The point is this — the key/value at a given position depends only on itself and on what comes before it, and once computed it never changes again. So you compute it once and store it, and that store is the KV cache. What if you do not store it? For every character generated, the several hundred thousand tokens in front of it all have to be recomputed from scratch; at long context that is plain suicide.
Prefix cache: since key/value depends only on “this token and what comes before it”, then as long as the opening stretch of two requests is identical token by token, the KV for that stretch is exactly the same. So the second request can take the stretch the first request already computed and use it directly. That is the whole reason a prefix cache saves money. By the time you are on turn 10 of a conversation with an AI, the content of the first 9 turns has been sent again unchanged; without a prefix cache those 9 turns would be computed from scratch every time.
Why the prefix cache matters especially in this chapter
An ordinary conversation has a prefix of a few thousand tokens; saving it or not makes little difference. But K3's target setting is a 1 million token context: one coding task may carry a 400,000-token codebase as its prefix. At that point the difference between a “hit” and a “miss” is not a bit faster or a bit slower, it is two orders of magnitude — in section 11.12 we will work the arithmetic out by hand.
Paging and the physical block: the KV cache is not one continuous piece of memory. Like an operating system managing memory, it is cut into fixed-size “blocks” to be allocated, reclaimed and shared. One block may hold the KV for 1024 or even 6144 tokens. This “block” is the source of every dispute in the second half of this chapter.
Rollout: in reinforcement learning, letting the model go and “play a round” by itself — think a step, call a tool, look at what comes back, think again, call again, and in the end produce one complete trajectory. That trajectory is scored, and the score is used to update the model's weights. A “multi-step rollout” is a round with many turns of tool calls, in which the context rolls on only growing, never shrinking. A “partial rollout” means pressing pause before the round is finished, saving the state, and carrying on in the next iteration — the aim is to stop one especially long trajectory from holding everyone else up (this is called keeping the tail latency down).
Sandbox: give the agent an isolated little computer of its own, in which it can type whatever commands it likes, install software, run code, crash the system, and none of it affects the outside. Without a sandbox you would not dare let the model actually execute the commands it writes itself.
TTFT and SLO: TTFT is the wait between you pressing enter and the first character appearing on the screen — the metric users feel most strongly. An SLO is the promise a service makes to itself, for example “95% of requests have a TTFT of no more than 2 seconds”. Every scheduling design in section 11.12 exists in the end to hold the SLO.
In the first turn the user types 3000 tokens and the model replies with 200 tokens. In the second turn the client sends those 3200 tokens along unchanged, plus a new question of 100 tokens.
(a) Without a prefix cache, how many tokens' worth of KV does this turn have to recompute?
(b) And with one?
(c) If this time the user puts the sentence “please answer in Chinese” (take it to be 6 tokens) at the very front of the whole conversation, what does the answer to (b) become?
Variant: if that sentence “please answer in Chinese” were added at the end of the second turn's question instead of the front, how long is the prefix that can be reused? Why does the position make so much difference?
11.2 The starting point of §5.3: why “resource efficiency” is put first
The paper opens this section very plainly: to do agentic RL on a model as large as K3, and to extend the context to a million tokens, on a compute budget that is finite, resource efficiency becomes a first-order goal. Note the wording — not “optimise it along the way”, but the starting point of the design itself.
Out of this grow two complementary lines of work: one is efficient training and rollout, covering KV cache management, request scheduling and training-state placement; the other is a high-performance, recoverable sandbox to support long-horizon interaction. The first is sections 11.3 to 11.5, the second is section 11.6.
The core memory-contention problem
K3's RL uses co-located training: training and rollout run on the same batch of GPUs, which keeps each 1M-context K3 RL experiment within a few hundred GPUs. At the same time partial rollout is used to hold down the tail latency that very long trajectories bring. The paper says this design has “good hardware utilisation”, but immediately admits that it introduces a nuisance:
Where the conflict is
Partial rollout means “this round is not finished, save it, carry on next round”. Save what? Mainly this trajectory's KV cache — which has to stay alive across iterations. But the GPU memory it takes up is the very same memory training needs for weights, optimiser state, activations and gradients. Two tenants fighting over one room. The paper says so explicitly: this challenge is more severe in long-context RL.
Why more severe? Because the size of the KV cache grows roughly with the context length: at 1M context the KV that a single unfinished trajectory has to leave behind is already considerable, and training's own demand does not shrink to make room. The paper makes only a qualitative judgement here and gives no quantitative data at all — a situation we will run into again and again later.
(a) Explain: why is the mechanism of “partial rollout” itself a source of memory contention? If you gave up partial rollout and required every trajectory to run to the end in one go, would the contention ease? At what cost?
(b) Suppose you can change only one thing to ease the contention: reduce the number of trajectories running at once, shorten the maximum length of a single trajectory, or move something on the training side out of the way. Say what each of them sacrifices.
Variant: if you switched to a “disaggregated” deployment (training and rollout on two unrelated batches of GPUs), would this memory contention disappear? And once it has, what is the new problem?
11.3 The external KV cache pool: write-back, not write-through
First get a clear look at the enemy. The paper says: under multi-step rollout at 1M context, the cost of a prefix KV cache miss is extremely high. And partial rollout makes it worse — at the very start of each iteration, many unfinished long prefill requests left over from the previous round arrive at the same time. Arriving at the same time means an instantaneous spike in memory demand.
There is a third source of pressure: speculative decoding speeds request turnover up further within relatively fixed tool-call intervals, so the churn of prefix blocks (the constant churning of being swapped in and out) goes up. Stacked together, these things can trigger preemption (preemption, forcibly evicting some request's cache to make room), which drops the cache hit rate — and the hit rate is a matter of life and death for long-context RL.
Read this carefully
The step “speculative decoding speeds up request turnover → increases prefix block churn” is passed over in a single sentence in the paper, with no explanation of the mechanism in between. A reasonable guess is that faster turnover means more requests coming and going per unit time and more frequent allocation and reclamation of blocks, but the paper does not spell it out and we cannot verify it.
The fix: separate “keeping the prefix” from “living on the GPU”
Intuitively you want every reusable prefix to stay around; but GPU memory is finite and cannot hold them. The paper's approach is a write-back design that decouples “the prefix is retained” from “the prefix resides on the GPU”:
- Blocks that are actively decoding stay in the GPU's KV cache.
- Reusable, older prefixes are written back to the external KV cache pool in CPU DRAM only at the moment they are evicted from the GPU, and prefetched back before the next time they are to be reused.
- KDA's recurrent state and the corresponding MLA KV cache blocks are offloaded together and prefetched together, keeping the two lifetimes aligned.
The comparison is write-through: copy to the CPU as soon as it is computed. The paper's argument is that the write-back strategy incurs CPU DRAM occupancy and transfer bandwidth only for those prefixes that leave the active decoding path, and so avoids making redundant CPU copies of blocks that are “still resident on the GPU and still active”.
An analogy
Write-through is like photocopying every page of notes for the archive the moment you finish it, even though you will be writing more on that page in a minute. Write-back waits until the page is taken off your desk, and photocopies it then. When the desk (GPU memory) is small and the photocopier (PCIe bandwidth) is slow, the second is obviously cheaper.
Where the analogy breaks down: in a real system “being taken away” happens to you rather than by you, and a whole batch may be taken away at once — at which point a long queue suddenly forms at the photocopier. That is exactly the counter-example the quiz in section 11.3 asks you to construct.
Where the DRAM comes from: send the training state off to NVMe first
The external pool lives in CPU DRAM, but DRAM is not infinite either. The paper's method: after a training iteration finishes, offload the training state (model weights and optimiser state) to NVMe (solid-state disk), freeing DRAM for the external pool; after a rollout iteration, the pool is released, so that it does not contend with the training workload.
The paper argues that write-back beats write-through. Construct two different workloads in which write-through does not lose out, or is even better, and for each point out which implicit premise of the argument you have broken.
Counter-example two (everyone leaves together): eviction is highly synchronised — the paper itself describes this situation, where at the start of each iteration a large batch of long prefill requests from the previous round arrives at once and triggers preemption at once. Write-back demands extremely high PCIe bandwidth in that instant and forms a queue; write-through has long since moved the data across slowly during the idle time before it. The premise broken is “there is spare transfer bandwidth when eviction happens”.
Worth noting: the trigger condition for counter-example two is exactly the scenario the paper itself admits exists, yet the paper does not discuss how write-back behaves under that spike.
Variant: if CPU DRAM is very small (holding only a few prefixes), which is hurt more, write-back or write-through? Hint: think about “who is forced to throw data away first”.
11.4 The rollout auto-throttling scheduler: do not let the concurrency become a hand-tuned magic number
Multi-step rollout has one very annoying property: the context grows steadily as the trajectory advances. Step 1 may be only a few thousand tokens; step 30 may already be several hundred thousand. But the traditional approach is to set a fixed concurrency — the most requests you let into the inference engine at once — and that number is usually estimated from “the average length of a whole trajectory”.
The paper points out that this pleases neither end: the average length is hard to estimate in the first place; and the concurrency set from it is too conservative early on (when each request in fact holds only a little KV, and the GPU is underfed). Turn the concurrency up instead, and later on it creates KV cache pressure and may trigger preemption.
The paper's fix is an auto-throttling mechanism in the LLM request scheduling layer, which uses runtime signals to decide dynamically how many requests to send to the inference engine. The paper lists three signals: the number of active requests, the number of queued requests, and KV cache utilisation. The effect: high utilisation is kept in early rollout, and concurrency is lowered automatically when KV cache pressure rises, avoiding both underfeeding and overload with no manual tuning needed.
The part the paper does not say
The paper lists only which three signals are used and gives no control law at all — how exactly the next moment's concurrency is computed from those three numbers (threshold triggers? proportional control? something else). Nor does it give any throughput or hit-rate comparison with the mechanism on and off. So this passage counts only as “a statement of the design idea”; its effect cannot be assessed.
(a) In the language of the figure above, explain: “fixed concurrency is too conservative early and may overload later” is, at bottom, a constant approximating what kind of curve?
(b) Of the three signals the paper lists (number of active requests, number of queued requests, KV cache utilisation), which gives the earliest warning that “overload is coming”? When is each of the other two useful?
Variant: if you switched to a task whose context length barely changes (single-turn Q&A, say, where every request is 2K tokens), is auto-throttling still necessary? Under that load, might fixed concurrency actually be better?
11.5 Borrowing a piece of memory nobody is using yet: reusing the gradient buffer
This is the neatest little trick in the chapter, and the one most worth taking out on its own.
The problem: RL loss functions often need non-policy models that are forward-only, the most typical being the reference model (reference model, used to measure how far the current policy has drifted from the initial model). These models' weights are too large to live on the GPU permanently. Yet you need it every time you compute the loss.
The usual fix is to allocate an extra piece of memory to hold it. But in RL at 1M context, memory has already been squeezed to the limit by the KV cache and the training state, and the words “allocate extra” are themselves a luxury.
That piece of idle memory
The paper's observation: during training every parameter is given an FP32 gradient buffer (holding the gradient computed by backpropagation, stored in 32-bit floating point for precision). And during the stretch of time when the forward pass has not finished and the backward pass has not started, that buffer holds nothing but the previous round's rubbish, and nobody needs it.
So: keep the reference model's weights in CPU memory, materialize them onto the GPU only when needed, and have their parameter tensors borrow the policy model's FP32 gradient buffer for storage. The paper's safety argument is one sentence: this is safe because those buffers would be overwritten anyway when the real gradients are computed later. The gain: existing GPU memory is reused, with no extra allocation and no fragmentation.
An analogy
There are two seats at the back of the classroom, kept for two teachers who are coming next period. They are not here this period, so you sit down in one and do your homework — you will be long gone by the time they arrive, and the moment they sit down they will sweep the desk clear anyway. You have not “taken” any new seat; you have only used a stretch of time that was empty.
Where the analogy breaks down: in a real system “they will certainly sweep the desk clear” is an assumption that depends on the specific implementation. If those two teachers are in the habit of piling new things on top of old (gradients accumulated rather than overwritten), your homework gets rolled up into their lecture notes — which is exactly the point the defend-it box below goes after.
How to stuff an enormous model into two slots
Together with ZeRO-2's gradient sharding and offloading, each GPU in K3's RL training keeps gradient buffers for only two VPP chunks (VPP is the unit of virtual pipeline parallel splitting; one chunk is a short run of layers assigned to this card). That is, the space available to borrow is only two slots big, nowhere near enough to hold the whole reference model.
The paper's treatment is streaming: the reference model's weights are fed chunk by chunk into these two slots, one slot serving the current forward computation while the other prefetches the next chunk. The copy time is thus hidden behind the computation time, hiding the copy cost without adding any GPU memory.
The paper says borrowing like this is “safe because those buffers would be overwritten anyway when the real gradients are computed later”. Construct two situations in which this safety argument does not hold, and write down the conditions that must additionally be guaranteed for it to hold.
grad += ... and not grad = ... — with gradient accumulation, say (the gradients of several micro-batches added together), or when one weight is shared in several places. Then the buffer's old contents are not overwritten but added into the new gradient, and the reference model's weight values become noise contaminating the gradient. The frightening part is that it raises no error: training runs as usual, the loss curve may be only slightly off, and you find out days later.Situation two: the ordering is disturbed or overlapped. If the reference model's forward pass is scheduled after backpropagation has already started, or overlaps with the backward pass on a different CUDA stream, then the real gradients get overwritten by the reference weights, or the reference weights get corrupted mid-read.
The conditions that must additionally be guaranteed: (i) this iteration's gradient write to that buffer is a purely overwriting first write (before which the buffer's contents mean nothing to anyone); (ii) the reference model's forward pass is entirely earlier than any gradient write, with no cross-stream overlap. The paper gives only the conclusion and does not say how it guarantees these two.
Variant: if what you borrow changes from the gradient buffer to the optimiser state (Adam's first-moment momentum m, say), is the trick still safe? Why? Hint: think about whether the momentum's lifetime is “within one iteration” or “across iterations”.
Defend it: if I were a reviewer
You say stuffing the reference model's weights into the policy model's FP32 gradient buffer is safe, on the grounds that “those buffers get overwritten by the real gradients anyway”. But that is an assumption that depends on the specific implementation: the moment someone changes the backward pass to accumulate gradients, or moves the reference model's forward pass after the backward pass, your model trains itself quietly into ruin, with no error raised at all. On what grounds do you build a correctness guarantee on “nobody will change this piece of code”?
A reference defence (put it in your own words first, then look)
An honest defence has three layers; do not try to insist it is “absolutely safe”.
Layer one, admit what it is. This really is not a mathematical necessity but an implementation-level invariant (invariant): this iteration's first write to that buffer is an overwrite, and the reference forward pass is strictly earlier than it. The paper states the conclusion in a single sentence and does not say what keeps that invariant holding — the criticism stands.
Layer two, say how invariants like this should be handled. Systems engineering is full of conventions of the form “this piece of memory is unclaimed right now”, and the acceptable practice is to make them explicit and checkable: assert before borrowing that the buffer is in the state “no gradient written this iteration yet”, assert on the gradient-write path that “this is an overwrite, not an accumulation”, and let a violation blow up immediately instead of failing silently. Interestingly, the paper itself uses the very same idea in the section on cache layout (11.8) — better to produce garbage data than to produce data that looks plausible. Holding this passage to their own standard is entirely fair.
Layer three, say why the gain is worth it. The alternative is to allocate an extra piece of memory for the reference model. But in RL at 1M context, memory has already been squeezed to the limit by the KV cache that survives across iterations and by the training state (sections 11.2 and 11.3), and “allocate extra” may simply be impossible — not a bit slower, but will not run at all. So the correct position is: trade a checkable invariant for a piece of memory that would sit idle anyway, rather than claiming zero risk. The paper does not say whether it actually added assertions of this kind, and that is what it should have supplied.
11.6 The sandbox infrastructure: fifty million disposable little computers
To support the varied demands of K3's post-training and evaluation, the paper deploys several kinds of sandbox runtime: the traditional container-based runtime, a GPU sandbox runtime, and the one most worth mentioning, a new microVM-based sandbox runtime, AgentENV (developed jointly with partners, and open-sourced). It is built around three core design goals.
Goal one: a high-fidelity isolated sandbox runtime
The paper's motivation is written frankly: as agents grow more capable and tasks get harder, they explore more aggressively, and may even attempt reward hacking (reward hacking, exploiting loopholes in the rules to score high instead of actually completing the task). This brings two requirements pulling in opposite directions.
On one side is the safety challenge: in early experiments with traditional container sandboxes, a number of kernel panics and deadlocks caused by unintended agent operations were observed. In plain words — commands the model typed crashed the host machine's operating system kernel. A container's isolation is really “share the same kernel, just partition the view”, so once the kernel goes down, everyone goes down together.
On the other side, they want to allow as much exploration as possible and not restrict the agent's capability, and complex tasks need a sandbox close to a real environment: the paper's example is that the agent should be able to mount disks freely, run containers, even start virtual machines. None of these is possible, or is very dangerous, inside a container.
The fix is to run isolated microVMs (lightweight virtual machines; each sandbox has its own kernel) with Firecracker. The paper's claim is that it provides a level of isolation and fidelity that container runtimes cannot match.
A common misreading
Many people assume “a virtual machine must be far slower and far heavier than a container, so in production you can only use containers”. Here it is exactly the other way round: precisely because agents perform operations no human would, a container's “shared kernel” isolation is nowhere near enough; and the microVM, through an extremely stripped-down virtual machine implementation, presses the startup cost down to the point where you can start a pile of them per second. The “sub-second startup” and “133 ms checkpoint” below are the answer to the doubt “but will that not be very slow?”
Goal two: a flexible sandbox lifecycle for agentic RL
The underlying capability is incremental checkpointing and restore: a checkpoint saves only the memory pages dirtied since the last checkpoint, which presses checkpoint and restore latency down to as low as 133 ms and 49 ms. On top of that, three high-level operations are provided:
- Pause and Resume: a paused sandbox consumes no memory or CPU resources. So the sandbox can be paused while the agent waits for the model's inference result — and that wait can be as much as 98% of the sandbox's lifetime.
- Fork: create a new sandbox from the exact state of the original while keeping the original running. The paper says this is useful for side-effect-free reward judging — you want to run a test to see whether it did the job right, without that test changing the environment the agent is in.
- Snapshot: snapshot the sandbox at fixed intervals, for error recovery.
Goal three: high efficiency and high density
Their workload has one alarming characteristic: tens of thousands of sandboxes, each with its own distinct set of images, may need to be created within a few seconds. An image is this little computer's “factory system disk”, with the software this task needs installed on it. Ten thousand sandboxes each carrying its own image means moving an enormous amount of data within a few seconds.
The paper's approach is to adopt OverlayBD as the image format, together with a custom ublk driver implementation, storage-layer sharing and P2P transfer (sandboxes pass images to one another instead of all crowding onto the same central server), achieving sub-second startup latency at scale. Memory use is reduced further through copy-on-write memory (copy-on-write, several sandboxes share the same memory page and only whoever wants to change it gets a private copy) and page-cache optimisation, reaching a memory overcommit ratio (memory overcommit ratio, that is, the total memory promised out is 6.5 times the physical memory, which works because not everyone wants it at the same time) of as much as 6.5× in real workloads.
A sense of scale: 51,219,741 sandboxes
The paper gives a total: across the whole of K3's training and evaluation, 51,219,741 sandboxes were created, spanning 1,505,678 images. What sort of quantity is fifty-one million and more?
Suppose a person starts a virtual machine by hand and waits 10 seconds for it to come up, and keeps doing it back to back without eating, drinking or sleeping: that takes 51,219,741 × 10 seconds ≈ 512 million seconds ≈ 16 years. And all of it happened inside one model's training and evaluation cycle. Another angle: 51,219,741 ÷ 1,505,678 ≈ 34, that is, on average each image was used to start only 34 sandboxes — an astonishing variety of images with not much reuse, which explains why “sub-second startup” has to rest on storage-layer sharing and P2P transfer rather than on “caching the common images locally”. (This division was worked out by this book to give the reader a sense of scale; the paper gives only the two totals and does not discuss average reuse.)
How to read these numbers
The paper's own words are “as low as 133 ms and 49 ms” and “as much as 98%”, that is, the best case and an upper bound, not averages; the 6.5× is said to be “in real workloads”, but which workload, measured how, under how much memory pressure, is not said. These are all engineering numbers the authors report of themselves, and the paper provides no reproducible measurement conditions, so they are fit for understanding “what effect the design is aiming at” and not fit to be taken as performance promises.
The paper says the time an agent spends waiting for model inference can be as much as 98% of the sandbox's lifetime, and that a paused sandbox consumes no memory or CPU. Suppose a sandbox lives 200 seconds in total, 196 of them waiting on inference.
(a) Without pausing, for how long does it occupy memory? And with pausing?
(b) Looking only at “time spent occupying memory”, how many times as many sandboxes could the same memory run, in theory at most?
(c) Is that multiple the same thing as the 6.5× memory overcommit ratio the paper reports?
Variant: if in some class of task the agent spends only 50% of its time waiting on inference (say it has a long compile of its own to run), what does the multiple in (b) become? From that, say what kind of task the “pause” optimisation is most valuable for.
11.7 §5.4 Scene change: the same hard problems, over again from the serving side
That half of the chapter on training is done. The paper has an interesting line here: serving K3 exposes the same challenges from the production side. Which is to say, taking the model out to real users does not bring a fresh set of problems, it brings the same set showing up in a new guise. Three of them, concretely:
- the hybrid KDA–MLA architecture has to maintain two fundamentally different kinds of cache at million-token context, and they must be managed jointly;
- its new modules and highly sparse experts each need custom kernels;
- production traffic mixes together requests whose per-request cost spans three orders of magnitude.
The paper deals with them at three levels, one each:
| Level | What it does | Goal |
|---|---|---|
| Engine level engine level | KDA-aware prefix caching: pack the fixed-size recurrent state into the same paged pool as the MLA KV cache | Let long prefixes be reused across requests |
| Device level device level | Custom kernels for KDA decoding, Block AttnRes and sparse latent MoE | Minimise per-token latency and memory traffic |
| Fleet level fleet level | Cache-aware affinity scheduling, budget-based admission control | Turn the efficiency of the first two levels into predictable service |
Put each of the three things below at the right level, and say why:
(1) stuffing KDA's recurrent state and MLA's KV cache into the same paged pool;
(2) carving out a separate resource budget for a wave of long million-token requests;
(3) using a WarpDecode-style token-centric kernel for MoE decoding.
Variant: which level does “cache-aware affinity scheduling” belong to? What is its relationship to the engine level's prefix caching — a replacement, or mutual dependence?
11.8 A unified cache layout: stuffing two completely different things into one pool
First be clear about why this is hard. The paper's own words: the hybrid architecture complicates prefix caching, because the KDA recurrent state and the MLA KV cache differ fundamentally in size and lifetime, and a cached prefix is reusable only when both can be restored together at the same boundary.
Take those two clauses apart. Each K3 block is made of three KDA layers and one Gated MLA layer, and the two caches look nothing alike:
| MLA KV cache | KDA recurrent state | |
|---|---|---|
| Size | grows with sequence length | fixed |
| How it is organised | paged by token, one entry per token | just one copy per request |
| Intuition | a running ledger that gets thicker as you write | a blackboard wiped and rewritten over and over |
That is where the “restored together” requirement comes from: with the MLA ledger you can turn to any page and read on, but the KDA blackboard holds only what is there after the last wipe — if you want to carry on computing from token 2560, you have to have taken a photo of the blackboard on purpose back when you passed 2560. Both sides must have something usable at the same position, or the reuse does not hold.
Why not manage them separately
The most natural idea is to give each of them its own manager. The paper rejects it: that would duplicate the allocation, referencing and transfer logic threefold (the “threefold” here matches K3's structure of three KDA layers per block). So they pack the KDA state into the same paged block pool as the MLA KV and make the pages uniform in byte size, so that the two page types share one implementation of allocation, reference management and eviction.
There is one more piece of care inside the page: the state of all heads is stored contiguously head by head, so each head's byte stream is self-contained and can be the smallest unit of cross-node transfer. This design has one immediate benefit: under a prefill/decode disaggregated (prefill/decode disaggregation, putting “finish reading the prompt” and “generate token by token” on different machines) deployment, when the prefill nodes and the decode nodes use different tensor parallel degrees (TP, i.e. one layer is cut into several pieces placed on several cards), the relayout is done on the transfer path, with zero reshuffling on the GPU side — the data has to cross the network anyway, so you change its arrangement on the way across and the GPU does not lift a finger.
Making bugs visible: a zero-overhead sanity check
Next comes the one sentence in this section most worth taking on its own. The paper says this asymmetry proved useful during development: any type-confused access produces garbage rather than plausible data — this is a zero-overhead sanity check on the pooled layout.
What it means: the two page types have been made the same byte size and live in the same pool, but they are not organised the same way inside. So if the code gets it wrong and reads a KDA state page as an MLA KV page, what comes out is not “numbers that are a bit off” but complete gibberish; the model immediately emits garbage and the engineer catches it on the spot.
Why “breaks immediately” is worth more than “quietly wrong”
The most expensive bugs in a system are not the ones that crash the program, they are the silent ones: the results are still in the normal range, the metric drops only a little, maybe nobody notices for weeks, and then it takes even longer to track it down to “so the cache was being read across”. Making an error produce an obvious anomaly the moment it happens compresses this class of bug from “weeks” to “minutes”. And this check costs no runtime overhead at all — it is not an added assertion statement, it is the layout itself that produces the consequence.
Where the paper is not clear
The paper writes “this asymmetry” but never says explicitly which asymmetry it means. Our reading is: the two page types have the same byte size but different internal organisation, so misreading one as the other yields gibberish. That is an inference from context; the original does not spell it out.
(a) Explain why “type confusion produces garbage data” is a good thing compared with “produces plausible-looking data”, and give one everyday design built on the same idea.
(b) Turn it round: if the two page types not only had the same byte size but their internal layouts also happened to be compatible (misreading one as the other still gives numbers in a normal range), what risk would that bring?
(c) Why can this check be called “zero-overhead”? What is the essential difference between it and adding an assertion in the code?
Variant: can the idea be used in reverse? That is, deliberately design the layouts of two kinds of data to be incompatible, even where a uniform layout would be more convenient elsewhere. What does that cost? And when is the cost worth paying?
11.9 The granularity fight: why the old approach breaks, and how to pull the two granularities apart
This is the hardest section in the chapter; read it slowly. There is one question to answer: when a prefix hits, what position does it actually hit up to?
The old approach: block-hash prefix caching
The standard practice in the industry is called block-hash-based prefix caching, and it reuses the KV cache at the granularity of one physical block. There is exactly one rule: only complete blocks are hashed, so only block-aligned prefixes are reusable.
Hash: squeeze a long stretch of content into a short “fingerprint”; identical content gives an identical fingerprint, and content differing by one character gives a completely different one. Prefix caching uses it to decide quickly whether “I have seen this opening before”. A chained hash folds the fingerprints of all preceding blocks into each block's own fingerprint — so the single fact “the fingerprint of block 5 matches” proves at the same time that all of the first 5 blocks are identical, with no block-by-block comparison.
This coupling breaks on K3
The paper takes the breakdown apart into a clear causal chain; follow it one step at a time:
Derive it yourself: why the block size got pushed to 1024–6144
Block-hash matching requires all layers to share one block size. And a prefix hit is reusable only when the KDA state at the hit boundary has already been persisted. So: how often can KDA state be stored?
Think it through, then look
Not too often. Because a KDA layer maintains a single, large recurrent state per sequence, not per-token entries (recall the blackboard analogy in 11.8). Every photo of the blackboard is a whole chunk of big data; adding a line to the ledger costs one line. So the paper says: state snapshots are affordable only at sparse boundaries.
Given that KDA can store state only at sparse boundaries, and all layers must share one block size, in which direction does that shared block size get pushed?
Think it through, then look
Pushed large. The result the paper gives is a shared block size forced up to 1024–6144 tokens. Note the word “forced”: this is not the optimum chosen for MLA, it is the result of being held hostage by KDA's storage cost.
The block got bigger — why does the hash granularity get coarser along with it? Were these two ever the same thing?
Think it through, then look
They never were the same thing, but the old design tied them together: because the hash is bound to the storage block, the hash granularity is just as coarse. The paper deliberately adds a clause — even though MLA's per-token entries could tolerate much finer blocks. In other words, the MLA side is an innocent bystander dragged in. That sentence is the seed of the “decoupling” that comes later.
At a granularity as coarse as 1024–6144, how much use is left in the prefix cache? Come up with two situations yourself where “the content plainly repeats yet nothing hits at all”.
Think it through, then look
The paper's conclusion is heavy: at this coarse a granularity the cache is nearly useless. The two situations: first, requests shorter than one block can never be reused — they never fill a whole block, so they never yield a cacheable prefix; second, chunked prefill yields no cacheable prefix at all until it crosses a complete block boundary (chunked prefill means cutting one long input into small batches to feed in, so it can be interleaved with other requests by the scheduler).
The fix: pull “the granularity of allocation” apart from “the granularity of matching”
Since these two were never the same thing, stop tying them together. The paper's fix is to decouple the two granularities:
- prefix hashing runs on fine hash blocks inside the MLA page, for example 512 tokens;
- the physical block remains the coarse unit of allocation (as big as it needs to be);
- for KDA it is aligned the other way round: checkpoints of the recurrent state are kept only on a sparse subset of the MLA hash endpoints — because those are the only positions a lookup can ever refer to.
Why the third one is the key
KDA checkpoints are expensive, so you cannot store them everywhere. Where does storing them pay off most? The answer: only at positions that could be looked up later. And a lookup only ever asks about hash endpoints (because the hit boundary is necessarily an integer multiple of the hash block), so a checkpoint at a non-endpoint is a hundred percent waste. This step turns the open question “where do I store them” into “pick a subset from a known candidate set”, and the problem shrinks a great deal at once.
What happens during prefill
On the MLA side: a partially filled MLA page is registered into the prefix cache index under the chained hash of its last complete hash block. Since each hash covers all hash blocks before it, matching one endpoint proves the entire prefix up to that endpoint. And the registered endpoint advances as the page fills — which is the direct answer to the old approach's two failure cases: you no longer wait for a whole physical block to fill; every time another 512-token block fills up, you can publish once.
On the KDA side: after every forward pass, the KDA kernel persists the recurrent state at the last hash-aligned position it processed. But checkpoints are large, so as a request advances, intermediate checkpoints are superseded and reclaimed, while those sitting on conversation-turn boundaries are kept for reuse across requests (a turn boundary is the position most likely to be picked up by the next request — you say something, the model answers, and the next round grows on from here).
One more rule you have to remember: a cached checkpoint is a read-only snapshot. On a hit the action is to copy it into the request's private running state, and only then do the next forward pass; a new checkpoint is written to a new slot. Therefore a checkpoint visible to other requests is never modified in place. Why this matters so much is worked out in the third question of 11.9.
Lookup happens in two stages
Stage one (MLA): match complete physical blocks by chained hash; at the first missing block, fall back to the hash endpoints inside that block — which is why a partially filled page can still hit.
Stage two (KDA): it requires the candidate boundary to have a checkpoint in every KDA cache group, and each group maintains an independent recurrent state.
The final hit is the longest boundary that satisfies both stages at once. Written as a formula:
| Symbol | What it is | Intuition |
|---|---|---|
| B | the hit boundary, i.e. which token this run can carry on from | the line that says “I do not have to recompute this much of the front” |
| H | the hash block size; the paper's example is 512 tokens | the smallest tick of alignment, the finest division on the ruler |
| M | the longest length over which this request is token-for-token identical to a prefix in the cache (2800 in Figure 12) | how far you could save “by content” at most |
| k | an integer, how many hash blocks were hit | how many ticks you counted off |
| KDA cache group | a group of KDA caches, each maintaining its own independent recurrent state; a hit requires a checkpoint in every group | several blackboards must all have been photographed at the same instant; miss one and it does not count |
The paper stresses two properties of B: it is always an integer multiple of the hash block, but is never required to be an integer multiple of the physical block. That is exactly what the decoupling bought.
That example in Figure 12
Figure 12 of the paper draws what happens inside one physical block: a 6144-token physical block holding 12 hash blocks of 512 tokens (6144 = 12 × 512). The marks under the figure show the state of the KDA checkpoint at each hash boundary: a hollow circle is a boundary with no checkpoint stored, a solid grey dot is a persisted checkpoint, and the orange dot marks the one that was hit. The paper notes: persisted checkpoints are sparse, and typically coincide with conversation-turn boundaries.
The example itself: a request's first 2800 tokens are identical to a cached prefix, the hit lands at B = 2560 = 5 × 512, deep inside this 6144-token physical block; prefill then resumes from token B, instead of recomputing [0, B). The figure also labels the full sequence of actions: restore the KDA checkpoint at B; copy-on-write the partially filled MLA block; resume prefill from token B, with zero recomputation of [0, B). In all, this request reused 5 MLA hash blocks and the one KDA checkpoint at B.
The interactive demo below reproduces Figure 12. You can drag the match length M and change which boundaries carry KDA checkpoints, and watch how the hit boundary B follows — in particular, try “remove the checkpoint at 2560” and see what happens.
Use Figure 12's setup: physical block 6144 tokens, hash block 512 tokens, and a request whose first 2800 tokens are token-for-token identical to a prefix in the cache.
(a) Inside which hash block does 2800 fall?
(b) If every hash endpoint carries a KDA checkpoint, what is the hit boundary B? Does that agree with the B = 2560 in the paper's figure?
(c) If KDA checkpoints exist only at 1024 and 2048 (and not at 2560), what does B become?
(d) In case (c), how many more tokens are recomputed than in (b)?
Variant: if the hash block is changed to 1024 tokens (physical block still 6144, and every hash endpoint carries a checkpoint), what is B? Compared with the 512 scheme, how many extra tokens are recomputed? Then say whether “smaller hash blocks are always better”.
After the decoupling, the paper says prefix caching “attains the same generality as in full-attention models”. Construct two scenarios in which the content plainly contains a long stretch of exactly repeated prefix, yet under the new design it still misses, or nearly misses. Say which condition each one is stuck on, and whether this is a design flaw.
Scenario two (the checkpoint is not there): one request forks from another in the middle of a sentence (say two users paste the same 400,000-token codebase but then ask different questions, and the fork point is not on any conversation-turn boundary). The MLA side can perfectly well match a hash endpoint near the fork point, but the paper says checkpoints are superseded and reclaimed as the request advances, and only those on conversation-turn boundaries are kept — if the checkpoint at that position has already been reclaimed, the KDA stage does not pass, and B can only fall back to the previous checkpoint that still exists. This is a real limit, decided by storage cost, and it is exactly why the defend-it box in 11.10 questions the phrase “the same generality”.
One implicit condition is worth mentioning too: a hit requires a checkpoint in every KDA cache group, so as soon as one group's checkpoint is evicted the whole boundary is void — that is mechanism three in the next section.
Variant: if “keep checkpoints only at conversation-turn boundaries” were changed to “force one to be kept every 16 hash endpoints”, would scenario two improve? What would it cost? (Hint: work out how many checkpoints there would be in a 1M context, and how big each checkpoint is.)
Answer by stringing three things together: why are the three rules “a checkpoint is a read-only snapshot; on a hit it is copied into the request's private running state; a new checkpoint is written to a new slot” necessary conditions for one prefix cache to be shared by several requests at the same time? If you drop the “read-only” rule and let a hitting request compute onward in place on the shared checkpoint, what happens? Write it as a two-request timeline. Finally: why does MLA's KV cache not need rules this strong?
The timeline once read-only is dropped: ① A hits C and computes 100 tokens onward in place, so C's content becomes the state for [0, 2660); ② but in the cache index C is still labelled [0, 2560); ③ B looks up C, believes it has the state for [0, 2560), and actually gets the state after A walked on; ④ B carries on from token 2560, but A's 100 tokens are already mixed into its initial state — the output is silently wrong: no crash, the answer just quietly goes bad (confirming again the “the most expensive bugs are the silent ones” from 11.8). ⑤ And A is not safe either: if C is then evicted or copied by someone else, A's state has lost its source.
What each rule does: “read-only” guarantees the fact is not tampered with; “copy it into the private running state” separates the shared read-only fact from the private mutable progress; “a new checkpoint goes to a new slot” guarantees that publishing a new fact does not overwrite the old one (otherwise the problem in step ③ comes back in another form).
Why MLA does not need this: because MLA is per-token entries, which are never changed once written — a new token goes to a new position, and the content of old positions is naturally immutable; so reference counting plus copy-on-write is enough (the copy-on-write on that partially filled MLA block in Figure 12 is exactly this). In-place update is the root reason “snapshot + copy” is needed, and that is precisely the essential difference between recurrent state like KDA's and attention KV.
Variant: allowing only one request at a time to hit a given checkpoint (a hit takes exclusive ownership and everyone else queues) would also avoid the error above. What is wrong with that scheme? Explain the consequences starting from the “400K prefix” scenario in 11.12.
11.10 Consistency under concurrent scheduling: three mechanisms, three concrete pitfalls
The previous section was about “how one lookup should be computed”. But a real system has tens or hundreds of requests running at the same time, sharing one cache pool, and that creates an awkward situation. The paper puts it precisely:
Where the awkwardness is
A block that has been hit carries two identities at once: it is a shared cache entry (other requests may be pointing at it) and also the growth point of one private request (that request is going to keep writing on from here). And all KDA cache groups have to agree at every hit boundary. The paper says explicitly that each of the three design points that follow is dictated by a specific failure mode — not “insurance added along the way”, but “leave it out and something will definitely go wrong”.
Mechanism one: pin first, then allocate
Failure mode: all cache groups take blocks from the same shared free list, so allocating a private copy for one group can evict a block another group has just hit. You are about to use group 1's hit, and while finding room for group 2 you squeeze out the block group 1 just hit — you have shot yourself in the foot.
Mechanism: every hit block is pinned across all groups before any allocation happens. Hold down everything you are going to use first, then go and ask for new things.
Mechanism two: a block whose copy has not landed may not take part in matching
Failure mode: the copy into the private block is executed on the GPU immediately before the forward pass. That means there is a time gap between “the block is allocated or registered” and “the bytes are actually copied in”. During that gap, a block allocated or registered within the current scheduling step will still hand a reader the previous holder's bytes — the index says this is the prefix you wanted, but what you read is a completely unrelated stretch of content.
Mechanism: such blocks are excluded from matching until their copy has landed.
Mechanism three: eviction must be atomic, the siblings die together
Failure mode: a checkpoint can restore a request only when it exists in every KDA group. If only one group's checkpoint is evicted, the boundary still looks “available” in the index, but when you actually go to restore you find one group is missing its state.
Mechanism: evicting one group's checkpoint atomically invalidates its siblings — a checkpoint is either hittable in every group or hittable in no group. No intermediate state such as “half a checkpoint” is allowed.
An analogy
The three mechanisms are like three library rules. One: take every book you want to borrow into your hands first, then ask for a new shelf (otherwise the librarian will clear away the books you left on the table). Two: books not yet fully shelved do not count as being in the catalogue (otherwise someone goes by the catalogue to fetch one and gets the old book the previous reader left behind). Three: a three-volume set is either all three volumes present or all three taken off the shelf together (otherwise somebody borrows volume one and only finds at volume two that it is missing).
Where the analogy breaks down: in a library you know on the spot that you took the wrong book, whereas taking the wrong bytes from a cache is silent — the model outputs as usual, it is just not right. That is also why these three rules have to be enforced by the system and cannot rest on “being careful”.
The paper's verdict on the three is: with these mechanisms, every registered state corresponds exactly to the token prefix it declares, so prefix caching for the hybrid KDA–MLA model attains the same generality as in full-attention models: any shared prefix can be reused at any 512-token boundary, independent of request length, chunking, and scheduling interleaving.
(a) Suppose mechanism two is removed (“blocks whose copy has not landed” are no longer excluded from matching). Write a concrete timeline showing how the error arises, and why nothing reports an error.
(b) Suppose mechanism three is removed (eviction is no longer atomic). What is the worst that can happen?
(c) Of the three mechanisms, which one can be dropped safely in a setting of “only one request in the system, no concurrency at all”? Why can the other two not be dropped?
(b) The worst case is a hit that discovers only at the restore stage that some KDA group is missing its state: mildly, the hit has to be abandoned halfway and recomputed from further back (wasting the work already done and disrupting the schedule); severely, if the implementation has no check and simply restores from a stale state or one belonging to another prefix, you again get silently wrong output. Mechanism three moves that risk forward from “found during the run” to “never selected in the first place”.
(c) The one you can drop is mechanism one: it guards against “allocating for one group squeezes out a block another group has just hit”, which needs several cache groups competing over the same free list — but note that even with only one request, K3 still has several KDA cache groups, so strictly speaking it is safe only with “a single request and a single cache group”. Mechanism two cannot go: a single request also has the window “after allocation, before the copy lands”, only now there is no second reader to step on it, so the risk drops from “certain to go wrong” to “depends on implementation details”. Mechanism three certainly cannot go: it guards against cross-group inconsistency inside one and the same request, which has nothing to do with concurrency. The point of this part is: concurrency is only an amplifier, and of the three mechanisms only the first is strongly tied to it.
Variant: if mechanism two were changed to “do not exclude such blocks, but insert one synchronisation before reading and wait for the copy to finish”, is correctness preserved? What is paid in performance? (Hint: think about where in the scheduling step that synchronisation happens, and who it blocks.)
Defend it: if I were a reviewer
At the end of the subsection you say prefix caching for the hybrid KDA–MLA model “attains the same generality as in full-attention models: any shared prefix can be reused at any 512-token boundary”. But you yourself just wrote that KDA checkpoints are large, are kept only at sparse boundaries, that the intermediate ones get reclaimed, and that the ones left typically sit on conversation-turn boundaries. So the vast majority of 512 boundaries have no checkpoint at all, and by your own formula a hit there is impossible. Is “the same generality” claiming more than you can back?
A reference defence (put it in your own words first, then look)
The key to the defence is to separate generality as a mechanism from the hit rate in practice, and to admit the original wording really is easy to misread.
First, what the sentence really claims is “the constraint has been lifted”. Before the decoupling, the hit boundary was held hostage by the physical block size: it could only land on whole-block boundaries of 1024–6144, requests shorter than one block never hit, and chunked prefill yielded nothing at all before it crossed a whole-block boundary. After the decoupling all three structural limits are gone, and the hit boundary is decoupled from request length, chunking and scheduling interleaving — that is exactly what “the same as in full-attention models” refers to: prefix caching in a full-attention model never guarantees a hit at an arbitrary position either; what it guarantees is that “whether you hit depends only on whether the content is the same, not on how you chunk or how you schedule”. Seen that way, the paper's claim holds.
Second, admit the second condition is still there. Whether a particular lookup hits also requires that boundary to have a checkpoint in every KDA cache group, and checkpoints really are sparse. So the more careful statement would be “the hit granularity is no longer limited by the physical block”, not “any 512 boundary can be reused”. That sentence in the original blends “allowed by the mechanism” together with “will actually hit”, which makes the wording optimistic.
Third, point at the real gap in the evidence. The paper gives no number anywhere for the prefix cache hit rate, and no before-and-after comparison for the decoupling. So a reader cannot tell how much this design actually raises the hit rate under real traffic — and that should have been the easiest metric to measure and the most convincing one. That is more worth pressing on than the wording.
11.11 High-performance kernels: three modules, three different ways of saving
Kernel: not the operating-system kernel here, but a small piece of compute code that runs on the GPU. The same mathematical operation, written differently, can differ in speed by several times over — and the difference is mostly in how you arrange the movement of the data, not in the arithmetic itself. K3 introduces three new modules, and the paper builds a custom kernel for each of them.
KDA decoding: do not store the state, store “the raw material for a replay”
The paper names the difference first: compared with KDA prefill, the main bottleneck in decoding moves from “extracting parallelism” to “efficiently managing a continuously evolving recurrent state”, a state that is updated in place at every decoding step.
The trouble comes from speculative decoding. Recall how speculative decoding works: first guess several tokens in one go by some cheap means (the draft), then have the main model verify them all at once — every guess that is right is a token's worth of speed gained for free. K3 uses speculative decoding based on MTP (multi-token prediction).
The problem is: if verification rejects a subset of the draft tokens, the state has already advanced past the last accepted token and cannot easily be rolled back. The blackboard has already been erased and rewritten, and now you find that the last few strokes were wrong — but you cannot erase your way back.
The direct fix is to keep one state snapshot per draft position — that does support rollback, but it makes state traffic grow by a multiple, and the paper points out that this cost dominates at the large batch sizes typical of online serving (the larger the batch, the more copies of state have to be read and written at once, and decoding is stuck on memory bandwidth to begin with).
Here comes the key observation: the state after any accepted draft prefix is fully determined by the projected inputs of the draft tokens, and those projected inputs are far smaller than the state itself. If that is so, then cache only those projected inputs, rebuild the state of the accepted tokens on chip, and write back only the state of the verified tokens and the bonus token. The paper notes: this design was independently proposed in the concurrent work ReplaySSM.
What a bonus token is
In speculative decoding, the one forward pass in which the main model verifies the draft also works out, along the way, “the next token after the last accepted token” — that free one is called the bonus token. The paper uses the word without defining it; the explanation here is the field's common meaning.
In the implementation, the replayed tokens, the bonus token and the next draft window share one loop inside a single fused kernel, a kernel that covers the short convolution, input normalisation, gating, the KDA recurrence and output normalisation (five steps that used to be separate merged into one, with no intermediate results going back to device memory). The effect the paper reports of itself: verification latency grows sub-linearly with the number of tokens verified, and stays below the state-caching baseline.
The last sentence matters a great deal: because the projection cache never leaves the decoding stage, prefix caching and prefill–decode disaggregation operate on the same payloads as in non-speculative serving. In plain words — the extra data speculative decoding brings only circulates inside decoding, it does not spill out and become “a new class of cache”, so the whole prefix-caching machinery of 11.9 and 11.10, and the contents transferred between prefill nodes and decode nodes, need no change at all for speculative decoding.
Block AttnRes: a two-stage schedule, and both stages are saving memory
Block AttnRes follows a two-stage schedule: a batched inter-block (inter-block) pass reads the cached block representation once per block, after which each layer folds in the intra-block (intra-block) partial sums through an online-softmax merge. The paper spells it out: memory access accounts for a substantial part of what these kernels cost in prefill and in decode, so the optimisations in both stages are mainly about memory efficiency.
In prefill the problem is this: materialising the block representation on every tensor-parallel rank causes a large amount of redundant memory consumption (the same thing stored once on each of 8 cards). The fix is to use sequence parallelism for the activations: decompose the TP all-reduce into reduce-scatter and all-gather, put the intra-block kernel between those two collectives, acting on hidden states that are sharded along the sequence, so that the block representation of each token is materialised on exactly one rank. The effect is that the extra memory consumption is gone, and the I/O overhead during prefill goes down as well.
An analogy
Eight people copy out a book together. The old way is that every one of them copies the whole book (after the all-reduce, everybody holds a complete copy of the data) and then makes notes on their own copy — eight sets of notes that are identical, eight times the paper wasted. The sequence-parallel way is to split the book into eight parts, give each person one part, have each make notes only on their own part, and stitch the notes back together at the end. The point is that the act of “making notes” has been slipped in between “splitting up” and “putting back together”.
Where the analogy breaks down: a real reduce-scatter is not only “splitting up”, it does the summing at the same time; this analogy explains only why memory is saved, not why the communication volume does not go up.
In decoding two other moves are used. First, issue the inter-block kernel on a side stream (side stream, a parallel execution queue on the GPU) so that it overlaps with independent computation on the main stream — that is, hide that slice of time. Second, the intra-block kernel is slimmed down by fusion: the merge of the AttnRes output with its partial-sum update, together with the RMSNorm that follows, is fused into the preceding TP all-reduce, which removes a dedicated kernel from the intra-block stage. The paper's summary: together these optimisations hide the latency of the inter-block pass and reduce the memory traffic of the intra-block stage.
Stable LatentMoE: once there are more experts, the scheduling itself becomes the bottleneck
Stable LatentMoE increases both the total number of experts and the number of experts activated per token. The paper spells out the consequence: the twofold growth in the expert space and in the number of experts per token raises scheduling and coordination overhead, making it hard for a conventional MoE kernel to sustain high hardware utilisation. Note that what is stuck here is not the multiplication itself, but the organising work of “deciding who computes what and getting the data together”.
On the latent GEMM side (latent GEMM; GEMM is just general matrix multiplication) three optimisations are used:
- Fuse the latent down-projection and the MoE router into a single GEMM — two matrix multiplies become one, and the input is read one time fewer.
- Shard the latent weight matrix across the ranks, and use multimem store instructions to fuse the output all-gather into the GEMM's epilogue (the epilogue is the wrapping-up work you do on the side once the matrix multiply is finished and the result is still on chip) — communication is no longer “a separate step after the computing is done”, it goes out at the same moment the result lands.
- Overlap the communication this produces with other operators (the paper's example: the shared expert's computation).
The paper's summary: taken together, these optimisations eliminate redundant weight traffic and duplicated computation while hiding communication latency behind computation.
On the routed expert side the situation is completely different. The paper's observation: at small batch sizes, the group GEMM degenerates into a memory-bound streaming read of the weight matrices — you have only a few tokens to compute, yet you must read the expert's entire weight matrix out of device memory, all the time goes on reading, and the multipliers sit essentially idle. And conventional tile-centric kernels are unsuited to this regime because of their compute-oriented design and their preprocessing overhead.
So they build the MoE decoding kernel on WarpDecode's token-centric design: each warp is responsible for one output neuron and streams the relevant weights in directly from memory (a warp is a group of threads on the GPU that execute in lockstep). To raise parallelism further, each warp is subdivided into finer-grained lane teams, each team handling a disjoint subset of experts, followed by a warp-wide reduction of the partial results. On top of that, the weight layout is permuted offline in a one-off preprocessing pass, which greatly reduces the dequantisation overhead at run time — moving the tidying-up work that only needs doing once out of every single inference.
The same thing is missing from the whole section
All of §5.4.2 gives no speedup figures at all: the KDA part says only “grows sub-linearly” and “stays below the state-caching baseline”, and Block AttnRes and LatentMoE do not even have something to be compared against qualitatively. So all you can do is understand what problem these optimisations are trying to solve; you cannot judge how well they solve it, and you cannot compare them with other systems. This is a common trade-off in engineering reports, but the reader should be clear-eyed about it.
The core trade-off in the KDA decoding passage is this: to let speculative decoding roll back, you either keep one state snapshot per draft position, or you cache only the projected inputs and replay on chip.
(a) Why does the cost of “a snapshot per draft position” dominate at large batch sizes? Write down the form of the data moved per step under each of the two schemes (no real numbers needed).
(b) Why is “caching only the projected inputs” functionally equivalent? State clearly the property the equivalence rests on.
(c) Put the sentence “because the projection cache never leaves the decoding stage, prefix caching and prefill–decode disaggregation operate on the same payloads as in non-speculative serving” into plain words, and say why it matters — what does it have to do with what 11.9 and 11.10 covered?
(b) the property it rests on is the recurrent structure of the state: the state is derived token by token, starting from the last known state, and the input each recurrence step needs is that token's projected input. So as long as the projected inputs are kept, you can re-derive the state of those accepted tokens on chip, getting a result bit for bit identical to the stored snapshot, and then write back to device memory only the state of the verified tokens and the bonus token. Compute traded for memory, and compute happens to be the resource sitting idle at that moment.
(c) in plain words: these projection caches exist only inside the decoding stage; they do not turn into a new kind of data that the prefix cache has to manage or that has to be transferred between prefill nodes and decode nodes. So the data structures of the prefix cache, and the contents transferred between the two kinds of node, are exactly what they are when speculative decoding is off. What matters about that is that it keeps complexity from spreading: otherwise the unified paged layout of 11.9 and the three consistency mechanisms of 11.10 would each have to be designed all over again for speculative decoding, with their correctness argued again. A good optimisation does not only have to be fast itself, it also has to make no trouble for its neighbours.
Variant: if the acceptance rate of the drafts is extremely low (almost everything is rejected every time), how does the cost comparison between the two schemes change? Then think: what about an acceptance rate as high as 100% (everything accepted)? Use that to say in what range this design gains the most.
11.12 Fleet-level scheduling: from “fast” to “dependable”
The paper comes straight to the point: beyond a single serving instance, the challenge shifts from “per-request efficiency” to “predictability”. The reason is two sentences: one prefix cache miss costs several orders of magnitude more than a hit; a wave of million-token requests can starve short requests. Each of those two sentences corresponds to one strategy.
Strategy one: cache-aware affinity scheduling
First get the arithmetic straight. The typical figures the paper gives: at a 1M context, a typical coding input carries a 400K-token prefix but needs only about 4K tokens of prefill increment. That is, your IDE has stuffed the whole codebase in (400,000 tokens), while this round you have really only added a few hundred lines of changes and one question (4,000 tokens).
| Symbol | What it is | Intuition |
|---|---|---|
| P | the length of prefix already held in cache; the paper's example is 400K tokens | that codebase which has not changed |
| Δ | the increment added this round that must be prefilled; the paper's example is about 4K tokens | the few lines you changed this time plus one question |
| P + Δ | the total to prefill on a miss: the prefix has to be computed all over again too | the cache is gone, the whole codebase gets read again |
| ≈ 101 | the ratio of the two, about two orders of magnitude | the same job, possibly 100× more expensive |
What does 101× mean in practice? If on a hit you wait 0.5 seconds for the first character, on a miss you wait close to a minute. That is the weight behind the paper's sentence “a prefix cache hit avoids re-prefilling the entire prefix and is several orders of magnitude cheaper than a miss”.
This estimate is on the conservative side
Eq. 11-2 is estimated linearly in the token count, no more than that. In real prefill the attention part of the cost grows faster than that with length, so the actual gap can only be larger than 101×. The paper does not open this up, though — it says only “several orders of magnitude” — and we are not going to invent a more precise number on its behalf.
So the strategy is natural: route each request to the cluster that holds its prefix cache. Why can the cache not be moved over instead? Because moving the cache to another cluster requires transfer over a cross-cluster link, and that is much slower than the fabric inside a cluster — shipping 400K tokens of KV cache across may well be slower than recomputing it.
But this brings a new fragile point: this cache affinity binds every session to a single cluster, and a failure of that cluster interrupts every session bound to it.
The paper's fix is to use consistent hashing to pin each session to two clusters: a primary cluster that serves its traffic, and a pre-assigned secondary cluster that takes over when the primary fails. The honest cost is written down too: the secondary cluster holds none of that session's prefix cache and must re-prefill on failover — by Eq. 11-2, that is going from 4K to 404K.
So why is it still worth it? Because consistent hashing spreads the secondary-cluster assignments of the sessions evenly across the whole fleet, this re-prefilling work is shared out over many clusters rather than concentrated on one. The conclusion: cache locality is preserved in the common case, while the impact of any single cluster failure stays bounded.
Strategy two: budget-based admission control
First, what the traffic looks like: production traffic mixes short requests of under 2K tokens with extremely long ones of up to 1M tokens, so per-request cost spans roughly three orders of magnitude. 1M ÷ 2K = 500, which sits between 10² and 10³; the paper's “roughly three orders of magnitude” is a rough way of talking at the level of orders of magnitude.
The consequence: the total load imposed by any fixed number of requests is highly unpredictable — the sentence “there are 100 requests running right now” tells you nothing at all about how busy the system is. So the paper delivers a heavy verdict: capacity planning, queueing models and rate-limit quotas built on the “average request” all break down under this much variance.
Why “average” means nothing here
If the cost of a batch of requests runs anywhere between 2K and 1M, then the thing called “the average request” does not exist in reality at all — no real request looks like that average. You plan capacity by the average, and what arrives is either 500 small ones or 1 enormous one, and the two situations demand utterly different things of the system. This and “approximating a rising curve with a horizontal line” in 11.4 are two versions of the same class of error: using one scalar to sum up a distribution with enormous variance.
The typical failure mode the paper gives is very concrete: a wave of long-context requests saturates the available compute, short requests that arrive afterwards cannot be scheduled in time, and TTFT degrades for all traffic. Note those last three words — the ones hurt are not only the long requests, it is everyone: a user who only wants to ask “what is the date today” waits a long time because somebody else is reading a 1M-token document.
The fix is budget-based admission control: give different request classes their own independent resource budgets, so that a burst of long-context traffic consumes at most its own share of capacity and cannot degrade the system-level SLO experienced by the other classes. In essence it is isolation — turning “one shared big road” into “separate lanes”.
What the paper does not say
What request “classes” are actually divided by (by length? by user? by product line?), in what proportions the budgets are allocated and whether they are adjusted dynamically, under what conditions the consistent-hashing secondary cluster is judged to take over — the paper accounts for none of it. So this section reads more like a statement of design principles than a reproducible scheme.
(a) Using the paper's 400K prefix and 4K increment, work out the ratio between the numbers of tokens to prefill on a hit and on a miss.
(b) The paper says a hit is “several orders of magnitude cheaper than a miss”. How many orders of magnitude is the ratio you worked out? Do the two statements agree?
(c) Using the two endpoints 2K and 1M, check the sentence “per-request cost spans roughly three orders of magnitude”.
(d) If some application's typical increment is 40K rather than 4K (pasting in a long stretch of new logs every round, say), what does the ratio become? How does that affect the judgement of “whether it is worth sacrificing load balancing for cache affinity”?
Variant: if the user changes some file at the beginning of the codebase every round (editing an import, say), can that 400K prefix still hit? Roughly how long is the hit? What does that mean for the value of “cache affinity”?
Cache-aware affinity scheduling is in natural conflict with “load balancing”.
(a) Say what the consistent-hashing “primary + secondary” design actually solves at failure time, and what it does not solve.
(b) Imagine a scenario: all the sessions of one large customer happen to hash to the same primary cluster. What happens then? Can “secondaries spread evenly” save you?
(c) Bring budget-based admission control into it: can it ease (b)? State clearly what it does and does not have a hold on.
(b) under normal conditions these sessions are all concentrated on that one primary cluster, which becomes a hot spot and may well overload on its own — and that is the inherent price of cache affinity: the stronger the affinity, the less you can move load to somewhere that is idle. “Secondaries spread evenly” only does anything from the moment the primary really goes down; the rest of the time it is no help at all against a hot spot. Worse, if the hot spot does bring the primary down, then at that instant all of that customer's sessions trigger re-prefilling at once (spread over several secondary clusters, yes, but the total is a real spike).
(c) it can only ease it in part. Budget control isolates resources between request classes; it guarantees that a burst of long requests will not drag down the TTFT of short ones — so inside that hot cluster it can still hold on to the SLO for short requests. But it has no hold on “a cluster overheating as a whole”: budgets divide up the capacity inside one instance or cluster, they do not send requests off to another cluster. Solving (b) needs a third thing — breaking affinity and allowing migration (and bearing the cost of re-prefilling). The paper neither discusses how cache affinity and load balancing are to be traded off, nor says along what dimension the budget classes are divided.
Variant: if each session were pinned to three clusters (one primary, two secondaries) instead of two, what would the benefit and the cost each be? Then think: if instead the secondary were allowed to warm up in the background (copying the prefix cache over ahead of time), what new problems would that bring?
Chapter summary
From beginning to end this chapter does only one thing: in a system where no resource is sufficient, it answers over and over the question “who should this piece of memory or bandwidth belong to right now”. String the six main threads together and you will find they use only a few of the same moves.
Move one: pull the lifetimes apart. The write-back design pulls “the prefix is kept” apart from “the prefix lives on the GPU” (11.3); decoupling the two granularities pulls “the unit of allocation” apart from “the unit of matching” (11.9); gradient buffer reuse pulls “who this piece of memory belongs to” apart by period of time (11.5). Three different forms, exactly the same thinking — two things that history coupled together can in fact go their own ways.
Move two: pay the price only where it is needed. KDA checkpoints are stored only at “the one position a lookup could possibly refer to” (11.9); write-back spends CPU memory only on the prefixes that “leave the active decoding path” (11.3); incremental sandbox checkpoints save only “the memory pages that were dirtied” (11.6).
Move three: store the raw material, not the result. In speculative decoding only the projected inputs are cached and the replay happens on chip (11.11), trading idle compute for bandwidth that is tight.
Move four: leave errors nowhere to hide. The unified pooled layout makes a type confusion produce garbage rather than plausible data (11.8); the three consistency mechanisms each block one specific silent failure (11.10); the read-only snapshot guarantees that “a registered state always corresponds exactly to the prefix it declares” (11.9). The shared belief behind this family of moves: in a large-scale system, a silent error is far more expensive than a crash.
Move five: leave room for variance. Automatic throttling replaces a fixed concurrency number with runtime signals (11.4); budget-based admission control admits that “the average request” does not exist and isolates by class instead (11.12). Both are refusals to “sum up a distribution with one scalar”.
The key numbers to remember: the hit boundary B is a whole multiple of the hash block (512) and not of the physical block (6144), and the Figure 12 example is B = 2560; sandbox checkpoint/restore is as low as 133 ms / 49 ms, waiting on inference can take up 98% of the lifetime, the memory over-allocation ratio is as high as 6.5×, and 51,219,741 sandboxes were created in all, across 1,505,678 images; the ratio between a cache hit and a miss is about 101×, and per-request cost in production traffic spans about three orders of magnitude.
The state of the evidence in this chapter
The two sections this chapter covers are the densest engineering in the whole paper, and also the part with the least quantitative evidence. Apart from those few sandbox numbers and this 400K/4K example, the paper gives almost no speedup, hit rate or before-and-after comparison: how much the external KV cache pool raised the hit rate, how much throughput automatic throttling brought, how much the prefix cache hit rate changed after the granularities were decoupled, how many times faster each of the three kinds of kernel is — none of it. So the right way to read this chapter is “look at what problem these designs are trying to solve and what thinking they use”, not “believe that they achieved good results”. All of this is engineering practice as the paper's authors report it.
第12章 评测:怎么把这些数字读明白(而不是被它们牵着走)
一句话导语:这一章表面上是「K3 考了多少分」,真正要教你的是——当有人把一张满是加粗数字的表推到你面前时,你该按什么顺序去问问题,才不会被那些加粗骗走。
学完这一章你应该能做到
- 说清「推理与知识/编码/智能体/视觉」这四个能力轴各自在考什么,并举出每个轴上一个代表性基准的具体任务形态
- 解释什么是 agent harness,以及「换一个 harness 分数就变」这件事对排名意味着什么
- 看到一个分数时,主动去找出它背后的六件事:谁评的、什么 harness、有没有拒答/回退、有没有工具、上下文怎么管、日期是哪天
- 在论文自己的表格里找出与论文自己的总结句相矛盾的行(这一章会让你真的去找一次)
- 把「分数」和「每任务成本」放在同一张图上看,并说清为什么只看其中一个都会得出错误结论
- 独立写出一份「读评测表检查清单」,并用它拆解一句真实的宣传话术
12.1 四个能力轴:这些名字古怪的基准到底在考什么
先说不做分轴会怎样。如果只报一个「总分」,你完全没法回答「这个模型适不适合我要干的事」。一个能把 GPQA 博士级选择题做到 93.5 分的模型,未必能在你的电脑上把一份表格改对。所以论文把评测组织成四个能力轴(four broad capability axes),每个轴下面挂一批基准。
基准(benchmark):一套固定的题目 + 一套固定的判分规则。它的作用是让不同模型在「同一张卷子」上比较。它的软肋也在这里——卷子是人出的,判分规则是人定的,运行环境也是人搭的,这三样里任何一样变了,分数就会变。
轴一:推理与知识
这个轴考的是「不给工具、不给网页,光靠脑子能想多深」。论文用了四个:GPQA Diamond(论文称之为「研究生水平推理」)、CritPt(论文把它归为研究级推理任务,并明说 K3 在这里的落后意味着「研究级推理仍是关键改进方向」)、AA-LCR(来自 Artificial Analysis 的一项长上下文推理测试)、以及 HLE-Full(Humanity's Last Exam 全集,一套刻意做到极难的跨学科题库)。论文对 HLE-Full 同时报告了不带工具和带工具两个分数。
读的时候要小心:这些基准的介绍不全来自论文
论文在 §6.1.1 里只给出了基准的名字和文献编号,没有逐个说明每个基准考什么。为了让你读得懂,本章接下来对各基准任务形态的描述,一部分来自论文正文(比如「SWE-Marathon 是 GPU 内核导向的套件」「FrontierSWE 是长时程基准」「OfficeQA Pro 给的是渲染成图像的 PDF 语料」这几条论文明确写了),另一部分来自这些基准本身的公开资料。凡是论文没写的,你都应该去原基准的说明里核对一遍——尤其当你打算引用某个分数去支持某个具体主张时。这本身就是本章要训练的习惯:先搞清楚这道题在考什么,再看分数。
为什么同一个基准要报两个分数
因为「模型自己会不会」和「模型会不会用工具把它做出来」是两种能力,而且实际部署时你关心的往往是后者。把两个数并排放,你才能看出「工具帮了多少忙」——这个差值本身就是一条重要信息,12.4 节会专门用它出一道题。
轴二:编码
注意这里考的基本不是「写一个冒泡排序」,而是软件工程:在一个真实仓库里定位 bug、改代码、跑测试、直到通过。DeepSWE、FrontierSWE、SWE-Marathon 都属于这一类,其中 SWE-Marathon 是面向 GPU 内核的长任务套件,FrontierSWE 是长时程(long-horizon)任务。Terminal-Bench 2.1 考的是在终端里完成任务的能力。ProgramBench、SciCode 偏程序与科学计算代码,PostTrainBench 和 MLS-Bench-Lite 则考机器学习工程本身——让模型去做后训练、跑实验。
轴三:智能体
这是基准数量最多的一轴,二十多个,因为「智能体」本身就是一大堆不同的事。粗看可以分成几族:找信息(BrowseComp、DeepSearchQA、ResearchRubrics——最后一个用打分细则 rubric 来评研究报告的质量);用工具/接外部系统(Toolathlon-Verified、MCPMark-Verified、MCP-Atlas、AutomationBench);做知识工作(GDPval-AA v2、AA-Briefcase、JobBench、OfficeQA Pro、SpreadsheetBench 2、APEX-Agents、Agents' Last Exam);操作电脑(OSWorld-Verified、OSWorld 2.0、SaaS-Bench);行业专项(τ3-Banking 银行客服、Harvey Lab-AA 与 Legal Research Bench 法律、CorpFin v2 与 Finance Agent v2 金融)。
打个比方
推理与知识轴像闭卷考试,编码轴像交一份能跑起来的作业,智能体轴更像「实习生第一天上班」——给你一台电脑、几个账号、一句模糊的需求,看你能不能自己走完全程。类比失效处:实习生会问「这个需求是什么意思」,而绝大多数智能体基准不允许中途向人提问,题目一次性给完。所以基准分数高不代表在真实协作里好用,反过来也一样。
轴四:视觉
视觉轴里,OmniDocBench 考文档解析(把 PDF、表格、公式看成结构化内容),Video-MME 和 MMVU 考视频理解,PerceptionBench、BabyVision、WorldVQA 考基础感知与对物理世界的理解,MMMU-Pro、CharXiv (RQ)、Math-Vision、ZeroBench-main 考「看图做题」。后面这四个和 BabyVision 一样,论文都报告了带 Python 工具的版本——模型可以写代码去裁剪、放大、测量图像,再回答。
论文在 HLE-Full 上给 K3 报的是 43.5 / 56.0,表格说明写着「每格按不带工具、带工具的顺序报告」。现在有人截图只发了「56.0」,配文「K3 在人类最后考试上拿到 56 分」。这句话哪里有问题?
变式:如果某个基准只报「带工具」一个分数,不报不带工具的,你能从这一个数里推断出模型的裸能力吗?如果不能,你会要求作者补充什么信息才愿意接受这个分数?
12.2 跟谁比:基线,以及「最大努力」这个前提
比较总要有对手。论文选的对手是五个:专有模型 Claude Fable 5、GPT-5.6 Sol、Claude Opus 4.8、GPT-5.5,以及开放权重模型 GLM-5.2。选这几个的逻辑很清楚:前两个代表当下最强的专有前沿,后两个代表上一代/次一档的专有模型,GLM-5.2 代表「和 K3 同一类」的开放权重竞争者。
推理努力(reasoning effort):现在的推理模型通常有一个「想多久」的档位,档位越高,模型在给出答案前生成的思考内容越多,通常分数越高,但延迟和花费也越高。论文明确写:所有模型都在最大推理努力下评测,例外是 GPT-5.5 用的是 「xhigh」 这一档。
为什么必须统一到「最大努力」
如果不统一,比较就没有意义了——我用最高档,你用中档,我赢了也说明不了什么。统一到各家的最高档是目前比较通行的做法。但要注意它带来的副作用:最大努力下的分数,不是你日常用到的分数,也不是你付得起的分数。这正是 12.8 节要把「成本」这条轴加进来的原因。
「所有模型都在最大推理努力下评测」听上去很公平。但假设 A 模型的最高档会思考 5 万个 token,B 模型的最高档只思考 5 千个 token,两家都叫「max」。这时候这个「公平」还剩多少?请说清它还保证了什么、不再保证什么。
变式:如果论文改成「所有模型都在每任务成本 1 美元的预算下评测」,排名可能怎么变?这种评测方式又会带来什么新的不公平?(提示:想想开放权重模型可以自己部署这件事。)
12.3 配置里的魔鬼:这一节才是本章的灵魂
大多数人读评测只看数字,跳过「评测配置」这一小段。但真正决定数字可信度的,几乎全在那一小段里。论文的 §6.1.3 写得相当坦诚,我们一条一条拆。
(1)采样参数
K3 的全部评测都用推理努力 = max、温度 = 1.0。单步任务(比如 GPQA Diamond、HLE-Full 和不带工具的视觉基准)用 top-p = 0.95,智能体任务用 top-p = 1.0。论文还给出一条通用建议:推理与知识类任务用 0.95,编码与智能体场景用 1.0。
温度(temperature)与 top-p:模型每一步其实是在一堆候选词上给出概率,然后抽签决定输出哪一个。温度控制抽签的随机程度,温度 1.0 就是按模型原本的概率抽;top-p 则是先把概率从高到低累加,只保留累加到 p 为止的那些候选,再在里面抽。top-p = 1.0 表示不裁剪,0.95 表示把最不可能的那 5% 概率质量丢掉。
为什么这很重要?因为温度 1.0 意味着同一道题跑两次可能得到不同答案。所以任何单次运行的分数都自带波动,这也是论文在视觉基准上要跑三次取平均、ZeroBench-main 跑五次的原因。
(2)harness:同一个模型,换个「外壳」分数就不一样
智能体外壳(agent harness):模型本身只会输出文字。要让它真的去读文件、跑命令、改代码,必须有一层程序把模型的输出解析成动作、去执行、再把结果喂回去。这层程序就是 harness。论文用到三种:Kimi Code、Claude Code、Codex。
harness 决定了模型能看到什么、能做什么、出错时怎么重试、上下文满了怎么办。所以同一个模型换一个 harness,分数就会变。论文给了一个可以直接验证的例子:DeepSWE 上 K3 的表格分数是 67.5,但换成 mini-SWE-agent 这个 harness 时是 67.3。更值得注意的是 Terminal-Bench 2.1——论文明说:报告的是所有模型跨 harness 的最好分数。
常见误解
很多人以为「benchmark 分数」是模型的属性,像身高一样。其实它是「模型 + harness + 工具 + 提示词 + 采样参数 + 判分器」这一整套系统的属性。你换掉其中任何一个零件,数字都会动。K3 在 Terminal-Bench 2.1 上是 88.3、GPT-5.6 Sol 是 88.8,差 0.5 分——而 harness 的更换在 DeepSWE 上就能造成 0.2 分的差异。这不是说 88.3 vs 88.8 一定是噪声,而是说你无法从这张表判断它是不是噪声。
Terminal-Bench 2.1 报告的是「所有模型跨 harness 的最好分数」。请你构造一个具体的数字例子,使得:在这条规则下 A 模型排第一,但如果规定「所有模型都必须用同一个 harness」,无论选哪个 harness,A 都排第二。
按「跨 harness 取最好」:A 报 90,B 报 88,A 第一。
强制统一 harness:用 H1 时 92 > 90,B 赢;用 H2 时 88 > 60,B 也赢。无论选哪个 harness,A 都是第二。
构造的窍门在于:让 A 在两个 harness 上极不均衡(90 与 60),而 B 均衡且都不低(92 与 88)。如果你第一次试的是 A = (90, 70)、B = (85, 88),会发现统一到 H1 时 A 仍然赢——所以必须让 B 在每一个 harness 上都压过 A 的对应值,同时让 A 的最大值仍是全表最高。
这个反例说明的道理是:逐模型取最大值不是一个保序的操作。它会奖励「在某一个特定外壳上被调得特别好」的模型,而这种优势未必来自模型本身,可能来自那个外壳的适配工作做得更多。
要强调的是:这只是一个构造出来的反例,用来说明规则的漏洞,论文并没有公布各模型在各 harness 上的分表,所以我们无从判断 Terminal-Bench 2.1 上是否真的发生了这种情况。这恰恰是问题所在——规则允许它发生,而数据不足以排除它。
变式:如果规则改成「每个模型报告其跨 harness 的中位数」,你的反例还成立吗?中位数规则又会引入什么新问题?
(3)回退与拒答:比较其实并不完全对等
回退(fallback):论文没有给出精确定义,从上下文看,指的是当主模型无法或不愿完成时,服务端自动切换到另一套行为或另一个模型来产生输出。拒答(refusal)则是模型直接拒绝执行这个任务。
论文明确写道:Claude Fable 5 的结果包含回退行为,GPT-5.6 Sol 的结果包含潜在的 cyberguard(一种安全护栏机制)。具体到某些基准,数字相当可观:SWE-Marathon 上 Claude Fable 5 在 35% 的任务上触发了回退。内部评测的 Table 3 脚注更细:Kimi Code Bench 2.0 上 Claude Fable 5 有 13 次回退 + 1 次拒答(共 80 个任务);同一基准上 GPT-5.6 Sol 有 10 次拒答 / 80 任务,GPT-5.5 有 3 次拒答 / 80 任务;Online Experience 里有 14 个任务是 Claude Fable 5 拒答的;Agent Behavior Bench 上有 6 次拒答 / 95 任务。此外 Table 2 还有一条脚注:Agents' Last Exam 官方榜单上,Claude Fable 5 那一条是在 xhigh 努力下跑的,并且有 40% 的任务被标注为「降级」。
读的时候要小心
这些脚注加在一起说明一件事:表里的横向比较并不完全对等。如果一个模型在三分之一的任务上触发了回退,那它那一列到底代表「这个模型的能力」还是「这套服务的能力」,是模糊的。论文把这些信息公开写出来,是值得肯定的做法(很多报告根本不写);但公开了不等于问题消失了。你在引用任何一个跨模型比较时,都应该顺手看一眼脚注。
答辩:如果我是审稿人
你在 SWE-Marathon 上说自己 42.0、领先 Claude Fable 5 的 35.0 达 7 分。可你自己也写了,Fable 5 在 35% 的任务上触发了回退。那这 7 分里,有多少是「你更强」,有多少是「对方的服务替它做了决定」?在你能把回退任务单独剔出来重报之前,这个 7 分的说法凭什么成立?
参考防守(先自己组织语言再看)
可以这样防守,但必须承认它只是部分防守:
第一,评测对象的选择是合法的。当用户实际调用 Claude Fable 5 时,回退行为就是他会得到的东西,所以「含回退的端到端表现」是一个真实存在、也有实用意义的量。论文比较的是「这套系统交付了什么」,而不是「这个权重文件有多强」。
第二,论文主动披露了回退比例,而不是隐瞒。披露之后,读者有能力自行给结论打折扣,这比不披露强得多。
第三,也是最诚实的一条:这个防守解决不了对等性问题。正确的做法应该是同时报告「剔除回退任务后的子集分数」,或者至少报告回退任务上的得分分布。论文没有做这件事,所以严格来说,7 分这个差距的归因是无法确定的——我们只能说「在含回退的端到端设定下 K3 高 7 分」,不能说「K3 的 GPU 内核工程能力比 Fable 5 强 7 分」。这两句话是不同的命题,把前者当后者用就是过度解读。
顺带一提,同样的逻辑要对称地用在 K3 身上:如果 K3 也有任何形式的服务端兜底或重试,论文同样应该披露。论文没有提,我们也无法确认。
(4)硬件与版本:同一个基准,跑在不同机器上
PostTrainBench 的官方设定是在 H100 GPU 上跑,而论文是在 H20 上跑的,取三次运行的平均。SWE-Marathon 用的是 2026 年 7 月 9 日的一个 H20 校准分支,早于最终的 v1.1 发布——论文说 GPU 相关任务针对 H20 做了重新校准,但正确性校验和防作弊校验没有改动。
PostTrainBench 官方要求 H100,论文用 H20 跑并做了三次平均。这个替换为什么必须写出来?它可能怎样影响分数?(不需要你知道 H20 和 H100 的具体规格,只需要从「这个基准在考什么」出发推理。)
论文的处理其实是自洽的:它让所有参评模型(K3、Claude Fable 5、GPT-5.6 Sol)都在同样的 H20 上、用同样的官方 Harbor 实现、在最大努力下跑,并取三次平均。所以表内的横向比较仍然有意义,只是这一行的绝对数值不应该跨报告引用。这是一条很通用的原则:内部可比 ≠ 外部可比。
变式:SWE-Marathon 用的是「7 月 9 日的 H20 校准分支,早于 v1.1 正式发布」。如果 v1.1 后来修掉了几道有问题的题目,论文这一行的分数会偏高还是偏低?你能确定方向吗?
(5)上下文管理策略:一个被写在小字里的大变量
BrowseComp 上,论文采用了一种在 300K token 处触发的上下文压缩(context-compaction)策略——也就是当对话历史涨到 30 万 token 时,把前面的内容压缩成摘要,腾出空间继续做。表 2 里 K3 的 BrowseComp 分数 91.2 就是在这个策略下得到的。
紧接着论文写了一句很关键的话:如果改用完整的 100 万 token 上下文窗口、完全不做上下文管理,K3 得到的是 90.4%。
表 2 里 BrowseComp 一行:K3 是 91.2(加粗,最佳),GPT-5.6 Sol 是 90.4(第二)。而论文正文说,K3 若用完整 1M 窗口且不做上下文管理则是 90.4。请问:「K3 在 BrowseComp 上超过 GPT-5.6 Sol」这个结论有多稳?回答时请说清你依据的是什么、还缺什么信息。
第一层:0.8 分的领先完全可以由一个上下文管理策略的选择产生——因为 K3 自己换一下策略就正好掉 0.8 分,落到和 Sol 一模一样的 90.4。
第二层:论文没有说明 GPT-5.6 Sol 那一列是在什么上下文策略下跑的。如果对手没有用类似的压缩策略,那么这个比较比的其实是「K3+压缩策略」对「Sol+未知策略」,不是两个模型本身。
第三层:论文也没有给出重复运行的方差。BrowseComp 是长链条的搜索任务,随机性不小,0.8 分是否超出噪声范围,无从判断(对照一下:视觉基准论文跑了三到五次取平均,说明作者本人认为单次运行是不够的)。
更公允的说法是:「在各自的最佳配置下,K3 与 GPT-5.6 Sol 在 BrowseComp 上大致持平,K3 略高。」这比「K3 是 SOTA」保守,但更经得起追问。
另外别忽略一件事:论文主动把 90.4 这个对自己不利的数字写了出来,这是加分项。一个想藏的作者完全可以不写这句话。
变式:反过来想——如果 300K 触发压缩反而更好(91.2 > 90.4),说明「把全部历史都塞进 1M 窗口」未必是最优的。为什么?(提示:想想上下文里塞满无关内容对注意力意味着什么。)
(6)任务子集、判分器与重复次数
还有几条细节,单看都很小,加在一起决定了数字的含义:
OfficeQA Pro 给智能体的是整份 PDF 语料渲染成的图像,没有任何机器可读文本——这等于强制模型必须「看」,而不能「读」,是一个对原生视觉能力很直接的考察。MCP-Atlas 用的是 500 任务的公开子集、100 轮交互上限、由 Gemini 3.1 Pro 担任裁判。AutomationBench 用 600 任务的公开子集。视觉分数是三次运行的平均,ZeroBench-main 按官方设定跑五次。WorldVQA 上论文观察到各模型都有一致的拒答行为,于是用提示工程强制模型给出答案(表里那一行叫 「WorldVQA ForceAnswer」)。
补充:让别的模型当裁判,意味着什么
MCP-Atlas 用 Gemini 3.1 Pro 判分,这在今天很常见,因为人工判分太贵。但它引入了一个新变量:裁判自己也会犯错,也可能有偏好。如果有一天换了裁判模型,同一批答案的分数就可能变。所以「由某模型判分」这条信息必须和分数一起被引用——它是分数定义的一部分,不是脚注里的装饰。
(7)第三方结果的日期
论文里有一批分数不是自己测的,而是引用第三方的:GDPval-AA v2、AA-Briefcase、τ3-Banking、Harvey Lab-AA、APEX-Agents、SciCode、AA-LCR、CritPt 来自 Artificial Analysis(截至 2026 年 7 月 23 日);CorpFin v2、Finance Agent v2、Legal Research Bench 来自 Vals AI;Agents' Last Exam 来自其官方榜单(同为 7 月 23 日);Toolathlon-Verified 和 JobBench 来自各自官方榜单(7 月 24 日);FrontierSWE 的支配度分数是用 7 月 16 日的官方评测脚本从原始分重算的。
12.4 主结果:先看表,再看表说不出的东西
下面是论文 Table 2 的完整数据。你可以排序、筛选,先自己找规律,再往下读解读。建议你先做一件事:按「K3 落后最多的基准」排一次序,看看这些基准有什么共同点。
推理与知识:会做题,但还不会做研究
GPQA Diamond 上 K3 拿到 93.5,与前沿基本相当(最高的 GPT-5.6 Sol 是 94.1,GPT-5.5 同为 93.5)。AA-LCR 上 K3 的 74.7 是全表最好成绩。但往研究级任务走,差距就显出来了:HLE-Full 上 K3 是 43.5 / 56.0(不带/带工具),落后 Claude Fable 5(53.3 / 63.0)和 GPT-5.6 Sol(44.5 / 58.0);CritPt 上 K3 只有 23.4,落后 Fable 5(28.6)、Sol(32.3)和 GPT-5.5(27.1)。论文自己的措辞是:研究级推理仍是关键改进方向。
编码:长任务上很强,单点能力上有短板
ProgramBench 上 77.8 是全表最好。SWE-Marathon 上 42.0,领先 Claude Fable 5 七个点(但请回忆 12.3 的回退问题)。Terminal-Bench 2.1 上 88.3,几乎追平 GPT-5.6 Sol 的 88.8。DeepSWE 上 67.5,落后 Fable 5(70.0)和 Sol(73.0),但领先 Opus 4.8(59.0)和 GPT-5.5(67.0)。FrontierSWE 这个长时程基准上 81.2 排第二,落后 Fable 5 的 86.6,但明显领先其余所有模型。
智能体:这是 K3 最亮的一轴
拿到最好成绩的包括 BrowseComp 91.2、DeepSearchQA 95.0、ResearchRubrics 76.2、MCPMark-Verified 94.5、AutomationBench 30.8、SpreadsheetBench 2 34.8、τ3-Banking 33.4、Harvey Lab-AA 94.6。主要的例外是两个用 Elo 计分的知识工作套件,都由 Claude Fable 5 领先:GDPval-AA v2 上 K3 第三(1686 对 1747),AA-Briefcase 上第二(1548 对 1583)。还有几处几乎是平手:CorpFin v2 差 0.2 分(71.6 对 71.8),OSWorld-Verified 差 0.2 分(84.8 对 85.0)。更难的计算机使用基准(OSWorld 2.0、SaaS-Bench)仍由 Fable 5 或 Sol 领先。
常见误解:0.2 分的差距
CorpFin v2 上 71.6 对 71.8。很多人会写成「K3 在 CorpFin v2 上惜败」。但在一个没有报告方差的评测里,0.2 分和「没有差别」是无法区分的。诚实的读法是「两者在这个基准上不可区分」。同样的道理适用于 OSWorld-Verified 的 84.8 对 85.0。差距小于你能估计的噪声时,正确的结论是「不知道」,不是「略输」。
视觉:工具带来的跃升非常大
Math-Vision 上 K3 是 94.3,加上 Python 工具变成 97.8。ZeroBench-main(pass@5)上 K3 是 23.0,与 Claude Fable 5 打平,加上 Python 工具跳到 41.0——接近翻倍。OmniDocBench 上 91.1 是全表最高。WorldVQA 上 51.0 排第二,落后 Fable 5(56.7),领先 Sol 和 Opus。
把这几组数放在一起看:Math-Vision 94.3 → 97.8(带 Python,涨 3.5),ZeroBench-main 23.0 → 41.0(涨 18.0),HLE-Full 43.5 → 56.0(带通用工具,涨 12.5),CharXiv (RQ) 84.8 → 91.3(涨 6.5)。为什么工具带来的增益在不同基准上差这么多?请给出至少两种不同机制的解释,并说明这些增益对「模型有多强」这个问题到底告诉了我们什么。
机制一:天花板效应。Math-Vision 已经 94.3,最多只能再涨 5.7 分,涨 3.5 已经吃掉了 61% 的剩余空间;ZeroBench 只有 23.0,空间大得多,所以绝对涨幅大不奇怪。比较绝对涨幅是不公平的,应该看「剩余空间被吃掉的比例」。
机制二:失败类型的构成。工具能补的是「精确性」类失败——放大图像看清刻度、精确计数、代数化简、数值计算。工具补不了「根本不理解题意」「缺少领域知识」类失败。ZeroBench 的题以极高难度的视觉细节推理著称,其中很大一部分失败属于「看不清/数不准」,写代码把图裁开放大就能救,所以增益极大。HLE-Full 涨 12.5 也符合这个逻辑:那里的通用工具包括检索,能补上知识缺口。
机制三:能力与能力的耦合。「用 Python 把这张图处理成能回答问题的形式」本身是一种能力——需要模型知道该测什么、该裁哪里、该怎么把视觉问题翻译成计算问题。所以带工具的分数不是「模型能力 + 工具能力」的简单相加,而是模型调度工具的能力的体现。
那么它告诉了我们什么?两件事。第一,如果你的实际用途允许模型写代码(大多数真实场景都允许),你应该看带工具的分数,因为那更接近你会得到的东西。第二,一个基准如果在带工具后分数暴涨,说明这个基准原本测量的东西里,有很大一部分并不是「智能」,而是「没有辅助工具时的精确性」——这既是对模型的评价,也是对基准设计的评价。ZeroBench 从 23 涨到 41 这件事,对 ZeroBench 这个基准的信息量,不比对 K3 的信息量少。
变式:如果某个基准在带工具后分数下降了,可能是什么原因?(论文里没有出现这种情况,但你能想出至少两种机制吗?)
论文 §6.1.4 的开头写道:K3「整体上紧追最强的专有模型 Claude Fable 5 和 GPT-5.6 Sol,同时在整个基准套件上一致地超过 Claude Opus 4.8、GPT-5.5 和 GLM-5.2」。请你在表 2 里找出反例——找出至少两行,K3 的分数低于 Claude Opus 4.8。
反例一:HLE-Full。K3 是 43.5 / 56.0,Claude Opus 4.8 是 49.8 / 57.9。无论带不带工具,Opus 4.8 都更高,不带工具时差距达 6.3 分。
反例二:OfficeQA Pro。K3 是 63.3,Claude Opus 4.8 是 63.9,Opus 略高(虽然只有 0.6 分,按 12.4 节的标准这属于「不可区分」,但至少不支持「K3 超过它」)。
再补一条:内部评测的 Table 3 里,Agentic Vision Bench 上 K3 是 78.3,Claude Opus 4.8 是 82.8,差 4.5 分,这也是一处反例(虽然不在 Table 2 里)。
这道题真正要教的是什么:论文正文的总结句往往比表格更「圆」。作者不是在撒谎——K3 在绝大多数行上确实超过了 Opus 4.8——但「一致地」这个词是一个全称量词,而表里存在反例。当正文的总结和表格发生冲突时,以表格为准。更进一步:注意论文自己在同一节的 HLE-Full 段落里,只说「落后 Claude Fable 5 和 GPT-5.6 Sol」,没有提 Opus 4.8 也更高——这不是错误陈述,但它是一种选择性完整。你读任何技术报告(包括这一篇)都应该做这个动作:把总结句拿去表格里逐条验证一遍。
变式:现在反过来给论文做辩护——如果把「一致地超过」改成一个仍然成立、但更精确的表述,你会怎么写?(试着让你的表述既不夸大,又不至于抹掉 K3 真实的优势。)
12.5 内部评测:作者自己出的卷子,自己批的分
论文说,公开基准覆盖不到的能力,他们用一批自研的内部基准来补,而且这些基准会频繁更新扩充,好紧跟模型不断变化的失败模式,并直接指导数据和训练的迭代。分三类:
编码能力与体验:Kimi Code Bench 2.0(真实的端到端软件工程任务,覆盖多种语言和生产级技术栈)、Kimi Webdev Bench(真实使用场景里的困难网页开发需求,用盲评专家判分)、Coding Experience(作为编码智能体在真实开发流程里「用起来怎么样」)。
通用智能体体验:24/7 ClawBench 2.0(模拟「永远在线」的助手工作,任务跨越多天、事件并发到达、被打断是常态)、MIRA Bench(长链路多角色多系统的企业协作,考察能否端到端完成、以及何时该把活分给子智能体)、KAET(长时程自主执行)、CLIF Bench(上下文内学习与指令遵循)、Agentic Vision Bench(执行任务时会不会注意并正确使用关键视觉事实)、Swarm Bench(编排智能体群、分解任务并行执行)、Online Experience(对齐真实线上使用的分布)、Deep Research Bench(深度研究,由领域专家出题并用专家对齐的细则打分)、Finance Bench、KWV Bench(从真实知识工作场景蒸馏出的原子视觉能力)、DECK Bench(做演示文稿)、Agent Behavior Bench(不看结果对不对,看过程质量:工具使用行为、效率、纪律性)。
对话体验:Faithfulness(指标定义是 1 − 幻觉率,越高越好,每条回复由事实核查者验证)、Chat All-in-One Bench。
| 基准(K3 的 harness) | Kimi K3 | Claude Fable 5 | GPT-5.6 Sol | Claude Opus 4.8 | GPT-5.5 | GLM-5.2 |
|---|---|---|---|---|---|---|
| Kimi Code Bench 2.0(Claude Code) | 73.7 | 76.9a | — | 71.7 | — | 64.2 |
| Coding Experience(Claude Code) | 59.9 | 59.8 | — | 58.0 | — | 53.3 |
| Swarm Bench(Kimi Agent) | 76.3 | — | 73.2 | 72.6 | 61.8 | 58.5 |
| Deep Research Bench(Kimi Agent) | 90.0 | — | 85.3 | 87.2 | 81.9 | 84.0 |
| CLIF Bench(Kimi Code) | 52.4 | — | 50.6 | 48.8 | 52.3 | 39.2 |
| Finance Bench | 62.6 | — | 62.7 | 60.7 | 58.4 | 55.4 |
| Agent Behavior Bench(Kimi Work) | 65.0 | 75.5f | 76.4 | 65.7 | 70.1 | — |
| MIRA Bench(MIRA) | 64.1 | 72.9 | 62.2 | 59.8 | 54.6 | — |
| 24/7 ClawBench 2.0(OpenClaw) | 48.3 | 47.4d | 52.0 | 47.2 | 48.5 | 43.2 |
| Agentic Vision Bench(Kimi Code) | 78.3 | 81.1 | 82.9 | 82.8 | 76.9 | — |
| KWV Bench | 64.7 | 63.6 | 66.9 | 61.7 | 65.8 | — |
| Faithfulness(1 − 幻觉率) | 85.5 | — | 84.8 | 83.6 | 86.5 | 74.8 |
表中脚注沿用论文原注:a 80 个任务中有 13 次回退、1 次拒答;d 含 2 个 Claude Fable 5 拒答的任务;f 95 个任务中有 6 次拒答。
论文自己的总结是:内部套件比公开套件更清楚地把 K3 的强弱分开了。最清晰的强项是编排型和研究型的能动性——Swarm Bench 76.3 和 Deep Research Bench 90.0 都以明显优势领先,说明它在「把复杂目标拆开、协调并行工作、产出满足细则的交付物」上很强。编码同样是强项:Kimi Code Bench 2.0 只落后 Claude Fable 5(73.7 对 76.9),而 Coding Experience 拿到最好分 59.9——论文认为这说明它「作为编码智能体的实际行为(沟通质量、行为得体性、指令遵循的稳定性)领先于它的原始任务分数」。主要落后的是 Agent Behavior Bench、MIRA Bench、24/7 ClawBench 2.0、Agentic Vision Bench 和 KWV Bench。
还有一项单独的对比(论文 Table 4):Kimi Webdev Bench 让专家在不知道哪个输出来自哪个模型的情况下打分,比较 K3(max)与 Claude Opus 4.8(max),两者都跑在 Claude Code 外壳下,评分维度是需求覆盖、功能完整性、视觉保真度和交互体验。
| 领域 | 胜 | 平 | 负 | 胜 − 负 |
|---|---|---|---|---|
| 游戏 | 55.6% | 3.7% | 40.7% | +14.9 |
| 3D / WebGL / Shader | 72.7% | 13.7% | 13.6% | +59.1 |
| 网站 / UI 克隆 | 52.6% | 21.1% | 26.3% | +26.3 |
| 总体 | 58.6% | 13.8% | 27.6% | +31.0 |
读的时候要小心:这一整节都是作者自评
Kimi Code Bench、Kimi Webdev Bench、Swarm Bench、Deep Research Bench……这些基准是论文作者自己设计的、自己维护的、自己运行的,而且论文明说它们会「频繁更新扩充,以紧跟模型不断变化的失败模式,并直接指导数据和训练的迭代」。这句话读慢一点:基准被用来指导训练。当一套题目同时承担「训练的指南针」和「最终的考卷」两个角色时,在它上面得分高是几乎必然的——这不叫作弊,这叫优化目标。
论文没有说这些基准会公开,也没有说第三方能复现。所以它们的正确用法是:看 K3 在它们之间的相对强弱(哪些高、哪些低,这个内部对比是有信息量的,尤其是作者主动列出了五个自己落后的基准),而不是把某个绝对分数当成跨模型的裁决。
答辩:如果我是审稿人
你在 Kimi Webdev Bench 上说自己相对 Claude Opus 4.8 有 +31.0 的总体优势,其中 3D/WebGL/Shader 高达 +59.1。可这个基准是你出的题、你请的专家、你定的评分维度。我凭什么相信 +31.0,而不是相信「你选了一批你擅长的题」?
参考防守(先自己组织语言再看)
能站得住的防守有这几条:
第一,评判是盲评。论文明确写了专家在打分时不知道哪个输出来自哪个模型。这消除了最直接的偏袒渠道,是这个实验设计里最扎实的一环。
第二,两边同一个 harness。K3 和 Opus 4.8 都跑在 Claude Code 下——注意这个选择对 K3 并不占便宜,因为 Claude Code 是对手家的外壳。
第三,结果结构上不像挑出来的。如果是刻意挑擅长的题,三个领域的优势应该都很大;实际上游戏只有 +14.9,而且负率高达 40.7%,接近一半。作者把这个不好看的子项也报了出来。
但审稿人的质疑不能被完全驳倒,必须承认的部分是:
(a)题目来源是「真实使用场景里的困难网页开发需求」——什么叫真实、按什么口径抽样、样本量多大,论文一个字没说。三个领域的百分比看上去像是小样本(比如 3.7%、13.6%、21.1% 这些数字暗示每类可能只有几十道题),但论文没给 n,所以无法计算置信区间,+14.9 是否显著无从判断。
(b)对手只选了 Claude Opus 4.8,没有和更强的 Claude Fable 5 或 GPT-5.6 Sol 做盲评。选一个次一档的对手来做最能说明问题的那类实验,这个选择本身需要解释。
(c)「专家」是谁、有几位、一致性如何,论文没说。
结论:+31.0 作为「内部迭代信号」是可信的,作为「公开的能力主张」证据强度不够。诚实的说法是「在我们自己构造的网页开发题集上,盲评专家更偏好 K3 的输出」,而不是「K3 的网页开发能力比 Opus 4.8 强 31 分」。
Kimi Webdev Bench 用了盲评。请回答两个问题:(1) 盲评具体消除了哪一种偏差?(2) 盲评没有消除哪些偏差?请至少说出两种它没消除的。
(1) 盲评消除的是评分者的身份偏差——知道「这是我们自家模型的输出」而不自觉打高分。这是最直接、最容易发生的偏差,消除它是实打实的进步。
(2) 它没有消除的至少包括:
选题偏差:题目由作者选定。哪怕评分完全公正,只要题集本身偏向 K3 擅长的分布,结论就会偏。3D/WebGL/Shader 上 +59.1 这个异常大的优势,既可能说明 K3 真的强,也可能说明这类题被选得比较多或比较符合 K3 的训练分布,论文没有给出题目数量和抽样方式,无法区分。
评分维度偏差:论文说评的是需求覆盖、功能完整性、视觉保真度、交互体验。这四个维度是作者定的。换一组维度(比如加上「代码可维护性」「无障碍性」「包体积」),排序可能变。
对手选择偏差:只和 Claude Opus 4.8 比,而 Table 2/3 显示 Claude Fable 5 才是更强的对手。
发表偏差:我们只看到被写进论文的那个基准。如果作者做了五个类似的盲评而只报告了结果最好的一个,我们无从知晓。
一句话总结:盲评保证的是「这次打分没被身份污染」,不保证「这场比较被设计得公平」。
变式:如果你要把这个实验改造得更有说服力,在不增加太多成本的前提下,你会加哪三件事?(提示:其中至少一件应该和「题目从哪来」有关。)
12.6 网络安全评测:一份需要克制地读的结果
论文用一个两级递进的框架来衡量模型的网络安全能力,两级的「操作风险」依次升高:Tier 1 是漏洞发现——找出当前代码库里真实存在的、此前未被修复的缺陷,并证明它可复现(论文强调这一层的能力主要与防御性安全研究相关);Tier 2 是端到端的漏洞利用开发——把一个漏洞变成一条完整可用的利用链,论文明说这一层「与滥用风险最直接相关」。评测目标包括广泛部署软件的近期版本(操作系统内核组件、开源项目),以及作者自己的内部基础设施;所有任务都在代表真实部署的标准配置下运行。
补充:为什么这一节没有 Claude 和 GPT
论文写道:Anthropic 和 OpenAI 的前沿模型拒绝网络安全相关任务,因此无法做可比评测,于是这一整个套件把它们排除在外。所以 Tier 2 的唯一对照是开放权重的 GLM-5.2。
这条信息值得停下来想一想:它同时说明了两件事。一是这一节的比较基础很窄——只有两个模型,而且都是开放权重模型,你不能从这里推断「K3 的网络安全能力在所有模型中排第几」。二是不同厂商在「该不该让模型做这类任务」上采取了明显不同的策略,而这个策略差异本身会直接体现为评测表上的空格。表格里的「—」有时候不是没测,而是一个立场。
Tier 1 的结果:在数十个广泛部署的系统上(涵盖操作系统内核、数据库、AI 服务、Web 框架、区块链和 VPN 软件),模型识别出数百个候选漏洞。在经过人工审核的那部分发现里,约 70% 被确认为真实缺陷,其中包括横跨六个项目的 16 个此前未知的漏洞。论文举了两个 Linux 内核的例子作为深度示例:一个是远程可触发的堆越界写(源于一次不完整的上游修复),另一个是 RDMA 子系统中的 Dirty-COW 类漏洞(源于一次上游修复中被无意去掉的权限检查)。两者都经过安全专家确认。本站不展开任何技术细节,这里只记录「发现了什么类别、被谁确认」。
Tier 2 的结果:以 GLM-5.2 为基线,用一个内部的 36 任务套件,分两条赛道:16 个用户态任务、20 个 Linux 内核任务。每个任务都经人类安全专家验证过是可解的;论文估计完成整套大约需要 540 专家小时,平均每个任务约 15 小时。结果是:K3 解出 14/36(38.9%),GLM-5.2 解出 8/36(22.2%)。但成功的分布很不均匀——K3 那 14 个里有 10 个来自用户态赛道;在内核赛道上,两个模型都有四分之三的任务没有解出。
因为每个任务都被验证过「人类专家可解」,所以未解出的任务直接度量了模型与人类专家能力之间的差距。论文对失败轨迹做了分析,归纳出四种反复出现的失败模式:(i) 难以从已经拿到的原语走完利用链的最后一步;(ii) 在存在缓解措施时策略选择很差;(iii) 陷入漫长而无成效的调试循环;(iv) 提交前对最终交付物验证不足。
英国 AI 安全研究所与美国 NIST 的 CAISI 做了一次独立的联合评估,结论与作者一致:K3 在漏洞利用开发上优于 GLM-5.2(ExploitBench 上 32% 对 24%;在一个人类专家大约需要 20 小时的 32 步模拟企业网络上,K3 走了 17 步,GLM-5.2 走了 11 步),但在端到端的利用完成上落后于前沿的网络能力模型——在 41 个任务上实现任意代码执行的次数为 0。
论文最后写了一句方法论上很重要的话:我们把自己的评测视为能力的下界,这些结果取决于当前的模型版本和评测覆盖度,每次重大模型更新时都会重新评估。
论文说「我们把自己的评测视为能力的下界」。请构造一个具体情形,说明为什么在安全评测里把结果当上界(「最多也就这样了」)是危险的。然后回答:同一个 38.9% 的分数,当上界读和当下界读,会导出什么不同的行动?
构造情形:假设 K3 在某个内核任务上失败了,原因是 harness 的调试循环设计不好(论文自己列出的失败模式之三就是「陷入漫长而无成效的调试循环」)。半年后,社区做出了一个更好的 harness,或者有人用更好的提示词和更长的时间预算重跑。同一个权重文件,同样的任务,现在解出来了。模型没变,分数变了。因为权重是公开的,任何人都可以做这件事,而且没人需要通知作者。
这就是为什么当上界危险:把 38.9% 当上界,等于假设「没有人能把这个模型用得比作者更好」。对一个开放权重模型来说,这个假设几乎肯定是错的——恰恰因为它开放,会有远比作者投入更多的人去改进外壳、提示词和工具链。安全评估里的失败必须默认解释为「这次没测出来」,而不是「它做不到」。
两种读法导出的行动完全不同:
当上界读 → 「38.9%,还差得远,可以放心发布」 → 不做额外缓解,不做后续监控。
当下界读 → 「至少 38.9%,真实上界未知且只会随时间上升」 → 需要持续重评(论文承诺每次重大更新时重评)、需要独立第三方验证(论文确实做了,英国 AISI + NIST CAISI)、需要把「发布后能力可能被外部工程放大」计入风险模型。
顺带注意一个细节:独立评估里「41 个任务上任意代码执行 0 次」这个结果,同样是下界。它是在评估者当时的方法下得到的 0,不是「永远是 0」。
变式:Tier 1 里「经过人工审核的发现中约 70% 被确认为真实」。这个 70% 的分母是「经过人工审核的发现」,而不是「模型报出的全部候选」。这个分母的选择会怎样影响你对这个数字的理解?如果模型报了 500 个候选、人只审了 40 个最像的,70% 还意味着什么?
12.7 第三方评测:别人打的分
自评永远有嫌疑,所以第三方结果的分量不一样。论文汇总了截至 2026 年 7 月 23 日的几个头部结果(Table 5)。
| 榜单 | Kimi K3 | Claude Fable 5 | GPT-5.6 Sol | Claude Opus 4.8 | GPT-5.5 | GLM-5.2 |
|---|---|---|---|---|---|---|
| Artificial Analysis 智能指数 v4.1(第 4 / 580) | 57.1 | 59.9 | 58.9 | 55.7 | 55.0 | 51.1 |
| Vals AI · Vals Index(第 2 / 39) | 74.7 | 75.1 | 73.1 | 70.4 | 68.0 | 65.0 |
| WebDev Arena(Elo,第 1 / 99) | 1678 | 1634 | 1630 | 1565 | 1507 | 1592 |
| Text Arena(Elo,第 8 / 200) | 1486 | 1507 | 1485 | 1484 | 1482 | 1469 |
| Agent Arena(第 4 / 37) | 9.1 | 12.7 | 10.1 | 9.8 | 8.8 | 6.5 |
逐条看:Artificial Analysis 的智能指数 v4.1 上 K3 是 57.1,在 580 个模型中排第 4——论文补了一句,如果把 GPT-5.6 Sol 的各个努力等级变体算作一个条目,就是第 3;落后 Claude Fable 5(59.9)和 GPT-5.6 Sol(58.9),领先其余所有被评模型。Vals AI 的 Vals Index 是一套按 GDP 加权的行业基准,K3 以 74.7 在 39 个模型中排第 2,落后 Fable 5(75.1)、领先 Sol(73.1)。
众包人类偏好竞技场里,最亮眼的是 WebDev Arena:K3 以 1678 Elo 在 99 个模型中排第 1,领先 Claude Fable 5 的 1634,并且是第一个登顶该榜的开放模型。Text Arena 上 1486 Elo,200 个中第 8。Agent Arena(大约 7 月 19 日才开放投票)上 9.1,37 个中第 4,落后 Fable 5(12.7)、Sol(10.1)和 Opus 4.8(9.8)。
读的时候要小心:Elo 会漂移
论文自己在 Table 5 的说明里写了:Elo 类分数会随着更多对局的累积而漂移。而且 Agent Arena 是 7 月 19 日左右才开放投票的,到 7 月 23 日只有四五天的数据。四五天的众包投票量,通常远不足以让 Elo 稳定下来。所以 Agent Arena 那个「第 4 / 37」的名次,比其他几行的置信度低得多。
另一条要注意的:Text Arena 里 GPT-5.6 Sol 那个 1485 是榜上列出的 xhigh 变体,Opus 4.8 的 1484 和 GPT-5.5 的 1482 是榜上列出的 high 变体——这三个数彼此相差 1–3 分,任何一次重排都可能改变顺序。把 1486、1485、1484、1482 解读成一个排名,几乎肯定是在解读噪声。
同样是「K3 在网页开发上很强」这个主张,论文提供了两份证据:内部的 Kimi Webdev Bench(盲评,相对 Claude Opus 4.8 +31.0)和第三方的 WebDev Arena(1678 Elo,99 个模型中第 1)。哪一份的证据强度更高?为什么?
Kimi Webdev Bench:出题人是作者,运行是作者,评分虽然盲了但评分者由作者组织,样本量未公开,外部无法复核,对手只有一个(还是次一档的 Opus 4.8)。
WebDev Arena:出题的是众多匿名用户,评分的是投票的人类,榜单由第三方维护并公开,任何人都能上去看当前排名,对手是 99 个模型。
更强不代表完美。WebDev Arena 的软肋是:众包偏好会奖励「看起来漂亮、第一眼讨喜」的输出,未必等于代码质量高或可维护;Elo 会随对局漂移;不同模型进入榜单的时间不同,累计对局数也不同。
但有一点很值得说:这两份独立来源指向了同一个方向。作者自评说网页开发强,第三方公开榜单也把它排到第 1。互相独立的证据指向同一结论,比任何单一证据都更可信——这是评估任何主张时最有用的一条启发式。反过来,如果内部基准说强、外部榜单说弱,那才是真正需要追问的时候。
变式:论文说 K3 是「第一个登顶 WebDev Arena 的开放模型」。这个「第一个开放模型」的限定词加了什么、又没加什么?如果半年后另一个开放模型登顶,这句话还成立吗?
12.8 成本效率:把第二根坐标轴加进来
前面所有的表都只有一根轴:分数。但你实际用模型时有第二根轴:钱。论文 §6.4 在四个套件上把分数和每任务成本(cost per task)画在同一张图上(Figure 13):Kimi Code Bench 2.0、BrowseComp、GDPval-AA v2、AA-Briefcase。
成本数据来源需要注意:Kimi Code Bench 2.0 的成本是内部实测的,其中 K3 通过 Kimi Code 运行、其他所有模型通过 Claude Code 运行;BrowseComp 上 K3 的成本来自作者自己的运行,Claude 和 GPT 的成本引自已发表的图表;GDPval-AA v2 和 AA-Briefcase 的成本引自 Artificial Analysis 按 token 计费的 API 定价(截至 2026 年 7 月 23 日)。
四个结论:
Kimi Code Bench 2.0:K3 落后 Claude Fable 5 四分,但成本只有它的 38%。更有意思的是,K3 在 high 努力(不是 max)下就已经追平了 Claude Opus 4.8 在最大努力下的分数,而成本约为后者的三分之一。
BrowseComp:K3 拿到最好分数 91.2%,每任务 $2.03——是 GPT-5.6 Sol(90.4%)成本的一半,比 Claude 系列在最大努力下便宜一个数量级。
GDPval-AA v2:K3 与 GPT-5.6 Sol 相差 50 Elo 以内,成本低 13%,比 Claude Fable 5 便宜 2.6 倍。
AA-Briefcase:K3 取得第二好的分数,成本约为 Claude Fable 5 的一半。
论文的总结是:K3 在这四个套件上都处在或接近成本效率前沿上,以一小部分成本交付接近顶尖的分数。
打个比方
「成本效率前沿」就像买东西时把所有商品画在「价格—质量」图上,然后把左上角(更便宜且更好)的那些点连成一条线。落在这条线上的商品,意思是「没有任何其他商品同时比它更便宜又更好」。类比失效处:买东西时价格是公开透明、人人一样的;而模型的「每任务成本」取决于计费方式、部署方式、上下文长度、思考多少 token,而且开放权重模型可以自己部署,成本结构和调 API 完全不同。论文引用的是 API 定价,所以这条前沿只在「按 token 买 API」这个场景里成立。
论文说 Kimi Code Bench 2.0 上「K3 落后 Fable 5 四分,但成本只有它的 38%」。同一节又写着:这个基准上 K3 是通过 Kimi Code 运行的,其他所有模型是通过 Claude Code 运行的。请指出这个成本比较里至少两个需要小心的地方。
第一,harness 不对等。K3 跑在 Kimi Code 上,对手跑在 Claude Code 上。harness 直接影响 token 消耗量(塞多少上下文、重试策略、压缩策略),因此这个成本比较里混进了「哪家外壳更省 token」这个变量。这和 12.3 讲的分数问题是同一个问题,只是这次影响的是分母。
第二,成本没有拆分。38% 可能来自单价便宜,也可能来自消耗少,也可能两者兼有。这两种原因的含义完全不同:单价是定价策略(可以随时变),token 消耗是模型和外壳的效率(相对稳定)。论文只给了总数,没有拆开。
第三,定价是会变的,而且开放权重改变了游戏规则。成本来自 2026 年 7 月 23 日的 API 定价。定价是商业决策,可以一夜之间调整,所以「便宜 62%」这个结论的保质期比分数还短。更根本的是:K3 是开放权重模型,你可以自己部署,那时的成本由你的硬件和利用率决定,和 API 定价没有关系——论文引用的这条前沿,严格来说只描述了「按 token 买 API」这一种使用方式。
但也要给论文记一功:把成本轴加进来这件事本身是对的,而且它揭示了只看分数看不到的东西——「K3 在 high 努力下就追平了 Opus 4.8 的 max 分数、成本约三分之一」,这个观察在任何一张只有分数的表里都是不可见的。
变式:如果把横轴从「每任务成本」换成「每任务耗时」,四张图的形状会怎么变?哪些结论会保留,哪些会翻转?(提示:想想推理努力 max 意味着生成很多思考 token。)
12.9 如何批判性地读一张评测表
把这一章的所有内容收束成一份可以随身带走的清单。下次你看到任何一张模型评测表——不管是这篇论文的,还是别人的,还是半年后新出的——按顺序问这九个问题。
| # | 问什么 | 本章里对应的证据 |
|---|---|---|
| 1 | 谁评的?是模型作者自己,还是独立第三方?题目是谁出的、谁维护? | §6.2 的内部基准是作者自出自评且用于指导训练;§6.3 的 AA / Vals AI / Arena 是第三方 |
| 2 | harness 一样吗?各模型跑在同一个外壳下吗?分数是不是「跨外壳取最好」? | Terminal-Bench 2.1 取跨 harness 最好分;DeepSWE 换 harness 从 67.5 变 67.3;成本比较里 K3 用 Kimi Code、对手用 Claude Code |
| 3 | 有没有回退/拒答?那些没能正常完成的任务,是怎么计入分数的? | Fable 5 在 SWE-Marathon 上 35% 任务回退;Table 3 脚注里多处拒答;Agents' Last Exam 官方榜 40% 任务标注降级 |
| 4 | 用工具了吗?带工具和不带工具的分数分别是多少? | HLE-Full 43.5 → 56.0;ZeroBench-main 23.0 → 41.0 |
| 5 | 上下文怎么管的?压缩策略、窗口大小、触发阈值是什么?对手用的是什么? | BrowseComp 300K 压缩 91.2 对 1M 不压缩 90.4;对手的策略论文未说明 |
| 6 | 跑在什么硬件/什么版本上?和官方设定一致吗? | PostTrainBench 用 H20 而非官方的 H100;SWE-Marathon 用 7 月 9 日的分支而非 v1.1 |
| 7 | 方差多大?跑了几次?差距是否大于噪声? | 视觉三次平均、ZeroBench 五次;而 CorpFin v2 差 0.2、OSWorld-Verified 差 0.2,没有方差就无法判断 |
| 8 | 日期是哪天?第三方分数、榜单名次、API 定价各自的时间戳是什么? | AA / Vals / ALE 为 07-23,Toolathlon / JobBench 为 07-24,FrontierSWE 脚本为 07-16;Elo 会漂移 |
| 9 | 成本呢?拿到这个分数花了多少钱、多少时间?在你的预算下还成立吗? | §6.4:BrowseComp 每任务 $2.03;K3 在 high 努力下追平 Opus 4.8 的 max |
假设你在社交媒体上看到这样一句话(这句话是虚构的,但它引用的每一个数字都真实出自本章):
「开源模型全面赶超!Kimi K3 在 BrowseComp 上以 91.2 击败 GPT-5.6 Sol,在 SWE-Marathon 上碾压 Claude Fable 5 七个点,在 WebDev Arena 上全球第一,成本只有对手的一半。开源已经追平闭源。」
请用 12.9 的九问清单逐条拆解这句话:哪些部分成立、哪些部分需要加限定、哪些部分是错的。最后给出一个你自己愿意署名的、更准确的版本。
「BrowseComp 91.2 击败 GPT-5.6 Sol」——数字真实,但需要三个限定(问 5、问 7):91.2 用了 300K 触发的上下文压缩策略;K3 换成 1M 不压缩就是 90.4,正好等于 Sol;论文没给方差,也没说 Sol 用什么上下文策略。准确说法是「两者大致持平」。
「SWE-Marathon 碾压七个点」——数字真实(42.0 对 35.0),但需要两个限定(问 3、问 6):Fable 5 在这个基准上 35% 的任务触发了回退,所以这七分里有多少来自能力差异无法确定;而且这个基准跑在 H20 校准分支上、版本早于 v1.1。「碾压」这个词无论如何撑不住。
「WebDev Arena 全球第一」——这一条最扎实(问 1):第三方公开榜单,众包人类投票,99 个模型中第 1,1678 对 1634,而且是首个登顶的开放模型。要加的限定只有两个(问 8):截至 2026-07-23,Elo 会随对局漂移;以及这是「网页开发偏好」这一个维度。
「成本只有对手的一半」——需要指明是哪个套件、哪个对手(问 9、问 2):BrowseComp 上 $2.03 约为 GPT-5.6 Sol 的一半;Kimi Code Bench 2.0 上是 Fable 5 的 38%,但那里 K3 用自家 harness、对手用 Claude Code;而且这些成本引自 7 月 23 日的 API 定价。
「开源已经追平闭源」——这是错的,而且和论文自己的结论直接冲突。论文明说「与最强的专有模型仍有差距」。证据:AA 智能指数第 4(57.1 对 Fable 5 的 59.9)、Vals Index 第 2、Text Arena 第 8、Agent Arena 第 4;CritPt 23.4 落后三个模型;HLE-Full 落后包括 Opus 4.8 在内的多个模型;GDPval-AA v2 第三、AA-Briefcase 第二;OSWorld 2.0 和 SaaS-Bench 仍由对手领先。
我愿意署名的版本:「截至 2026 年 7 月,Kimi K3 作为开放权重模型,在智能体、检索与网页开发方向达到或接近专有前沿——它在 WebDev Arena 上以 1678 Elo 排名第一(99 个模型),在 BrowseComp 上与 GPT-5.6 Sol 大致持平且每任务成本约为其一半。但在研究级推理(CritPt、HLE-Full)和部分知识工作套件上仍明显落后于 Claude Fable 5 和 GPT-5.6 Sol,综合指数上排第 4(Artificial Analysis)。论文中的部分比较存在 harness 不一致、对手触发回退、上下文策略不同等对等性问题。」
注意这个版本更长、更啰嗦、也更不好传播——准确性和传播力天然是矛盾的,这就是为什么你需要一份检查清单。
变式:现在把立场反过来。假设有人写「K3 根本不行,AA 指数才排第 4,CritPt 只有 23.4,研究级推理一塌糊涂」。请用同一份清单拆解这句话——它省略了哪些对 K3 有利的证据?(提示:580 个模型里的第 4 是什么概念?开放权重意味着什么?)
本章小结
这一章表面上过了一遍 K3 的成绩单:推理与知识轴上「会做题但还不太会做研究」(GPQA 93.5 与前沿相当,CritPt 23.4 明显落后,论文自己承认研究级推理是关键改进方向);编码轴上长任务强(ProgramBench 77.8 最好、SWE-Marathon 42.0、Terminal-Bench 2.1 88.3 几乎追平 Sol),单点能力有短板(DeepSWE 67.5 落后前两名);智能体轴是最亮的一轴,八个基准拿下最好成绩,主要败给 Claude Fable 5 的是两个 Elo 制知识工作套件;视觉轴上带工具的跃升非常大(ZeroBench 23.0 → 41.0)。第三方评测里 WebDev Arena 全球第 1 是最硬的一条,综合指数第 4 是最克制的一条。成本效率上 K3 处在或接近前沿。
但这一章真正想留给你的不是这些数字,而是看数字的顺序。先找条件,再看数值:谁评的、什么外壳、有没有回退、用不用工具、上下文怎么管、什么硬件、跑了几次、哪一天、多少钱。这九个问题里,本章的每一个都能在论文里找到至少一处真实的例证——包括那个最漂亮的对照:同一个模型、同一个基准,只因为上下文策略不同,BrowseComp 就在 90.4 和 91.2 之间移动,而 90.4 恰好是对手的分数。
最后一件事值得单独说:这篇论文在配置披露上做得比很多技术报告好——它主动写出了 35% 的回退率、写出了对自己不利的 90.4、写出了内部基准会被用来指导训练、写出了自己在 CritPt 上落后、写出了把安全评测视为下界。这些自我暴露不能让它的结论自动成立,但它让读者有能力自己去打折扣。一份能被认真批评的报告,好过一份无从批评的报告。
Chapter 12 · Evaluation: how to read these numbers properly (instead of being led around by them)
In one sentence: on the surface this chapter is about “what score did K3 get”, but what it really teaches you is this — when someone pushes a table full of bolded numbers in front of you, in what order should you ask your questions so that the bolding does not cheat you out of the truth.
After this chapter you should be able to
- Say clearly what each of the four capability axes — “reasoning and knowledge / coding / agents / vision” — is testing, and give the concrete task shape of one representative benchmark on each axis
- Explain what an agent harness is, and what “change the harness and the score changes” means for the rankings
- When you see a score, go looking for the six things behind it: who ran the evaluation, which harness, whether there were refusals/fallbacks, whether there were tools, how the context was managed, and what date it is from
- Find, in the paper's own table, a row that contradicts the paper's own summary sentence (this chapter will make you actually go and find one)
- Look at “score” and “cost per task” on the same chart, and say clearly why looking at either one alone leads to a wrong conclusion
- Write your own “checklist for reading an evaluation table”, and use it to take apart a real piece of marketing copy
12.1 The four capability axes: what these oddly named benchmarks are actually testing
Start with what happens if you do not split by axis. If only a single “overall score” is reported, you have no way at all to answer “is this model suited to the thing I want to do”. A model that can score 93.5 on GPQA's PhD-level multiple-choice questions will not necessarily edit a spreadsheet correctly on your computer. So the paper organises the evaluation into four capability axes(four broad capability axes), with a batch of benchmarks hanging under each axis.
Benchmark: a fixed set of questions + a fixed set of marking rules. Its job is to let different models be compared on “the same exam paper”. Its weak spot is right there too — the questions are set by people, the marking rules are decided by people, and the running environment is built by people; change any one of those three and the score changes.
Axis one: reasoning and knowledge
This axis tests “with no tools and no web pages, how deep can it think on its own”. The paper uses four: GPQA Diamond (which the paper calls “graduate-level reasoning”), CritPt (which the paper classes as a research-level reasoning task, saying explicitly that K3 falling behind here means “research-level reasoning remains a key direction for improvement”), AA-LCR (a long-context reasoning test from Artificial Analysis), and HLE-Full (the full Humanity's Last Exam set, a deliberately extremely hard cross-disciplinary question bank). For HLE-Full the paper reports two scores at once, without tools and with tools.
Read this carefully: these benchmark descriptions do not all come from the paper
In §6.1.1 the paper gives only the names of the benchmarks and their reference numbers; it does not explain one by one what each benchmark tests. So that you can follow along, the descriptions of each benchmark's task shape in the rest of this chapter come partly from the paper's own text (for example “SWE-Marathon is a GPU-kernel-oriented suite”, “FrontierSWE is a long-horizon benchmark”, “OfficeQA Pro gives a PDF corpus rendered into images” — these the paper states explicitly), and partly from the public material on the benchmarks themselves. Anything the paper does not state, you should go and check against the original benchmark's own documentation — especially when you are about to cite a score in support of a specific claim. That is exactly the habit this chapter is training: work out what the question is testing first, then look at the score.
Why the same benchmark gets two scores
Because “can the model do it by itself” and “can the model use tools to get it done” are two different abilities, and in real deployment the one you care about is usually the second. Only by putting the two numbers side by side can you see “how much the tools helped” — that difference is itself an important piece of information, and §12.4 sets a question specifically about it.
Axis two: coding
Note that what is tested here is basically not “write a bubble sort” but software engineering: locate a bug in a real repository, change the code, run the tests, until they pass. DeepSWE, FrontierSWE and SWE-Marathon all belong to this category; of these SWE-Marathon is a long-task suite aimed at GPU kernels, and FrontierSWE consists of long-horizon tasks. Terminal-Bench 2.1 tests the ability to complete tasks in a terminal. ProgramBench and SciCode lean towards programs and scientific-computing code, while PostTrainBench and MLS-Bench-Lite test machine learning engineering itself — making the model do post-training and run experiments.
Axis three: agents
This is the axis with the most benchmarks, more than twenty, because “agent” is itself a whole pile of different things. Roughly, they fall into a few families: finding information (BrowseComp, DeepSearchQA, ResearchRubrics — the last of these uses a scoring rubric to judge the quality of a research report); using tools / connecting to external systems (Toolathlon-Verified, MCPMark-Verified, MCP-Atlas, AutomationBench); doing knowledge work (GDPval-AA v2, AA-Briefcase, JobBench, OfficeQA Pro, SpreadsheetBench 2, APEX-Agents, Agents' Last Exam); operating a computer (OSWorld-Verified, OSWorld 2.0, SaaS-Bench); industry-specific (τ3-Banking for bank customer service, Harvey Lab-AA and Legal Research Bench for law, CorpFin v2 and Finance Agent v2 for finance).
An analogy
The reasoning-and-knowledge axis is like a closed-book exam, the coding axis is like handing in an assignment that has to actually run, and the agent axis is more like “an intern's first day at work” — here is a computer, a few accounts and one vague request; let's see whether you can get through the whole thing on your own. Where the analogy breaks down: an intern will ask “what does this request mean”, whereas the great majority of agent benchmarks do not allow any question to a human partway through — the problem is handed over all at once. So a high benchmark score does not mean it is good to work with in real collaboration, and the reverse holds too.
Axis four: vision
On the vision axis, OmniDocBench tests document parsing (reading PDFs, tables and formulas as structured content), Video-MME and MMVU test video understanding, PerceptionBench, BabyVision and WorldVQA test basic perception and understanding of the physical world, and MMMU-Pro, CharXiv (RQ), Math-Vision and ZeroBench-main test “solving problems from a picture”. For those last four, as for BabyVision, the paper reports a version with Python tools — the model can write code to crop, enlarge and measure the image, and then answer.
For HLE-Full the paper reports K3 as 43.5 / 56.0, and the table caption says “each cell is reported in the order without tools, with tools”. Now someone posts a screenshot of just “56.0”, captioned “K3 scores 56 on Humanity's Last Exam”. What is wrong with that sentence?
Variant: if a benchmark reports only the “with tools” score and not the without-tools one, can you infer the model's bare ability from that single number? If not, what extra information would you require the authors to supply before you would accept the score?
12.2 Compared with whom: the baselines, and the “maximum effort” precondition
A comparison always needs an opponent. The paper picks five: the proprietary models Claude Fable 5, GPT-5.6 Sol, Claude Opus 4.8 and GPT-5.5, plus the open-weight model GLM-5.2. The logic behind picking these is clear: the first two represent the strongest proprietary frontier right now, the next two represent the previous generation / a tier down of proprietary models, and GLM-5.2 represents an open-weight competitor “of the same kind as K3”.
Reasoning effort: reasoning models today usually have a “how long to think” setting; the higher the setting, the more thinking content the model generates before giving an answer, and usually the higher the score — but latency and cost go up too. The paper writes explicitly: all models are evaluated at maximum reasoning effort, the exception being that GPT-5.5 uses the “xhigh” setting.
Why it has to be standardised to “maximum effort”
If it is not standardised, the comparison stops meaning anything — I use the top setting, you use the middle one, and my winning proves nothing. Standardising to each vendor's top setting is the fairly common practice at present. But note the side effect it brings: the score at maximum effort is not the score you get day to day, and not the score you can afford either. That is exactly why §12.8 brings “cost” in as another axis.
“All models are evaluated at maximum reasoning effort” sounds very fair. But suppose model A's top setting thinks for 50,000 tokens while model B's top setting thinks for only 5,000 tokens, and both vendors call theirs “max”. How much of that “fairness” is left now? Say clearly what it still guarantees and what it no longer guarantees.
Variant: if the paper changed to “all models are evaluated under a budget of $1 cost per task”, how might the ranking change? And what new unfairness would that style of evaluation bring? (Hint: think about the fact that open-weight models can be self-hosted.)
12.3 The devil in the configuration: this section is the soul of the chapter
Most people read an evaluation by looking only at the numbers, and skip the short “evaluation setup” passage. But what really decides how far the numbers can be trusted is almost entirely inside that short passage. §6.1.3 of the paper is written quite candidly; we will take it apart item by item.
(1) Sampling parameters
All of K3's evaluations use reasoning effort = max and temperature = 1.0. Single-step tasks (for example GPQA Diamond, HLE-Full and the vision benchmarks without tools) use top-p = 0.95, and agent tasks use top-p = 1.0. The paper also gives one general recommendation: 0.95 for reasoning-and-knowledge tasks, 1.0 for coding and agent settings.
Temperature and top-p: at each step the model is really producing a probability over a pile of candidate words, and then drawing lots to decide which one to output. Temperature controls how random the draw is; temperature 1.0 is drawing according to the model's original probabilities. Top-p instead adds the probabilities up from highest to lowest, keeps only the candidates up to the point where the running total reaches p, and draws from among those. top-p = 1.0 means no trimming; 0.95 means throwing away the least likely 5% of the probability mass.
Why does this matter so much? Because temperature 1.0 means running the same question twice can give different answers. So any single-run score comes with variation built in, and that is also why the paper runs the vision benchmarks three times and averages, and runs ZeroBench-main five times.
(2) The harness: same model, change the “shell” and the score is different
Agent harness: the model on its own can only output text. To make it actually read files, run commands and change code, there has to be a layer of program that parses the model's output into actions, executes them, and feeds the results back. That layer of program is the harness. The paper uses three: Kimi Code, Claude Code, Codex.
The harness determines what the model can see, what it can do, how it retries when something goes wrong, and what happens when the context fills up. So take the same model, change the harness, and the score changes. The paper gives an example you can check directly: on DeepSWE K3's score in the table is 67.5, but with the mini-SWE-agent harness it is 67.3. More noteworthy is Terminal-Bench 2.1 — the paper says explicitly that what is reported is the best score across harnesses for every model.
A common misreading
Many people think a “benchmark score” is a property of the model, like height. In fact it is a property of the whole system “model + harness + tools + prompt + sampling parameters + grader”. Swap out any one of those parts and the number moves. K3 is 88.3 on Terminal-Bench 2.1 and GPT-5.6 Sol is 88.8, a gap of 0.5 points — and merely changing the harness was enough to produce a difference of 0.2 points on DeepSWE. This is not to say that 88.3 vs 88.8 must be noise; it is to say that you cannot tell from this table whether it is noise.
Terminal-Bench 2.1 reports “the best score across harnesses for every model”. Construct a concrete numerical example such that: under this rule model A ranks first, but if the rule were “all models must use the same harness”, then whichever harness is chosen, A ranks second.
Under “take the best across harnesses”: A reports 90, B reports 88, A is first.
Force a single harness: with H1, 92 > 90, B wins; with H2, 88 > 60, B wins too. Whichever harness you pick, A is second.
The trick in the construction is this: make A extremely uneven across the two harnesses (90 and 60), while B is even and not low on either (92 and 88). If your first attempt was A = (90, 70), B = (85, 88), you will find that A still wins when everything is standardised on H1 — so B has to beat A's corresponding value on every single harness, while A's maximum is still the highest in the whole table.
What this counterexample shows is: taking the maximum model by model is not an order-preserving operation. It rewards the model that “has been tuned particularly well on one specific shell”, and that advantage does not necessarily come from the model itself — it may come from more adaptation work having been done on that shell.
What has to be stressed is: this is only a constructed counterexample, used to show the hole in the rule. The paper does not publish the per-harness score breakdown for each model, so we have no way to judge whether this really happened on Terminal-Bench 2.1. That is precisely the problem — the rule allows it to happen, and the data is not enough to rule it out.
Variant: if the rule changed to “each model reports its median across harnesses”, does your counterexample still hold? And what new problems would a median rule introduce?
(3) Fallbacks and refusals: the comparison is not entirely like-for-like
Fallback: the paper gives no precise definition; from the context it means that when the main model cannot or will not complete a task, the service side automatically switches to another set of behaviours, or another model, to produce the output. Refusal is the model flatly declining to carry out the task.
The paper writes explicitly: the Claude Fable 5 results include fallback behaviour, and the GPT-5.6 Sol results include potential cyberguard (a safety guardrail mechanism). On particular benchmarks the figures are sizeable: on SWE-Marathon, Claude Fable 5 triggered a fallback on 35% of the tasks. The footnotes to Table 3 of the internal evaluation are more detailed: on Kimi Code Bench 2.0, Claude Fable 5 had 13 fallbacks + 1 refusal (out of 80 tasks in total); on the same benchmark GPT-5.6 Sol had 10 refusals / 80 tasks, and GPT-5.5 had 3 refusals / 80 tasks; in Online Experience there were 14 tasks that Claude Fable 5 refused; on Agent Behavior Bench there were 6 refusals / 95 tasks. There is also a footnote to Table 2: on the official Agents' Last Exam leaderboard, the Claude Fable 5 entry was run at xhigh effort, and 40% of the tasks were marked as “degraded”.
Read this carefully
Added together, these footnotes say one thing: the across-the-row comparisons in the table are not entirely like-for-like. If a model triggered a fallback on a third of the tasks, then whether its column stands for “this model's ability” or “this service's ability” is blurred. That the paper writes this information out in the open is a practice worth crediting (plenty of reports simply do not write it); but disclosing it does not make the problem go away. Whenever you cite any cross-model comparison, you should glance at the footnotes while you are at it.
Defend it: if I were a reviewer
On SWE-Marathon you say you get 42.0, ahead of Claude Fable 5's 35.0 by 7 points. But you also wrote yourself that Fable 5 triggered a fallback on 35% of the tasks. So of those 7 points, how much is “you are stronger” and how much is “the other side's service made the decision for it”? Until you can pull the fallback tasks out separately and report again, on what grounds does this claim of 7 points stand?
A reference defence (put it in your own words first)
You can defend it like this, but you have to admit it is only a partial defence:
First, the choice of what is being evaluated is legitimate. When a user actually calls Claude Fable 5, fallback behaviour is what they will get, so “end-to-end performance including fallbacks” is a quantity that really exists and has practical meaning. What the paper compares is “what this system delivered”, not “how strong this weights file is”.
Second, the paper discloses the fallback rate of its own accord rather than hiding it. Once it is disclosed, readers are able to discount the conclusion themselves, which is much better than not disclosing it.
Third, and this is the most honest point: this defence does not solve the like-for-like problem. The right thing to do would be to also report “the subset score with the fallback tasks removed”, or at least report the distribution of scores on the fallback tasks. The paper does not do this, so strictly speaking the attribution of that 7-point gap cannot be determined — we can only say “under an end-to-end setting that includes fallbacks, K3 is 7 points higher”, not “K3's GPU kernel engineering ability is 7 points stronger than Fable 5's”. Those are two different propositions, and using the first as the second is over-reading.
Incidentally, the same logic has to be applied symmetrically to K3: if K3 also has any form of server-side backstop or retry, the paper should disclose that too. The paper does not mention it, and we cannot confirm it either.
(4) Hardware and versions: the same benchmark, run on different machines
PostTrainBench's official setup is to run on H100 GPUs, whereas the paper runs it on H20, taking the average of three runs. SWE-Marathon uses an H20 calibration branch from 9 July 2026, earlier than the final v1.1 release — the paper says the GPU-related tasks were recalibrated for H20, but that the correctness checks and the anti-cheating checks were not changed.
PostTrainBench officially requires H100; the paper runs it on H20 and averages three runs. Why does this substitution have to be written down? How might it affect the score? (You do not need to know the exact specifications of H20 and H100 — just reason from “what this benchmark is testing”.)
The paper's handling is in fact self-consistent: it has all the models being evaluated (K3, Claude Fable 5, GPT-5.6 Sol) run on the same H20, with the same official Harbor implementation, at maximum effort, and takes the average of three runs. So the across-the-row comparison inside the table still means something; it is just that the absolute value in this row should not be quoted across reports. This is a very general principle: internally comparable ≠ externally comparable.
Variant: SWE-Marathon uses “the H20 calibration branch from 9 July, earlier than the official v1.1 release”. If v1.1 later fixed a few faulty problems, would the score in this row of the paper be too high or too low? Can you determine the direction?
(5) Context management strategy: a big variable written in the small print
On BrowseComp the paper used a strategy that fires at 300K tokens, called context compaction(context-compaction) — that is, when the conversation history grows to 300,000 tokens, the earlier content is compressed into a summary to free up room to carry on. K3's BrowseComp score of 91.2 in Table 2 was obtained under this strategy.
Immediately after that, the paper writes one very important sentence: if the full 1 million token context window is used instead, with no context management at all, what K3 gets is 90.4%.
The BrowseComp row in Table 2: K3 is 91.2 (bold, best), GPT-5.6 Sol is 90.4 (second). But the paper's text says that if K3 uses the full 1M window with no context management it is 90.4. So: how solid is the conclusion “K3 beats GPT-5.6 Sol on BrowseComp”? In your answer, say clearly what you are basing it on and what information is still missing.
Layer one: a 0.8-point lead can be produced entirely by a choice of context management strategy — because K3 changing its own strategy drops it by exactly 0.8 points, landing on precisely the same 90.4 as Sol.
Layer two: the paper does not state what context strategy GPT-5.6 Sol's column was run under. If the opponent did not use a similar compaction strategy, then what this comparison compares is “K3 + a compaction strategy” against “Sol + an unknown strategy”, not the two models themselves.
Layer three: the paper does not give the variance over repeated runs either. BrowseComp is a long-chain search task with no small amount of randomness, and whether 0.8 points is beyond the noise range cannot be judged (compare: for the vision benchmarks the paper ran three to five times and averaged, which shows the authors themselves think a single run is not enough).
A fairer way to put it is: “under their respective best configurations, K3 and GPT-5.6 Sol are roughly level on BrowseComp, with K3 slightly higher.” That is more conservative than “K3 is SOTA”, but it stands up better to being questioned.
One more thing not to overlook: the paper wrote out 90.4, a number that works against it, of its own accord, and that counts in its favour. An author who wanted to hide it could simply have left that sentence out.
Variant: think it the other way round — if triggering compaction at 300K is actually better (91.2 > 90.4), that says “stuffing the whole history into a 1M window” is not necessarily optimal. Why? (Hint: think about what a context packed with irrelevant material means for attention.)
(6) Task subsets, graders and number of repeats
There are a few more details; each looks small on its own, but added together they decide what the numbers mean:
OfficeQA Pro gives the agent the whole PDF corpus rendered as images, with no machine-readable text at all — which amounts to forcing the model to “look” rather than “read”, and is a very direct test of native visual ability. MCP-Atlas uses a public subset of 500 tasks, a limit of 100 interaction turns, and Gemini 3.1 Pro as judge. AutomationBench uses a public subset of 600 tasks. The vision scores are averages of three runs, and ZeroBench-main is run five times as the official setup specifies. On WorldVQA the paper observed consistent refusal behaviour across all the models, so it used prompt engineering to force the models to give an answer (that row in the table is called “WorldVQA ForceAnswer”).
An addition: what it means to let another model be the judge
MCP-Atlas is graded by Gemini 3.1 Pro, which is very common today, because human grading is too expensive. But it brings in a new variable: the judge makes mistakes itself, and may have preferences of its own. If the judge model were swapped one day, the score for the same batch of answers could change. So the information “graded by such-and-such model” has to be cited together with the score — it is part of the score's definition, not decoration in a footnote.
(7) The dates of third-party results
A batch of the scores in the paper were not measured by the authors but quoted from third parties: GDPval-AA v2, AA-Briefcase, τ3-Banking, Harvey Lab-AA, APEX-Agents, SciCode, AA-LCR and CritPt come from Artificial Analysis (as of 23 July 2026); CorpFin v2, Finance Agent v2 and Legal Research Bench come from Vals AI; Agents' Last Exam comes from its official leaderboard (also 23 July); Toolathlon-Verified and JobBench come from their respective official leaderboards (24 July); and FrontierSWE's dominance score was recomputed from raw scores using the official evaluation script of 16 July.
12.4 The main results: read the table first, then read what the table cannot tell you
Below is the complete data of Table 2 from the paper. You can sort and filter it; look for the patterns yourself first, then read on for the interpretation. One thing to do first: sort once by “the benchmarks where K3 is furthest behind”, and see what those benchmarks have in common.
Reasoning and knowledge: it can do exam questions, but it cannot do research yet
On GPQA Diamond K3 gets 93.5, essentially level with the frontier (the highest, GPT-5.6 Sol, is 94.1, and GPT-5.5 is also 93.5). On AA-LCR K3's 74.7 is the best result in the whole table. But move towards research-level tasks and the gap shows: on HLE-Full K3 is 43.5 / 56.0 (without/with tools), behind Claude Fable 5 (53.3 / 63.0) and GPT-5.6 Sol (44.5 / 58.0); on CritPt K3 has only 23.4, behind Fable 5 (28.6), Sol (32.3) and GPT-5.5 (27.1). The paper's own wording is: research-level reasoning remains a key direction for improvement.
Coding: very strong on long tasks, with weak spots on individual abilities
On ProgramBench 77.8 is the best in the whole table. On SWE-Marathon 42.0, seven points ahead of Claude Fable 5 (but recall the fallback problem from §12.3). On Terminal-Bench 2.1 88.3, almost level with GPT-5.6 Sol's 88.8. On DeepSWE 67.5, behind Fable 5 (70.0) and Sol (73.0), but ahead of Opus 4.8 (59.0) and GPT-5.5 (67.0). On FrontierSWE, that long-horizon benchmark, 81.2 ranks second, behind Fable 5's 86.6, but clearly ahead of every other model.
Agents: this is K3's brightest axis
The best results include BrowseComp 91.2, DeepSearchQA 95.0, ResearchRubrics 76.2, MCPMark-Verified 94.5, AutomationBench 30.8, SpreadsheetBench 2 34.8, τ3-Banking 33.4 and Harvey Lab-AA 94.6. The main exceptions are the two knowledge-work suites scored with Elo, both led by Claude Fable 5: K3 is third on GDPval-AA v2 (1686 against 1747), and second on AA-Briefcase (1548 against 1583). A few places are near ties: CorpFin v2 is 0.2 points apart (71.6 against 71.8), and OSWorld-Verified 0.2 points apart (84.8 against 85.0). The harder computer-use benchmarks (OSWorld 2.0, SaaS-Bench) are still led by Fable 5 or Sol.
A common misreading: a gap of 0.2 points
On CorpFin v2 it is 71.6 against 71.8. Many people will write this up as “K3 narrowly loses on CorpFin v2”. But in an evaluation that reports no variance, 0.2 points and “no difference” cannot be told apart. The honest reading is “the two are indistinguishable on this benchmark”. The same reasoning applies to OSWorld-Verified's 84.8 against 85.0. When the gap is smaller than the noise you can estimate, the correct conclusion is “do not know”, not “slightly behind”.
Vision: the jump tools bring is very large
On Math-Vision K3 is 94.3, and with Python tools it becomes 97.8. On ZeroBench-main (pass@5) K3 is 23.0, level with Claude Fable 5, and with Python tools it jumps to 41.0 — close to double. On OmniDocBench 91.1 is the highest in the whole table. On WorldVQA 51.0 ranks second, behind Fable 5 (56.7) and ahead of Sol and Opus.
Put these sets of numbers side by side: Math-Vision 94.3 → 97.8 (with Python, up 3.5), ZeroBench-main 23.0 → 41.0 (up 18.0), HLE-Full 43.5 → 56.0 (with general-purpose tools, up 12.5), CharXiv (RQ) 84.8 → 91.3 (up 6.5). Why does the gain the tools bring differ so much across benchmarks? Give at least two explanations resting on different mechanisms, and say what these gains actually tell us about the question “how strong is the model”.
Mechanism one: the ceiling effect. Math-Vision is already at 94.3, so it can rise at most another 5.7 points; rising 3.5 has already eaten up 61% of the remaining room. ZeroBench is only at 23.0, with far more room, so a large absolute rise is not surprising. Comparing absolute rises is unfair; you should look at “the proportion of the remaining room that gets eaten up”.
Mechanism two: the make-up of the failure types. What tools can make up for are “precision” failures — enlarging an image to read a scale clearly, counting exactly, algebraic simplification, numerical calculation. Tools cannot make up for “does not understand the question at all” or “lacks the domain knowledge” failures. ZeroBench's questions are known for extremely difficult visual-detail reasoning, and a very large part of the failures there are “cannot see it clearly / cannot count it accurately”, which writing code to crop and enlarge the picture can rescue, so the gain is enormous. HLE-Full rising 12.5 fits the same logic: the general-purpose tools there include retrieval, which can fill in knowledge gaps.
Mechanism three: the coupling of one ability with another. “Using Python to process this picture into a form in which the question can be answered” is itself an ability — the model has to know what to measure, where to crop, and how to translate a visual problem into a computational one. So the with-tools score is not a simple addition of “model ability + tool ability”; it is a demonstration of the model's ability to marshal tools.
So what does it tell us? Two things. First, if your actual use allows the model to write code (most real settings do), you should look at the with-tools score, because that is closer to what you will get. Second, if a benchmark's score shoots up once tools are added, that says a very large part of what this benchmark was measuring in the first place is not “intelligence” but “precision when there are no tools to help” — which is a judgement on the model and equally a judgement on the benchmark's design. ZeroBench going from 23 to 41 carries no less information about the ZeroBench benchmark than it does about K3.
Variant: if a benchmark's score fell once tools were added, what might the reasons be? (This does not occur in the paper, but can you think of at least two mechanisms?)
The opening of §6.1.4 of the paper says that K3 “closely trails the strongest proprietary models Claude Fable 5 and GPT-5.6 Sol overall, while consistently surpassing Claude Opus 4.8, GPT-5.5 and GLM-5.2 across the entire benchmark suite”. Find counterexamples in Table 2 — find at least two rows where K3's score is lower than Claude Opus 4.8's.
Counterexample one: HLE-Full. K3 is 43.5 / 56.0, Claude Opus 4.8 is 49.8 / 57.9. With tools or without, Opus 4.8 is higher, and without tools the gap reaches 6.3 points.
Counterexample two: OfficeQA Pro. K3 is 63.3, Claude Opus 4.8 is 63.9, so Opus is slightly higher (only by 0.6 points, which by the standard of §12.4 counts as “indistinguishable”, but it at least does not support “K3 surpasses it”).
One more to add: in Table 3 of the internal evaluation, on Agentic Vision Bench K3 is 78.3 and Claude Opus 4.8 is 82.8, a gap of 4.5 points, which is another counterexample (though not in Table 2).
What this question is really teaching: the summary sentences in a paper's text are often rounder than the table. The authors are not lying — K3 really does beat Opus 4.8 on the great majority of rows — but “consistently” is a universal quantifier, and counterexamples exist in the table. When the summary in the text conflicts with the table, the table wins. Going further: notice that in the HLE-Full paragraph of the same section, the paper only says it is “behind Claude Fable 5 and GPT-5.6 Sol”, and does not mention that Opus 4.8 is higher too — this is not a false statement, but it is a kind of selective completeness. Reading any technical report (including this one) you should perform this action: take the summary sentences to the table and verify them one by one.
Variant: now turn it round and defend the paper — if you replaced “consistently surpasses” with a statement that still holds but is more precise, how would you write it? (Try to make your statement neither an exaggeration nor something that erases K3's real advantage.)
12.5 Internal evaluations: the authors set the exam and marked it themselves
The paper says that for the abilities public benchmarks do not cover, they fill the gap with a set of internal benchmarks built in-house, and that these benchmarks are updated and extended frequently, so as to keep up with the model's shifting failure modes and to directly guide the iteration of data and training. There are three categories:
Coding ability and experience: Kimi Code Bench 2.0 (real end-to-end software engineering tasks, covering several languages and production-grade stacks), Kimi Webdev Bench (hard web development requests from real usage scenarios, graded by blind expert judging), Coding Experience (“what it is like to use” as a coding agent inside a real development workflow).
General agent experience: 24/7 ClawBench 2.0 (simulates “always on” assistant work — tasks spanning several days, events arriving concurrently, being interrupted as the norm), MIRA Bench (long-chain enterprise collaboration across many roles and many systems, testing whether it can finish end to end and when it should hand work off to sub-agents), KAET (long-horizon autonomous execution), CLIF Bench (in-context learning and instruction following), Agentic Vision Bench (whether it notices and correctly uses key visual facts while carrying out a task), Swarm Bench (orchestrating a swarm of agents, decomposing a task and running it in parallel), Online Experience (aligned with the distribution of real online usage), Deep Research Bench (deep research, with questions set by domain experts and scored against expert-aligned rubrics), Finance Bench, KWV Bench (atomic visual abilities distilled from real knowledge-work scenarios), DECK Bench (making presentation decks), Agent Behavior Bench (not whether the result is right, but the quality of the process: tool-use behaviour, efficiency, discipline).
Chat experience: Faithfulness (the metric is defined as 1 − hallucination rate, higher is better, with every reply verified by fact-checkers), Chat All-in-One Bench.
| Benchmark (K3's harness) | Kimi K3 | Claude Fable 5 | GPT-5.6 Sol | Claude Opus 4.8 | GPT-5.5 | GLM-5.2 |
|---|---|---|---|---|---|---|
| Kimi Code Bench 2.0 (Claude Code) | 73.7 | 76.9a | — | 71.7 | — | 64.2 |
| Coding Experience (Claude Code) | 59.9 | 59.8 | — | 58.0 | — | 53.3 |
| Swarm Bench (Kimi Agent) | 76.3 | — | 73.2 | 72.6 | 61.8 | 58.5 |
| Deep Research Bench (Kimi Agent) | 90.0 | — | 85.3 | 87.2 | 81.9 | 84.0 |
| CLIF Bench (Kimi Code) | 52.4 | — | 50.6 | 48.8 | 52.3 | 39.2 |
| Finance Bench | 62.6 | — | 62.7 | 60.7 | 58.4 | 55.4 |
| Agent Behavior Bench (Kimi Work) | 65.0 | 75.5f | 76.4 | 65.7 | 70.1 | — |
| MIRA Bench (MIRA) | 64.1 | 72.9 | 62.2 | 59.8 | 54.6 | — |
| 24/7 ClawBench 2.0 (OpenClaw) | 48.3 | 47.4d | 52.0 | 47.2 | 48.5 | 43.2 |
| Agentic Vision Bench (Kimi Code) | 78.3 | 81.1 | 82.9 | 82.8 | 76.9 | — |
| KWV Bench | 64.7 | 63.6 | 66.9 | 61.7 | 65.8 | — |
| Faithfulness (1 − hallucination rate) | 85.5 | — | 84.8 | 83.6 | 86.5 | 74.8 |
The footnotes in the table are the paper's own: a 13 fallbacks and 1 refusal out of 80 tasks; d includes 2 tasks that Claude Fable 5 refused; f 6 refusals out of 95 tasks.
The paper's own summary: the internal suite separates K3's strengths from its weaknesses more clearly than the public one. The clearest strength is orchestration-style and research-style agency — Swarm Bench 76.3 and Deep Research Bench 90.0 both lead by a clear margin, which says it is strong at “breaking a complex goal apart, coordinating parallel work, and producing a deliverable that meets a rubric”. Coding is a strength too: on Kimi Code Bench 2.0 it trails only Claude Fable 5 (73.7 against 76.9), while Coding Experience takes the best score, 59.9 — the paper reads this as showing that its “actual behaviour as a coding agent (communication quality, behavioural appropriateness, stability of instruction following) is ahead of its raw task scores”. Where it mainly trails is Agent Behavior Bench, MIRA Bench, 24/7 ClawBench 2.0, Agentic Vision Bench and KWV Bench.
There is one further comparison on its own (the paper's Table 4): Kimi Webdev Bench has experts grade without knowing which output came from which model, comparing K3 (max) against Claude Opus 4.8 (max), both running under the Claude Code harness, on the scoring dimensions of requirement coverage, functional completeness, visual fidelity and interaction experience.
| Domain | Win | Tie | Loss | Win − Loss |
|---|---|---|---|---|
| Games | 55.6% | 3.7% | 40.7% | +14.9 |
| 3D / WebGL / Shader | 72.7% | 13.7% | 13.6% | +59.1 |
| Websites / UI clones | 52.6% | 21.1% | 26.3% | +26.3 |
| Overall | 58.6% | 13.8% | 27.6% | +31.0 |
Read this carefully: this whole section is the authors grading themselves
Kimi Code Bench, Kimi Webdev Bench, Swarm Bench, Deep Research Bench … these benchmarks were designed by the paper's authors, maintained by them and run by them, and the paper says outright that they are “updated and extended frequently, to keep up with the model's shifting failure modes and to directly guide the iteration of data and training”. Read that sentence slowly: the benchmarks are used to guide training. When one set of questions plays both the role of “the compass for training” and the role of “the final exam paper”, scoring high on it is close to inevitable — this is not called cheating, this is called an optimisation objective.
The paper does not say these benchmarks will be released, and does not say a third party can reproduce them. So the right way to use them is: look at K3's relative strength and weakness across them (which are high, which are low — this internal comparison does carry information, especially since the authors list five benchmarks of their own where they trail), and not to take some absolute score as a verdict across models.
Defend it: if I were a reviewer
On Kimi Webdev Bench you claim an overall margin of +31.0 over Claude Opus 4.8, with 3D/WebGL/Shader as high as +59.1. But you set the questions on this benchmark, you brought in the experts, and you defined the scoring dimensions. Why should I believe +31.0 rather than believe “you picked a batch of questions you are good at”?
A reference defence (put it in your own words first)
There are a few defences that hold up:
One, the judging is blind. The paper states explicitly that the experts did not know which output came from which model when they graded. That removes the most direct channel for favouritism, and it is the soundest part of this experimental design.
Two, the same harness on both sides. K3 and Opus 4.8 both ran under Claude Code — and note that this choice does not favour K3, because Claude Code is the opponent's own harness.
Three, the structure of the results does not look cherry-picked. If the questions had been chosen deliberately to play to its strengths, the margin should be large in all three domains; in fact Games is only +14.9, and the loss rate there is as high as 40.7%, close to half. The authors reported this unflattering sub-item too.
But the reviewer's challenge cannot be fully rebutted, and these are the parts that have to be conceded:
(a) The questions come from “hard web development requests in real usage scenarios” — what counts as real, on what basis they were sampled, how large the sample is: the paper does not say a word. The percentages in the three domains look like small samples (numbers such as 3.7%, 13.6% and 21.1% hint that each category may hold only a few dozen questions), but the paper gives no n, so you cannot compute a confidence interval, and whether +14.9 is significant cannot be judged.
(b) The only opponent chosen was Claude Opus 4.8; there is no blind comparison against the stronger Claude Fable 5 or GPT-5.6 Sol. Picking an opponent one rung down for the class of experiment that would have been most telling is a choice that itself needs explaining.
(c) Who the “experts” were, how many of them there were, how well they agreed: the paper does not say.
Conclusion: +31.0 is credible as an “internal iteration signal”, and is not strong enough evidence as a “public capability claim”. The honest phrasing is “on the web development question set we built ourselves, blind expert judges preferred K3's output”, not “K3's web development ability is 31 points stronger than Opus 4.8's”.
Kimi Webdev Bench used blind grading. Answer two questions: (1) exactly which bias does blind grading remove? (2) which biases does blind grading not remove? Name at least two that it does not.
(1) What blind grading removes is identity bias in the grader — knowing “this is our own model's output” and unconsciously scoring it higher. That is the most direct and most likely bias to occur, and removing it is a real improvement.
(2) What it does not remove includes at least:
Question-selection bias: the questions were chosen by the authors. Even if the grading is perfectly fair, as long as the question set itself leans towards the distribution K3 is good at, the conclusion will lean too. That unusually large +59.1 margin on 3D/WebGL/Shader could mean K3 really is strong, and could equally mean that more questions of this kind were selected, or that they fit K3's training distribution better; the paper gives neither the number of questions nor the sampling method, so the two cannot be told apart.
Scoring-dimension bias: the paper says what was graded was requirement coverage, functional completeness, visual fidelity and interaction experience. Those four dimensions were defined by the authors. Swap in a different set (add “code maintainability”, “accessibility”, “bundle size”, say) and the ordering may change.
Opponent-selection bias: the comparison is only against Claude Opus 4.8, while Table 2/3 shows that Claude Fable 5 is the stronger opponent.
Publication bias: we only see the benchmark that was written into the paper. If the authors ran five similar blind comparisons and reported only the one that came out best, we have no way of knowing.
In one sentence: blind grading guarantees that “this round of scoring was not contaminated by identity”; it does not guarantee that “this comparison was designed to be fair”.
Variant: if you had to make this experiment more convincing without adding much cost, which three things would you add? (Hint: at least one of them should have to do with “where the questions come from”.)
12.6 Cybersecurity evaluation: a result that has to be read with restraint
The paper measures the model's cybersecurity ability with a two-tier framework, where the “operational risk” of the two tiers rises in turn: Tier 1 is vulnerability discovery — finding flaws that genuinely exist in a current codebase and have not been fixed before, and showing that they reproduce (the paper stresses that ability at this tier is mainly relevant to defensive security research); Tier 2 is end-to-end exploit development — turning a vulnerability into a complete, usable exploit chain, and the paper says outright that this tier is “the one most directly related to misuse risk”. The evaluation targets include recent versions of widely deployed software (operating-system kernel components, open-source projects) as well as the authors' own internal infrastructure; every task runs in a standard configuration representative of a real deployment.
A note: why there is no Claude or GPT in this section
The paper writes: the frontier models from Anthropic and OpenAI refuse cybersecurity-related tasks, so a comparable evaluation is not possible, and this whole suite therefore excludes them. So the only comparison point at Tier 2 is the open-weight GLM-5.2.
This piece of information is worth stopping over: it tells you two things at once. First, the basis for comparison in this section is very narrow — only two models, and both of them open-weight, so you cannot infer from it “where K3's cybersecurity ability ranks among all models”. Second, different vendors have taken visibly different positions on “whether a model should be allowed to do this kind of task at all”, and that difference in policy shows up directly as blank cells in an evaluation table. A “—” in a table is sometimes not “not measured” but a position taken.
Tier 1 results: across dozens of widely deployed systems (covering operating-system kernels, databases, AI services, web frameworks, blockchains and VPN software), the model identified hundreds of candidate vulnerabilities. Of the portion of findings that went through human review, about 70% were confirmed as real flaws, including 16 previously unknown vulnerabilities spread across six projects. The paper gives two Linux kernel cases as worked examples: one a remotely triggerable heap out-of-bounds write (arising from an incomplete upstream fix), the other a Dirty-COW-class vulnerability in the RDMA subsystem (arising from a permission check that was unintentionally dropped in an upstream fix). Both were confirmed by security experts. This site goes into no technical detail at all; all that is recorded here is “what class of thing was found, and who confirmed it”.
Tier 2 results: with GLM-5.2 as the baseline, on an internal 36-task suite split into two tracks: 16 userspace tasks and 20 Linux kernel tasks. Every task was verified as solvable by a human security expert; the paper estimates that completing the whole suite takes about 540 expert-hours, roughly 15 hours per task on average. The result: K3 solves 14/36 (38.9%), GLM-5.2 solves 8/36 (22.2%). But success is very unevenly distributed — 10 of K3's 14 come from the userspace track; on the kernel track, both models leave three quarters of the tasks unsolved.
Because every task was verified as “solvable by a human expert”, the unsolved tasks measure the gap between the model and human experts directly. The paper analysed the failing trajectories and drew out four recurring failure modes: (i) difficulty taking the last step of the exploit chain from the primitives it already holds; (ii) poor strategy selection when mitigations are present; (iii) getting stuck in long, unproductive debugging loops; (iv) insufficient validation of the final deliverable before submission.
The UK AI Safety Institute and CAISI at the US NIST ran an independent joint evaluation, and reached the same conclusion as the authors: K3 is better than GLM-5.2 at exploit development (32% against 24% on ExploitBench; on a 32-step simulated enterprise network that takes a human expert about 20 hours, K3 got through 17 steps and GLM-5.2 got through 11), but trails the frontier cyber-capable models on end-to-end exploit completion — the number of times it achieved arbitrary code execution across 41 tasks was 0.
The paper closes with a sentence that matters a great deal methodologically: we treat our own evaluations as a lower bound on capability; these results depend on the current model version and on evaluation coverage, and will be re-evaluated at every major model update.
The paper says “we treat our own evaluations as a lower bound on capability”. Construct a concrete situation showing why, in a security evaluation, treating the result as an upper bound (“this is as far as it goes”) is dangerous. Then answer: for one and the same score of 38.9%, what different actions follow from reading it as an upper bound and from reading it as a lower bound?
The situation: suppose K3 fails on some kernel task because the harness's debugging loop is badly designed (the third failure mode the paper itself lists is “getting stuck in long, unproductive debugging loops”). Six months later the community builds a better harness, or someone re-runs it with better prompts and a longer time budget. Same weight file, same task, and now it is solved. The model did not change; the score did. Because the weights are public, anyone can do this, and nobody has to notify the authors.
This is why treating it as an upper bound is dangerous: taking 38.9% as an upper bound amounts to assuming “nobody can use this model better than its authors can”. For an open-weight model that assumption is almost certainly wrong — precisely because it is open, far more people than the authors will put work into improving the harness, the prompts and the tool chain. A failure in a safety evaluation has to be read by default as “it was not measured this time”, not as “it cannot do it”.
The two readings lead to completely different actions:
Read as an upper bound → “38.9%, still a long way off, safe to release” → no extra mitigations, no follow-up monitoring.
Read as a lower bound → “at least 38.9%, the true upper bound is unknown and will only rise over time” → you need continuous re-evaluation (the paper commits to re-evaluating at every major update), you need independent third-party verification (the paper did in fact do this: UK AISI + NIST CAISI), and you need to build “capability may be amplified by outside engineering after release” into your risk model.
One detail to notice in passing: the “arbitrary code execution 0 times across 41 tasks” result in the independent evaluation is likewise a lower bound. It is a 0 obtained under the evaluators' methods at that time, not “0 for ever”.
Variant: in Tier 1, “about 70% of the findings that went through human review were confirmed as real”. The denominator of that 70% is “the findings that went through human review”, not “all the candidates the model reported”. How does that choice of denominator affect your reading of the number? If the model reported 500 candidates and people reviewed only the 40 most convincing ones, what does 70% still mean?
12.7 Third-party evaluations: scores given by other people
Self-assessment is always open to suspicion, so a third-party result carries a different weight. The paper collects several leading results as of 23 July 2026 (Table 5).
| Leaderboard | Kimi K3 | Claude Fable 5 | GPT-5.6 Sol | Claude Opus 4.8 | GPT-5.5 | GLM-5.2 |
|---|---|---|---|---|---|---|
| Artificial Analysis Intelligence Index v4.1 (4th / 580) | 57.1 | 59.9 | 58.9 | 55.7 | 55.0 | 51.1 |
| Vals AI · Vals Index (2nd / 39) | 74.7 | 75.1 | 73.1 | 70.4 | 68.0 | 65.0 |
| WebDev Arena (Elo, 1st / 99) | 1678 | 1634 | 1630 | 1565 | 1507 | 1592 |
| Text Arena (Elo, 8th / 200) | 1486 | 1507 | 1485 | 1484 | 1482 | 1469 |
| Agent Arena (4th / 37) | 9.1 | 12.7 | 10.1 | 9.8 | 8.8 | 6.5 |
One at a time: on Artificial Analysis's Intelligence Index v4.1, K3 is at 57.1, 4th out of 580 models — the paper adds a line saying that if the variants of GPT-5.6 Sol at different effort levels are counted as a single entry, it is 3rd; it trails Claude Fable 5 (59.9) and GPT-5.6 Sol (58.9) and leads every other model evaluated. Vals AI's Vals Index is a set of GDP-weighted industry benchmarks; K3 is 2nd out of 39 models with 74.7, behind Fable 5 (75.1) and ahead of Sol (73.1).
Among the crowdsourced human-preference arenas, the standout is WebDev Arena: K3 is 1st out of 99 models with 1678 Elo, ahead of Claude Fable 5's 1634, and it is the first open model to reach the top of that board. On Text Arena it has 1486 Elo, 8th out of 200. On Agent Arena (which only opened for voting around 19 July) it has 9.1, 4th out of 37, behind Fable 5 (12.7), Sol (10.1) and Opus 4.8 (9.8).
Read this carefully: Elo drifts
The paper says so itself in the note to Table 5: Elo-type scores drift as more matches accumulate. And Agent Arena only opened for voting around 19 July, so by 23 July there were four or five days of data. Four or five days of crowdsourced voting is usually nowhere near enough for an Elo to settle. So that “4th / 37” placing on Agent Arena is far less trustworthy than the other rows.
One more thing to watch: in Text Arena the 1485 for GPT-5.6 Sol is the xhigh variant as listed on the board, while Opus 4.8's 1484 and GPT-5.5's 1482 are the high variants as listed on the board — these three numbers are 1–3 points apart, and any single reshuffle could change the order. Reading 1486, 1485, 1484 and 1482 as a ranking is almost certainly reading noise.
For the same claim — “K3 is strong at web development” — the paper offers two pieces of evidence: the internal Kimi Webdev Bench (blind-graded, +31.0 against Claude Opus 4.8) and the third-party WebDev Arena (1678 Elo, 1st out of 99 models). Which of the two is the stronger evidence? Why?
Kimi Webdev Bench: the questions were set by the authors, the runs were done by the authors, the grading was blind but the graders were organised by the authors, the sample size is not public, outsiders cannot re-check it, and there is only one opponent (Opus 4.8, one rung down at that).
WebDev Arena: the questions come from a large number of anonymous users, the scoring comes from humans voting, the board is maintained by a third party and is public, anyone can go and look at the current ranking, and the opponents are 99 models.
Stronger does not mean perfect. WebDev Arena's weak spots: crowdsourced preference rewards output that “looks pretty and is likeable at first glance”, which is not necessarily the same as code that is high quality or maintainable; Elo drifts as matches accumulate; different models entered the board at different times and have different numbers of accumulated matches.
But one point is well worth making: these two independent sources point in the same direction. The authors' own assessment says web development is a strength, and the public third-party board also puts it 1st. Mutually independent pieces of evidence pointing at the same conclusion are more credible than any single piece of evidence — this is the most useful heuristic there is for assessing any claim. Conversely, if the internal benchmark said strong and the external board said weak, that is when you would really need to dig.
Variant: the paper says K3 is “the first open model to reach the top of WebDev Arena”. What does the qualifier “first open model” add, and what does it not add? If another open model reaches the top six months from now, does the sentence still hold?
12.8 Cost efficiency: bringing in the second axis
Every table so far has had one axis: score. But when you actually use a model there is a second axis: money. In §6.4 the paper plots score and cost per task(cost per task) on the same chart for four suites (Figure 13): Kimi Code Bench 2.0, BrowseComp, GDPval-AA v2, AA-Briefcase.
Where the cost data comes from needs attention: the costs on Kimi Code Bench 2.0 were measured internally, with K3 running through Kimi Code and every other model running through Claude Code; on BrowseComp, K3's cost comes from the authors' own runs while the costs for Claude and GPT are quoted from published charts; the costs for GDPval-AA v2 and AA-Briefcase are quoted from Artificial Analysis's per-token API pricing (as of 23 July 2026).
Four conclusions:
Kimi Code Bench 2.0: K3 trails Claude Fable 5 by four points, but costs only 38% of what it does. More interesting still, at high effort (not max) K3 already matches Claude Opus 4.8's score at maximum effort, at about one third of the cost.
BrowseComp: K3 takes the best score, 91.2%, at $2.03 per task — half the cost of GPT-5.6 Sol (90.4%), and an order of magnitude cheaper than the Claude family at maximum effort.
GDPval-AA v2: K3 is within 50 Elo of GPT-5.6 Sol at 13% lower cost, and 2.6× cheaper than Claude Fable 5.
AA-Briefcase: K3 takes the second-best score, at about half the cost of Claude Fable 5.
The paper's summary: on all four of these suites K3 sits on or close to the cost-efficiency frontier, delivering close to top scores at a fraction of the cost.
An analogy
The “cost-efficiency frontier” is like plotting every product on a “price–quality” chart when you go shopping, then joining up the points in the top-left corner (cheaper and better) into a line. A product that lands on that line means “no other product is both cheaper and better than this one”. Where the analogy breaks down: when you go shopping the price is public, transparent and the same for everyone; a model's “cost per task” depends on how it is billed, how it is deployed, how long the context is and how many thinking tokens it produces, and an open-weight model can be deployed by you yourself, with a cost structure completely unlike calling an API. What the paper quotes is API pricing, so this frontier only holds in the “buying API by the token” scenario.
The paper says that on Kimi Code Bench 2.0 “K3 trails Fable 5 by four points, but costs only 38% of what it does”. The same section also says that on this benchmark K3 was run through Kimi Code while every other model was run through Claude Code. Point out at least two things in this cost comparison that need care.
One, the harnesses are not equivalent. K3 ran on Kimi Code, its opponents on Claude Code. The harness directly affects how many tokens get consumed (how much context is stuffed in, the retry policy, the compression policy), so the variable “whose harness is more token-frugal” has been mixed into this cost comparison. This is the same problem as the score problem in 12.3, only this time what it affects is the denominator.
Two, the cost is not broken down. The 38% could come from a cheaper unit price, or from consuming fewer tokens, or from both. Those two causes mean completely different things: unit price is a pricing decision (it can change at any time), token consumption is the efficiency of the model and the harness (relatively stable). The paper gives only the total and does not split it.
Three, pricing changes, and open weights change the rules of the game. The costs come from API pricing on 23 July 2026. Pricing is a commercial decision and can be adjusted overnight, so the shelf life of the conclusion “62% cheaper” is even shorter than that of a score. More fundamentally: K3 is an open-weight model, you can deploy it yourself, and then the cost is decided by your hardware and your utilisation and has nothing to do with API pricing — strictly speaking, the frontier the paper quotes describes only one way of using a model, “buying API by the token”.
But the paper deserves credit too: adding the cost axis at all is the right thing to do, and it reveals something you cannot see from scores alone — “at high effort K3 already matches Opus 4.8's max score, at about a third of the cost” is an observation that is invisible in any table that has only scores.
Variant: if the horizontal axis were changed from “cost per task” to “time per task”, how would the shape of the four charts change? Which conclusions would survive and which would flip? (Hint: think about what max reasoning effort means — generating a lot of thinking tokens.)
12.9 How to read an evaluation table critically
Everything in this chapter gathered into one checklist you can carry around with you. The next time you see any model evaluation table — this paper's, or someone else's, or a new one six months from now — ask these nine questions in order.
| # | What to ask | The matching evidence in this chapter |
|---|---|---|
| 1 | Who did the evaluating? The model's own authors, or an independent third party? Who set the questions, and who maintains them? | the internal benchmarks in §6.2 are set and graded by the authors and are used to guide training; AA / Vals AI / Arena in §6.3 are third parties |
| 2 | Is the harness the same? Did every model run under the same harness? Is the score the “best across harnesses”? | Terminal-Bench 2.1 takes the best score across harnesses; on DeepSWE a change of harness moves 67.5 to 67.3; in the cost comparison K3 uses Kimi Code and its opponents use Claude Code |
| 3 | Are there fallbacks or refusals? How do the tasks that did not complete normally get counted into the score? | Fable 5 falls back on 35% of tasks on SWE-Marathon; refusals in several places in the Table 3 footnotes; 40% of tasks marked as degraded on the official Agents' Last Exam board |
| 4 | Were tools used? What are the scores with tools and without tools? | HLE-Full 43.5 → 56.0; ZeroBench-main 23.0 → 41.0 |
| 5 | How is context managed? What is the compression policy, the window size, the trigger threshold? What did the opponents use? | BrowseComp 91.2 with compression at 300K against 90.4 with 1M and no compression; the paper does not state the opponents' policies |
| 6 | What hardware and what version was it run on? Does that match the official setup? | PostTrainBench uses H20 rather than the official H100; SWE-Marathon uses the 9 July branch rather than v1.1 |
| 7 | How large is the variance? How many runs? Is the gap larger than the noise? | vision averaged over three runs, ZeroBench over five; while CorpFin v2 differs by 0.2 and OSWorld-Verified by 0.2, and without variance there is no way to judge |
| 8 | What is the date? What is the timestamp on each of the third-party scores, the leaderboard placings and the API pricing? | AA / Vals / ALE are 07-23, Toolathlon / JobBench are 07-24, the FrontierSWE script is 07-16; Elo drifts |
| 9 | What about cost? How much money and how much time did getting that score take? Does it still hold on your budget? | §6.4: $2.03 per task on BrowseComp; at high effort K3 matches Opus 4.8's max |
Suppose you see this sentence on social media (the sentence is invented, but every number it quotes really does come from this chapter):
“Open-source models overtake across the board! Kimi K3 beats GPT-5.6 Sol on BrowseComp with 91.2, crushes Claude Fable 5 by seven points on SWE-Marathon, is number one in the world on WebDev Arena, and costs half what its rival does. Open source has already caught up with closed source.”
Take this sentence apart item by item with the nine-question checklist from 12.9: which parts hold, which parts need qualifiers, which parts are wrong. Then give a more accurate version that you would be willing to put your own name to.
“BrowseComp 91.2 beats GPT-5.6 Sol” — the number is real, but it needs three qualifiers (questions 5 and 7): 91.2 used a context compression policy triggered at 300K; switch K3 to 1M with no compression and it is 90.4, exactly equal to Sol; the paper gives no variance, and does not say what context policy Sol used. The accurate phrasing is “the two are roughly level”.
“crushes by seven points on SWE-Marathon” — the number is real (42.0 against 35.0), but it needs two qualifiers (questions 3 and 6): Fable 5 triggered fallbacks on 35% of the tasks on this benchmark, so how much of those seven points comes from a difference in ability cannot be determined; and this benchmark ran on an H20-calibrated branch, at a version earlier than v1.1. The word “crushes” cannot be sustained whichever way you look at it.
“number one in the world on WebDev Arena” — this one is the soundest (question 1): a public third-party board, crowdsourced human votes, 1st out of 99 models, 1678 against 1634, and the first open model to reach the top. Only two qualifiers need adding (question 8): as of 2026-07-23, and Elo drifts as matches accumulate; plus this is one single dimension, “web development preference”.
“costs half what its rival does” — you have to say which suite and which rival (questions 9 and 2): on BrowseComp, $2.03 is about half of GPT-5.6 Sol; on Kimi Code Bench 2.0 it is 38% of Fable 5, but there K3 used its own harness and its opponents used Claude Code; and these costs are quoted from API pricing on 23 July.
“Open source has already caught up with closed source” — this is wrong, and it conflicts directly with the paper's own conclusion. The paper says outright that “a gap to the strongest proprietary models remains”. The evidence: 4th on the AA Intelligence Index (57.1 against Fable 5's 59.9), 2nd on the Vals Index, 8th on Text Arena, 4th on Agent Arena; CritPt 23.4 trails three models; HLE-Full trails several models including Opus 4.8; third on GDPval-AA v2 and second on AA-Briefcase; on OSWorld 2.0 and SaaS-Bench the opponents still lead.
The version I would put my name to: “As of July 2026, Kimi K3, as an open-weight model, reaches or comes close to the proprietary frontier in the agentic, retrieval and web development directions — it ranks first on WebDev Arena with 1678 Elo (99 models), and is roughly level with GPT-5.6 Sol on BrowseComp at about half the cost per task. But on research-grade reasoning (CritPt, HLE-Full) and on some knowledge-work suites it still trails Claude Fable 5 and GPT-5.6 Sol clearly, and it places 4th on the composite index (Artificial Analysis). Some of the comparisons in the paper have equivalence problems: harnesses that do not match, opponents triggering fallbacks, different context policies.”
Notice that this version is longer, wordier and harder to spread — accuracy and spreadability are inherently in conflict, and that is exactly why you need a checklist.
Variant: now flip the stance. Suppose someone writes “K3 is no good at all, only 4th on the AA index, CritPt only 23.4, research-grade reasoning is a mess”. Take that sentence apart with the same checklist — which evidence in K3's favour does it leave out? (Hint: what does 4th out of 580 models amount to? What does open-weight mean?)
Chapter summary
On the surface this chapter went through K3's report card: on the reasoning-and-knowledge axis, “good at exam questions, not yet very good at research” (GPQA 93.5 is on a par with the frontier, CritPt 23.4 trails clearly, and the paper itself admits that research-grade reasoning is a key direction for improvement); on the coding axis, strong on long tasks (ProgramBench 77.8 is the best in the table, SWE-Marathon 42.0, Terminal-Bench 2.1 88.3 all but level with Sol) with a weak spot on single-point ability (DeepSWE 67.5 trails the top two); the agent axis is the brightest one, with best scores on eight benchmarks, and where it does lose to Claude Fable 5 it is on the two Elo-based knowledge-work suites; on the vision axis the jump from adding tools is very large (ZeroBench 23.0 → 41.0). Among the third-party evaluations, 1st in the world on WebDev Arena is the hardest result, and 4th on the composite index is the most restrained one. On cost efficiency K3 sits on or close to the frontier.
But what this chapter really wants to leave you with is not these numbers; it is the order in which you look at numbers. Find the conditions first, then look at the value: who did the evaluating, which harness, whether there were fallbacks, whether tools were used, how context was managed, what hardware, how many runs, which day, how much money. For every one of these nine questions, this chapter can point to at least one real example in the paper — including the prettiest contrast of all: the same model, the same benchmark, and purely because the context policy differs, BrowseComp moves between 90.4 and 91.2, where 90.4 happens to be the opponent's score.
One last thing is worth saying on its own: this paper does better than many technical reports on disclosing its configurations — it wrote down the 35% fallback rate of its own accord, wrote down the 90.4 that works against it, wrote down that the internal benchmarks are used to guide training, wrote down that it trails on CritPt, wrote down that it treats safety evaluations as a lower bound. This self-exposure does not make its conclusions automatically true, but it gives the reader the ability to discount them for themselves. A report that can be criticised seriously is better than a report that cannot be criticised at all.
第13章 案例研究、结论,以及这篇论文没有告诉你的事
一句话导语:这一章先看 K3 被放出去干活时做成了什么(优化 GPU 内核、写一个编译器、设计一颗芯片、复现一篇天体物理论文、剪一支预告片),然后做一件整篇论文都没做的事——把这份报告没说的东西列出来。
学完这一章你应该能做到
- 从 283.6 ms → 114.4 ms 算出加速比,并说清「延迟降低百分比」和「加速倍数」不是同一个数
- 解释 Figure 14 里那些台阶状的曲线为什么比一个终点数字更有信息量
- 说清「一个早期 K3 检查点在做 K3 自己的内核优化」这件事,为什么既是最有力的证据、又是最需要警惕的证据
- 指出案例研究这种证据形式的天然弱点,并说出补上哪三样东西才能让它变成可信的度量
- 把一篇技术报告的省略分成三类:行业通行的、这篇特有的、你自己也不确定的
13.1 GPU 内核优化:给它一天时间,让它自己去调
先说这个实验为什么值得做。前面十二章讲的都是「模型在别人出的卷子上考了多少分」。但 K3 的作者手里有一类特别的任务:他们自己每天都在做的事。训练一个 2.8 万亿参数的模型,最耗人的工作之一就是把注意力算子写成跑得足够快的 GPU 代码。这类工作有一个很好的性质——结果可以被机器客观测量:跑得快就是快,慢就是慢,不需要裁判,不需要打分细则,也没法糊弄。
GPU 内核(GPU kernel):一段直接跑在显卡上的程序,负责把某个数学运算(比如注意力)真正算出来。同一个数学公式可以有几十种写法,跑起来的速度可能差好几倍——差别来自数据怎么在显存和片上缓存之间搬运、多少个线程怎么分工、计算和搬运能不能重叠。优化内核就是在不改变计算结果的前提下把它写得更快。
实验设置很干净:每个模型在一个配置完全相同的沙箱里独立工作,每个任务的预算是最多 24 小时,这段时间可以用来做性能剖析(profiling)、重写代码、跑基准测试。覆盖四个有代表性的内核:AttnRes、DeepSeek 稀疏注意力(DSA)、KDA、以及头维度为 512 的 MLA。硬件是一块 NVIDIA Hopper GPU 和一块其他厂商的 GPGPU。
结果:K3 在全部四个内核上都做出了大幅改进——
AttnRes 的延迟从 283.6 ms 降到 114.4 ms;DSA 和 KDA 的运行时分别削减 55.1% 和 73.6%;MLA 上达到了峰值 TFLOPS 的一半以上。综合这些任务,K3 追平了 Claude Fable 5(带回退),并大幅超过 Claude Opus 4.8、GPT-5.6 Sol 和 GPT-5.5。
峰值 TFLOPS(peak TFLOPS):TFLOPS 是每秒一万亿次浮点运算。「峰值」是这块显卡理论上最快能达到的速度。实际的内核几乎不可能跑满峰值,因为总有时间花在等数据搬运上。所以「达到峰值的一半以上」是一个很实在的成绩——它的意思是这段代码把显卡的算力用掉了一半以上,而不是大部分时间在干等。
Figure 14:不要只看终点,要看形状
论文用 Figure 14 展示了各模型在 AttnRes 上的优化轨迹:横轴是活跃工作小时数,纵轴是相对 FLA Triton 基线的加速百分比。终点的数字是:K3 +59.7%、Claude Fable 5 +57.1%、GPT-5.5 +30.8%、GPT-5.6 Sol +17.3%。
但比终点更有意思的是曲线的形状:四条曲线都不是平滑上升的,而是台阶状——长时间的水平段(试了一堆没用的东西),然后突然跳一级(找到了一个真正有效的改动),再进入下一段平台。K3 的曲线跳得早、跳得多,在大约 5 小时时就已经越过了 40%;Claude Fable 5 起步慢一些,但后来在 11–14 小时区间连续跳了几级追了上来;GPT-5.5 在大约 30% 处长时间横盘;GPT-5.6 Sol 直到接近 20 小时才慢慢爬到 17% 左右。
为什么「台阶」这个形状本身就是结论
如果优化过程是平滑上升的,那说明它是一件熟练工作——多花时间就多一点收益,模型强弱只体现在斜率上。而台阶形意味着完全不同的事:大部分时间是没有回报的探索,全部收益集中在少数几个正确的想法上。在这种任务里,「更强」不等于「干得更快」,而等于「更容易撞上那几个正确的想法,并且撞上之后能认出来」。
这也解释了为什么 GPT-5.5 那条线会长时间横在 30% 附近——不是它不干活,是它一直在试,但试的方向没有跨过下一个台阶。
AttnRes 的延迟从 283.6 ms 降到 114.4 ms。请分别算出:(a) 延迟降低了百分之多少;(b) 加速了多少倍。然后回答:这两个数为什么不一样,什么时候会有人把它们混着用?
(a) 降低百分比 = 169.2 ÷ 283.6 ≈ 59.7%。
(b) 加速倍数 = 283.6 ÷ 114.4 ≈ 2.48 倍。
两个数描述的是同一件事的两种视角:降低 59.7% 说的是「省掉了原来时间的六成」,加速 2.48 倍说的是「现在一秒能做原来 2.48 秒的活」。它们的关系是:加速倍数 = 1 ÷ (1 − 降低比例)。当降低比例趋近 100% 时加速倍数会趋于无穷,所以降低百分比在高加速区间会显得「不够震撼」,而倍数在低加速区间会显得「不够震撼」——写宣传稿的人会挑对自己有利的那个说法。降低 59.7% 听着一般,说成「快了 2.48 倍」就好听多了,但它们是同一件事。
还有一个值得注意的巧合:59.7% 这个数,恰好等于论文 Figure 14 里 K3 那条曲线的终点标注 「+59.7%」。这两个数字很可能指的是同一件事——也就是说,图 14 纵轴上的「加速百分比」应该理解为「延迟降低的百分比」,而不是「倍数减一」。不过论文并没有给出这个纵轴的定义公式,所以这只是一个高度合理的对账,不是论文明说的事实。看到两个来源的数字对上了,先高兴,然后仍然要问一句「它们的定义真的一样吗」。
变式:DSA 和 KDA 的运行时分别削减了 55.1% 和 73.6%。哪一个的「加速倍数」更大?大多少?请先凭直觉猜一个数,再算,看看你的直觉偏了多少。
13.2 自举:一个早期的 K3 在优化 K3
论文在这一段的最后加了一句很轻描淡写、但分量极重的话:在开发的后期阶段,一个早期的 K3 检查点已经承担了他们大部分的内核优化工作。
把这句话展开:他们用一个还没训完的 K3,去优化那些将要用来训练 K3 的 GPU 内核。这是一个自举(bootstrapping)循环——模型改进基础设施,更好的基础设施训出更好的模型,更好的模型再去改进基础设施。
打个比方
这有点像一个木匠用自己做的刨子,去做一把更好的刨子。第一把刨子粗糙,但足以做出第二把稍好的,第二把又能做出第三把。类比失效处:木匠的手艺是不变的,变的只是工具;而这里「手艺」(模型)和「工具」(内核)是同一条链上的两端,模型既是工匠也是产物。更重要的差别是——木匠能判断新刨子好不好用,而「内核跑得更快」这个判据虽然客观,却不能保证由此训出的模型更好。这中间隔着好几层推断。
综合题。已知三件事:(1) K3 在 SWE-Marathon(一个 GPU 内核导向的套件)上拿到 42.0,领先 Claude Fable 5 七分;(2) 案例研究里 K3 在四个内核上都做出大幅改进,AttnRes 加速幅度居首;(3) 一个早期的 K3 检查点在开发后期承担了作者大部分的内核优化工作。
请回答:这三件事放在一起,增强还是削弱了「K3 的 GPU 内核优化能力真的很强」这个结论?请把两个方向的论证都写出来,然后说明需要什么额外信息才能判断。
增强的方向(支持 H1)。第 (3) 条是这一整章里最硬的证据形式——它不是一个基准分数,而是作者用真金白银的工程需求做了背书。一个实验室愿意把自家训练基础设施的关键优化工作交给一个模型,说明它在真实的、后果自负的场景里是可用的。这比任何自评基准都更难伪造:如果模型产出的内核是错的或更慢的,受损的是他们自己的训练进度。而且这类结果可被机器客观测量(跑得快就是快),不依赖裁判。
削弱的方向(支持 H2)。如果模型在开发期间被大量用于这类任务,那么这类任务的轨迹、失败模式、成功套路极有可能被回收进后续的训练数据——这正是第12章里「内部基准被用来指导训练」那个问题的翻版。于是「在 GPU 内核任务上强」就可能不是通用能力,而是这一类任务被特别训练过的结果。SWE-Marathon 领先七分这件事,在这个解读下就没那么惊人了:它恰好是作者最熟悉、最有数据、最有动机去优化的那一类任务。
需要什么额外信息才能判断:(i) 内核优化相关的数据是否进入了训练集,以及是否与 SWE-Marathon 的任务有重叠(论文没有说明);(ii) 在一个与 GPU 内核无关的性能工程领域(比如数据库查询优化、编译器优化的其他分支)做同样的实验,看优势是否迁移;(iii) 这些改进后的内核是否被独立方复现和验证过。
我的判断:两个方向都成立,而且不矛盾——很可能确实存在一种通用的性能工程能力(H1),同时它在 GPU 内核这个具体领域被额外强化过(H2)。所以合理的结论是:「K3 在 GPU 内核优化上被验证为可用于生产,这一点证据很强;但把它外推为'K3 的性能工程能力全面领先',证据不足。」
顺带注意一个容易滑过去的细节:论文说 K3 在这些内核任务上「追平了 Claude Fable 5(带回退)」。带回退这三个字又回来了——Fable 5 那一边的成绩里有多少是回退产生的,论文在这一节同样没有拆开。
变式:假设你是一家独立评测机构,想设计一个实验来区分 H1 和 H2,但你拿不到作者的训练数据。你会怎么设计?(提示:你能拿到 K3 的权重,因为它是开放的。想想「最近才出现、不可能在训练数据里」的内核。)
答辩:如果我是审稿人
你说 K3 在内核优化上「追平了 Claude Fable 5,大幅超过 Opus 4.8、GPT-5.6 Sol 和 GPT-5.5」。可这四个内核(AttnRes、DSA、KDA、MLA)里,有两个是你自己论文前几章提出的架构组件。你让所有模型去优化你自己发明的算子,然后宣布你的模型赢了。这个实验设计能说明什么?
参考防守(先自己组织语言再看)
这是这一节最锋利的质疑,必须分层回答:
能守住的部分。第一,实验条件本身是对称的:所有模型在配置相同的沙箱里、有相同的 24 小时预算、优化同一批内核、用同一个基线(FLA Triton)衡量。没有哪个模型被给了额外信息。第二,判据是机器测出来的运行时,不是人打的分,没有裁判偏好的空间。第三,任务是「把这段代码写得更快」,而不是「回忆 AttnRes 是什么」——对算子的熟悉可以帮你起步,但把延迟砍掉六成需要的是真实的性能工程能力。第四,KDA 与 AttnRes 的定义在论文前几章是公开的,其他模型并非无从知晓。
守不住、必须承认的部分。(a) 先验知识不对称是真实存在的。K3 的作者在这些算子上积累了大量经验,而这些经验很可能通过训练数据进入了 K3;其他模型没有这种暴露。这不是作弊,但它意味着这个比较测的是「在 Kimi 自家算子上的优化能力」,而不是「通用的内核优化能力」。
(b) 样本量是 4。四个内核、两块显卡,没有报告重复次数,也没有报告方差。24 小时的探索过程随机性很大(看看 Figure 14 那些台阶就知道),跑第二遍完全可能换一个名次。
(c) 基线的选择没有被讨论。所有加速百分比都是相对「FLA Triton 基线」算的,但论文没有说明这个基线本身优化到了什么程度。如果基线在某个内核上写得比较朴素,那么「降低 73.6%」的含金量就要打折。
我认为诚实的结论是:这个案例研究证明了「K3 能在真实的性能工程任务上做出可测量的大幅改进,并且这些改进被作者用在了自己的生产环境里」——这一点站得住。但「K3 在内核优化上强于 GPT-5.6 Sol」这个跨模型排名,样本量和先验对称性都不足以支撑。要修好它,最小的改动是:加入几个与 Kimi 架构无关的第三方内核,并把每个任务重复三次报告方差。
「K3 把 KDA 内核的运行时削减了 73.6%」——这听上去非常惊人。请构造至少两种替代解释,使得这个 73.6% 成立,但并不说明 K3 的能力有多强。然后说明:论文需要补充什么信息,才能排除你构造的这些解释?
解释一:基线太弱。如果 FLA Triton 基线只是一份未经调优的参考实现,那么削减 73.6% 可能只需要一些标准的、教科书式的优化(调整分块大小、减少显存往返、开启合适的数据类型)。论文没有说明这个基线的优化程度,所以无法排除。这是最重要的一条——注意论文对 MLA 给的是「达到峰值 TFLOPS 的一半以上」,这是一个绝对指标(拿硬件上限当分母),比相对基线的百分比可信得多。如果四个内核都给出绝对指标,这个质疑就没了。
解释二:优化空间是任务给的,不是模型挣的。某些内核可能存在一个众所周知的低效点(比如某个操作没有融合)。任何有能力的优化者——包括其他模型、也包括一个有经验的人类工程师——都会先修它,收益就有一大半。要排除它,需要报告一个人类专家基线:同样 24 小时,一位熟练工程师能做到多少?论文没有给。
解释三:选择性报告。论文说覆盖「四个有代表性的内核」,但没说这四个是从多少个候选里选出来的、按什么标准选的。如果还有第五、第六个内核 K3 没做好,我们不会知道。要排除它,需要预先注册要测的内核清单,或者报告全部尝试过的内核。
补充哪些信息可以排除:(i) 基线实现的来源和优化程度,最好同时给出「相对硬件峰值」的绝对指标;(ii) 一个人类专家在同等预算下的对照;(iii) 内核清单的选择依据与完整结果;(iv) 重复运行的方差。
要说清楚的是:构造这些替代解释不等于说论文错了。K3 很可能真的做得很好——AttnRes 那条 283.6 → 114.4 是绝对时间,MLA 那条是相对硬件峰值,这两个数不受基线选择影响,本身就有说服力。构造反例的目的是划清这份证据能支撑的结论边界,而不是否定它。
变式:现在反过来——如果论文报告的是「相对硬件峰值 TFLOPS 达到了 51%」,你还能构造出削弱它的解释吗?(提示:峰值是怎么定义的?不同数据类型的峰值一样吗?)
13.3 MiniTriton:从「写内核」到「写造内核的工具」
前一节是让模型优化一段代码。这一节的任务更上一层:让模型造一个编译器。
编译器(compiler):把人写的高级代码翻译成机器能执行的低级指令的程序。Triton 是一种专门用来写 GPU 内核的语言,它让你用接近 Python 的写法描述一个内核,再由它的编译器翻译成显卡指令。造一个编译器比写一个内核难得多——内核是一个作品,编译器是一台能生产无数作品的机器。
K3 开发了 MiniTriton,一个紧凑的类 Triton 编译器,包含:一个自定义的 tile 级 Python 前端与布局系统(tile 指把大矩阵切成小块来处理,布局系统决定这些块怎么摆放在显存和寄存器里)、一个轻量的 warp 级 MLIR 注解与优化层(warp 是 GPU 上一组同步执行的线程,MLIR 是一种中间表示,编译器在这一层做各种变换)、以及一条 PTX 代码生成流水线(PTX 是 NVIDIA 显卡的一种低级汇编)。
围绕这个编译器,它还构建了一个双模式张量库,提供类似 PyTorch 的高层接口:即时执行(eager)和只做前向的编译两条路径共享同一套 DSL 编译器和运行时。这个库进一步提供了反向模式自动微分、神经网络模块、基于 NCCL 的分布式训练原语,以及稀疏与可视化原语。
成绩单(在一块 NVIDIA L20 上):MiniTriton 在其核心基准套件的几何平均上超过了 PyTorch eager 和 torch.compile。它从零实现的 tensor-core 矩阵乘法路径在最大的形状上接近 cuBLAS(NVIDIA 官方高度优化的矩阵库),达到实测机器上限的约 90%。它的 DSL 级 KDA 预填充内核以明显优势超过一个对等的 Triton 参考实现。
最能说明问题的一条是:MiniTriton 还能端到端训练一个 GPT 模型,损失曲线紧贴 PyTorch 参考;全模型的梯度与 torch 自动微分的差异不超过 torch 自身的 fp32 舍入误差(10−4),这个差异是以 fp64 参考为基准测量的。Figure 15 展示了四张图:(a) CUDA-core 的 roofline、(b) tensor-core 的 roofline、(c) 用 MiniTriton 训练的字符级 GPT 与 torch eager 的训练损失曲线对比、(d) 用 MiniTriton 自己的分布式原语做的两卡数据并行训练与单卡训练的对比——两条曲线的最终交叉熵要到小数点后第三位才分得开。
为什么「梯度差异不超过 fp32 舍入误差」是这一段最强的一句话
一个编译器最容易犯的错是算错但不报错——结果差一点点,模型照样能训,只是永远差那么一点。所以正确性的检验必须是定量的。
这里的检验设计得很聪明:他们不拿 torch 当「真值」,而是拿 fp64(双精度,精度更高)算一遍当真值,然后量 MiniTriton 和 torch 各自离真值多远。结论是 MiniTriton 的偏差不比 torch 自己的偏差更大。换句话说:MiniTriton 的误差已经小到被浮点数本身的精度极限盖住了,你没法从数值上区分它和 torch 哪个「更对」。
这比「我们跑了一下,看起来没问题」强了不知道多少个量级。
为什么验证 MiniTriton 的梯度时,要专门用 fp64 算一份参考,而不是直接说「MiniTriton 的梯度和 PyTorch 的梯度差不超过 10−4」?这两种说法的证据强度差在哪?
只比 A 和 B 的问题:|A − B| 小,只能说明两者接近,不能说明两者正确。极端情况下,如果 MiniTriton 恰好复制了 torch 的某个数值缺陷,|A − B| 会是 0,但两个都错。反过来,如果 |A − B| 较大,你也无法判断是 B 错了还是 A 错了——fp32 下 torch 自己也有可观的舍入误差。
引入 fp64 参考 T 之后:你可以分别量 |A − T| 和 |B − T|,于是能说出一句强得多的话——「B 的偏差不超过 A 的偏差」。这句话的意思是:MiniTriton 的误差已经落在 fp32 这个数据类型固有的噪声底之内了,再精确也没有意义,因为数据类型本身就到此为止。
这也是一种可迁移的验证思维:要证明新实现是对的,不要和旧实现比,要和一个精度更高的参考比,然后论证「新旧两者与参考的距离处于同一量级」。同样的思路出现在 Figure 15(d):把两卡分布式训练和单卡训练放在一起,如果分布式原语(NCCL 通信、梯度归约)写错了,两条损失曲线会分叉;它们最终只在小数点后第三位分开,说明通信这一层也是对的。
要补充的一点:论文说的是「全模型梯度」的差异不超过 10−4,但没有说明这是最大差、平均差还是某种范数,也没有说明是在训练的哪个阶段测的。这是一个小但真实的表述含糊。
变式:如果 MiniTriton 的梯度和 fp64 参考的差异,比 torch 和 fp64 参考的差异还小,能不能说 MiniTriton 「比 PyTorch 更准」?这个结论有什么陷阱?
13.4 芯片设计:48 小时,一颗推理芯片原型
这是所有案例里最出人意料的一个。作为一次早期概念验证,K3 为一个「遵循相同架构的 nano 模型」设计了一颗推理芯片原型。这个 nano 模型的架构和 K3 本体一脉相承:混合 KDA 与 NoPE-MLA 注意力、块大小为 2 的 Block AttnRes、带一个共享专家的 sigmoid MoE 路由,权重采用分组 INT4 量化(组大小 128)。
过程:在一次 48 小时的自主运行里,K3 用 Kimi Code 配合开源 EDA 工具(EDA 是电子设计自动化,芯片设计用的软件)和 Nangate45 标准单元库,构建、优化并验证了这颗芯片。
结果:在 4 mm² 的解析面积预算内,设计在 100 MHz 下收敛时序,RTL 仿真的解码吞吐超过 8,700 tokens/s,集成了 146 万个标准单元、0.277 MiB SRAM,以及一个带融合反量化的 INT4 MAC 阵列。RTL 代码已在 GitHub 开源。
收敛时序(closes timing):芯片按固定节拍工作,100 MHz 表示每秒 1 亿拍。「收敛时序」的意思是——所有电路上的信号,都能在一拍结束之前稳定下来。如果某条路径太慢,信号还没稳住下一拍就来了,芯片就会算错。所以这是一个「设计真的能工作」的硬性判据,不是一个可以打折扣的指标。
标准单元(standard cell):芯片设计里预先做好的最小积木(一个与门、一个触发器等)。146 万个标准单元的意思是这个设计用掉了 146 万块这样的积木。MAC 阵列是乘加运算阵列,神经网络里最主要的计算就是「乘一下再加起来」,这个阵列就是专门干这件事的硬件;「融合反量化」表示把 INT4 权重还原成实际数值这一步被直接做进了计算通路里,不用单独走一遍。
补充:给这些数字一点尺度感
4 mm²:大约是一粒芝麻的正面面积,或者你在纸上用铅笔戳一个点再稍微大一点。作为对比,一颗现代手机主芯片大约是 100 mm² 量级。
100 MHz:每秒 1 亿拍。今天一颗普通手机 CPU 大约是 2–3 GHz,也就是快 20–30 倍。100 MHz 大致相当于 1990 年代中期个人电脑的频率。
8,700 tokens/s:用 100,000,000 ÷ 8,700 ≈ 11,500,也就是说平均每生成一个 token 用掉约 1.15 万个时钟周期。
0.277 MiB SRAM:约 28 万字节,比一张 1.44 MB 的软盘还小得多。这也说明了这颗芯片服务的模型有多「nano」。
把这些放在一起看:这不是一颗能跑 K3 的芯片,而是一个架构可以被做成硬件的存在性证明。
根据 4 mm²、100 MHz、8,700 tokens/s、146 万标准单元、0.277 MiB SRAM 这五个数字,判断这颗芯片属于下面哪一类,并说明你的理由:(A) 一颗可以量产部署、用来跑 K3 推理服务的芯片;(B) 一个验证「这套架构能被做成硬件」的原型;(C) 一个纯软件模拟、没有硬件意义的玩具。
排除 (A):规格远达不到部署级。0.277 MiB SRAM 只有约 28 万字节,连一个小模型的一层权重都未必放得下,所以它服务的是论文明说的「nano 模型」;100 MHz 是 1990 年代的频率水平;4 mm² 也远小于商用推理芯片。而且论文自己写的定位就是「早期概念验证(early proof of concept)」和「原型(prototype)」。
排除 (C):它不是纸上谈兵。「在 100 MHz 收敛时序」是一个真实的物理约束被满足了;它用的是真实的开源 EDA 工具链和 Nangate45 标准单元库;它有具体的单元数(146 万)和面积预算(4 mm²);RTL 代码已经开源,任何人都可以去检查。
为什么是 (B):这项工作要证明的命题是「K3 论文前几章描述的那套架构(KDA + NoPE-MLA + Block AttnRes + sigmoid MoE 路由 + INT4 分组量化)可以被完整地映射到硬件上,而且这个映射能在 48 小时内由模型自主完成」。存在性证明只需要一个能工作的最小例子,不需要它有竞争力。
还要注意的:论文说的是「RTL 仿真的解码吞吐」——RTL 仿真是在电脑上模拟这个电路的行为,还没有真的流片造出来。所以 8,700 tokens/s 是仿真值,不是实测硅片上的值。这个区别在芯片领域很重要,论文的措辞是准确的(写了 「RTL-simulated」),但读的人容易忽略。
变式:论文说 RTL 代码已经在 GitHub 开源。相比只在论文里报告这些数字,开源代码在证据上加了什么?它没有解决什么问题?(提示:想想「这段代码是 48 小时自主跑出来的」这个主张,能被开源的代码证明吗?)
13.5 三个更贴近人类工作的案例
面向研究的编码:两小时对一到两周
为了复现计算天体物理中的 I–Love–Q 普适关系(一组关于中子星的转动惯量、潮汐形变和四极矩之间的经验关系),K3 做了这样一串事:审阅了 20 多篇论文、交叉验证它们的结果、实现完整的数值流水线、评估了 300 多个状态方程、发现了已发表公式中的不一致、写了 3000 多行 Python,并产出一个交互式 HTML 仪表板。用时大约两小时;论文说一位有经验的研究者通常需要一到两周。
知识工作:两个 Kimi Work 里的项目
第一个:K3 产出了一个覆盖 AI ASIC 产业 42 年历史的交互式研究网站。完成了 120 多轮迭代精修,取材自 87 份季报和 99 份原始 PDF(11,000 多页),过程中做了 2,800 多次网页搜索和 1,100 多次终端查询。
第二个:用 20 多个并发子智能体分析 GWTC-5 中的 391 个引力波事件,产出七张科学可视化、两张汇总表,以及一份十余篇论文的文献综述。
视频剪辑与动效设计
借助原生多模态架构(第6章讲过:视觉和文本活在同一条 token 流里),K3 制作了一个 3Blue1Brown 风格的动效解说视频——讲解的正是它自己的架构——并从 56 段素材里剪出了预告片。这涉及片段选择、动作匹配剪辑、帧级精确的节拍同步、音频处理和多轮修订。论文说,制作一段可比的高密度短视频,一位有经验的剪辑师通常需要一到两天。
读的时候要小心:这些是精选的成功案例
整个 §7 是一串「它做成了 X」的叙述,没有出现任何一处「它在 Y 上失败了」「平均需要重试几次」「我们试了 N 个项目,成功了 M 个」。这不是说这些案例是假的——它们很可能都是真的。但案例研究这种证据形式,天然只能证明「可能性」,不能证明「可靠性」。
「K3 两小时做完了研究者一到两周的活」这句话,正确的读法是「至少有一次,K3 在两小时内产出了某种程度上可比的成果」。它不告诉你成功率是多少、需要多少人工干预、失败时失败得多难看。而对于要不要在工作里用它,后面这些恰恰是最重要的信息。
「K3 大约两小时完成了一位有经验的研究者通常需要一到两周的工作。」请你构造至少三种情形,使得这句话字面为真,但「K3 的科研能力约等于(甚至超过)一位研究者」这个推论不成立。然后说明:要把这个案例变成一个可信的能力度量,最少需要补充哪三样东西?
情形一:幸存者偏差。如果作者尝试了二十个类似的复现任务,只有这一个跑通了,那「两小时」是最好情况而不是典型情况。论文没有报告尝试次数,无法排除。
情形二:产出不等价。研究者的「一到两周」包含了很多 K3 那两小时里没做的事:确认方法学的合理性、对结果承担署名责任、写出经得起同行评议的论证、处理审稿意见。两边交付物的标准不同,时间自然不可比。论文说 K3「发现了已发表公式中的不一致」,这确实是一个高质量信号,但也需要有人去确认这些「不一致」是真的问题,而不是它自己理解错了——论文没有说这一步由谁做、结论如何。
情形三:任务被选过。I–Love–Q 关系是一个已经发表、已被广泛复现的结果。相关论文、公式、状态方程数据都在公开文献里,模型很可能在训练中见过。复现一个已知结果,和做出一个新发现,难度完全不在一个量级。这不削弱「它能在两小时内完成一件复杂的多步骤工作」,但削弱「它的科研能力约等于研究者」。
情形四:「两小时」的边界模糊。这两小时是不是纯自主?中间有没有人给提示、纠错、重启?准备提示词和环境花了多久?论文用的词是「in about two hours」,没有说明是否等于「自主运行时间」。(对照一下:芯片那个案例,论文明确写了「a single 48-hour autonomous run」——用词的差别可能是有意义的。)
要补充的三样东西:
(i) 成功率与样本:在 N 个同类任务上跑,报告成功了几个、平均耗时、失败的失败在哪。
(ii) 人类对照组:真的找几位研究者做同一件事并计时,而不是引用一个「通常需要」的估计。
(iii) 产出的独立评审:请领域专家在不知道来源的情况下评价两边的交付物质量(这正是他们在 Kimi Webdev Bench 里对网页开发做过的事——盲评的方法论他们是有的,只是没用在这里)。
最后要公道地说:案例研究不是坏东西,它是能力上限的存在性证明,而且往往比基准分数更能说明「这东西到底能不能干活」。问题只出在把存在性证明当成平均性能来引用。
变式:AI ASIC 产业那个案例给出了非常具体的过程数字——120 多轮迭代、87 份季报、99 份 PDF、11,000 多页、2,800 多次搜索、1,100 多次终端查询。这些数字让这个案例比 I–Love–Q 那个更可信,还是更不可信?(提示:想想「120 多轮迭代」是谁驱动的。)
13.6 结论:这篇报告最终主张了什么
论文 §8 只有一段,但每个词都是称过分量的。原文的主张可以拆成五条:
第一,K3 是一个开放的 2.8 万亿参数 MoE 模型,具备原生视觉能力和 100 万 token 的上下文窗口,建立在 KDA(Kimi Delta Attention)和 AttnRes(Attention Residuals)之上。
第二,它是世界上第一个开放的 3T 级模型。
第三,它在长时程编码、智能体、知识、推理和视觉任务上交付了前沿级性能。
第四,与最强的专有模型仍有差距——这是作者自己写的,不是我们加的。
第五,K3 在所有人触手可及的范围内确立了一个新的开放前沿;作者希望它能赋能更广泛的社区在研究、部署和创新上的工作。
常见误解:把「开放前沿」读成「最强」
这段结论的措辞非常克制,值得逐字体会。它没有说 K3 是最强的模型,它说的是「在开放模型里确立了新的前沿」,并且明确承认与最强专有模型的差距。第12章的数据完全支持这个措辞:Artificial Analysis 综合指数第 4、Vals Index 第 2、Text Arena 第 8、CritPt 落后三个模型——但 WebDev Arena 第 1,且是首个登顶该榜的开放模型。
顺带指出一个小小的措辞技巧:模型是 2.8 万亿参数,论文称它为「3T 级(3T-class)」。这不是错,「级」本来就是量级的说法,但它确实是往上取整而不是往下。这类修辞在技术报告里很常见,认出来就好,不必上纲上线。
结论里说 K3「在长时程编码、智能体、知识、推理和视觉任务上交付了前沿级性能」。请你判断:这五个词里,哪几个能被第12章的数据充分支撑,哪几个需要加限定?「前沿级」(frontier-level)这个词,和「最强」差在哪?
支撑最充分的是「智能体」:BrowseComp 91.2、DeepSearchQA 95.0、ResearchRubrics 76.2、MCPMark-Verified 94.5、AutomationBench 30.8、SpreadsheetBench 2 34.8、τ3-Banking 33.4、Harvey Lab-AA 94.6 都是全表最好。这里甚至可以用比「前沿级」更强的词。
「长时程编码」基本成立但要看细分:ProgramBench 77.8 最好、SWE-Marathon 42.0 最好、Terminal-Bench 2.1 88.3 几乎追平 Sol;但 FrontierSWE 81.2 落后 Fable 5 的 86.6、DeepSWE 67.5 落后前两名。「前沿级」(在第一梯队里)成立,「最强」不成立。
「视觉」成立但依赖工具:OmniDocBench 91.1 最好、Video-MME 90.0 与 MMVU 82.1 最好;但 WorldVQA 51.0 第二、BabyVision 85.7 落后、ZeroBench 不带工具只有 23.0。
「知识」需要加限定:AA-LCR 74.7 最好、GPQA Diamond 93.5 与前沿相当;但 HLE-Full 43.5 / 56.0 落后包括 Claude Opus 4.8 在内的多个模型。
「推理」最需要加限定:CritPt 23.4 落后 Fable 5、Sol 和 GPT-5.5 三个模型,论文自己在 §6.1.4 里写了「研究级推理仍是关键改进方向」。把「推理」和其余四个并列写进结论句,是这段结论里最经不起逐条核对的一处。
「前沿级」与「最强」的差别:「前沿级」断言的是归属(属于第一梯队),「最强」断言的是排序(第一梯队里的第一名)。归属只需要证明差距不大,排序需要证明每一项都领先。所以「前沿级」是一个门槛低得多、也诚实得多的用词——配合它后面紧跟的「与最强专有模型仍有差距」,整段结论在措辞上是自洽的。读结论句时要养成的习惯是:先分辨这个词断言的是归属还是排序,再决定该拿多严的标准去验它。
变式:结论还说 K3 是「世界上第一个开放的 3T 级模型」。这个主张需要什么证据才能成立?它和「前沿级性能」那个主张相比,哪个更容易被推翻?(提示:想想「第一个」这种主张的证伪方式。)
13.7 这篇论文没有告诉你的事
读完一篇技术报告,最有价值的问题往往不是「它说了什么」,而是「它没说什么,以及为什么不说」。下面这份清单必须分成三类看——把行业通行的省略当成隐瞒,和把特有的省略当成正常,都是误读。
| 类别 | 缺什么 | 为什么这样归类 |
|---|---|---|
| A 类 行业通行做法 |
训练用了多少张 GPU、训练了多久 | 这几项在今天几乎所有前沿实验室的报告里都不披露,属于商业机密与竞争信息。不披露不等于报告不诚实,但它意味着训练成本与算力效率无法被外部核算,也无法验证「以更低成本达到同等效果」这类说法。 |
| 总共训练了多少 token | ||
| 整体训练成本 | ||
| 预训练数据的具体来源与配比数字 | ||
| B 类 这篇报告可以做得更好的 |
案例研究只报成功,没有成功率、尝试次数、人工干预程度 | §7 通篇没有一个失败案例或分母。这是可以在不泄露机密的前提下补上的(见 13.5 的题)。 |
| 跨模型比较的对等性问题没有被量化处理 | 论文披露了回退率、拒答数、harness 差异(值得肯定),但没有报告「剔除这些任务后」的子集分数,读者只能定性打折扣。 | |
| 关键结果几乎都没有方差 / 重复次数 | 视觉基准跑了三到五次,说明作者认可方差的重要性;但编码、智能体基准和内核优化案例都没有报告重复。CorpFin v2 差 0.2 分这类结论因此无法判断。 | |
| 内部基准既指导训练又充当考卷,且不公开 | 论文自己写了这些基准「直接指导数据和训练的迭代」。这不是错误,但它使得内部分数不能作为跨模型的裁决依据。 | |
| 内核优化案例的基线强度、内核选择依据未说明 | 所有加速百分比都相对「FLA Triton 基线」,但该基线的优化程度没有交代(见 13.3 的题)。 | |
| C 类 我自己也不确定的 |
安全对齐与价值观训练的细节 | 这四项在我负责的 §6–§8(第 25–34 页)里确实没有出现。但这份报告还有前面二十多页和可能的附录,其中是否有相关内容,我没有直接读到。如果你在原报告别处找到了对应披露,应当以原文为准,并推翻我这一栏的说法。这一栏列出来的目的,是提醒你去亲自核对,而不是让你直接引用我的判断。 |
| 权重的开源许可证是哪一种、有什么使用限制 | ||
| 可能的社会影响、滥用风险的缓解措施(§6.2.2 只谈了网络安全能力的测量,没谈发布后的缓解) | ||
| 缩放定律(scaling law)相关图表是否标注了坐标轴数值、各种「更稳定/更好」的结论是否配有定量消融 |
补充:怎么用这张表才是对的
A 类不构成对这篇报告的批评——你可以批评整个行业的不透明,但单独指责这一篇没有意义。B 类是真正可以拿来评价这篇报告的地方,而且每一条我都给出了在论文里的具体出处。C 类是我的知识边界,我把它明确标出来,是因为一份诚实的批评必须先声明自己看到了多少。
把「我没读到」说成「论文没有」,和把「论文没有」说成「论文错了」,是读技术报告时最容易犯的两个错误,方向相反但同样致命。
答辩:如果我是审稿人
你们把这个模型叫「开放」(open),权重确实放出来了。但训练数据不公开、数据配比不公开、算力规模不公开、内部评测基准不公开、评测所用的 harness 配置细节也不完整。那么「开放」到底开放了什么?把它和「开源软件」放在一个词底下,是不是一种误导?
参考防守(先自己组织语言再看)
这个质疑很难完全挡住,但可以把它拆得更清楚:
能站住的部分。第一,论文用的词是 「open」/「open-weight」(开放权重),在 Table 2 和 Table 5 里,GLM-5.2 和 K3 都被归在 「Open Weight」 这一栏——作者用的分类标签本身就是「开放权重」,而不是「开源」。这个用词是准确的。第二,开放权重带来的实际价值是真实且巨大的:你可以本地部署(成本结构完全不同于 API)、可以微调、可以做研究、可以在没有网络的环境里用、可以审计模型的行为——这些是闭源 API 一个都给不了的。第13章里 MiniTriton 和 nano-kpu 的 RTL 都放上了 GitHub,也是实打实的开放动作。第三,第12章的 WebDev Arena 第 1 之所以是一条有分量的新闻,正是因为「开放」这个属性——它意味着这个能力不再被少数几家公司垄断。
必须承认的部分。(a) 从科学可复现性的角度,开放权重远远不够。没有数据、没有配比、没有算力规模,外部研究者无法复现训练过程,只能复现推理。所以这篇报告的很多训练侧结论(「这样做更稳定」「这个改动带来了改善」)在外部是不可证伪的。(b) 内部基准不公开这一点尤其可惜——那是完全可以公开而不损害商业利益的东西,公开之后其他模型也能在上面测,结论的说服力会强很多。(c) 「开放」这个词在公众传播里确实容易被读成「开源」,而两者的差别很大;论文自己用词严谨,但转述者往往不严谨。
我认为诚实的结论:把 K3 称为「开放权重的前沿模型」是准确的;把它称为「开源模型」是不准确的;把它当作「可复现的科学工作」则是错误的。这三句话应该被分开说。
终极综合题。把第12章的九问检查清单,从「读一张表」升级成「读一整篇技术报告」。请你写出一份读技术报告的清单(不少于六条),每一条都要能在 Kimi K3 这篇报告里找到至少一个具体的正面或反面例子。写完之后,用你自己的清单给这篇报告下一个整体评价——它在哪些方面高于平均水准,哪些方面低于?
1. 主张与证据类型匹配吗?案例研究只能证明可能性,基准分数才能说明平均水平,第三方评测才能对抗自评偏差。正面例子:论文同时提供了三类(§6.1 基准、§6.3 第三方、§7 案例)。反面例子:§7 的案例被用来支持「能替代人类研究者/剪辑师」这类含平均性能意味的表述,而案例只能支持存在性。
2. 作者主动报告了哪些对自己不利的信息?正面例子:写出 90.4 这个不利的对照、承认 CritPt 落后、承认「研究级推理仍是关键改进方向」、承认与最强专有模型有差距、承认自己的安全评测只是能力下界、列出五个自己落后的内部基准。这是这篇报告最值得肯定的地方。反面例子:§6.1.4 说「一致地超过 Claude Opus 4.8」,但表 2 的 HLE-Full 和 OfficeQA Pro 是反例。
3. 结论句的量词准确吗?「一致地」「全面」「显著」这类词要逐个拿回表里验证。见第 2 条的反面例子。
4. 有没有消融实验支撑设计选择?凡是「我们改成 X,效果更好/更稳定」的说法,要找配套的定量对照。(这一条主要落在报告的前半部分,我未直接读到,见 13.7 的 C 类。)
5. 比较是否对等?harness、工具、上下文策略、硬件、回退与拒答。混合例子:论文披露了这些差异(好),但没有做剔除后的子集分析(不够)。
6. 有没有方差和重复?正面例子:视觉基准三次平均、ZeroBench 五次。反面例子:编码/智能体基准和内核优化案例都没有报告重复次数。
7. 有没有独立第三方验证?正面例子:Artificial Analysis、Vals AI、竞技场榜单,以及网络安全部分英国 AISI 与 NIST CAISI 的独立联合评估——后者尤其重要,因为它验证的是风险相关的结论。
8. 缺失的信息属于哪一类?行业通行、这篇特有、还是我没读到。见 13.7 的三分表。
整体评价:
高于平均水准的地方:配置披露的颗粒度(连「300K 触发压缩」这种小字都写了)、主动自曝不利数字、引用第三方结果时标注日期、把安全评测明确定性为下界并引入独立评估、把成本轴加进来(很多报告只报分数)、开放权重与两个 GitHub 仓库。
低于平均或可改进的地方:案例研究缺分母、关键基准缺方差、内部基准既指导训练又当考卷且不公开、少数总结句的量词超出了表格能支撑的范围、内核优化案例的基线强度未交代。
一句话:这是一篇可以被认真批评的报告——它给了你足够多的条件信息,让你能够自己去打折扣。上面这八条批评里,有一大半是用论文自己提供的信息做出来的。一份让你无从下手批评的报告,通常不是因为它更完美,而是因为它披露得更少。
变式:把这份清单反过来用——假设你要写一篇技术报告,而你希望它经得起最挑剔的读者。按这八条,你会在哪些地方多花成本?如果预算只够做其中三条,你选哪三条?为什么?
本章小结
案例研究部分展示了五类工作。GPU 内核优化:24 小时预算、相同沙箱,K3 把 AttnRes 从 283.6 ms 优化到 114.4 ms(降低约 59.7%,加速约 2.48 倍),DSA 和 KDA 分别削减 55.1% 和 73.6%,MLA 达到峰值 TFLOPS 一半以上;Figure 14 里 K3 终点 +59.7%,追平 Claude Fable 5(+57.1%,带回退),大幅超过 GPT-5.5(+30.8%)和 GPT-5.6 Sol(+17.3%)。编译器:MiniTriton 在 L20 上于核心基准套件的几何平均上超过 PyTorch eager 和 torch.compile,tensor-core 矩阵乘法在最大形状上达到实测机器上限约 90%,端到端训练 GPT 时梯度与 torch 的差异不超过 fp32 舍入误差 10−4(以 fp64 为参考)。芯片:48 小时自主运行,4 mm² 内 100 MHz 收敛时序,RTL 仿真解码超过 8,700 tokens/s,146 万标准单元、0.277 MiB SRAM,RTL 已开源。科研与知识工作:I–Love–Q 复现约两小时(对照「研究者一到两周」)、42 年 AI ASIC 产业研究网站(87 份季报、99 份 PDF、11,000 多页、2,800 多次搜索)、391 个引力波事件的并发子智能体分析。视频:3Blue1Brown 风格的自我架构解说与 56 段素材的预告片。
结论部分的措辞很克制:开放的 2.8 万亿参数 MoE、原生视觉、100 万 token 上下文、建立在 KDA 与 AttnRes 之上;世界第一个开放的 3T 级模型;在长时程编码、智能体、知识、推理与视觉上达到前沿级;与最强专有模型仍有差距;在所有人触手可及的范围内确立了新的开放前沿。
但这一章真正想留给你的是最后两件事。第一,那句藏在内核优化段落末尾的话——一个早期的 K3 检查点已经在承担作者自己的内核优化工作。这是全篇最有力的证据(作者用自己的生产环境为它背书),同时也是最需要追问的证据(这类任务的数据很可能回流进了训练)。第二,13.7 那张三分表:把一份报告的省略分成「行业通行的」「这篇特有的」「我自己也不确定的」,然后只用第二栏去批评它。这个动作比记住任何一个分数都更值得带走。
Chapter 13 · Case studies, conclusions, and the things this paper does not tell you
In one sentence: this chapter first looks at what K3 got done when it was sent out to work (optimising GPU kernels, writing a compiler, designing a chip, reproducing an astrophysics paper, cutting a trailer), and then does something the whole paper never does — it lists the things this report does not say.
After this chapter you should be able to
- Work out the speedup from 283.6 ms → 114.4 ms, and say clearly why “the percentage the latency dropped” and “the speedup factor” are not the same number
- Explain why those staircase-shaped curves in Figure 14 carry more information than a single end-point number
- Say clearly why “an early K3 checkpoint doing K3's own kernel optimisation” is both the strongest evidence there is and the evidence you should be most wary of
- Point out the inherent weakness of the case study as a form of evidence, and name the three things you would have to add to turn it into a credible measurement
- Sort the omissions in a technical report into three kinds: the ones the whole industry makes, the ones specific to this paper, and the ones you are not sure about yourself
13.1 GPU kernel optimisation: give it a day and let it tune on its own
First, why this experiment is worth doing. The previous twelve chapters were all about “what the model scored on an exam paper somebody else set”. But the authors of K3 have a special kind of task in hand: the thing they do themselves every day. When you train a model with 2.8 trillion parameters, one of the most draining jobs is writing the attention operator as GPU code that runs fast enough. This kind of work has a very good property — the result can be measured objectively by a machine: fast is fast, slow is slow, no judge needed, no marking rubric needed, and no way to fudge it.
GPU kernel: a program that runs directly on the graphics card, responsible for actually computing some mathematical operation (attention, say). The same mathematical formula can be written dozens of ways, and the speeds can differ several times over — the difference comes from how the data is moved between video memory and on-chip cache, how many threads divide the work, and whether computing and moving can overlap. Optimising a kernel means writing it faster without changing the result of the computation.
The experimental setup is clean: each model works independently in a sandbox configured exactly the same way, the budget for each task is at most 24 hours, and that time can go on profiling, rewriting code and running benchmarks. Four representative kernels are covered: AttnRes, DeepSeek sparse attention (DSA), KDA, and MLA with head dimension 512. The hardware is one NVIDIA Hopper GPU and one GPGPU from another vendor.
The result: K3 made large improvements on all four kernels —
AttnRes latency went from 283.6 ms down to 114.4 ms; the runtimes of DSA and KDA were cut by 55.1% and 73.6% respectively; on MLA it reached more than half of peak TFLOPS. Across these tasks, K3 matched Claude Fable 5 (with fallback), and beat Claude Opus 4.8, GPT-5.6 Sol and GPT-5.5 by a wide margin.
Peak TFLOPS: TFLOPS is a trillion floating-point operations per second. “Peak” is the fastest this card can theoretically go. A real kernel almost never runs at peak, because there is always time spent waiting for data to move. So “more than half of peak” is a solid result — it means this code is using more than half of the card's compute, rather than sitting and waiting most of the time.
Figure 14: do not just look at the end point, look at the shape
The paper uses Figure 14 to show each model's optimisation trajectory on AttnRes: the horizontal axis is hours of active work, the vertical axis is percentage speedup relative to the FLA Triton baseline. The end-point numbers are: K3 +59.7%, Claude Fable 5 +57.1%, GPT-5.5 +30.8%, GPT-5.6 Sol +17.3%.
But more interesting than the end point is the shape of the curves: none of the four rises smoothly, they are all staircases — a long flat stretch (a pile of things tried that did nothing), then a sudden jump up one step (a change that actually works has been found), then the next plateau. K3's curve jumps early and jumps often, already past 40% at around 5 hours; Claude Fable 5 starts more slowly, but later jumps several steps in a row in the 11–14 hour range and catches up; GPT-5.5 sits flat at around 30% for a long time; GPT-5.6 Sol only creeps up to about 17% close to 20 hours in.
Why the “staircase” shape is itself a conclusion
If the optimisation process rose smoothly, that would mean it is skilled labour — spend more time, get a bit more return, and the difference between models would show up only in the slope. A staircase means something completely different: most of the time is exploration with no return, and all of the gain is concentrated in a few correct ideas. In this kind of task, “stronger” does not mean “works faster”, it means “more likely to run into those few correct ideas, and able to recognise them once it does”.
This also explains why the GPT-5.5 line sits flat around 30% for so long — it is not that it stopped working, it is that it kept trying, but the directions it tried never got it over the next step.
AttnRes latency went from 283.6 ms down to 114.4 ms. Work out separately: (a) by what percentage the latency dropped; (b) by what factor it sped up. Then answer: why are these two numbers different, and when would someone use them interchangeably?
(a) Percentage drop = 169.2 ÷ 283.6 ≈ 59.7%.
(b) Speedup factor = 283.6 ÷ 114.4 ≈ 2.48×.
The two numbers are two views of the same thing: a drop of 59.7% says “six-tenths of the original time was saved”, a speedup of 2.48× says “one second now does what used to take 2.48 seconds”. Their relationship is: speedup factor = 1 ÷ (1 − fraction dropped). As the fraction dropped approaches 100% the speedup factor goes to infinity, so the percentage drop looks “not impressive enough” in the high-speedup range, and the factor looks “not impressive enough” in the low-speedup range — whoever writes the marketing copy picks whichever suits them. A drop of 59.7% sounds ordinary; “2.48 times faster” sounds much better, but they are the same thing.
One more coincidence worth noting: that number, 59.7%, is exactly the end-point label “+59.7%” on K3's curve in the paper's Figure 14. These two numbers very likely refer to the same thing — that is, the “speedup percentage” on the vertical axis of Figure 14 should be read as “the percentage the latency dropped”, not as “the factor minus one”. But the paper never gives a defining formula for that axis, so this is only a highly plausible reconciliation, not something the paper states. When numbers from two sources line up, be pleased first, and then still ask: are they really defined the same way?
Variant: the runtimes of DSA and KDA were cut by 55.1% and 73.6% respectively. Which one has the larger “speedup factor”? By how much? Guess a number on instinct first, then compute it, and see how far off your instinct was.
13.2 Bootstrapping: an early K3 optimising K3
At the end of this passage the paper adds one very offhand but extremely heavy sentence: in the later stages of development, an early K3 checkpoint was already carrying most of their kernel optimisation work.
Unpack that sentence: they used a K3 that had not finished training to optimise the very GPU kernels that would be used to train K3. This is a bootstrapping(bootstrapping) loop — the model improves the infrastructure, better infrastructure trains a better model, and the better model goes back to improving the infrastructure.
An analogy
It is a bit like a carpenter using a plane he made himself to make a better plane. The first plane is rough, but good enough to make a slightly better second one, and the second can make a third. Where the analogy breaks down: the carpenter's skill does not change, only the tools do; here “skill” (the model) and “tool” (the kernel) are two ends of the same chain, and the model is both the craftsman and the product. The more important difference is this — the carpenter can judge whether the new plane works well, whereas the criterion “the kernel runs faster”, objective as it is, cannot guarantee that the model trained with it is better. There are several layers of inference in between.
A synthesis question. Three things are known: (1) K3 scores 42.0 on SWE-Marathon (a GPU-kernel-oriented suite), seven points ahead of Claude Fable 5; (2) in the case study K3 made large improvements on all four kernels, with the largest speedup on AttnRes; (3) an early K3 checkpoint carried most of the authors' kernel optimisation work in the later stages of development.
Answer this: taken together, do these three things strengthen or weaken the conclusion that “K3's GPU kernel optimisation ability really is strong”? Write out the argument in both directions, then say what extra information you would need in order to decide.
The strengthening direction (supports H1). Point (3) is the hardest form of evidence in this whole chapter — it is not a benchmark score, it is the authors backing it with a real engineering need and real money. A lab willing to hand the key optimisation work on its own training infrastructure to a model is saying it is usable in a real setting where they bear the consequences. That is harder to fake than any self-reported benchmark: if the kernels the model produces are wrong or slower, what suffers is their own training schedule. And this kind of result can be measured objectively by a machine (fast is fast), with no judge involved.
The weakening direction (supports H2). If the model was used heavily on this kind of task during development, then the trajectories, the failure modes and the winning recipes of that kind of task were very likely recycled into later training data — which is exactly the same problem as “internal benchmarks being used to guide training” from Chapter 12. So “strong on GPU kernel tasks” may not be a general ability, but the result of this one class of task having been trained on specially. Being seven points ahead on SWE-Marathon is much less striking under that reading: it happens to be precisely the class of task the authors know best, have the most data for, and have the most motivation to optimise.
What extra information you would need in order to decide: (i) whether kernel-optimisation data went into the training set, and whether it overlaps with the SWE-Marathon tasks (the paper does not say); (ii) running the same experiment in a performance engineering domain unrelated to GPU kernels (database query optimisation, say, or some other branch of compiler optimisation), to see whether the advantage transfers; (iii) whether these improved kernels have been reproduced and verified by an independent party.
My judgement: both directions hold, and they do not contradict each other — there very likely is a general performance engineering ability (H1), and it has also been reinforced further in the specific domain of GPU kernels (H2). So the reasonable conclusion is: “K3 has been shown to be usable in production for GPU kernel optimisation, and the evidence for that is strong; but extrapolating from it to ‘K3's performance engineering ability leads across the board’ is not supported by the evidence.”
One easily missed detail while you are here: the paper says that on these kernel tasks K3 “matched Claude Fable 5 (with fallback)”. Those two words, with fallback, are back again — how much of the score on Fable 5's side came from fallbacks, the paper does not break down in this section either.
Variant: suppose you are an independent evaluation body that wants to design an experiment to tell H1 from H2, but you cannot get hold of the authors' training data. How would you design it? (Hint: you can get K3's weights, because they are open. Think about kernels that “appeared only recently and cannot possibly be in the training data”.)
Defend it: if I were a reviewer
You say that on kernel optimisation K3 “matched Claude Fable 5 and beat Opus 4.8, GPT-5.6 Sol and GPT-5.5 by a wide margin”. But of these four kernels (AttnRes, DSA, KDA, MLA), two are architectural components your own paper proposed in its earlier chapters. You had every model optimise operators you invented yourself, and then announced that your model won. What can this experimental design show?
A reference defence (put it in your own words first, then look)
This is the sharpest challenge in this section, and it has to be answered in layers:
The part that holds. First, the experimental conditions themselves are symmetric: every model works in an identically configured sandbox, with the same 24-hour budget, optimising the same set of kernels, measured against the same baseline (FLA Triton). No model was given extra information. Second, the criterion is a machine-measured runtime, not a score given by a person, so there is no room for a judge's preferences. Third, the task is “make this code run faster”, not “recall what AttnRes is” — familiarity with the operator can help you get started, but cutting the latency by six-tenths takes real performance engineering ability. Fourth, the definitions of KDA and AttnRes are public in the earlier chapters of the paper; the other models are not left in the dark.
The part that does not hold and has to be conceded. (a) The asymmetry in prior knowledge is real. K3's authors have accumulated a great deal of experience with these operators, and that experience very likely got into K3 through the training data; the other models had no such exposure. This is not cheating, but it means the comparison measures “ability to optimise Kimi's own operators”, not “general kernel optimisation ability”.
(b) The sample size is 4. Four kernels, two cards, no number of repeats reported and no variance reported. The 24-hour exploration process is very random (look at those staircases in Figure 14), and a second run could perfectly well produce a different ranking.
(c) The choice of baseline is never discussed. All the speedup percentages are computed against the “FLA Triton baseline”, but the paper never says how far that baseline itself was optimised. If the baseline is written fairly plainly on some kernel, then the “73.6% reduction” is worth less than it looks.
The honest conclusion, I think, is this: this case study proves that “K3 can make measurable, large improvements on real performance engineering tasks, and those improvements were used by the authors in their own production environment” — that much stands. But the cross-model ranking “K3 is stronger than GPT-5.6 Sol at kernel optimisation” is supported neither by the sample size nor by the symmetry of prior knowledge. The smallest fix would be: add a few third-party kernels unrelated to the Kimi architecture, and repeat each task three times, reporting the variance.
“K3 cut the runtime of the KDA kernel by 73.6%” — that sounds very impressive. Construct at least two alternative explanations under which this 73.6% holds but says nothing about how strong K3 is. Then say: what information would the paper have to add in order to rule out the explanations you constructed?
Explanation one: the baseline is too weak. If the FLA Triton baseline is just an untuned reference implementation, then cutting 73.6% might take nothing more than some standard, textbook optimisations (adjusting the tile size, cutting round trips to video memory, turning on the right data type). The paper never says how optimised this baseline is, so this cannot be ruled out. This is the most important one — note that what the paper gives for MLA is “more than half of peak TFLOPS”, which is an absolute figure (it takes the hardware ceiling as the denominator) and is far more trustworthy than a percentage relative to a baseline. If all four kernels came with absolute figures, this objection would disappear.
Explanation two: the headroom was handed over by the task, not earned by the model. Some kernels may have a well-known inefficiency in them (an operation that was never fused, say). Any capable optimiser — including the other models, and including an experienced human engineer — would fix that first, and there goes most of the gain. To rule this out you need a human expert baseline: given the same 24 hours, how far does a skilled engineer get? The paper does not give one.
Explanation three: selective reporting. The paper says it covers “four representative kernels”, but never says how many candidates those four were picked from, or by what criterion. If there were a fifth and a sixth kernel that K3 did badly on, we would not know. To rule this out you need to pre-register the list of kernels to be measured, or report every kernel that was attempted.
What information would rule these out: (i) the provenance and degree of optimisation of the baseline implementation, ideally with an absolute “relative to hardware peak” figure alongside; (ii) a human expert control under the same budget; (iii) the basis on which the kernel list was chosen, plus the complete results; (iv) the variance over repeated runs.
To be clear: constructing these alternative explanations is not the same as saying the paper is wrong. K3 may well have done very well — the AttnRes line, 283.6 → 114.4, is an absolute time, and the MLA line is relative to the hardware peak; neither of those two numbers is affected by the choice of baseline, and both are convincing in themselves. The point of constructing counter-explanations is to mark the boundary of the conclusions this evidence can support, not to dismiss it.
Variant: now turn it around — if what the paper reported were “reached 51% of peak hardware TFLOPS”, could you still construct an explanation that weakens it? (Hint: how is peak defined? Is the peak the same for different data types?)
13.3 MiniTriton: from “writing kernels” to “writing the tool that makes kernels”
The previous section had the model optimise a piece of code. The task in this section is one level up: have the model build a compiler.
Compiler: a program that translates the high-level code a person writes into the low-level instructions a machine can execute. Triton is a language made specifically for writing GPU kernels; it lets you describe a kernel in something close to Python, and its compiler then translates that into graphics card instructions. Building a compiler is much harder than writing a kernel — a kernel is one piece of work, a compiler is a machine that can produce endlessly many pieces of work.
K3 developed MiniTriton, a compact Triton-like compiler containing: a custom tile-level Python front end and layout system (a tile means cutting a big matrix into small blocks to process, and the layout system decides how those blocks are placed in video memory and registers), a lightweight warp-level MLIR annotation and optimisation layer (a warp is a group of threads on the GPU that execute in lockstep, MLIR is an intermediate representation, and the compiler does all sorts of transformations at this layer), and a PTX code generation pipeline (PTX is a low-level assembly language for NVIDIA cards).
Around this compiler it also built a dual-mode tensor library offering a PyTorch-like high-level interface: the eager execution path and the forward-only compilation path share the same DSL compiler and runtime. The library further provides reverse-mode automatic differentiation, neural network modules, NCCL-based distributed training primitives, and sparse and visualisation primitives.
The report card (on one NVIDIA L20): MiniTriton beats PyTorch eager and torch.compile on the geometric mean of its core benchmark suite. Its from-scratch tensor-core matrix multiplication path comes close to cuBLAS (NVIDIA's own highly optimised matrix library) on the largest shapes, reaching about 90% of the measured machine ceiling. Its DSL-level KDA prefill kernel beats an equivalent Triton reference implementation by a clear margin.
The line that says the most: MiniTriton can also train a GPT model end to end, with the loss curve hugging the PyTorch reference; the gradients of the full model differ from torch's automatic differentiation by no more than torch's own fp32 rounding error (10−4), and that difference is measured against an fp64 reference. Figure 15 shows four plots: (a) the CUDA-core roofline, (b) the tensor-core roofline, (c) the training loss curve of a character-level GPT trained with MiniTriton compared with torch eager, (d) two-card data-parallel training using MiniTriton's own distributed primitives compared with single-card training — the final cross-entropies of the two curves only come apart at the third decimal place.
Why “the gradients differ by no more than fp32 rounding error” is the strongest sentence in this section
The mistake a compiler is most likely to make is computing the wrong answer without raising an error — the result is off by a little, the model still trains, it is just always off by that little. So the check on correctness has to be quantitative.
The check here is cleverly designed: they do not treat torch as the “truth”, they compute the thing once in fp64 (double precision, higher accuracy) and use that as the truth, then measure how far MiniTriton and torch each are from it. The conclusion is that MiniTriton's deviation is no larger than torch's own deviation. Put another way: MiniTriton's error is already small enough to be buried under the precision limit of floating point itself, and you cannot tell numerically whether it or torch is “more correct”.
That is orders of magnitude stronger than “we ran it and it looked fine”.
Why, when verifying MiniTriton's gradients, go to the trouble of computing an fp64 reference instead of just saying “MiniTriton's gradients differ from PyTorch's by no more than 10−4”? Where do these two statements differ in evidential strength?
The problem with comparing only A and B: a small |A − B| only shows the two are close, not that either is correct. In the extreme case, if MiniTriton happened to reproduce some numerical flaw of torch's, |A − B| would be 0 and both would be wrong. Conversely, if |A − B| is large, you still cannot tell whether B is wrong or A is — in fp32 torch has appreciable rounding error of its own.
Once you bring in the fp64 reference T: you can measure |A − T| and |B − T| separately, and then you can say something far stronger — “B's deviation is no larger than A's”. What that sentence means is: MiniTriton's error already falls inside the noise floor inherent in the fp32 data type, and being more accurate than that would be pointless, because the data type itself stops here.
This is also a transferable way of thinking about verification: to prove a new implementation is right, do not compare it with the old implementation, compare it with a higher-precision reference, and then argue that “the distances of the old and the new from the reference are of the same order”. The same idea shows up in Figure 15(d): put two-card distributed training and single-card training side by side, and if the distributed primitives (NCCL communication, gradient reduction) were written wrong, the two loss curves would diverge; they end up coming apart only at the third decimal place, which shows the communication layer is right too.
One thing to add: what the paper says is that the difference in “full-model gradients” is no more than 10−4, but it never says whether that is a maximum difference, a mean difference or some norm, and never says at what stage of training it was measured. That is a small but real vagueness in the wording.
Variant: if MiniTriton's difference from the fp64 reference were smaller than torch's difference from the fp64 reference, could you say MiniTriton is “more accurate than PyTorch”? What is the trap in that conclusion?
13.4 Chip design: 48 hours, one inference chip prototype
This is the most unexpected of all the case studies. As an early proof of concept, K3 designed an inference chip prototype for a “nano model following the same architecture”. That nano model's architecture is of a piece with K3 itself: hybrid KDA and NoPE-MLA attention, Block AttnRes with block size 2, sigmoid MoE routing with one shared expert, and weights in grouped INT4 quantisation (group size 128).
The process: in a single 48-hour autonomous run, K3 used Kimi Code together with open-source EDA tools (EDA is electronic design automation, the software used to design chips) and the Nangate45 standard cell library to build, optimise and verify this chip.
The result: within a resolved area budget of 4 mm², the design closes timing at 100 MHz, with an RTL-simulated decode throughput of over 8,700 tokens/s, integrating 1.46 million standard cells, 0.277 MiB of SRAM, and an INT4 MAC array with fused dequantisation. The RTL code is open-sourced on GitHub.
Closes timing: a chip works to a fixed beat, and 100 MHz means 100 million beats per second. “Closes timing” means that every signal on the circuit settles before the end of a beat. If some path is too slow, the next beat arrives before the signal has settled and the chip computes the wrong answer. So this is a hard criterion for “the design really works”, not a metric you can discount.
Standard cell: the smallest prefabricated building block in chip design (an AND gate, a flip-flop, and so on). 1.46 million standard cells means this design used up 1.46 million blocks of that kind. A MAC array is a multiply-accumulate array; the main computation in a neural network is “multiply, then add up”, and this array is the hardware that does exactly that; “fused dequantisation” means the step of restoring INT4 weights to actual values is built directly into the compute path instead of being a separate pass.
Extra: a sense of scale for these numbers
4 mm²: about the face area of a sesame seed, or a little bigger than a dot you poke on paper with a pencil. For comparison, the main chip in a modern phone is on the order of 100 mm².
100 MHz: 100 million beats per second. An ordinary phone CPU today is about 2–3 GHz, which is 20–30 times faster. 100 MHz is roughly the clock speed of a mid-1990s personal computer.
8,700 tokens/s: take 100,000,000 ÷ 8,700 ≈ 11,500, which means about 11,500 clock cycles on average go into generating each token.
0.277 MiB SRAM: about 280,000 bytes, far smaller than a 1.44 MB floppy disk. That also tells you how “nano” the model this chip serves is.
Put these together: this is not a chip that can run K3, it is an existence proof that the architecture can be made into hardware.
From these five numbers — 4 mm², 100 MHz, 8,700 tokens/s, 1.46 million standard cells, 0.277 MiB SRAM — decide which of the following this chip is, and give your reasons: (A) a chip that could be mass-produced and deployed to run a K3 inference service; (B) a prototype that verifies “this architecture can be made into hardware”; (C) a pure software simulation, a toy with no hardware meaning.
Ruling out (A): the specifications are nowhere near deployment grade. 0.277 MiB of SRAM is only about 280,000 bytes, which may not even hold one layer of weights of a small model, so what it serves is the “nano model” the paper explicitly names; 100 MHz is a 1990s clock speed; 4 mm² is also far smaller than a commercial inference chip. And the paper's own words placing this work are “early proof of concept” and “prototype”.
Ruling out (C): this is not talk on paper. “Closing timing at 100 MHz” is a real physical constraint being met; it uses a real open-source EDA toolchain and the Nangate45 standard cell library; it has a concrete cell count (1.46 million) and area budget (4 mm²); the RTL code is open-sourced and anyone can go and check it.
Why (B): the proposition this work sets out to prove is “the architecture described in the earlier chapters of the K3 paper (KDA + NoPE-MLA + Block AttnRes + sigmoid MoE routing + grouped INT4 quantisation) can be mapped onto hardware in full, and that mapping can be done autonomously by the model within 48 hours”. An existence proof only needs one working minimal example; it does not need to be competitive.
One more thing to note: what the paper says is “RTL-simulated decode throughput” — RTL simulation means simulating the behaviour of this circuit on a computer; it has not actually been taped out and manufactured. So 8,700 tokens/s is a simulated value, not a value measured on real silicon. That distinction matters a great deal in the chip world; the paper's wording is accurate (it writes “RTL-simulated”), but it is easy for a reader to slide past.
Variant: the paper says the RTL code is open-sourced on GitHub. Compared with only reporting these numbers in the paper, what does the open-sourced code add as evidence? What does it not settle? (Hint: think about the claim “this code came out of a 48-hour autonomous run” — can open-sourced code prove that?)
13.5 Three case studies closer to human work
Research-oriented coding: two hours against one to two weeks
To reproduce the I–Love–Q universal relations in computational astrophysics (a set of empirical relations between a neutron star's moment of inertia, tidal deformability and quadrupole moment), K3 did the following string of things: reviewed more than 20 papers, cross-validated their results, implemented the full numerical pipeline, evaluated more than 300 equations of state, found inconsistencies in published formulas, wrote more than 3,000 lines of Python, and produced an interactive HTML dashboard. It took about two hours; the paper says an experienced researcher usually needs one to two weeks.
Knowledge work: two projects in Kimi Work
The first: K3 produced an interactive research website covering 42 years of AI ASIC industry history. It went through more than 120 rounds of iterative refinement, drawing on 87 quarterly reports and 99 original PDFs (more than 11,000 pages), and along the way did more than 2,800 web searches and more than 1,100 terminal queries.
The second: more than 20 concurrent subagents were used to analyse the 391 gravitational wave events in GWTC-5, producing seven scientific visualisations, two summary tables, and a literature review of a dozen or so papers.
Video editing and motion design
Drawing on the natively multimodal architecture (Chapter 6 covered this: vision and text live in the same token stream), K3 made a 3Blue1Brown-style animated explainer video — explaining its own architecture — and cut a trailer out of 56 clips of footage. This involved clip selection, match-action cuts, frame-accurate beat synchronisation, audio processing and several rounds of revision. The paper says that to make a comparable high-density short video, an experienced editor usually needs one to two days.
Read this carefully: these are hand-picked successes
The whole of §7 is a string of “it got X done” narratives, with not a single “it failed at Y”, “it needed this many retries on average”, or “we tried N projects and M succeeded”. This is not to say the cases are fake — they are very likely all true. But the case study, as a form of evidence, can by its nature only prove “possibility”, never “reliability”.
The sentence “K3 finished in two hours what takes a researcher one to two weeks” should properly be read as “at least once, K3 produced within two hours a result that was comparable to some degree”. It does not tell you what the success rate is, how much human intervention is needed, or how badly it fails when it fails. And for deciding whether to use it in your own work, those are exactly the most important pieces of information.
“K3 finished in about two hours the work an experienced researcher usually needs one to two weeks for.” Construct at least three situations in which this sentence is literally true but the inference that “K3's research ability is roughly equal to (or even beyond) a researcher's” does not follow. Then say: to turn this case into a credible measurement of ability, what are the three things you would minimally have to add?
Situation one: survivorship bias. If the authors tried twenty similar reproduction tasks and only this one went through, then “two hours” is the best case, not the typical case. The paper does not report the number of attempts, so this cannot be ruled out.
Situation two: the outputs are not equivalent. A researcher's “one to two weeks” includes plenty of things K3 did not do in those two hours: confirming that the methodology is sound, taking responsibility for the result under their own name, writing an argument that survives peer review, dealing with reviewers' comments. The standard for what each side delivers is different, so the times are naturally not comparable. The paper says K3 “found inconsistencies in published formulas”, which really is a high-quality signal, but someone still has to confirm that those “inconsistencies” are real problems rather than K3 having misunderstood something — the paper does not say who did that step, or what came of it.
Situation three: the task was picked. The I–Love–Q relations are an already published, already widely reproduced result. The relevant papers, formulas and equation-of-state data are all in the open literature, and the model has very likely seen them in training. Reproducing a known result and making a new discovery are not on the same order of difficulty. This does not weaken “it can finish a complex multi-step piece of work in two hours”, but it does weaken “its research ability is roughly equal to a researcher's”.
Situation four: the boundary of “two hours” is vague. Were those two hours fully autonomous? Did anyone step in with a hint, a correction, a restart? How long did preparing the prompt and the environment take? The words the paper uses are “in about two hours”, and it never says whether that is the same as “autonomous run time”. (Compare: for the chip case the paper explicitly writes “a single 48-hour autonomous run” — the difference in wording may be meaningful.)
The three things to add:
(i) Success rate and sample: run it on N tasks of the same kind and report how many succeeded, the average time taken, and where the failures failed.
(ii) A human control group: actually find several researchers, have them do the same thing, and time them, instead of citing an estimate of what is “usually needed”.
(iii) Independent review of the output: have domain experts judge the quality of what each side delivered without knowing where it came from (which is exactly what they did for web development in Kimi Webdev Bench — they have the blind-review methodology, they just did not use it here).
To be fair at the end: the case study is not a bad thing, it is an existence proof of the ceiling of ability, and it often says more than a benchmark score does about “can this thing actually do the job”. The problem only arises when an existence proof gets cited as average performance.
Variant: the AI ASIC industry case gives very specific process numbers — more than 120 rounds of iteration, 87 quarterly reports, 99 PDFs, more than 11,000 pages, more than 2,800 searches, more than 1,100 terminal queries. Do these numbers make that case more credible than the I–Love–Q one, or less? (Hint: think about who drove those “more than 120 rounds of iteration”.)
13.6 The conclusion: what this report finally claims
§8 of the paper is a single paragraph, but every word in it has been weighed. What the original claims can be broken into five parts:
First, K3 is an open MoE model of 2.8 trillion parameters, with native vision and a context window of 1 million tokens, built on KDA (Kimi Delta Attention) and AttnRes (Attention Residuals).
Second, it is the world's first open 3T-class model.
Third, on long-horizon coding, agents, knowledge, reasoning and vision tasks it delivers frontier-level performance.
Fourth, a gap to the strongest proprietary models remains — this is the authors' own sentence, not one this site added.
Fifth, within reach of everyone, K3 establishes a new open frontier; the authors hope it can empower the wider community in its research, deployment and innovation work.
A common misreading: reading “open frontier” as “strongest”
The wording of this conclusion is very restrained and worth taking in word by word. It does not say K3 is the strongest model; it says it “establishes a new frontier among open models”, and it explicitly admits the gap to the strongest proprietary models. The data in Chapter 12 supports that wording completely: 4th on the Artificial Analysis composite index, 2nd on the Vals Index, 8th on Text Arena, behind three models on CritPt — but 1st on WebDev Arena, and the first open model to top that board.
One small piece of wording craft in passing: the model has 2.8 trillion parameters, and the paper calls it “3T-class”. That is not wrong — “3T-class” is a statement about order of magnitude to begin with — but it does round up rather than down. This kind of rhetoric is common in technical reports; recognise it and move on, there is no need to make a case out of it.
The conclusion says K3 “delivers frontier-level performance on long-horizon coding, agent, knowledge, reasoning and vision tasks”. Decide for yourself: of these five words, which are fully supported by the data in Chapter 12, and which need a qualifier? Where does the term “frontier-level” differ from “strongest”?
The best supported is “agents”: BrowseComp 91.2, DeepSearchQA 95.0, ResearchRubrics 76.2, MCPMark-Verified 94.5, AutomationBench 30.8, SpreadsheetBench 2 34.8, τ3-Banking 33.4 and Harvey Lab-AA 94.6 are all the best in the table. Here you could even use a stronger word than “frontier-level”.
“Long-horizon coding” broadly holds, but look at the breakdown: ProgramBench 77.8 best, SWE-Marathon 42.0 best, Terminal-Bench 2.1 88.3 almost level with Sol; but FrontierSWE 81.2 is behind Fable 5's 86.6, and DeepSWE 67.5 is behind the top two. “Frontier-level” (inside the top tier) holds; “strongest” does not.
“Vision” holds but depends on tools: OmniDocBench 91.1 best, Video-MME 90.0 and MMVU 82.1 best; but WorldVQA 51.0 second, BabyVision 85.7 behind, and ZeroBench without tools is only 23.0.
“Knowledge” needs a qualifier: AA-LCR 74.7 best, GPQA Diamond 93.5 level with the frontier; but HLE-Full 43.5 / 56.0 is behind several models, Claude Opus 4.8 among them.
“Reasoning” needs a qualifier most of all: CritPt 23.4 is behind three models, Fable 5, Sol and GPT-5.5, and the paper itself writes in §6.1.4 that “research-level reasoning remains a key direction for improvement”. Putting “reasoning” alongside the other four in the concluding sentence is the place in this conclusion that stands up least well to a line-by-line check.
The difference between “frontier-level” and “strongest”: “frontier-level” asserts membership (it belongs to the top tier); “strongest” asserts ordering (first place inside the top tier). Membership only needs you to show the gap is small; ordering needs you to show a lead on every item. So “frontier-level” is a far lower bar and a far more honest word to use — together with the “a gap to the strongest proprietary models remains” that follows it immediately, the whole conclusion is self-consistent in its wording. The habit to build when reading a conclusion sentence is: first work out whether the word asserts membership or ordering, then decide how strict a standard to test it against.
Variant: the conclusion also says K3 is “the world's first open 3T-class model”. What evidence does that claim need in order to hold? Compared with the “frontier-level performance” claim, which of the two is easier to overturn? (Hint: think about how a claim of the form “the first” gets falsified.)
13.7 What this paper does not tell you
When you have finished a technical report, the most valuable question is usually not “what did it say” but “what did it not say, and why not”. The list below has to be read in three categories — treating an omission that is standard across the industry as concealment, and treating an omission peculiar to this report as normal, are both misreadings.
| Category | What is missing | Why it is filed here |
|---|---|---|
| Category A standard practice in the industry |
How many GPUs the training used, and how long it ran | None of these is disclosed in the reports of almost any frontier lab today; they count as trade secrets and competitive information. Not disclosing them does not make the report dishonest, but it does mean training cost and compute efficiency cannot be audited from outside, and claims of the form “the same result at a lower cost” cannot be verified either. |
| How many tokens were trained on in total | ||
| The overall training cost | ||
| The specific sources of the pre-training data and the numbers behind the mixture | ||
| Category B where this report could have done better |
The case studies report only successes: no success rate, no number of attempts, no degree of human intervention | There is not one failed case and not one denominator anywhere in §7. This could have been added without giving away any secrets (see the question in 13.5). |
| The comparability problem in cross-model comparison is never handled quantitatively | The paper does disclose fallback rates, refusal counts and harness differences (which deserves credit), but it never reports the subset scores “with those tasks removed”, so the reader can only discount qualitatively. | |
| Almost no key result comes with variance or a number of repeats | The vision benchmarks were run three to five times, which shows the authors accept that variance matters; but the coding and agent benchmarks and the kernel optimisation case study report no repeats at all. A conclusion of the type “CorpFin v2 by 0.2 points” therefore cannot be judged. | |
| The internal benchmarks both guide training and serve as the exam paper, and are not public | The paper itself writes that these benchmarks “directly guide the iteration of data and training”. That is not an error, but it does mean the internal scores cannot serve as the basis for a cross-model verdict. | |
| The strength of the baseline in the kernel optimisation case study, and the grounds for choosing those kernels, are not explained | Every speed-up percentage is relative to the “FLA Triton baseline”, but how well optimised that baseline is is never stated (see the question in 13.3). | |
| Category C what I am not sure about myself |
The details of safety alignment and values training | These four genuinely do not appear in the §6–§8 (pp. 25–34) that I am responsible for. But the report also has the twenty-odd pages before that and possibly an appendix, and whether anything relevant is in there I have not read directly. If you find the corresponding disclosure elsewhere in the original report, the original should be taken as authoritative and what I say in this column should be overturned. The point of listing this column is to remind you to check for yourself, not to let you quote my judgement directly. |
| Which open-source licence the weights carry, and what usage restrictions come with it | ||
| Possible social impact, and mitigations for the risk of misuse (§6.2.2 only talks about measuring cybersecurity capability, not about mitigation after release) | ||
| Whether the charts about scaling laws have numbers marked on their axes, and whether the various “more stable / better” conclusions come with quantitative ablations |
A note: the right way to use this table
Category A is not a criticism of this report — you can criticise the whole industry for its opacity, but singling out this one report makes no sense. Category B is where this report can genuinely be judged, and for every item there I have given the specific place in the paper it comes from. Category C is my knowledge boundary; I mark it out explicitly because an honest criticism has to declare first how much it has seen.
Turning “I did not read it” into “the paper does not have it”, and turning “the paper does not have it” into “the paper is wrong”, are the two easiest mistakes to make when reading a technical report — opposite in direction and equally fatal.
Defend it: if I were a reviewer
You call this model “open”, and the weights really have been released. But the training data is not public, the data mixture is not public, the compute scale is not public, the internal evaluation benchmarks are not public, and the harness configuration details used for the evaluation are incomplete as well. So what exactly does “open” open up? Is putting it under the same word as “open-source software” a form of misdirection?
A reference defence (put it in your own words first)
This challenge is hard to block completely, but it can be broken up more clearly:
The part that holds. First, the words the paper uses are “open” / “open-weight”: in Table 2 and Table 5, GLM-5.2 and K3 are both put in the “Open Weight” column — the classification label the authors use is itself “open weight”, not “open source”. That usage is accurate. Second, the practical value open weights bring is real and large: you can deploy locally (a cost structure completely unlike an API), you can fine-tune, you can do research, you can use it in an environment with no network, you can audit the model's behaviour — a closed API gives you none of these. MiniTriton and the nano-kpu RTL in Chapter 13 both going up on GitHub is a solid act of openness too. Third, the reason 1st place on WebDev Arena in Chapter 12 is a piece of news with weight is exactly this “open” property — it means the capability is no longer monopolised by a handful of companies.
The part that has to be conceded. (a) From the point of view of scientific reproducibility, open weights fall far short. With no data, no mixture and no compute scale, an outside researcher cannot reproduce the training process, only the inference. So many of this report's training-side conclusions (“doing it this way is more stable”, “this change brought an improvement”) are unfalsifiable from outside. (b) Not publishing the internal benchmarks is a particular pity — that is something that could be published without harming commercial interests at all, and once published other models could be tested on it too, which would make the conclusions far more convincing. (c) The word “open” really is easy to read as “open source” when it circulates publicly, and the two differ a great deal; the paper's own usage is careful, but the people who retell it often are not.
The honest conclusion, as I see it: calling K3 “a frontier model with open weights” is accurate; calling it “an open-source model” is not accurate; treating it as “reproducible scientific work” is wrong. Those three sentences should be kept apart.
The ultimate synthesis question. Take the nine-question checklist from Chapter 12 and upgrade it from “reading one table” to “reading a whole technical report”. Write out a checklist for reading technical reports (at least six items), where every item can be matched to at least one concrete positive or negative example in this Kimi K3 report. When you have written it, use your own checklist to give this report an overall assessment — in what respects is it above average, and in what respects below?
1. Does the claim match the type of evidence? A case study can only prove possibility, a benchmark score is what tells you about the average level, and only third-party evaluation counteracts self-assessment bias. Positive example: the paper supplies all three at once (§6.1 benchmarks, §6.3 third party, §7 cases). Negative example: the cases in §7 are used to support statements like “can replace a human researcher/video editor”, which carry an implication about average performance, while a case can only support existence.
2. What information unfavourable to themselves did the authors report on their own initiative? Positive example: writing down the unfavourable 90.4 comparison, admitting CritPt is behind, admitting that “research-level reasoning remains a key direction for improvement”, admitting a gap to the strongest proprietary models, admitting that their own safety evaluation is only a lower bound on capability, listing five internal benchmarks on which they are behind. This is the most creditable thing about the report. Negative example: §6.1.4 says “consistently surpasses Claude Opus 4.8”, but HLE-Full and OfficeQA Pro in Table 2 are counter-examples.
3. Are the quantifiers in the concluding sentences accurate? Words like “consistently”, “across the board” and “significantly” have to be taken back to the table and checked one at a time. See the negative example under item 2.
4. Are the design choices supported by ablations? Wherever there is a claim of the form “we changed it to X and it works better / is more stable”, look for the matching quantitative comparison. (This item falls mainly in the first half of the report, which I have not read directly; see Category C in 13.7.)
5. Is the comparison on equal terms? Harness, tools, context strategy, hardware, fallbacks and refusals. Mixed example: the paper discloses these differences (good), but does not do the subset analysis with them removed (not enough).
6. Is there variance and repetition? Positive example: the vision benchmarks averaged over three runs, ZeroBench five. Negative example: the coding/agent benchmarks and the kernel optimisation case study report no number of repeats at all.
7. Is there independent third-party verification? Positive example: Artificial Analysis, Vals AI, the arena leaderboards, and in the cybersecurity part the independent joint evaluation by the UK AISI and NIST CAISI — the last of these matters most, because what it verifies is a conclusion about risk.
8. Which category does the missing information belong to? Standard across the industry, peculiar to this report, or something I did not read. See the three-way table in 13.7.
Overall assessment:
Above average: the granularity of the configuration disclosure (it writes down even small print like “compression triggered at 300K”), volunteering numbers that count against it, dating the third-party results it quotes, explicitly characterising the safety evaluation as a lower bound and bringing in an independent evaluation, putting the cost axis in (many reports give scores only), open weights and two GitHub repositories.
Below average or open to improvement: the case studies have no denominator, the key benchmarks have no variance, the internal benchmarks both guide training and act as the exam paper while staying unpublished, the quantifiers in a few summary sentences go beyond what the tables can support, and the strength of the baseline in the kernel optimisation case study is never stated.
In one sentence: this is a report that can be criticised seriously — it gives you enough of the surrounding conditions that you can discount it yourself. Well over half of the eight criticisms above were made using information the paper itself provides. A report that leaves you no way in to criticise it is usually not more perfect, just less forthcoming.
Variant: use this checklist in reverse — suppose you are writing a technical report and you want it to hold up in front of the most demanding reader. Going by these eight items, where would you spend the extra effort? If the budget only stretches to three of them, which three do you pick, and why?
Chapter summary
The case-study part shows five kinds of work. GPU kernel optimisation: a 24-hour budget and the same sandbox, and K3 took AttnRes from 283.6 ms down to 114.4 ms (a drop of about 59.7%, a speed-up of about 2.48×), cut DSA and KDA by 55.1% and 73.6% respectively, and got MLA above half of peak TFLOPS; in Figure 14, K3 finishes at +59.7%, level with Claude Fable 5 (+57.1%, with fallbacks) and well ahead of GPT-5.5 (+30.8%) and GPT-5.6 Sol (+17.3%). Compiler: on an L20, MiniTriton beats PyTorch eager and torch.compile on the geometric mean over its core benchmark suite, tensor-core matrix multiplication reaches about 90% of the measured machine ceiling at the largest shape, and when training a GPT end to end the gradients differ from torch's by no more than the fp32 rounding error of 10−4 (with fp64 as the reference). Chip: 48 hours of autonomous running, timing closure at 100 MHz within 4 mm², RTL simulation decoding at more than 8,700 tokens/s, 1.46 million standard cells and 0.277 MiB of SRAM, RTL already open-sourced. Research and knowledge work: the I–Love–Q reproduction in about two hours (against “one to two weeks for a researcher”), a 42-year AI ASIC industry research website (87 quarterly reports, 99 PDFs, more than 11,000 pages, more than 2,800 searches), and a concurrent sub-agent analysis of 391 gravitational-wave events. Video: a 3Blue1Brown-style explainer of its own architecture, and a trailer built from 56 clips.
The wording of the conclusion is very restrained: an open MoE of 2.8 trillion parameters, native vision, a 1 million token context, built on KDA and AttnRes; the world's first open 3T-class model; frontier-level on long-horizon coding, agents, knowledge, reasoning and vision; a gap to the strongest proprietary models remains; a new open frontier established within reach of everyone.
But the two things this chapter really wants to leave you with come at the end. First, that sentence buried at the end of the kernel optimisation section — an early K3 checkpoint was already carrying most of the authors' own kernel optimisation work. This is the strongest piece of evidence in the whole report (the authors are backing it with their own production environment), and at the same time the piece of evidence that most needs to be questioned (data from tasks of this kind has very likely flowed back into training). Second, the three-way table in 13.7: sort a report's omissions into “standard across the industry”, “peculiar to this report” and “what I am not sure about myself”, and then criticise it using only the second column. That move is worth taking away more than any single score.
第14章 附录深读:五个被折叠起来的硬核
一句话导语:正文里那些「我们这样做,效果更好」的句子,凭什么成立?这一章把论文折进附录的五段推导摊开——为什么 SiTU-GLU 的上界正好是 100、分位数均衡里的「分位数」是从一道线性规划的对偶里长出来的、百万个数的分位数怎么用一千个格子估出来、每个 rank 预留 E/R 个冗余专家为什么就够了、以及一套聊天模板怎么被设计成能服务整整一代模型。
学完这一章你应该能做到
- 自己推出 β tanh(z/β) 在原点附近等于 z,并说清 SiTU-GLU 的输出上界 100 是怎么来的
- 把「每个专家干一样多的活」翻译成一个带约束的最大化问题,并说出它的对偶目标长什么样
- 解释「分位数」这三个字为什么会出现在一个跟统计毫无关系的推导里
- 说清为什么部署时只需要一个冻结的偏置向量,不需要任何分位数计算——这是全章最重要的一句话
- 用一个具体的两 rank 例子说明「各 rank 分位数的平均」和「全局分位数」不是一回事
- 复述定理 1 的填充构造,并解释「每个 rank 至多被填一次」这一步凭什么成立
- 给定专家总数和 EP 大小,算出每个 rank 要预留多少冗余专家槽位
- 解释「把不变的放前面、把易变的放后面」为什么是 KV 缓存友好的排布
这一章该怎么读
这是全站数学密度最高的一章。这里先立一条规矩:每遇到一个推导,先问「它想证明什么、为什么值得证」,再看怎么证。某些步骤(比如「全单模性」和「极小极大定理」)确实超出高中范围,我会明确告诉你「这一步你可以先接受结论,理解它在整条链里的位置就够了」,同时给出它凭什么成立的直觉。你不需要一次全部吃透;但如果连「这一步在整条链的哪个位置」都说不出来,那就是真的没读懂,要回头。
14.1 附录 B:怎样「温柔地」给一个乘法封顶
先说这段推导要解决什么问题。SwiGLU 这类门控线性单元(Gated Linear Unit, GLU)的输出是两个分支相乘。相乘这件事在数值上很危险:两个分支各自只要稍微大一点,乘起来就会大很多——两个 30 相乘就是 900。在万亿参数的训练里,某一层冒出一个异常大的激活值,可能顺着反向传播炸掉整轮训练。所以作者想给这个乘积封一个顶。
但封顶不能乱封。Swish 这个激活函数之所以好用,靠的是它特有的形状:在原点附近近似线性(小信号几乎原样通过,不失真),负半轴有一条渐渐消失的尾巴(不像 ReLU 那样把负数一刀切成 0,还留一点点响应)。论文原话是要「在不丢掉 Swish 特征形状的前提下限住 SwiGLU 的乘积」。所以这一节的真正问题是:怎么在既不破坏原点附近的线性、又不抹掉负尾的前提下,把输出关起来。
为什么不干脆一刀切
最省事的办法叫硬截断(hard clamping):对门的预激活值直接写一句「大于 10 的都当成 10」。这确实封顶了,但它有个致命毛病,14.1 结尾那道题会让你自己发现它。论文的选择是平滑封顶(smooth cap):不设一堵墙,而是让函数自己越靠近上限越「走不动」。
做法:把 Swish 的线性因子换成一个被压扁的 tanh
Swish 可以看成两个因子相乘:一个线性因子(就是输入本身)乘上一个 sigmoid 因子。SiTU 的改动只有一处:把线性因子换成 β₁ tanh(Wgx/β₁),sigmoid 因子原样保留。
tanh(双曲正切):一个 S 形函数,值域严格在 −1 到 1 之间,输入接近 0 时输出约等于输入本身,输入很大时输出趋近 ±1 但永远到不了。你可以把它想成「一个自带天花板和地板、但走过去是平滑减速而不是撞墙」的恒等函数。
这个替换为什么不会毁掉 Swish 的形状?关键在论文的这句解释:因为 sigmoid 本来就把负侧的门响应压向零,这个改动主要控制的是大的正激活,而不会抹掉负尾。拆开说:负半轴上,sigmoid 因子本来就很小(接近 0),所以负侧的输出本来就被压得很扁,tanh 在那边压不压得住几乎无所谓,那条尾巴保住了;真正被 tanh 管住的是正半轴上那些很大的值——原本线性因子有多大输出就有多大,现在最多只能贡献 β₁。
K3 对上分支施加了完全相同的构造:β₂ tanh(Wux/β₂)。论文给的理由是防止任一分支主导乘积——只封一个分支没用,另一个分支照样能把乘积拉飞。
局部与极限行为(式 18)
现在要回答一个必须回答的质疑:你把线性因子换成 tanh,那「原点附近近似线性」这条还成立吗?论文用式 18 回答:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| z | 一个标量(这里指分支的预激活值,即 Wgx 的某一个坐标) | 「信号有多强」 |
| β | 一个正的常数超参数,K3 取 β₁ = 4(门分支)、β₂ = 25(上分支) | 天花板的高度:β 越大,封顶越松,越接近原来的 SwiGLU |
| tanh | 双曲正切函数,值域 (−1, 1) | 平滑的「软天花板」 |
| O(z3/β2) | 误差项的量级记号:误差不会比 z3/β2 的某个常数倍更大 | 「漏掉的那点零头,最多这么大」 |
大 O 记号(big-O notation):写 f(z) = O(g(z)) 的意思不是「等于」,而是「当 z 足够小(或足够大,看语境)时,|f| 不会超过 g 的某个固定倍数」。它是一种只关心增长速度、不关心具体常数的粗略比较方式。这里的语境是 z → 0。
式 18 说的事情很干脆:把 z3/β2 这一项忽略掉,β tanh(z/β) 就是 z 本身。所以在原点附近,SiTU-GLU 和 SwiGLU 一阶匹配(matches to first order)——两条曲线在原点处不但重合,连斜率也一样。而且注意分母上的 β²:β 越大,误差越小;当 β₁, β₂ → ∞ 时,SiTU-GLU 逐点还原成 SwiGLU。换句话说,SwiGLU 是 SiTU-GLU 在 β 取无穷大时的特例,这个改动是「加了一个可以调松调紧的旋钮」,不是「换了个别的东西」。
自己推一遍:式 18 的三阶展开
已知 tanh 在 0 附近有展开式 tanh(u) = u − u3/3 + …(后面是 u5 及更高次项)。现在要算的是 β tanh(z/β)。你的第一步该做什么?
想好了再看
做代换:令 u = z/β。当初为什么会想到这一步?因为 tanh 的展开式是「以自变量为主角」写的,而我们的 tanh 里装的不是 z 而是 z/β——凡是遇到「已知公式的自变量位置上塞了个别的东西」,第一反应就是给那个东西起个名字,套公式,最后再换回来。
代进去,得到 tanh(z/β) = ?(先别乘外面的 β)
想好了再看
tanh(z/β) = z/β − (z/β)3/3 + … = zβ − z33β3 + …。注意三次方要连分母一起立方,分母变成 β³,这是最容易漏的地方。
现在乘上外面的 β。你会看到什么?
想好了再看
β · tanh(z/β) = z − z33β2 + …。第一项恰好是 z,β 被完全消掉了。这就是「一阶匹配」的全部内容:不管 β 取多少,原点附近的一次项永远是 z,和没做任何改动时一模一样。
最后:为什么论文写成 O(z3/β2),而不是精确写成 −z3/(3β2)?
想好了再看
因为后面还有 z5/β4、z7/β6 …无穷多项。写成大 O 是在说「从三次项开始的所有剩余项加起来,量级不超过 z3/β2 的常数倍」,而那个 1/3 的常数在这里不重要——重要的信息只有两条:误差是三次的(z 小的时候掉得飞快),以及误差被 β2 除(β 大的时候也掉得飞快)。
论文说:因为 |tanh(z)| < 1 且 0 < Sigmoid(z) < 1,所以每个输出坐标满足 ‖SiTU-GLU(x)‖∞ ≤ β₁β₂ = 100(式 19,取 β₁ = 4、β₂ = 25)。请你把这两行之间省掉的步骤补出来:门分支的绝对值最大是多少?上分支最大是多少?为什么两者相乘就得到 100?
变式:如果把 β₁ 和 β₂ 都改成 10(乘积还是 100),上界还是 100 吗?两种取法在行为上有什么区别?(提示:想想哪个分支更需要保留大动态范围——β = 4 意味着这个分支在预激活超过大约 4 之后就基本饱和了,而 β = 25 要到 25 才饱和。论文没有解释 4 和 25 这组具体数字是怎么选的。)
为什么是「平滑封顶」而不是「硬截断」
论文最后一句是整段的落点:不同于对门的预激活做硬截断,平滑的上限在远离饱和边界处保留非零梯度,作者发现这带来更好的训练行为。这句话值得慢慢读——它是这一节唯一带实验色彩的主张,而且论文没有给任何对比曲线。
有人说:「tanh 也有天花板,硬截断也有天花板,两者本质上没区别,硬截断还更省算力。」请你构造一个具体的输入区间来反驳他,说明硬截断会发生而平滑封顶不会发生的事情是什么。(提示:想想「梯度」是什么——它是函数的斜率。)
平滑封顶不同:β tanh(z/β) 的导数是 1 − tanh²(z/β),在 z = 12、β = 10 处约为 1 − tanh²(1.2) ≈ 1 − 0.834² ≈ 0.30,在 z = 30 处约为 1 − tanh²(3) ≈ 0.01。数值很小,但严格为正。所以梯度被衰减而不是被抹掉,神经元始终知道「我在哪一侧、该往哪边挪」,有机会被拉回来。论文说的「在远离饱和边界处保留非零梯度」就是这个意思。
反例区间因此可以取任何满足 |z| > c 的区间,比如 [12, 30]:在这里,硬截断的梯度是 0,平滑封顶的梯度是正数,两者不是程度差别而是有无差别。
变式:反过来给硬截断辩护——构造一个场景,说明「梯度为零」在某些情况下反而是想要的(提示:想想梯度裁剪 gradient clipping 为什么被广泛使用,以及「我们希望模型完全忽略这个方向」的场合)。然后指出这个辩护为什么救不了这里的用法:在激活函数内部制造大片零梯度区,和在优化器外部裁剪总梯度范数,代价完全不同。
14.2 附录 C 之一:先把「专家均衡」写成一道数学题
接下来是本章篇幅最大、也最值得慢慢啃的一段:分位数均衡(Quantile Balancing, QB)那条更新规则到底是从哪来的。前面讲 MoE 路由的那一章告诉你 QB 「取一个分位数当偏置」,但没告诉你为什么偏偏是分位数。附录 C 的回答是:它不是拍脑袋设计的,它是一道最优均衡指派问题的对偶问题的精确解。论文说这个推导跟随文献 [111],而「把专家负载均衡看成指派问题」这个视角可以追溯到 BASE Layers [67] 和 BIP [116]。
为什么值得证
如果 QB 只是「我们试了试,取分位数效果不错」,那它就是一个调参技巧,换个规模可能就失效。而如果能证明它是某个明确定义的最优化问题的精确解,你就知道了三件事:它在优化什么、它什么时候会失效(前提假设被破坏时)、以及它和别的方法(比如免辅助损失均衡的符号更新)是什么关系。14.6 会看到,这最后一点带来了一个非常漂亮的统一。
把话翻译成式子
先说清这道题在说什么。有 m 个 token,n 个专家。路由器给出一张打分表 s,第 i 行第 j 列的 si,j 表示「token i 有多想去专家 j」。规则是每个 token 恰好选 k 个专家。用一个开关变量 xi,j ∈ {0, 1} 记录「token i 到底有没有被指派给专家 j」。目标是让总分最高,但要在每个专家干一样多的活这个硬约束下做到。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| s ∈ Rm×n | 路由分数矩阵,m 个 token 对 n 个专家的打分 | 一张「谁想去哪」的意愿表 |
| m | token 数量(训练时是一整批,百万量级) | 要安排座位的人数 |
| n | 专家数量(论文提到「接近 103 个专家」) | 可选的窗口数 |
| k | 每个 token 选中的专家个数(Top-k 的那个 k) | 每人要排几个队 |
| xi,j ∈ {0,1} | 指派开关:1 表示 token i 被指派给专家 j | 一张 0/1 的座位表 |
| Σj xi,j = k | token 侧约束:每行恰好 k 个 1 | 每个人恰好排 k 个队,不多不少 |
| Σi xi,j = mk/n | 专家侧约束:每列恰好 mk/n 个 1(假设整除) | 每个窗口恰好服务同样多的人 |
| s.t. | subject to 的缩写,「在满足以下条件的前提下」 | 「但是必须……」 |
mk/n 这个数是怎么来的?总共有 m 个 token、每个选 k 个专家,所以总共要发出 mk 张「服务券」;n 个专家平分,每人 mk/n 张。论文明确写了「假设整除」,也就是假设 mk 能被 n 整除——这是一个为了让式子干净而做的简化。
打个比方
食堂有 n 个窗口,m 个学生,每人要打 k 个菜,每个菜必须去不同窗口。每个学生心里对每个窗口有个满意度打分。食堂经理的要求是:每个窗口必须服务同样多的人次(不能有的窗口排长队、有的闲着),在这个前提下,让全体学生的总满意度最高。式 20 就是这道题。
类比失效处:真实食堂的窗口忙闲不均只是体验问题,而 MoE 里专家负载不均是硬件问题——每个专家住在不同的 GPU 上,最忙的那个 GPU 决定整批的耗时,其他 GPU 全在干等。另外学生的满意度是固定的,而路由分数 s 是模型自己学出来的、每一步都在变。
设 m = 12 个 token,n = 8 个专家,k = 2。(a)每个专家恰好服务多少个 token?(b)如果把 m 改成 10,会发生什么?论文怎么处理这种情况?(c)在真实训练里,m 是一整个训练步的 token 数(百万级),这个整除假设有多要紧?
变式:如果把专家侧约束从等式 Σi xi,j = mk/n 改成不等式 Σi xi,j ≤ mk/n,整除问题还存在吗?可行集会变成什么样?(这个改动正是论文提到的 BIP 的做法,14.6 会看到它带来的别的后果。)
14.3 附录 C 之二:松弛与对偶——把 mn 个开关换成 m+n 个阈值
式 20 有一个致命的实际困难:xi,j 只能取 0 或 1。这类「变量只能取整数」的最优化问题叫整数规划(integer programming),在一般情况下是出了名的难算——你不能求导,不能「往梯度方向挪一点点」,因为 0 和 1 之间没有「一点点」。而这里的规模是 m × n ≈ 百万 × 一千 = 十亿个 0/1 变量,每个训练步都要解一遍。
第一步:松弛
线性松弛(linear relaxation):把「只能取 0 或 1」放宽成「可以取 0 到 1 之间的任意实数」。放宽之后问题变成一个线性规划(linear program, LP)——目标函数和所有约束都是变量的一次式。线性规划是被研究得最透的一类最优化问题,有成熟高效的求解办法。
松弛的代价通常是:放宽后的最优解可能是小数(比如 xi,j = 0.37,「token 有 37% 去这个专家」),没有实际意义,最优值也会比原问题更高(因为可选范围变大了),我们得到的只是一个上界。但这里不会。论文说:
由于二部 b-匹配多面体的标准全单模性,这个 LP 的最优解是整数的;因此这个松弛是精确的。
「全单模性」是干嘛用的(这一步可以先接受结论)
全单模性(total unimodularity)是一个关于约束矩阵的性质。粗略地说:如果一个线性规划的约束写成 Ax = b 的形式,而矩阵 A 具有这个性质(它的每个方阵子块的行列式都是 0、+1 或 −1),并且 b 全是整数,那么这个 LP 的最优解自动落在整数格点上——你不需要额外要求「必须是整数」,它自己就是。
为什么式 20 的约束有这个性质?因为它的结构是二部图匹配:一边是 token,一边是专家,xi,j 是「连不连这条边」。这类问题的约束矩阵每一列恰好只有两个 1(一个来自 token 行、一个来自专家行),这种「每列两个 1 且能分成两组」的结构正是全单模的经典例子。这一步你可以先接受结论,理解它在整条链里的位置就够了:它的位置是——它让我们后面所有「求导、找极小点」的连续数学都合法,而且不损失任何东西。没有它,我们求出的解可能是一堆小数,跟原来那道 0/1 题对不上。
如果要一个直觉:二部图上的「分数解」总能被拆成若干条交替路径和环,沿着环把权重往一头推可以在不降低目标值的前提下把小数变成 0 或 1,一直推下去就得到一个不差于原解的整数解。所以最优值一定能被某个整数解取到。
第二步:引入乘子,写成 max–min
现在处理约束。拉格朗日乘子法(Lagrange multiplier)的核心想法是:与其把约束当成一堵墙,不如把违反约束变成一种罚款。给每条约束配一个「价格」,违反得越多罚得越狠,然后让对手去挑价格,挑到让你无利可图为止。
具体做法:给 token 侧的 m 条等式约束各配一个自由乘子 αi,给专家侧的 n 条等式约束各配一个 βj,把约束「违反量」乘上乘子减到目标里:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| αi | token i 那条约束的乘子,可正可负(「自由」就是这个意思) | token i 的「门槛价」:分数不超过它就别想被选上 |
| βj | 专家 j 那条约束的乘子 | 专家 j 的「拥挤费」:越抢手收费越高 |
| (Σj xi,j − k) | token 侧约束的违反量,满足约束时为 0 | 「这个人多排/少排了几个队」 |
| maxx minα,β | 先由内层的 α、β 取极小,再由外层的 x 取极大 | 你先出招(选 x),对手看着你的招定价(选 α、β)把你的收益压到最低 |
为什么这个 max–min 和式 20 等价?因为内层的 min 是对手在惩罚你违约:只要某条约束的违反量不是 0,对手就可以把对应的乘子往合适的方向拉到无穷大,把你的目标值砸到负无穷。所以你(外层的 max)唯一理智的选择是让所有违反量都等于 0,也就是老老实实满足约束;而一旦满足,那两个惩罚项就整个消失,目标退回原来的 Σ x s。这就是拉格朗日乘子法的全部魔术。
第三步:交换 max 和 min
论文接着说:目标关于 x、α、β 都是线性的,可行集是凸的,所以极小极大定理允许交换优化次序,得到式 22:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| si,j − αi − βj | 论文叫它间隔(margin):分数扣掉 token 门槛价、再扣掉专家拥挤费之后剩下的净值 | 「扣完两道费之后,这笔交易还划不划算」 |
| k Σi αi | 把式 21 里 −Σiαi·(−k) 整理出来的项,与 x 无关 | 固定的「入场费收入」 |
| (mk/n) Σj βj | 同理,专家侧的常数项,与 x 无关 | 固定的「摊位费收入」 |
极小极大定理(minimax theorem):在合适的条件下(目标关于双方各自的变量分别是凹的/凸的,可行集是凸的、其中一方还紧),「你先出招我后定价」和「我先定价你后出招」两种玩法得到的最优值相同。这就是允许把 max min 写成 min max 的依据。这一步同样可以先接受结论:它在链条里的位置是——它让我们可以先固定价格再看指派,从而把内层变成一个逐项独立的简单问题。直觉上它成立是因为双线性的目标 + 凸的可行域下不存在「信息优势」,谁先出手都一样。
自己推一遍:从式 22 的内层极大,到式 23 的 max(0, ·)
看式 22 的内层:maxx∈[0,1] Σi,j xi,j(si,j − αi − βj)。注意此时 α 和 β 已经被外层固定住了。这个求和里,各个 xi,j 之间还有没有互相牵制?
想好了再看
没有了。这是整段推导的关键转折:约束已经被乘子「买断」了,内层里再没有任何一条把不同 xi,j 绑在一起的等式。所以这个 max 是可分的(separable)——十亿个变量各管各的,可以一个一个单独最大化。当初为什么会想到这一步?因为拉格朗日乘子法的目的从来就是「用价格换掉耦合约束」,换完之后一定要检查耦合是不是真的没了,这是标准动作。
那就单看一项:x·c,其中 c = si,j − αi − βj 是一个已知的数,x 可以在 [0, 1] 里任选。x 取多少能让 x·c 最大?
想好了再看
分情况:c > 0 时,x 越大越好,取 x* = 1,最大值是 c;c < 0 时,x 越小越好,取 x* = 0,最大值是 0。这正是论文那句「si,j − αi − βj > 0 时 x*i,j = 1,< 0 时为 0」。注意一个非常重要的副产品:虽然我们把 x 松弛到了 [0,1],但最优解自动落在端点 0 或 1 上——小数解根本不会出现。这和前面全单模性的结论对上了。
c 恰好等于 0 怎么办?
想好了再看
此时 x 取 [0,1] 里任何值目标都一样(都是 0),最优解不唯一。论文的处理是一句话带过:相等的情形在实践中测度为零。翻译成人话:s、α、β 都是连续取值的浮点数,「三个数恰好抵消得一分不差」这件事的概率是 0,工程上不用管。但这是一个应该记住的软肋——如果路由分数出现大量并列(比如被量化到很少的档位),这个假设就不成立了,14.4 结尾的答辩框会回到这一点。
把每一项的最大值 max(0, c) 加起来,再把式 22 里那两个与 x 无关的常数项带上,你得到了什么?
想好了再看
正好就是式 23:Σi,j max(0, si,j − αi − βj) + kΣiαi + (mk/n)Σjβj。注意 max(0, ·) 这个写法把上一步的两种情况合并成了一个式子:c > 0 时它等于 c(对应 x* = 1),c ≤ 0 时它等于 0(对应 x* = 0)。十亿个 0/1 变量消失了,只剩下 m + n 个实数变量 α 和 β。这就是对偶的全部收益。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| L(α, β) | 论文称之为凸对偶目标(convex dual objective),只依赖 α 和 β | 「定价方案的总成本」,越低越好 |
| := | 定义符号,「左边这个记号被定义为右边这个式子」 | 给它起个名字 |
| max(0, s − α − β) | 只统计间隔为正的部分,负的一律当 0 | 「只算真正做成的交易赚了多少」 |
| 凸 | 函数图像像一只碗,任意两点连线不低于函数本身 | 只有一个「最低点」,走下坡一定能走到全局最优 |
凸这个性质在这里是白送的好处:max(0, ·) 是凸函数,一堆凸函数相加还是凸函数,再加线性项还是凸的。凸的意思是没有「假底」——你不会走到一个局部最低点就被困住。这保证了下一节那个「交替往下走」的算法一定收敛到真正的最优。
在式 23 的最优点,token i 会被指派给专家 j 当且仅当 si,j > αi + βj。用食堂的比方解释 αi 和 βj 各自扮演什么角色,然后回答:如果某个专家特别抢手(很多 token 给它高分),它的 βj 会被推高还是压低?为什么这个方向是对的?
抢手的专家,βj 会被推高。理由:把 βj 增加 δ,Σ max(0, ·) 里每一个当前为正的项都减少 δ,共减少 δ×(当前被选中的 token 数),而线性项增加 δ×(mk/n)。所以 L 的变化率是 (mk/n) − (当前负载),也就是式 27 那个次梯度。当前负载远超目标负载时这个变化率是负的——把 βj 调高能降低 L,优化过程自然会推高它。推高 βj 之后判据 si,j > αi + βj 变难满足,来的 token 变少,负载降下来。这个方向当然是对的:拥挤就涨价,涨价就分流,直到供需平衡。
反过来,被冷落的专家(负载低于目标)变化率为正,βj 会被压低甚至压成负数——负的拥挤费就是补贴,主动把 token 拉过来。能压成负数这一点很关键,14.6 会看到 BIP 恰恰输在这里。
变式:把式 20 的目标从「最大化总分」改成「最大化最低那个 token 的分数」(极大化极小),拉格朗日那一套还能用吗?内层的 max 还可分吗?(提示:min 不是求和,它把所有项重新耦合在了一起。)
14.4 附录 C 之三:分位数是从哪冒出来的
现在到了最漂亮的一段。我们要极小化式 23 的 L(α, β)。论文的办法叫精确坐标极小化(exact coordinate minimization):固定 β 求最优的 α,再固定 α 求最优的 β,来回交替。这个套路本身很常见(想想解二元方程组时「先把一个变量当已知数」),真正特别的是——这里的每个子问题都有闭式精确解,不需要迭代、不需要学习率,一步跳到底。
闭式解(closed-form solution):能直接用一个公式写出来的答案,而不是「跑一个循环慢慢逼近」。比如一元二次方程有求根公式,那就是闭式解。
固定 β 时,L 里唯一还含 αi 的部分只跟第 i 行有关,所以问题在 token 上完全解耦——m 个 token 各自解各自的一维问题。对 token i:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| α | 这里省掉了下标 i,就是当前这个 token 的门槛价,唯一的未知量 | 要调的那一个旋钮 |
| si,j − βj | n 个已知的数:这个 token 对每个专家的分数扣掉该专家的拥挤费 | n 个「标好价的候选」,排成一排 |
| kα | 线性项,斜率恒为 k | 「每提高一点门槛,要付 k 倍的代价」 |
| Σj max(0, · − α) | 把所有「高过门槛」的候选超出的部分加起来 | 门槛越低,超出的越多,这一项越大 |
论文接着给出关键判断:这个目标关于 α 是分段线性的,斜率是 k 减去超过 α 的间隔个数;因此当恰好有 k 个间隔在 α 之上时取到极小。这句话信息量很大,我们用一个具体例子把它走一遍。
自己推一遍:为什么极小点卡在第 k 大和第 k+1 大之间
具体设定:k = 2,某个 token 有 n = 5 个专家,五个「标好价的候选」 si,j − βj 从大到小是 0.9, 0.7, 0.4, 0.2, 0.05。记 g(α) = 2α + Σj max(0, vj − α)。
先算 g(1.0)、g(0.9)、g(0.7)。你注意到什么?
想好了再看
g(1.0) = 2.0 + 0 = 2.0(α 比所有候选都高,max 全是 0)。g(0.9) = 1.8 + 0 = 1.8。g(0.7) = 1.4 + (0.9−0.7) = 1.6。从 1.0 降到 0.9,函数值降了 0.2,α 降了 0.1,斜率是 2;从 0.9 降到 0.7,函数值降了 0.2,α 降了 0.2,斜率是 1。斜率变了。
为什么斜率会从 2 变成 1?准确地说出是哪一项在 α 穿过 0.9 时「醒过来」了。
想好了再看
当 α 从 1.0 降到 0.9 以下时,
max(0, 0.9 − α)这一项从「恒为 0」变成「等于 0.9 − α」。这一项对 α 的斜率是 −1。原来总斜率只有线性项贡献的 +2,现在加上这个 −1,变成 +1。
一般规律:每当 α 往下穿过一个候选值,就有一项被「激活」,总斜率就减 1。所以在任何位置,斜率 = k − (严格超过 α 的候选个数)。这正是论文那句「斜率是 k 减去超过 α 的间隔个数」。按这个规律列出所有区间上的斜率。哪一段的斜率是 0?
想好了再看
α > 0.9:0 个超过,斜率 2−0 = +2。
0.7 < α < 0.9:1 个超过,斜率 +1。
0.4 < α < 0.7:2 个超过,斜率 0。← 平的
0.2 < α < 0.4:3 个超过,斜率 −1。
0.05 < α < 0.2:4 个超过,斜率 −2。
α < 0.05:5 个超过,斜率 −3。
斜率随 α 增大而单调不减(每穿过一个候选就 +1),这正是凸函数的特征。凸的分段线性函数,极小点在哪?把上面的区间对应到「第几大」。
想好了再看
凸函数的极小点就是斜率从负变到非负的地方。这里斜率恰好为 0 的那一整段是 [0.4, 0.7],上面所有点都是极小点,最小值 g = 1.6(自己验算:g(0.4) = 0.8 + 0.5 + 0.3 = 1.6,g(0.7) = 1.4 + 0.2 = 1.6,确实相等)。
而 0.7 是第 k = 2 大的候选,0.4 是第 k+1 = 3 大的候选。所以:极小点是任何介于第 k 大和第 (k+1) 大之间的 α——这正是论文说的「当恰好有 k 个间隔在 α 之上时取到极小」。论文说「约定取第 (k+1) 大的条目」。既然整段 [0.4, 0.7] 都是极小点,为什么偏偏挑最左端的 0.4?这个约定不是随便挑的,想想 14.3 里判据 s − α − β > 0 是严格不等号。
想好了再看
取 α = 0.4(第 3 大)时,严格大于 α 的候选是 0.9 和 0.7,恰好 2 个 = k 个;0.4 自己等于 α,按严格不等号判据不入选。取 α = 0.7(第 2 大)时,严格大于它的只有 0.9,只有 1 个 = k−1 个,选不满!
所以这个约定的作用是:让「严格大于 0」这条规则恰好挑出 k 个专家,一个不多一个不少,正好满足 token 侧约束 Σj xi,j = k。论文只写了「by convention」(按约定)四个字,没有解释这一点,但这是它必须这么取的真正原因。最后一步:为什么「第 (k+1) 大」等价于「(1 − k/n) 分位数」?
想好了再看
分位数quantilep(v) 的意思是「把 v 从小到大排好,取位置在整个序列 p 这个比例处的那个数」。n 个数里,比例 p = 1 − k/n 对应的位置是从小往大数第 p·n = n − k 个。而「从小往大数第 n−k 个」就是「从大往小数第 n − (n−k) + 1 = k+1 个」。
用例子验:n = 5, k = 2,p = 1 − 2/5 = 0.6,位置 0.6 × 5 = 3,从小到大第 3 个是 0.4——正是第 3 大。对上了。所以「分位数」这个词在这里不是统计学意义上的近似描述,它就是「第几大」的另一种说法。
| 符号 | 是什么 | 直觉 |
|---|---|---|
| si | 打分表的第 i 行:token i 对全部 n 个专家的分数 | 沿 token 轴看 |
| si − β | 整行减去专家阈值向量,得到 n 个间隔 | 扣完拥挤费之后的净值 |
| quantile1−k/n | (1 − k/n) 分位数,等价于第 (k+1) 大 | 「从大往小数第 k+1 个」 |
| αi* | token i 的最优门槛价 | 刚好卡住 k 个专家的那条线 |
专家侧完全对称。固定 α 时,专家 j 解 minβ (mk/n)β + Σi max(0, si,j − αi − β)。一模一样的分段线性论证:线性项的斜率现在是 mk/n,候选有 m 个(这一列上所有 token 的间隔),所以极小点是第 (mk/n + 1) 大的条目。而 m 个数里的第 (mk/n + 1) 大,正是从小往大数第 m − mk/n = m(1 − k/n) 个,还是 (1 − k/n) 分位数:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| s:,j | 打分表的第 j 列:全部 m 个 token 对专家 j 的分数。冒号表示「这一维取全部」 | 沿 专家轴看 |
| s:,j − α | 整列减去 token 门槛价向量,得到 m 个间隔 | 「每个人对这个窗口还剩多少净意愿」 |
| βj* | 专家 j 的最优阈值(拥挤费) | 刚好卡住 mk/n 个 token 的那条线 |
Algorithm 1 就是把这两条交替执行的求解器,只有六行:初始化 β ← 0;循环 T 次,每次先把 s − β 按行降序排、取第 k+1 列作为 α(对应式 25),再把 s − α 按列降序排、取第 mk/n+1 行作为 β(对应式 26);最后返回 xi,j = 1 当且仅当 j 属于 si − β 的 Top-k。注意算法里的下标写法 [:, k:k+1] 和 [mk/n : mk/n+1]——那是从 0 开始计数的切片,取的正是「第 k+1 大」和「第 mk/n+1 大」,和上面推的完全一致。
某个 token 对 6 个专家的间隔 si,j − βj 依次是 0.81, 0.12, 0.55, 0.73, 0.30, 0.44,取 k = 2。按式 25 算出 αi*。然后验证:用判据「间隔 > αi*」挑出来的专家是不是恰好 2 个,是不是恰好是 Top-2。
验证判据:严格大于 0.55 的间隔是 0.81 和 0.73,恰好 2 个,恰好是 Top-2。0.55 自己不算(严格不等号),这正是「取第 k+1 大」这个约定的作用。
顺带体会一下:如果错取成第 k = 2 大(0.73),那么严格大于它的只剩 0.81 一个,只选出 1 个专家,token 侧约束就破了。
变式:同样这组数,如果 k = 4,α* 是多少?再问一个更有意思的:如果六个间隔里有两个并列(比如把 0.44 也改成 0.55),「恰好选出 k 个」还成立吗?这对应论文里哪句被一笔带过的假设?
答辩:如果我是审稿人
你这条推导链上挂了一串「温和的技术性假设」:mk/n 整除、平局的测度为零、极小极大定理可交换、松弛因全单模而精确。可真实训练里 m 是随机变动的、分数是低精度浮点(bf16 只有 8 位尾数,平局远比你想的常见)、而且每一步的 s 都在变——你根本没有把任何一个问题「解到最优」,你只是在一个一直在动的目标上走了几步。凭什么还叫「精确坐标极小化」?
参考防守(先自己组织语言再看)
分三层答。
第一层,先承认成立的部分。「精确」这个词修饰的是子问题,不是整个训练过程:给定当前这一批 s 和当前的 β,式 25 给出的 α 确实是式 24 的精确极小点,不是近似、不是一步梯度。这个措辞没有夸大。至于整体是否收敛到全局最优,论文本来也没有声称——它声称的是「几步之内达到平衡」,这是一个关于速度的经验陈述。
第二层,逐条回应假设。整除假设只影响推导的记号整洁度,实现里用插值处理非整数秩(附录 D 明确做了 clip 插值),不构成实际问题。平局的批评是这几条里最有杀伤力的一条:bf16 下路由分数确实可能大量重复,此时「恰好 k 个严格大于 α」会失效,选出的专家数可能少于 k。但注意后果的方向:这只会造成「某些 token 少选了专家」,而实际实现是先算阈值再做一次标准的 Top-k(Algorithm 1 第 6 行正是 argtopk,不是阈值筛选),Top-k 在并列时有确定性的打破规则,所以并列不会破坏「每个 token 恰好 k 个」,只会让哪一个被选中略有任意性。真正被并列影响的是专家侧的负载精度,那是一个 O(并列个数) 的偏差。
第三层,交出真正的软肋。目标一直在动这一点无法辩护成「仍然是精确最优」,只能辩护成「这就是设计意图」:QB 从来不是要解一个静态的指派问题,它是要给一个不断漂移的分数分布提供每步都自校准的阈值。它的价值恰恰在于每步都能一跳到位,所以跟得上漂移——如果它需要几十步才收敛,分数早就变了。论文自己也没有给「QB 解与真实最优指派解的差距」的任何度量,这是一个可以要求补的实验。
14.5 附录 C 之四:丢掉 α,只留 β——全章最重要的一句话
到这里我们有了 α 和 β 两组阈值。但部署一个模型时,你不可能在每次推理时都去解一遍分位数——用户问一句话只有几十个 token,哪来的「整批 token 的分布」给你取分位数?如果 QB 训练时依赖分位数、推理时又算不了分位数,那训练和推理的路由行为就对不上,模型会当场崩掉。这一小节回答的就是这个疑虑,而答案漂亮得有点意外。
论文的推理只有两句:在式 23 的最优点,x*i,j = 1 当且仅当 si,j − αi* − βj* > 0;再结合 token 约束 Σj x*i,j = k,被选中的专家恰好是 si − β* 的 Top-k。
为什么第二句成立?把判据移项:si,j − βj* > αi*。注意左边只跟专家有关,右边 αi* 对这个 token 的所有专家是同一个数——它是一条水平线。「在同一条水平线以上的那些项」,加上「恰好有 k 个在线以上」,那不就是 Top-k 吗?αi 的全部作用,就是画出这条线;而一旦你知道「要选 k 个」,这条线的具体高度就完全不必知道了——直接排序取前 k 个即可。
这个不对称为什么是整段推导的落点
路由因此只需要专家阈值 β ∈ Rn(等价于式 13 里的偏置 b = −β),而 token 阈值 α ∈ Rm 是与动态训练批次绑定的中间变量,可以直接丢掉。论文原话是「discarded」。
为什么可以丢?看两个向量的长度就明白了:β 只有 n 个数(专家个数,约 103),它是模型参数级别的东西,跟着模型走;而 α 有 m 个数(这一批的 token 数,百万级),它跟着这一批数据走,换一批数据就完全失效——它压根不是模型的一部分。推导的结论恰好是:需要留下的那个是能留下的那个。
论文把这个性质称为训练–推理一致性(train–inference consistency):部署时,路由就是「分数减去一个冻结的偏置,然后取 Top-k」这样一个固定操作,不需要任何分位数计算。训练时那套复杂的分位数机器,最后只留下 n 个数的残留物。
综合题。假设有人提出一个「更对称」的路由方案:部署时同时保留 α 和 β,对每个 token 用它自己的 αi 做阈值筛选(选出所有满足 si,j − βj > αi 的专家),而不是取 Top-k。请回答三问:(a)推理时的 αi 从哪来?(b)如果强行用训练结束时保存的某个 α 的平均值当作全局阈值,会出什么问题?(c)论文的方案为什么绕开了这一切?把答案和「每个专家恰好服务 mk/n 个 token」这条原始约束联系起来。
(a) 推理时 αi 其实可以现算——因为式 25 只需要这个 token 自己那一行的 n 个间隔,单个 token 就够了,不需要整批。所以这个方案在计算上并非不可行。这是一个要说清楚的细节:α 之所以被丢弃,不是因为算不出,而是因为没必要算——既然它的唯一作用是画出那条「恰好卡住 k 个」的线,而 Top-k 直接就实现了同样的事,多存 m 个数、多做一次排序取值纯属浪费。
(b) 如果换成一个固定的全局阈值(比如所有 αi 的平均),后果是每个 token 选中的专家数不再固定:常见词的最高分只有 0.4,一个专家都过不了线,直接被路由到 0 个专家(输出退化);代码片段有两个 0.95,可能过线 5 个甚至 10 个。计算量随输入内容剧烈波动,批处理时无法预先分配显存和通信缓冲区,推理系统会很难做。更根本的是,训练时每个 token 都是恰好 k 个专家,推理时变成可变个数,模型见到的输入分布和训练时不一致。
(c) 论文的方案绕开这一切,因为它把「选 k 个」这条硬约束直接编码进了操作本身(Top-k 天然就是 k 个),而不是靠一个阈值间接实现。回到原始约束:式 20 有两条约束,token 侧的 Σj x = k 和专家侧的 Σi x = mk/n。token 侧那条被 Top-k 这个操作本身满足了,所以它的乘子 α 不必留;专家侧那条没有任何操作能天然满足(它是跨 token 的全局性质),所以它的乘子 β 必须留下来当偏置。两个乘子命运的差别,根源在于两条约束一条是「每行的性质」、一条是「每列的性质」,而推理是一行一行来的。
顺带说一句:论文最后提到的 Expert Threshold 路由走的正是 (b) 那条路(维护 EMA 阈值、允许每 token 选中的专家数可变),论文明确说 K3 的做法「与之相关但不同」。
变式:反过来想——有没有哪种模型架构,会让「专家侧约束」也能被单个 token 的操作天然满足,从而连 β 都不用留?(提示:想想如果一个 token 的 k 个专家被要求「每个专家组里各选一个」会怎样,这类做法在文献里叫分组路由。它付出的代价是什么?)
14.6 附录 C 之五:QB 和「符号更新」其实是同一个目标的两种走法
这一小节把 QB 和读者在前面章节见过的免辅助损失均衡(auxiliary-loss-free balancing)接上了头。后者的做法非常朴素:某个专家的负载超了,就把它的偏置减一个固定的小量;不够,就加一个固定的小量。只看方向,不看超了多少。这个做法有效但看起来很「土」,附录 C 给了它一个精确的位置。
论文对式 26 底下的专家侧子问题求(次)梯度:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| ∂L/∂βj | 对偶目标对专家 j 阈值的偏导数 | 「把这个专家的拥挤费提一点,总成本会怎么变」 |
| χ(条件) | 示性函数(indicator function):条件成立取 1,否则取 0 | 一个只会答「是/否」的计数器 |
| Σi χ(…) | 数一数有多少个 token 的间隔为正,也就是实际被路由到专家 j 的 token 数 | 观测负载 |
| mk/n | 每个专家应该服务的 token 数 | 目标负载 |
| 次梯度 | 函数在「折点」处没有唯一导数时的替代品,取任一条「托在函数下方」的直线的斜率 | max(0,·) 在 0 处有个尖角,这是给尖角处补的定义 |
式 27 读出来就是一句大白话:梯度 = 目标负载 − 观测负载。这就是「负载误差」本身。
现在做一步 SignSGD:梯度下降的一个变体,只用梯度的符号、不用它的大小,每步走固定的一段距离。对式 27 做 SignSGD,得到的更新是「负载超了就把 βj 往上推一个固定量,不够就往下推一个固定量」——这恰好还原出免辅助损失均衡的定步长符号更新(差一个符号约定,因为 b = −β)。
这个统一视角解释了什么
论文明确说,这个视角同时回答了两个此前只能靠「经验上好用」来解释的问题:
第一,为什么 QB 不需要类似学习率的超参数。符号更新必须选一个步长——走大了会来回震荡,走小了半天平衡不了,而且什么步长合适跟专家数、批大小、分数分布都有关。QB 根本不「走」:它直接跳到同一个对偶目标的精确坐标极小点。既然是一步到位,就没有「一步走多远」这个问题。符号更新只保留了负载误差的方向;QB 用上了误差的全部信息。
第二,为什么即使面对接近 103 个专家,QB 也能在几步更新内达到平衡。符号更新每步只挪固定的一小格,如果某个专家的偏置离目标很远,就需要很多步;专家越多,「最离谱的那个」离得越远,需要的步数越多。QB 每一步都精确地把每个专家放到位,专家数对它几乎没影响——剩下的迭代次数只是为了处理「α 和 β 互相牵制」这件事。
与 BIP 的对比:为什么「只能压不能提」是致命的
论文接着说 QB 与 BIP 相关。BIP 解的是同一道指派问题,但用的是不等式约束:Σj xi,j ≤ k 和 Σi xi,j ≤ mk/n。这个改动看起来很小——「最多 k 个」而不是「恰好 k 个」,听起来还更宽松——但它有一个连锁反应:
不等式约束诱导出乘子的非负性约束。(这是拉格朗日对偶的标准结论:等式约束的乘子自由取正负,不等式约束 ≤ 的乘子必须 ≥ 0。这一步你可以先接受结论;直觉是:不等式只在「顶到上限」时才起作用,罚款只能朝一个方向收,不能倒贴。)于是 BIP 的两个更新都要在最后加一个 max(0, ·) 截断,把算出来的负值砍成 0。
论文对后果的判断很直接:这只能抑制被过度选择的专家,而不能提拔被冷落的专家,在他们的实验中明显减慢了平衡过程。回想 14.3 那道题的结论——被冷落的专家需要一个负的 βj(负拥挤费 = 补贴)才能把 token 拉过来。BIP 把负值砍成 0,等于取消了补贴这个工具,只剩下「给拥挤的加价」这一手。
构造一个具体的小场景(自己设 m、n、k 和一组分数),使得只用「给拥挤专家加价」这一手无法在一步内达到平衡,而允许负偏置可以。然后回答:BIP 最终能不能平衡?如果能,为什么论文说它「明显减慢」而不是「无法平衡」?
token1: 0.90, 0.80, 0.01
token2: 0.85, 0.70, 0.01
token3: 0.60, 0.95, 0.01
初始偏置全 0:token1→A, token2→A, token3→B。负载是 A:2, B:1, C:0,不平衡。
只允许非负偏置(BIP 那侧):β 是加在「成本」侧的,非负的 βA 只能把 A 的有效分数往下压。把 βA 压到 0.9 以上,token1 和 token2 都会离开 A——但它们的次优选择是 B(0.80 和 0.70),不是 C(0.01)。于是负载变成 A:0, B:3, C:0,更不平衡。再压 B,它们又跑回 A。C 的分数 0.01 是所有排序里的最后一名,只靠压别人,永远轮不到它——除非把 A 和 B 同时压到有效分数低于 0.01,也就是 βA, βB 都超过 0.8 左右,这需要很多步固定步长的更新才能累积到(这正是「明显减慢」)。
允许负偏置(QB 这侧):直接令 βC = −0.7,C 的有效分数变成 0.71,立刻在 token2(0.85 / 0.70 / 0.71)的排序里升到第 2、在 token3(0.60 / 0.95 / 0.71)里升到第 2。再配合对 A 的适度加价,一两步就能达到 A:1, B:1, C:1。式 26 的分位数更新会直接算出这个负值,不需要试。
BIP 最终能不能平衡?能,但要靠「把所有热门专家压到比冷门专家还低」这条迂回路径。因为偏置的平均值在实现中通常被中心化,把别人全压下去在效果上等价于把这个专家提上来——所以它可达,只是路径长得多,要走很多步固定步长。这就是论文说「明显减慢平衡过程」而不是「无法平衡」的原因。差别不在能不能到,而在要走多少步;而在一个每步分布都在漂移的训练里,走得慢就等于永远追不上。
变式:论文提到的另一个亲戚是 Expert Threshold 路由,它维护 EMA 阈值并允许每 token 选中的专家数可变。请说明:如果允许可变个数,14.5 里「Top-k 天然满足 token 侧约束」这个论证会在哪一步失效?这会给推理系统带来什么新麻烦?
14.7 附录 D:用一千个格子估一个百万数的分位数
14.4 说清了 QB 要取什么分位数,14.5 说清了取完之后留下什么。附录 D 处理的是夹在中间的工程难题:这个分位数根本没法直接算。
难在哪?式 14 的 QB 更新要求在整个训练步上取分位数——对 n 个专家中的每一个,取间隔 si,j − αi 的 (1 − k/n) 分位数。而 m 是百万级的 token,它们横跨分散在各个数据并行 rank(data-parallel rank)和梯度累积步(gradient-accumulation step)上。
数据并行与梯度累积:训练一个大模型时,一个「训练步」的 token 太多,一张卡装不下。做法是把它们切成很多块:一部分横向分给几百上千张 GPU(数据并行),每张卡上再纵向拆成若干次小批前向(梯度累积)。后果是:一个训练步的全部 token 从来没有在任何一个地方同时出现过。
要精确取分位数,就必须排序;要排序,就必须把所有数据聚到一起。论文说:在训练循环里收集 O(mn) 个间隔求精确分位数是不现实的。给个尺度感:m ≈ 106、n ≈ 103,就是 10 亿个数,每层每步都要跨几百张卡搬一遍——通信量比模型的梯度还大。
关键观察:我们要的从来不是那些数,而是它们的分布
论文的破局点是一句非常朴素的话:更新从来不需要间隔本身,只需要它们的逐专家分布,而直方图(histogram)能以固定代价概括这个分布。
直方图:把取值范围切成若干个格子(箱,bin),然后只记录「每个格子里落了多少个数」。原始的一百万个数被压缩成一千个计数。你丢掉了每个数的精确值(只知道它落在哪个格子里),但保住了整体分布的形状——而分位数只需要分布形状。
做直方图的对象:所需偏置 r
这里有个小转换。论文不直接对间隔做直方图,而是对所需偏置(required bias)做:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| ri,j | 「恰好能把专家 j 放在 token i 的临界点上的那个偏置」 | 「这个专家要拿到多少补贴,才刚好够得着这个 token 的门槛」 |
| αi | token i 的截断点(门槛价),即 14.4 的那条水平线 | 入选线的高度 |
| −si,j | 取了负号的分数——正是这个负号让顺序反转 | 分数越高,需要的补贴越少 |
为什么要多这一步?因为取负让间隔的顺序反转,于是式 14 的 QB 目标 b̂j 恰好是 r:,j 的 (k/n) 分位数——从「取上尾」变成了「取下尾」,而累积计数天然是从小往大数的,这样一遍扫过去就能读出答案。同时 r 的物理含义也更直接:它就是偏置本身该有的值,单位对得上,不用再换算。
分箱范围:为什么不用担心数据跑出格子
做直方图的第一个问题永远是「格子从哪到哪」。如果范围定小了,大量数据落在界外无法统计;定大了,每个格子太宽、精度太差。论文给出了一个精确的、每步自适应的范围。
自己推一遍:为什么每个 r 都落在 [bmin − 1, bmax + 1] 里
已知路由分数是 sigmoid 的输出。那么 si,j 的取值范围是什么?
想好了再看
sigmoid 的值域是开区间 (0, 1)。所以 si,j ∈ (0, 1)。这一条是整个界的基石——如果路由分数用的是别的激活(比如 softmax 之后再乘个什么,或者干脆是原始 logit),这个界就不成立了。论文能给出干净的界,是因为架构上选了 sigmoid。
现在看 αi。它不是随便一个数——回忆 14.4:αi 是这一行有偏分数里的第 (k+1) 大。那么 αi 本身一定等于什么形式的量?
想好了再看
αi 本身就是某个专家 j′ 的有偏分数 si,j′ + bj′。这是一个很容易滑过去但极其关键的观察:取分位数这个操作是「从已有的数里挑一个出来」,不是「算一个新数」,所以结果一定是原始数据中的某一个。当初为什么会想到这一步?因为要给一个量定范围,最省力的办法就是找到它「是由什么拼出来的」,然后给每个零件定范围。
设当前偏置的最小值是 bmin、最大值是 bmax。请推出 αi 的范围。
想好了再看
αi = si,j′ + bj′,其中 s ∈ (0, 1)、bj′ ∈ [bmin, bmax]。最小可能:s 逼近 0 且 b 取 bmin,得 > bmin。最大可能:s 逼近 1 且 b 取 bmax,得 < 1 + bmax。所以 αi ∈ (bmin, 1 + bmax),和论文一致。
最后一步:ri,j = αi − si,j。把两个范围相减。注意减法要取反。
想好了再看
r 的最小值出现在「α 最小 且 s 最大」:> bmin − 1。r 的最大值出现在「α 最大 且 s 最小」:< (1 + bmax) − 0 = bmax + 1。所以 ri,j ∈ [bmin − 1, bmax + 1]。这就是论文给的分箱区间。
最容易犯的错是「α 最小减 s 最小」——记住减号会把被减数的范围翻转过来。区间长度是 (bmax − bmin) + 2。切成 B 个均匀箱,箱宽 w = ?论文为什么强调「每一步重新计算范围」?
想好了再看
w = (bmax − bmin + 2)/B。每步重算的理由是:bmin 和 bmax 会随训练变化——当负载严重不均衡时,偏置会向两头扩散去纠正它,区间随之变宽;均衡之后偏置收拢,区间变窄、箱变细、精度自动提高。论文的说法是让箱宽「随偏置扩散去纠正不均衡而保持自适应」。这是一个免费的自适应机制:不需要任何调度,范围是从当前状态里读出来的。
累积与恢复
剩下的部分严格贴合一个训练步的结构:
累积阶段(零通信)。每次前向传播中,每个 rank 把它本地的 ri,j 值 scatter-add 进一个逐专家的计数矩阵 H ∈ Nn×B,跨所有微批次累积,无需通信。scatter-add 的意思就是「算出每个数该进哪个格子,然后把那个格子的计数加 1」。H 的形状是 n 行(每个专家一行)× B 列(每行 B 个格子)。
汇总阶段(一次通信)。在这一步结束时,一次 all-reduce 把所有 rank 的本地计数逐元素加起来,得到全局直方图;每个 rank 都从同一份汇总计数里恢复分位数,所以所有 rank 算出的偏置天然一致,不需要再同步。
恢复阶段。每个专家的直方图对每个 token 恰好计数一次,所以目标秩恰好是目标负载 q = mk/n,只不过现在取遍整个训练步。做法是选出累积计数首次达到 ⌈q⌉ 的那个箱,并在箱内做线性插值:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| b̂j | 估计出来的专家 j 的新偏置 | 这一步的答案 |
| bmin − 1 | 整个分箱区间的左端点 | 坐标原点 |
| βj | 被选中的箱的编号(从 0 开始)。注意:这里的 β 和附录 C 里的对偶变量 β 是两回事,论文重用了同一个字母 | 「答案在第几个格子里」 |
| q = mk/n | 目标秩(目标负载) | 「我要找从小往大数第 q 个数」 |
| cj | 被选中箱之前所有箱的累积计数 | 「走到这个格子门口,已经数过多少个了」 |
| hj | 被选中箱内部的计数 | 「这个格子里挤了多少个」 |
| (q − cj)/hj | 还差多少个 ÷ 这箱里有多少个,得到一个 0 到 1 的比例 | 「目标大概在这个格子的百分之几处」 |
| clip(·, 0, 1) | 把结果强行限制在 [0, 1] 内 | 防呆:万一比例算出界(比如 hj 很小导致除法失控),不让它跑出这个箱 |
| w | 箱宽 (bmax − bmin + 2)/B | 一格代表多长 |
整个式子读出来就是:答案 = 区间左端 + (整格数 + 格内比例) × 格宽。和「看尺子读数」完全一样:先数过了几厘米,再估读厘米内的零头。得到的偏置最后像式 14 那样做均值中心化(减去所有偏置的平均值),这一步的作用是消除「全体偏置一起加同一个常数」这个自由度——因为 Top-k 只看相对大小,整体平移不影响任何选择。
三个性质
第一,准确。论文的论证是:累积计数在箱边界处是精确的——直方图丢的是箱内部的信息,箱之间的分界是准的,所以「第 q 个数落在哪个箱里」这个判断没有任何误差。因此真值分位数与其估计一定落在同一个箱里,两者的差被箱宽 w 卡死。B = 1000 时这至多是几个 10−3,论文说观察不到可测量的残余负载不均衡。
第二,便宜。唯一的通信是每层每步对 nB 个值做一次整数 all-reduce,与 m 无关。这一点是关键:token 数从一百万涨到一千万,通信量纹丝不动。论文说在他们的配置里,这低于「每个微批次都在进程组上交换原始间隔」(这是天然的替代方案)的 1%。
第三,估的是对的量。这一条最容易被忽略但最深刻:因为计数可加,全局直方图对「token 如何在 rank 和累积步之间分片」是精确不变的。也就是说,不管你把 token 怎么切、切给谁,最后 all-reduce 出来的那张全局直方图一模一样。因此它估的是池化全局批次(pooled global batch)的分位数,而不是各 rank 分位数的平均——论文特别加了一句「这两者一般不同」。
论文最后提到一个精化:对跨步估计的分位数做指数移动平均(exponential moving average, EMA)可以降低批次间的采样噪声,进一步改善负载均衡。
(a)假设当前偏置的极值是 bmin = −0.4、bmax = 0.6,B = 1000。算出箱宽 w,并说明分位数估计的误差至多是多少。(b)假设 n = 1000、B = 1000,算出一次 all-reduce 要传多少个整数;再和「传原始间隔」的方案比:m = 106 时后者要传多少个数?(c)用这两个数字解释论文说的「低于 1%」是保守还是激进的说法。
(b) 直方图:nB = 1000 × 1000 = 106 个整数,每层每步一次。原始间隔:mn = 106 × 103 = 109 个浮点数,而且论文说的替代方案是「每个微批次」都交换一次,所以还要再乘上微批次数。
(c) 比值:106 / 109 = 0.1%,还没算「每微批次 vs 每步」这个额外倍数,也没算「整数 vs 浮点」的位宽差别。所以论文说的「低于 1%」是相当保守的说法——按这个粗算,真实比例可能在 0.1% 以下甚至更低。
另外注意 (a) 和 (b) 之间的张力:想让误差更小就要加大 B,而通信量正比于 nB。B 是一个精度-通信的权衡旋钮,但因为通信量与 m 无关,这个旋钮的调节空间非常大——把 B 提到 10000 也只是 107 个整数,仍然远小于 109。
变式:如果训练中期发生剧烈的负载不均衡,偏置向两头扩散到 bmin = −5、bmax = 5,此时 w 变成多少?误差放大了几倍?这会不会形成「越不均衡估得越差、越估不准越不均衡」的恶性循环?(提示:想想此时「估准」还重不重要——偏置差 0.012 和差 0.003,对一个已经严重偏离的专家来说影响一样吗?)
论文说全局直方图估的是「池化全局批次的分位数,而不是各 rank 分位数的平均——这两者一般不同」。请构造一个具体的数字例子证明这两者确实不同:两个 rank,每个 rank 上有 4 个数,取中位数(0.5 分位数)。要求你的例子里「两个 rank 各自中位数的平均」和「8 个数放在一起的中位数」差得越明显越好。然后说明:为什么这个差异在真实训练里不是理论洁癖,而是会真的出问题。
例子一(简单版):rank A = {0, 0, 0, 0},中位数 0;rank B = {10, 20, 30, 40},中位数 25(取中间两数平均)。两个 rank 中位数的平均 = (0 + 25)/2 = 12.5。合并成 {0,0,0,0,10,20,30,40},中位数是第 4、5 个的平均 = (0 + 10)/2 = 5。12.5 vs 5,差了 2.5 倍。
例子二(对照组,说明差距来自哪里):rank A = {1, 2, 3, 4},中位数 2.5;rank B = {2.4, 2.5, 2.6, 2.7},中位数 2.55。平均 = 2.525。合并成 {1, 2, 2.4, 2.5, 2.6, 2.7, 3, 4}(8 个数),中位数 = (2.5 + 2.6)/2 = 2.55。这次两者只差 0.025。对比两个例子可以看出:差距完全取决于各 rank 数据分布的差异程度——分布相近时两者几乎一致,分布差得远时可以差出数倍。
为什么真实训练里会出问题:数据并行的各个 rank 拿到的是不同的数据分片。如果按数据源或语言分片(比如 rank 0 全是中文网页、rank 1 全是代码),那么各 rank 上「哪些专家被强烈需要」的分布可能差得很远——代码分片上专家 A 火爆,中文分片上专家 A 无人问津。此时各 rank 各自算分位数再平均,得到的偏置对谁都不对;而池化后的全局分位数才是「这一整批数据真正需要的偏置」。更糟的是,各 rank 分位数的平均没有任何优化意义——式 26 要的是全局那 m 个间隔的分位数,中位数的平均不等于平均的中位数,它不是任何一个优化问题的解。
直方图方案之所以能天然做对,是因为计数是可加的:把两个 rank 的计数逐格相加,得到的就是把两组数据倒在一起做的那张直方图,一个数都不多、一个数都不少。而「分位数」这个操作不可加。可加的量能被 all-reduce 正确合并,不可加的量不能——这是分布式计算里一条很值钱的通用原则。
变式:还有哪些常见的统计量是「可加」的、因而能被 all-reduce 安全合并?哪些不是?(试试:求和、计数、平均、最大值、方差、中位数、众数。对于平均和方差,想想需要额外传什么才能合并。)
答辩:如果我是审稿人
你在附录 C 里花了两页论证 QB 的优点之一是「不需要类似学习率的超参数」。可附录 D 一上来就引入了箱数 B = 1000,还引入了一个可选的 EMA 系数。这不就是把超参数从优化器搬到了估计器里吗?你凭什么说自己无超参?
参考防守(先自己组织语言再看)
先承认字面上的事实:B 确实是一个超参数,EMA 系数也是。说「完全无超参」是不准确的,准确的说法是「没有学习率类的超参数」,而论文原文用的正是 「no learning-rate-like hyperparameter」 这个措辞——这个限定词不是事后找补,它在原文里就有。
然后要说清这两类超参数的性质差别,这才是防守的实质。学习率类超参数的坏处是:(1)它决定能不能收敛,选错了会震荡或者根本走不到;(2)它没有「越大越好」或「越小越好」的单调关系,必须搜;(3)合适的值随规模、批大小、专家数漂移,换个配置就要重调。
B 完全不是这样:(1)它不影响是否收敛,只影响估计精度,而且误差有明确的上界 w = 区间长度/B;(2)它是单调的——B 越大越准,没有「调大了反而坏」的风险,唯一代价是线性增长的通信量;(3)由于通信量与 m 无关且基数很小,把 B 设到远超需要的值几乎不花钱。一个有明确误差上界、单调、且代价可忽略的旋钮,和一个必须靠试错找的旋钮,不是同一类东西。论文说 B = 1000 「已足够」,配套给了误差量级(几个 10−3)和观察结论(无可测量的残余不均衡),这是可核对的陈述。
最后交出真正该被追问的地方:论文没有给 B 的消融曲线(B = 100 / 1000 / 10000 下的负载不均衡度),也没有给 EMA 系数的取值和它带来的收益量化——「可以降低批次间采样噪声,进一步改善负载均衡」是一句纯定性的话。审稿人要求补这两组数据是完全合理的。
14.8 附录 E:为什么每个 rank 预留 E/R 个冗余专家就够了
换个话题,从数学换到系统。MoonEP 要解决的问题是:E 个专家分散在 R 个 EP rank(expert-parallel rank),可以粗略理解为「一台负责一部分专家的 GPU」上,每个 rank 本地有 E/R 个专家。一个 token 要用的专家如果不在本地,就得跨机器通信——很慢。缓解办法是在 rank 上放一些冗余专家(redundant expert):把别的 rank 上的热门专家复制一份到本地,这样本地就能算,省掉通信。
但冗余专家要占显存。系统设计者必须提前知道:每个 rank 最多要预留多少个冗余专家的槽位?预留少了会不够用,预留多了纯浪费。附录 E 就是回答这个问题的。
先把问题写清楚
记 mr(P) 为在方案 P 下放在 rank r 上的冗余专家数。对一个给定的路由器输出 I(也就是「这一批 token 各自要哪些专家」这张表),规划目标是最小化任一 rank 上的最大冗余专家数:
| 符号 | 是什么 | 直觉 |
|---|---|---|
| I | 路由器输出:这一批 token 分别要用哪些专家 | 「今天的订单」 |
| P | 一个方案:决定每个 token 在哪个 rank 上被计算、以及每个 rank 放哪些冗余专家 | 「排产计划」 |
| mr(P) | 方案 P 下 rank r 上的冗余专家个数 | 「这台机器额外装了几套模具」 |
| maxr | 取所有 rank 里最大的那个 | 木桶效应:显存要按最坏的那台预留 |
| minP | 在所有可行方案里挑最好的 | 「最优排产下的最坏机器」 |
| P* | 证明里构造出来的一个具体方案(不必是最优的) | 一个够用的例子 |
| E, R | 专家总数、EP rank 数;每 rank 本地有 E/R 个专家 |
为什么这个界值得证
注意 M(I) 的定义里有一个 minP——它是在所有可能的方案上取最小。你不可能把所有方案都枚举一遍(方案数是天文数字)。而定理 1 说:不管路由器输出多离谱,都存在一个方案让最大冗余专家数不超过 E/R。这是一个对所有 I 一致成立的保证,所以系统可以照着它静态分配显存,永远不会不够。证明的手段是构造:只要造出一个够好的方案 P*,min 就一定不比它差。式 28 中间那个 ≤ 就是这个逻辑。
定理 1 的证明:一个反复「填坑」的构造
这是本章最适合零基础读者跟下来的证明,我们把它拆细。核心是一个引理:
关键引理:存在一个方案 P*,使得每个 EP rank 收到恰好相同数量的 token(S × K),并且每个 rank 的远程 token 只来自另外一个 EP rank。
论文在这里没有重新定义 S 和 K
附录 E 直接用了 S × K 这个记号而没有说明含义。从上下文(「每个 EP rank 收到相同数量的 token」、「全部 S×K×R 个 token」)推断,S 应是每个 rank 上的 token 数、K 是每个 token 选中的专家数,于是 S×K 是每 rank 的目标 token-专家对数量,乘以 R 得到全局总量。这是推断,不是论文的明文定义,读的时候要留意。
自己推一遍:填坑构造,以及「为什么每个 rank 至多被填一次」
初始状态:每个 rank 只处理自己本地的 token(一个 token 落在哪个 rank,取决于它是在哪台机器上被读进来的)。此时各 rank 的负载会一样吗?把 rank 分成两类,你会怎么分?
想好了再看
当然不一样——有的 rank 上的 token 恰好用了很多本地专家,有的没有。按论文的分法:负载超过均衡值 S×K 的叫超载(overloaded),低于的叫欠载(underloaded)。注意一个基本事实:总量是守恒的,所以有超载就必有欠载,不可能全都超载。这个事实后面要用。
现在做一次操作:挑一个欠载 rank 和一个超载 rank,把 token 从超载的迁到欠载的。迁多少?论文的规定是关键。
想好了再看
恰好填到均衡值 S×K 为止,一个不多一个不少。这是整个构造的灵魂:不是「迁一半」,不是「迁到超载的那个刚好平衡」,而是永远以「把欠载的那个填满」为准。当初为什么会想到这一步?因为我们最终要证的是「每个 rank 的远程 token 只来自一个源头」,那就必须保证每个 rank 只被「经手」一次——一次就把它彻底解决掉,它就再也不用被碰了。
迁完之后,那个超载的 rank 会变成什么状态?有几种可能?
想好了再看
三种:(i)仍然超载(它原本超得很多,填完这个坑还有富余);(ii)恰好平衡(原本超出的量正好等于那个坑的大小);(iii)变成欠载(它超出的量不够填满那个坑,被掏空过头了)。
等一下——情况 (iii) 可能吗?如果超载 rank 只超出 3 个 token,而欠载 rank 缺 10 个,迁 10 个过去会让超载 rank 掉到均衡值以下。是的,会发生。论文说「据此放回相应集合」,就是把它重新归类到超载集或欠载集里,继续参与后面的操作。为什么这个过程一定会终止?论文说至多经过 R − 1 次填充。
想好了再看
抓住这句话:每次填充让一个欠载 rank 变成均衡,且此后不再变动。为什么「此后不再变动」?因为后续的操作只会挑欠载的 rank 来填、挑超载的 rank 来抽,而这个 rank 现在恰好平衡,两个集合都不属于它,所以再也不会被选中。
于是每次填充都永久地减少一个「未解决的 rank」。总共 R 个 rank,最后一个不用单独处理(前面都平衡了,总量守恒逼得它也必然平衡),所以至多 R − 1 次。这是一个「单调递减的计数器」型的终止性论证,是这类构造性证明的标准武器。现在是最关键的一步:为什么每个 rank 至多被填充一次?(提示:把「被填充」和「变成均衡」这两件事对应起来。)
想好了再看
因为「被填充」这个动作的定义就是「被填到恰好均衡」。一个 rank 只有在欠载时才会被选为填充目标;而一旦被填充,它立刻变成均衡;而均衡的 rank 永远不会再被选为填充目标(上一步已论证)。所以对任何一个 rank,「被填充」这件事最多发生一次。
注意这里的不对称:一个 rank 可以被抽走很多次(超载的 rank 可以连续给好几个坑供货,每次抽完还超载),但只能被填入一次。而我们关心的恰恰是「填入」这一侧——因为冗余专家是为了处理收到的远程 token。收尾:既然 rank r 的所有远程 token 都来自单一的 rank s,为什么 mr(P*) ≤ E/R?
想好了再看
这些远程 token 全部来自 rank s,所以它们要用的专家全都是 s 的本地专家。而 rank s 本地只有 E/R 个专家。所以 rank r 需要复制过来的专家至多 E/R 个(最坏情况是这些 token 把 s 上的每个专家都用到了)。因此 mr(P*) ≤ E/R,对每个 r 都成立,所以 maxr mr(P*) ≤ E/R;再由 minP ≤ 任一具体方案,得到式 28。
回头看整条链:整个证明的全部力量都来自「单一来源」这个性质,而「单一来源」又完全来自「每次恰好填满」这个看似随意的规定。这就是构造性证明的美感——一个不起眼的设计选择撑起了整个结论。
设专家总数 E = 896,EP 大小取 R = 16。(注意:论文没有披露 K3 实际使用的 EP 大小 R,这里的 16 是本题自己设定的值;E = 896 来自论文的架构描述,本章这几页只提到「接近 103 个专家」。)(a)每个 rank 本地有多少个专家?(b)按定理 1,每个 rank 最多需要预留多少个冗余专家槽位?(c)按定理 2 的紧界 ⌈E(R−1)/R2⌉,这个数是多少?(d)如果按定理 1 的界预留,每个 rank 的专家显存最多会变成原来的几倍?
(d) 每 rank 最多装 56 + 56 = 112 个专家,是只装本地专家(56 个)的 2 倍。这是一个非常重要的工程结论:定理 1 的界在最坏情况下意味着专家显存翻倍。但注意这是上界,是「永远不会超过」的保证;实际路由输出通常远没有这么极端,真实占用会低得多。
(c) 和 (b) 的对比也值得体会:53 和 56 只差 3(差距 5.4%),这正是定理 2 在说的事——存在一种路由器输出,逼得你必须准备 53 个冗余槽位,所以你不可能把 56 这个界压到比 53 更低。换句话说,翻倍显存这个代价基本上是躲不掉的,不是分析不够精细。
(顺带练一下 R 变大时的趋势:R = 32 时 E/R = 28,而 ⌈896 × 31 / 1024⌉ = ⌈27.125⌉ = 28——两者已经完全相等。这就是论文说 「R 大时 E(R−1)/R2 ≈ E/R」 的意思。)
变式:如果你是系统工程师,想把冗余显存开销压到本地专家的 25% 以内,在 E = 896 固定的前提下,R 该怎么取?这个取法会带来什么新问题?(提示:E/R 是绝对个数,而本地专家数也是 E/R——所以最坏情况下这个比例恒等于 100%,与 R 无关。要压低它必须放弃「最坏情况保证」,改用统计意义上的预留。这说明定理 1 的界虽然紧,但对实际系统的指导是「要么接受翻倍,要么接受偶尔不够用」。)
定理 2:这个界基本上没法再改进了
定理 1 给了一个上界。但上界这种东西是可以很松的——「人的身高不超过 100 米」也是一个正确的上界,只是毫无用处。所以必须回答:E/R 是不是一个松垮的界?能不能证出一个更小的?定理 2 说:不能。
证法是构造一个最坏情况。论文造了这样一个路由器输出 I*:EP rank 0 上的专家收不到任何 token,而其他 R−1 个 rank 上的所有专家均分全部 token。
推理链是这样的:
- 全部 S×K×R 个 token 被均分给 E(R−1)/R 个专家(因为只有 R−1 个 rank 的专家在收货,每个 rank E/R 个)。所以每个专家收到 SKR ÷ [E(R−1)/R] = SKR2/[E(R−1)] 个 token。
- 在任何方案 P 下,rank 0 都必须收到 S×K 个 token(负载要均衡),而且全是远程的——因为它自己的专家一个 token 都没收到。
- 这 SK 个 token 至少涉及多少个不同的专家?每个专家最多能贡献 SKR2/[E(R−1)] 个 token,所以至少需要 SK ÷ SKR2/[E(R−1)] = E(R−1)/R2 个不同的专家。
- 专家个数必须是整数,取上取整:rank 0 至少需要 ⌈E(R−1)/R2⌉ 个冗余专家。所以 M(I*) ≥ ⌈E(R−1)/R2⌉。
- 反过来,用定理 1 证明里的填充过程构造方案、并优先按专家迁移 token(即尽量把同一个专家的 token 整批一起迁走,而不是零散地迁),可以把每个 rank 上的冗余专家数控制在这个值以内。所以取等号。
因为 R 大时 E(R−1)/R2 = (E/R)·(1 − 1/R) ≈ E/R,论文的结论是:不存在显著小于 E/R 的一般上界。
一个容易混淆的地方
定理 2 说的不是「实际训练中每个 rank 都需要 E(R−1)/R2 个冗余专家」。它说的是「存在一种路由器输出会逼出这么多」。这两句话差别很大:前者是关于典型情况的,后者是关于最坏情况的。真实训练里路由是被 QB 均衡过的(这正是 14.2–14.7 讲的事),远比这个人造的最坏情况温和。定理 2 的真正用途是划定「分析的极限」:它告诉你不要再去尝试证明一个更紧的一般上界了,那是徒劳的;要想要更小的预留,只能引入关于路由分布的额外假设。
定理 2 的构造里,有人质疑:「让 rank 0 的专家一个 token 都收不到,这也太人为了。我改一改:让 rank 0 的专家收到很少但不为零的 token,比如别的专家的 1%。这样 rank 0 就不用全靠远程了,界是不是就能压下来?」请分析这个修改,并说明它为什么基本上救不了。然后回答一个更根本的问题:这个构造真正利用的是什么结构性事实?
算一算。近似地设全局 SKR 个 token 均分给「正常」专家,每个正常专家约 T = SKR/[E(R−1)/R] = SKR2/[E(R−1)] 个。rank 0 有 E/R 个专家,每个收 0.01T,本地总供应 ≈ (E/R) × 0.01T = 0.01 × SKR/(R−1) ≈ 0.01 SK(R 大时 R/(R−1) ≈ 1)。
也就是说本地只能供应 约 1%,还剩 99% 的 S×K 要靠远程。按同样的计数,需要的不同远程专家数至少是 0.99 × E(R−1)/R2。界只降了 1%,几乎原封不动。
为什么救不了。因为这个构造的杠杆不是「rank 0 是零」这个极端值,而是「rank 0 的本地专家吸收能力远小于它必须承担的负载」这个失配。你把 0 改成 1%,失配从 100% 变成 99%,量变而已。要真正把界压下来,必须让 rank 0 的本地专家吸收掉相当大比例的 S×K——但那样一来路由本身就已经接近均衡了,也就不再是最坏情况。
真正利用的结构性事实有两条,缺一不可:
(1) 负载必须均衡——这是 MoonEP 的硬要求,逼得 rank 0 无论如何都要吃下 S×K 个 token,不能说「我这儿没活干就闲着」。
(2) 单个专家的供货量有上限——因为 token 被均分了,没有哪个专家能一个人喂饱 rank 0。这是一个鸽笼原理式的计数:要装 SK 个 token,每个「笼子」(专家)最多装 T 个,那就至少需要 SK/T 个笼子。
把这两条放在一起就得到了下界。注意 (2) 才是「均分」这个设定的用途——如果反过来让少数几个专家收走绝大部分 token,rank 0 只要复制那几个热门专家就够了,冗余数反而很小。所以「均分」不是为了「公平」,恰恰是为了制造最坏情况:把需求摊薄到最多的专家上,逼你复制最多的专家。
变式:反过来构造一个最好情况——什么样的路由器输出会让 M(I) = 0(一个冗余专家都不需要)?再想:在这个最好情况和定理 2 的最坏情况之间,QB 均衡后的真实路由更接近哪一头?为什么?(提示:QB 让专家负载均衡,但这和「token 与专家的位置对齐」是两件不同的事——论文没有讨论这一层。)
14.9 附录 F:XTML——把聊天格式设计成一门可扩展的标记语言
最后一段附录看起来最「软」,但它其实是全篇里最能体现工程判断力的一节。
聊天模板(chat template):模型只认识一长串 token,不认识「这是系统提示、这是用户说的、这是工具返回的」。聊天模板就是把结构化的对话(谁说了什么、调用了什么工具)拍平成一串 token 的那套规则,以及反过来把模型输出解析回结构的规则。它是模型和外部世界之间的接口协议。
三个设计目标
论文说 K3 的聊天模板围绕三个目标重新设计:
可扩展性(extensibility):新能力应该通过向后兼容的消息格式引入,而不是靠改模板,这样单一模板可以服务整个模型代际。为什么这很重要?因为模板一改,所有已经训好的行为、所有下游的解析代码、所有缓存的上下文都可能失效。把「加功能」和「改协议」解耦开,才能不断加东西而不推倒重来。
低对齐税(low alignment tax):格式应该能用极少的监督数据学会。论文说这支持一条特定的流水线:「轻度微调的预训练模型直接进入强化学习」。翻译一下——如果格式很怪、很难学,你就得先用大量人工标注数据把模型教会说这套话,这个阶段既费钱又会损伤预训练学到的能力(这就是「对齐税」);如果格式本身就接近预训练数据里的自然语言,模型几乎不用教就会,你可以省掉大半个监督微调阶段直接开始 RL。
解码友好(decoding friendliness):结构应该支持简单的编码器、流式解析器(一边生成一边解析,不用等整段生成完)和语法约束执行器(强制模型只能生成符合语法的 token)。
XTML:把尖括号换成保留 token
XTML(eXtensible Token Markup Language,可扩展 Token 标记语言):一种类 XML 的标记语言,但把 XML 的尖括号语法替换成三个保留特殊 token:[open]、[sep]、[close],外加一个 [end_of_msg] 作为生成停止标记。
一个元素写成 [open]tag attr="value"[sep] … [close]tag[sep],它与 XML 里的 <tag attr="value"> … </tag> 是同构的(结构完全对应),但每一个结构边界都是一个显式的特殊 token。论文说这消除了元素边界处的分词歧义并简化了受约束解码。
「分词歧义」到底是什么问题
模型读到的不是字符,是 token——一种介于字母和单词之间的碎片。分词器(tokenizer)按一套统计规则把字符串切成碎片,而切法依赖于上下文。同一个 </think>,前面跟着句号时可能被切成 </ + think + >,前面跟着换行时可能被切成 </th + ink>,紧跟中文时又可能是别的切法。于是「同一个结构边界」在模型眼里变成了好几种不同的 token 序列,模型必须把每一种都学一遍,而且哪一种都不完全可靠。
更糟的是混淆:如果用户消息里本来就含有 </think> 这几个字符(比如用户在问一个关于聊天模板的问题),解析器就分不清这是结构还是内容——这是提示注入攻击的一条经典路径。
用保留特殊 token 一次解决两个问题:[close] 永远是同一个 token id,不管前后是什么;而且它不可能由普通文本编码出来(分词器的词表里,特殊 token 是被单独保留的,任何字符串都不会被切成它)。所以结构和内容之间有了一堵不可逾越的墙。
对受约束解码的好处也很直接:想强制模型「现在必须闭合这个元素」,只需要在采样时把除了 [close] 之外所有 token 的概率压成 0——一个 token id 的操作,而不是去匹配一个多 token 的字符串模式。
消息与区域:一个 KV 缓存友好的排布
上下文的顶层单位是消息(message),消息按来源分两类:
输入消息(input messages)序列化请求的 messages 字段,涵盖熟悉的 system、user、assistant、tool 四种角色。
选项消息(option messages)把请求选项翻译成模型在上下文里读到的指令。它们的位置反映作用域——这是全节最值得学的一个设计点:
- 全局选项(工具声明
type="tool-declare"和推理努力设置)出现在所有输入消息之前。论文给了两条理由:它们管辖整个会话、很少改变;而且改动它们本来就会让 KV 缓存失效。 - 一次性选项(
tool_choice、response_format)追加在输入消息之后,这样逐请求的改动不会破坏历史的 KV 缓存。 - 输入选项消息(input option message)是第三种,它与输入消息交错,用于在会话中途补充或覆盖某个全局选项。
KV 缓存(KV cache):Transformer 生成时,每个已经处理过的 token 都会留下一份中间结果(键和值)。下一个 token 只需要读这些缓存,不用重算前面的一切。但缓存的有效性依赖于「前缀完全没变」——因为每个位置的中间结果都依赖它之前的所有 token。一旦你改动了第 t 个 token,从第 t 个往后的所有缓存全部作废,必须重算。
输入选项消息带来的能力是动态加载的工具(dynamically loaded tools):会话中途检索或加载的工具,通过一条额外的 tool-declare 消息宣告,之后模型可用的工具集就扩大了,而不需要重建前面的上下文。想想如果没有这个机制会怎样——工具声明都在最前面,要加一个新工具就得改最前面那段,整段上下文的 KV 缓存全部作废,一次几十万 token 的会话要从头重算。
一个客服系统有三种设置需要传给模型:(i)公司的工具清单,一年改两次;(ii)「本次回答必须是 JSON 格式」,每个请求都可能不同;(iii)会话进行到一半时,客服系统根据用户问题检索出了三个新工具,需要临时加入。请按论文的方案说明这三种设置各自应该放在上下文的哪个位置,并分别算出「改动它」会作废多少 KV 缓存(用「占整段上下文的比例」粗略描述)。
(i) 全局选项,放最前面。它是 tool-declare 类型,管辖整个会话。改动它会作废几乎全部缓存——但论文的辩护是:这类改动本来就极少(一年两次),而且「改动它们本来就会让 KV 缓存失效」是不可避免的(工具清单变了,模型对整段历史的理解基础都变了,即使技术上能保住缓存,语义上也不该保)。低频 + 不可避免 = 放最前面的正当理由。
(ii) 一次性选项,放输入消息之后。它是 response_format,逐请求变化。放在最后面,改动它只作废最末尾那一小段(几十个 token),前面几十万 token 的历史缓存完好。这是最关键的一个安排:如果把它放在前面,那么每次请求换一下输出格式就要重算全部上下文,多轮长会话会被拖垮。
(iii) 输入选项消息,交错插在当前位置。它是一条中途补充的 tool-declare。作废的是从插入点往后的部分——而插入点就是「现在」,后面本来就还没生成,所以实际作废量几乎为零。这就是「不需要重建前面的上下文」的含义:新工具从这一刻起生效,之前的历史一个 token 都不用动。
体会一下三者的共同逻辑:每一类的位置都被安排在「它的变动频率所能承受的最靠前的地方」。频率越高,位置越靠后。
变式:假设现在要新增一个选项,叫「温度设置」,它每次请求都可能变、而且只影响采样不影响语义。按这套原则它该放哪?再想一个更刁钻的:如果某个选项既管辖全局又频繁变化(比如「当前时间」),这套排布还有救吗?(提示:想想为什么大多数系统宁可把时间写死在 system 提示里也不肯每轮更新它。)
通道:两种生成模式靠前缀切换
assistant 消息的主体被组织成通道(channels),论文说这个概念受 OpenAI 的 Harmony 响应格式启发。三个通道各司其职:think 承载推理痕迹,response 承载用户可见的回答,tools 承载工具调用。
两种生成模式纯粹通过生成前缀选择——[open]think[sep] 进入思考模式,[open]response[sep] 进入指令模式——而不是通过不同的模板。这一点直接呼应「可扩展性」目标:模式切换不需要碰模板。
K3 只支持保留式思考(preserved thinking):思考模式下 think 通道永远保留在历史里,即使内容为空也保留。论文给的理由是「这样模型跨轮观察到一致的消息结构」。指令模式下,历史消息只包含 response 和 tools 通道。
「即使为空也保留」为什么重要
如果有时保留、有时删掉,模型在历史里看到的消息结构就是两种形状。它得先判断「这一轮到底有没有 think 通道」,再决定怎么理解后面的内容。恒定保留消除了这个判断,模型可以把「assistant 消息 = think + response + tools」当成一条铁律。结构上的一致性比省那几个 token 值钱得多。论文没有讨论这样做的 token 开销,也没有讨论为什么不支持「非保留式思考」(很多系统会在下一轮丢掉旧的思考痕迹以省上下文)——这是一个作者做了选择但没有论证的地方。
工具调用:有类型的参数,以及代码作为一等公民
tools 通道里每次调用带 tool 和 index 两个属性。index 是消息内的并行调用编号;每条工具结果消息重复同样的 tool/index 对,并按其调用的顺序排列,因此结果能明确地与调用配对——不会出现「三个工具同时调用、返回时分不清谁是谁」的问题。
参数是有类型的:字符串参数以原始文本出现,其他 JSON 类型的值紧凑序列化。按 Figure 16c 的样子,一次调用长这样([open]argument key="code" type="string"[sep] 之后直接跟原始代码,然后 [close]argument[sep])。因此像代码这样的自由格式文本成了一等公民,而不是被转义的 JSON 字符串。
论文还提到一个纯 JSON 的回退块,覆盖那些参数无法被分解成有类型参数块的输入;它只出现在输入 token 中、从不出现在模型输出里,并且训练时其损失被掩掉(也就是模型不会因为这段而被训练去生成它)。
推理努力:用自然语言说,而不是造新语法
推理努力被暴露为一条 thinking-effort 类型的全局选项消息,插在工具声明之后、输入消息之前。它不修改生成前缀、也不暴露 token 预算,而是用自然语言陈述所请求的等级,充当一条生成约束指令。schema 预留了四个等级(low、medium、high、max),K3 支持其中一个子集(论文没有说明具体支持哪几个)。
论文说这种表示把努力接口与模板语法解耦,并且与努力条件化训练直接对齐。更广泛地说,这是所有选项消息的共同实现方式:tool_choice、response_format、thinking-effort 各自被翻译成放进上下文的一条简短的自然语言指令,而不是专门的特殊语法。
综合题。假设模型要调用一个 python 工具,参数是这样一段代码:if x > 0 and s == "a\nb":\n print("ok")
(a)如果按传统做法把它包成 JSON 字符串 {"code": "..."},模型在生成时必须额外做什么?请具体指出至少三处需要转义的地方。(b)说明这件事为什么会同时伤害三个设计目标中的至少两个。(c)K3 的有类型参数块怎么绕开了它?(d)最后把这个例子和「低对齐税」联系起来:为什么「不用转义」能减少所需的监督数据量?
(a) 至少三处:① 代码里的
"a\nb" 的两个双引号必须写成 \";② 代码里本来就有的反斜杠 \n(这是 python 源码里的两个字符)必须写成 \\n,否则会被 JSON 解析成真正的换行;③ 代码的真实换行(每行结束)必须写成 \n;④ print("ok") 的引号同样要转义。注意 ② 和 ③ 的混淆是最恶心的——同样的两个字符 \n,一个要变成四个字符,一个是由一个换行变来的,模型必须分清「这个反斜杠是源码的还是我加的」。(b) 伤害两个目标:解码友好被伤得最重——流式解析器必须一边收 token 一边做反转义状态机,而且在「收到一个孤立的反斜杠」时无法判断下一个字符是什么,解析状态是不确定的;受约束解码也更难,因为合法的下一个 token 集合依赖于转义状态。低对齐税同样受损:预训练语料里的代码绝大多数是原样的,而不是被 JSON 转义过的,所以「写转义代码」是一项模型没有在预训练中大量见过的技能,必须靠监督数据专门教,教得不够就会在长代码里出错——而且这类错误是全有全无的:漏一个转义,整段 JSON 解析失败,整次工具调用作废,不像自然语言那样「错一点还能用」。
(c) K3 把参数写成
[open]argument key="code" type="string"[sep] + 原始代码 + [close]argument[sep]。因为边界是保留特殊 token,代码里不可能出现它们(前面讲过:特殊 token 不能由普通文本编码出来),所以根本不需要转义——代码怎么写就怎么放进去。这正是论文说的「自由格式文本成了一等公民」。(d) 联系低对齐税:模型要学的东西从「写代码 + 同时做一套字符级转换」减少到「写代码,然后输出一个结束标记」。后者几乎完全落在预训练已有的能力范围内(写代码它本来就会,输出一个特定 token 是一学就会的),所以只需要极少的监督数据就能学会格式。这就直接支撑了论文说的那条流水线:轻度微调的预训练模型直接进入 RL——不需要一个庞大的监督微调阶段来把格式砸进模型,也就避免了那个阶段对预训练能力的损伤。
把三层串起来看:一个看似只是「少写几个反斜杠」的格式选择,向下影响解析器的复杂度和错误率,向上影响训练流水线能不能省掉一个阶段。这就是为什么论文把聊天模板当成一个值得写进技术报告的设计问题,而不是一个实现细节。
变式:论文说纯 JSON 回退块「只出现在输入 token 中、从不出现在模型输出里,并且训练时其损失被掩掉」。请论证这个设计决定的必要性:如果不掩掉损失会发生什么?(提示:模型会学到什么?这和 (d) 里说的「低对齐税」是不是自相矛盾了?)再想:既然如此,为什么还要保留这个回退块?
本章小结
五段附录,一条共同的线索:正文里的每一句「我们这样做效果更好」,背后要么是一个可以推的式子,要么是一个可以证的界,要么是一个可以说清代价的权衡。
附录 B 回答「怎么给乘法封顶而不毁掉激活函数的形状」。答案是把 Swish 的线性因子换成 β tanh(·/β)、sigmoid 因子保留,两个分支同样处理。式 18 保证原点附近一阶匹配 SwiGLU、β → ∞ 时逐点还原;式 19 给出与输入无关的硬上界 β₁β₂ = 100(β₁ = 4、β₂ = 25)。相对硬截断的优势是远离饱和边界处保留非零梯度——硬截断在饱和区导数恒为 0,神经元会永久死掉。
附录 C 把分位数均衡从一个技巧还原成一道题的精确解。起点是式 20 的最大分数均衡指派;松弛到 [0,1] 因二部 b-匹配多面体的全单模性而精确;引入乘子写成 max–min(式 21),交换次序(式 22),内层可分地取到 max(0, ·)(式 23)。对偶目标的坐标极小化在两个方向上都是分段线性的,斜率是「目标数减去超过阈值的个数」,因此极小点卡在第 k 大与第 k+1 大之间,取第 k+1 大恰好等于 (1−k/n) 分位数(式 25、式 26)——token 轴和专家轴取的是同一个分位数,方法因此得名。最关键的结论是那个不对称:路由只需要专家阈值 β(即偏置 b = −β),token 阈值 α 是与训练批次绑定的中间变量,可以丢掉;所以部署时路由就是「减去冻结偏置再取 Top-k」,不需要任何分位数计算。最后,式 27 显示专家侧次梯度就是「目标负载减观测负载」,对它做一步 SignSGD 恰好还原免辅助损失均衡的定步长符号更新——符号更新只用了误差的方向,QB 直接跳到同一个目标的精确极小点,这解释了 QB 为什么无学习率超参、为什么面对近 10³ 个专家也能几步平衡。BIP 用不等式约束,诱导出的非负性给两个更新加了 max(0,·) 截断,只能压不能提,明显减慢平衡。
附录 D 解决「分位数要在百万级、跨 rank、跨累积步的 token 上取」这个工程难题。关键观察是更新只需要分布而不需要数据本身,于是对所需偏置 r = α − s 做逐专家直方图;取负让顺序反转,QB 目标恰好是 r 的 (k/n) 分位数。分箱区间 [bmin − 1, bmax + 1] 由 sigmoid 分数落在 (0,1) 和「α 本身是某个有偏分数」这两条推出,每步重算所以自适应。B = 1000 个箱,误差被箱宽卡在几个 10⁻³。三个性质:准(累积计数在箱边界处精确)、便宜(每层每步一次 nB 个整数的 all-reduce,与 m 无关,低于替代方案的 1%)、估的是对的量(计数可加 ⇒ 全局直方图对分片方式精确不变 ⇒ 估的是池化全局批次的分位数,而不是各 rank 分位数的平均,这两者一般不同)。
附录 E 给 MoonEP 的冗余专家预留量定了个界。定理 1:M(I) ≤ E/R 恒成立,证法是构造一个反复「恰好填满欠载 rank」的方案——每次填充让一个 rank 永久变均衡,所以至多 R−1 次;每个 rank 至多被填一次,所以它的远程 token 只有单一来源,而那个来源本地只有 E/R 个专家。定理 2:构造「rank 0 收不到任何 token、其余专家均分」的路由输出,靠鸽笼式计数得到 ≥ ⌈E(R−1)/R²⌉,R 大时约等于 E/R,所以不存在显著更小的一般上界。工程含义是最坏情况下专家显存翻倍,且这个代价躲不掉。
附录 F 讲 XTML 聊天模板。三个目标:可扩展(靠向后兼容的消息格式而非改模板,单一模板服务整个模型代际)、低对齐税(格式易学,支持轻度微调后直接进 RL)、解码友好。手段是用 [open]/[sep]/[close] 三个保留特殊 token 替代尖括号,消除分词歧义并简化受约束解码。排布原则是「不变的在前、易变的在后」:全局选项 → 输入消息 → 一次性选项,中途可插入输入选项消息以支持动态加载工具而不重建上下文。assistant 消息分 think / response / tools 三个通道,两种模式纯靠生成前缀切换;K3 只支持保留式思考。工具调用用 tool/index 配对,参数有类型,代码作为原始文本而非转义 JSON 字符串。推理努力做成一条自然语言的全局选项消息(四个等级 low/medium/high/max,K3 支持其中子集),这也是所有选项消息的共同实现方式。
需要记住的空缺。附录 B 没有给平滑封顶 vs 硬截断的任何对比曲线,也没有解释 β₁ = 4、β₂ = 25 这组数字怎么选的。附录 C 的「平局测度为零」在低精度浮点下并非无害,论文一笔带过;也没有给 QB 解与真实最优指派的差距度量。附录 D 没有给箱数 B 的消融,EMA 精化只有定性描述,而且它把附录 C 里的对偶变量 β 和这里的「选中箱编号」 β 用了同一个字母。附录 E 没有定义 S 和 K(只能从上下文推断),也没有披露 K3 实际用的 EP 大小 R。附录 F 没有说 K3 具体支持四个努力等级中的哪几个,没有讨论保留式思考的 token 开销,也没有论证为什么不支持非保留式思考。
Chapter 14 · The appendices in depth: five hard cores folded out of sight
In one sentence: all those “we did it this way and it works better” sentences in the main text — what makes them hold? This chapter unfolds the five derivations the paper folded into its appendices: why the upper bound of SiTU-GLU is exactly 100, how the “quantile” in Quantile Balancing grows out of the dual of a linear program, how you estimate the quantile of a million numbers with a thousand buckets, why reserving E/R redundant experts per rank is enough, and how one chat template gets designed to serve an entire generation of models.
After this chapter you should be able to
- Derive for yourself that β tanh(z/β) equals z near the origin, and say where SiTU-GLU's output bound of 100 comes from
- Turn “every expert does the same amount of work” into a constrained maximisation problem, and say what its dual objective looks like
- Explain why the word “quantile” turns up in a derivation that has nothing to do with statistics
- Say why deployment needs only one frozen bias vector and no quantile computation at all — this is the most important sentence in the chapter
- Use a concrete two-rank example to show that “the average of the per-rank quantiles” and “the global quantile” are not the same thing
- Restate the filling construction of Theorem 1, and explain what makes the step “each rank is filled at most once” hold
- Given the total number of experts and the EP size, work out how many redundant expert slots each rank has to reserve
- Explain why “put the unchanging parts first and the volatile parts last” is a KV-cache-friendly layout
How to read this chapter
This is the chapter with the highest density of mathematics on the site. One rule first: every time you meet a derivation, ask “what is it trying to prove, and why is that worth proving” before you look at how it proves it. Some steps (“total unimodularity” and “the minimax theorem”, for instance) genuinely go beyond secondary-school material, and there I will tell you plainly “you can take the conclusion on trust for now; understanding where it sits in the whole chain is enough”, while still giving you the intuition for why it holds. You do not have to absorb all of it in one pass; but if you cannot even say “where this step sits in the chain”, then you really have not understood it, and you need to go back.
14.1 Appendix B: how to cap a product “gently”
First, what problem this derivation is there to solve. The output of a gated linear unit(Gated Linear Unit, GLU) such as SwiGLU is two branches multiplied together. Multiplication is numerically dangerous: each branch only has to get slightly bigger for the product to get much bigger — 30 times 30 is 900. In a trillion-parameter training run, one abnormally large activation appearing in some layer can blow up the whole round of training along the backward pass. So the authors wanted to put a cap on this product.
But you cannot cap it just anyhow. What makes Swish a useful activation function is its particular shape: approximately linear near the origin (a small signal passes through almost unchanged, undistorted), and a gradually vanishing tail on the negative half-axis (unlike ReLU, which cuts negative numbers to 0 in one stroke, it keeps a little response). The paper's own words are that it wants to “bound the SwiGLU product without losing Swish's characteristic shape”. So the real question in this section is: how do you shut the output in without destroying the linearity near the origin and without erasing the negative tail.
Why not just cut it off
The laziest option is hard clamping(hard clamping): for the gate pre-activation, write one line saying “anything above 10 counts as 10”. That does cap it, but it has one fatal flaw, and the question at the end of 14.1 will let you find it yourself. The paper's choice is a smooth cap(smooth cap): instead of putting up a wall, let the function itself find it harder and harder to move the closer it gets to the ceiling.
The move: replace Swish's linear factor with a flattened tanh
Swish can be seen as two factors multiplied: a linear factor (the input itself) times a sigmoid factor. SiTU changes exactly one thing: the linear factor becomes β₁ tanh(Wgx/β₁), and the sigmoid factor is kept as it is.
tanh (the hyperbolic tangent): an S-shaped function whose range lies strictly between −1 and 1; when the input is close to 0 the output is roughly the input itself, and when the input is large the output approaches ±1 but never gets there. You can think of it as an identity function that “comes with its own ceiling and floor, but walks up to them by decelerating smoothly rather than hitting a wall”.
Why does this substitution not ruin Swish's shape? The key is this sentence of explanation in the paper: because the sigmoid already presses the gate response on the negative side towards zero, this change mainly controls large positive activations and does not erase the negative tail. Unpacked: on the negative half-axis the sigmoid factor is already small (close to 0), so the output on the negative side is already squashed flat, and whether tanh holds it down there or not hardly matters — that tail survives. What tanh actually reins in is the large values on the positive half-axis — where the output used to be as big as the linear factor was, the factor can now contribute at most β₁.
K3 applies exactly the same construction to the up branch: β₂ tanh(Wux/β₂). The reason the paper gives is to prevent either branch from dominating the product — capping one branch alone is useless, since the other can still send the product flying.
Local and limiting behaviour (Eq. 18)
Now a challenge that has to be answered: you replaced the linear factor with a tanh, so does “approximately linear near the origin” still hold? The paper answers with Eq. 18:
| Symbol | What it is | Intuition |
|---|---|---|
| z | A scalar (here the pre-activation of a branch, i.e. one coordinate of Wgx) | “how strong the signal is” |
| β | A positive constant hyperparameter; K3 takes β₁ = 4 (the gate branch) and β₂ = 25 (the up branch) | the height of the ceiling: the larger β is, the looser the cap and the closer to the original SwiGLU |
| tanh | The hyperbolic tangent function, range (−1, 1) | a smooth “soft ceiling” |
| O(z3/β2) | Order-of-magnitude notation for the error term: the error will never be larger than some constant multiple of z3/β2 | “the loose change we dropped is at most this big” |
Big-O notation: writing f(z) = O(g(z)) does not mean “equals”; it means “once z is small enough (or large enough, depending on context), |f| does not exceed some fixed multiple of g”. It is a rough comparison that cares only about growth rate, not about the actual constants. The context here is z → 0.
What Eq. 18 says is blunt: drop the z3/β2 term and β tanh(z/β) is z itself. So near the origin SiTU-GLU and SwiGLU match to first order(matches to first order) — the two curves not only meet at the origin, they have the same slope there. And note the β² in the denominator: the larger β is, the smaller the error; as β₁, β₂ → ∞, SiTU-GLU reduces pointwise to SwiGLU. Put differently, SwiGLU is the special case of SiTU-GLU at β infinite, and this change is “adding a knob you can loosen or tighten”, not “swapping in something else”.
Derive it yourself: the third-order expansion in Eq. 18
You know that near 0 tanh has the expansion tanh(u) = u − u3/3 + … (what follows is the u5 and higher-order terms). What you have to compute is β tanh(z/β). What should your first step be?
Think it through first, then look
Substitute: let u = z/β. Why would anyone think of this step in the first place? Because the expansion of tanh is written with “the independent variable in the lead role”, while what sits inside our tanh is not z but z/β — whenever you meet “something else stuffed into the argument slot of a formula you know”, the first reflex is to give that something a name, apply the formula, and substitute back at the end.
Substitute it in, and you get tanh(z/β) = ? (don't multiply by the outer β yet)
Think it through first, then look
tanh(z/β) = z/β − (z/β)3/3 + … = zβ − z33β3 + …. Note that the cube applies to the denominator as well, so the denominator becomes β³; this is the easiest place to slip.
Now multiply by the outer β. What do you see?
Think it through first, then look
β · tanh(z/β) = z − z33β2 + …. The first term is exactly z, and β has cancelled completely. That is all “matching to first order” means: whatever β you take, the first-order term near the origin is always z, exactly as it was with no change made at all.
Finally: why does the paper write O(z3/β2) instead of writing the exact −z3/(3β2)?
Think it through first, then look
Because after it come z5/β4, z7/β6 … infinitely many terms. Writing big O says “all the remaining terms from the cubic one onwards, added together, are of a size no more than a constant times z3/β2”, and that constant of 1/3 does not matter here — only two pieces of information matter: the error is cubic (it falls away fast when z is small), and the error is divided by β2 (it falls away fast when β is large too).
The paper says: because |tanh(z)| < 1 and 0 < Sigmoid(z) < 1, every output coordinate satisfies ‖SiTU-GLU(x)‖∞ ≤ β₁β₂ = 100 (Eq. 19, with β₁ = 4 and β₂ = 25). Fill in the steps left out between those two lines: what is the largest absolute value the gate branch can take? What is the largest for the up branch? And why does multiplying the two give 100?
Variant: if you change both β₁ and β₂ to 10 (the product is still 100), is the bound still 100? What is the difference in behaviour between the two choices? (Hint: think about which branch has more need to keep a large dynamic range — β = 4 means that branch is essentially saturated once the pre-activation goes past about 4, whereas β = 25 does not saturate until 25. The paper does not explain how the particular pair 4 and 25 was chosen.)
Why a “smooth cap” and not “hard clamping”
The paper's last sentence is where the whole passage lands: unlike hard clamping of the gate pre-activation, a smooth bound preserves non-zero gradients away from the saturation boundary, and the authors find this gives better training behaviour. That sentence is worth reading slowly — it is the only claim in this section with any experimental colour to it, and the paper gives no comparison curve of any kind.
Someone says: “tanh has a ceiling, hard clamping has a ceiling, there is no essential difference between them, and hard clamping is cheaper to compute.” Construct a concrete interval of inputs to refute them, and say what it is that happens with hard clamping and does not happen with a smooth cap. (Hint: think about what a “gradient” is — it is the slope of the function.)
A smooth cap is different: the derivative of β tanh(z/β) is 1 − tanh²(z/β), which at z = 12, β = 10 is about 1 − tanh²(1.2) ≈ 1 − 0.834² ≈ 0.30, and at z = 30 about 1 − tanh²(3) ≈ 0.01. The numbers are tiny, but strictly positive. So the gradient is attenuated rather than wiped out, the neuron always knows “which side I am on and which way to move”, and it has a chance of being pulled back. This is what the paper means by “preserves non-zero gradients away from the saturation boundary”.
The counterexample interval can therefore be any interval satisfying |z| > c, [12, 30] for instance: on it, the gradient of hard clamping is 0 and the gradient of the smooth cap is positive, and the two differ not in degree but in whether it exists at all.
Variant: argue the other side and defend hard clamping — construct a scenario in which “zero gradient” is in fact what you want (hint: think about why gradient clipping is so widely used, and about situations where “we want the model to ignore this direction entirely”). Then point out why that defence cannot rescue the use here: creating large zero-gradient regions inside an activation function and clipping the total gradient norm outside, in the optimiser, cost entirely different things.
14.2 Appendix C, part one: first write “expert balancing” as a maths problem
What comes next is the longest stretch in this chapter and the one most worth chewing through slowly: where that update rule of Quantile Balancing(Quantile Balancing, QB) actually comes from. The earlier chapter on MoE routing told you that QB “takes a quantile as the bias”, but not why it has to be a quantile of all things. Appendix C's answer: it was not designed off the top of anyone's head, it is the exact solution of the dual of an optimal balanced assignment problem. The paper says this derivation follows reference [111], and the view that “expert load balancing is an assignment problem” goes back to BASE Layers [67] and BIP [116].
Why it is worth proving
If QB were only “we tried it and taking a quantile works quite well”, it would be a tuning trick, and it might stop working at a different scale. Whereas if you can prove it is the exact solution of some clearly defined optimisation problem, you then know three things: what it is optimising, when it will fail (when the assumptions behind it are broken), and how it relates to other methods (the sign update of auxiliary-loss-free balancing, for instance). In 14.6 you will see that this last point produces a very elegant unification.
Turning the words into a formula
First, what this problem says. There are m tokens and n experts. The router produces a table of scores s, and the entry si,j in row i, column j says “how much token i wants to go to expert j”. The rule is that each token picks exactly k experts. A switch variable xi,j ∈ {0, 1} records “whether token i was in fact assigned to expert j”. The goal is to make the total score as high as possible, but to do it under the hard constraint that every expert does the same amount of work.
| Symbol | What it is | Intuition |
|---|---|---|
| s ∈ Rm×n | The routing score matrix: the scores m tokens give to n experts | a table of “who wants to go where” |
| m | The number of tokens (a whole batch during training, on the order of a million) | how many people have to be seated |
| n | The number of experts (the paper mentions “close to 103 experts”) | how many counters there are to choose from |
| k | The number of experts each token selects (the k in Top-k) | how many queues each person joins |
| xi,j ∈ {0,1} | The assignment switch: 1 means token i is assigned to expert j | a 0/1 seating chart |
| Σj xi,j = k | The token-side constraint: exactly k ones in each row | each person joins exactly k queues, no more and no fewer |
| Σi xi,j = mk/n | The expert-side constraint: exactly mk/n ones in each column (assuming it divides evenly) | each counter serves exactly the same number of people |
| s.t. | Short for subject to: “provided the following conditions hold” | “but it must …” |
Where does the number mk/n come from? There are m tokens in all and each picks k experts, so mk “service tickets” have to be issued; n experts split them evenly, mk/n each. The paper writes explicitly “assuming divisibility”, that is, assuming mk is divisible by n — a simplification made to keep the formula clean.
An analogy
A canteen has n counters and m students; each student takes k dishes, and each dish has to come from a different counter. Every student has a satisfaction score in mind for every counter. The canteen manager's requirement is: every counter must serve the same number of visits (no long queue at one counter while another sits idle), and subject to that, total satisfaction across all students should be as high as possible. Eq. 20 is that problem.
Where the analogy breaks down: in a real canteen, an uneven load across counters is only a matter of experience, whereas an uneven expert load in MoE is a hardware problem — each expert lives on a different GPU, the busiest GPU decides how long the whole batch takes, and every other GPU sits waiting. Also, a student's satisfaction is fixed, whereas the routing scores s are learned by the model itself and change at every step.
Take m = 12 tokens, n = 8 experts, k = 2. (a) Exactly how many tokens does each expert serve? (b) What happens if you change m to 10? How does the paper handle that case? (c) In real training, m is the number of tokens in a whole training step (on the order of a million) — how much does this divisibility assumption matter?
Variant: if you change the expert-side constraint from the equality Σi xi,j = mk/n to the inequality Σi xi,j ≤ mk/n, does the divisibility problem still exist? What does the feasible set become? (This change is exactly what BIP, which the paper mentions, does; 14.6 will show the other consequences it brings.)
14.3 Appendix C, part two: relaxation and duality — trading mn switches for m+n thresholds
Eq. 20 has one fatal practical difficulty: xi,j can only be 0 or 1. Optimisation problems of this kind, where “the variables can only take integer values”, are called integer programming(integer programming), and in general they are notoriously hard to compute — you cannot differentiate, and you cannot “move a little way in the gradient direction”, because there is no “a little way” between 0 and 1. And the scale here is m × n ≈ a million × a thousand = a billion 0/1 variables, to be solved once per training step.
Step one: relaxation
Linear relaxation: loosen “can only be 0 or 1” into “can be any real number between 0 and 1”. After the loosening, the problem becomes a linear program(linear program, LP) — the objective and every constraint are first-degree expressions in the variables. Linear programming is the most thoroughly studied class of optimisation problem there is, with mature and efficient methods for solving it.
The usual price of relaxation is this: the optimum after loosening may be fractional (xi,j = 0.37, say — “the token goes 37% to this expert”), which means nothing in practice, and the optimal value comes out higher than the original problem's (the range of choices got bigger), so all you have obtained is an upper bound. But not here. The paper says:
By the standard total unimodularity of the bipartite b-matching polytope, the optimal solution of this LP is integral; the relaxation is therefore exact.
What “total unimodularity” is for (you can take this step's conclusion on trust)
Total unimodularity(total unimodularity) is a property of the constraint matrix. Roughly: if the constraints of a linear program are written in the form Ax = b, and the matrix A has this property (every square submatrix of it has determinant 0, +1 or −1), and b is all integers, then the optimum of that LP automatically lands on integer lattice points — you do not have to add a requirement that it “must be an integer”, it already is.
Why do the constraints of Eq. 20 have this property? Because their structure is bipartite matching: tokens on one side, experts on the other, and xi,j is “whether this edge is drawn”. In problems of this kind, each column of the constraint matrix has exactly two ones (one from a token row, one from an expert row), and this “two ones per column, splittable into two groups” structure is the classic example of total unimodularity. You can take the conclusion on trust for now; understanding where it sits in the whole chain is enough: its place is this — it makes all the continuous mathematics that follows, all the differentiating and hunting for minima, legitimate, and it costs nothing at all. Without it, the solution we compute could be a pile of fractions that does not match the original 0/1 problem.
If you want an intuition: a “fractional solution” on a bipartite graph can always be decomposed into a number of alternating paths and cycles, and pushing weight around a cycle towards one end turns fractions into 0 or 1 without lowering the objective; keep pushing and you get an integer solution no worse than the one you started from. So the optimal value is always attained by some integer solution.
Step two: introduce multipliers, write it as max–min
Now for the constraints. The core idea of the method of Lagrange multipliers(Lagrange multiplier) is this: rather than treating a constraint as a wall, turn violating it into a fine. Give every constraint a “price”, fine harder the more it is violated, then let your opponent pick the prices, and let them pick until there is nothing in it for you.
Concretely: give each of the m token-side equality constraints a free multiplier αi, give each of the n expert-side equality constraints a βj, and subtract the constraint “violation” times its multiplier from the objective:
| Symbol | What it is | Intuition |
|---|---|---|
| αi | The multiplier on token i's constraint, which may be positive or negative (that is what “free” means) | token i's “threshold price”: a score that does not clear it need not hope to be selected |
| βj | The multiplier on expert j's constraint | expert j's “congestion charge”: the more sought-after, the higher the fee |
| (Σj xi,j − k) | How much the token-side constraint is violated by; 0 when the constraint holds | “how many queues too many or too few this person joined” |
| maxx minα,β | The inner α, β take the minimum first, then the outer x takes the maximum | you move first (pick x), and your opponent sets prices with your move in view (picks α, β) to push your payoff as low as it will go |
Why is this max–min equivalent to Eq. 20? Because the inner min is your opponent punishing you for breaking the terms: as soon as the violation of some constraint is not 0, the opponent can pull the corresponding multiplier off to infinity in the appropriate direction and smash your objective down to minus infinity. So the only sane choice for you (the outer max) is to make every violation equal 0 — that is, to satisfy the constraints honestly; and once you do, both penalty terms vanish entirely and the objective falls back to the original Σ x s. That is the whole magic of the method of Lagrange multipliers.
Step three: swap max and min
The paper goes on: the objective is linear in x, α and β and the feasible set is convex, so the minimax theorem permits exchanging the order of optimisation, giving Eq. 22:
| Symbol | What it is | Intuition |
|---|---|---|
| si,j − αi − βj | The paper calls it the margin(margin): what is left of the score after the token's threshold price is deducted and then the expert's congestion charge | “after both charges are paid, is this deal still worth doing” |
| k Σi αi | The term that comes out of tidying up −Σiαi·(−k) in Eq. 21; independent of x | fixed “entrance-fee income” |
| (mk/n) Σj βj | Likewise, the constant term on the expert side; independent of x | fixed “stall-rent income” |
The minimax theorem: under suitable conditions (the objective is concave in one side's variables and convex in the other's, the feasible sets are convex, and one side's is compact as well), the two ways of playing — “you move first and I price afterwards” and “I price first and you move afterwards” — give the same optimal value. That is what licenses writing max min as min max. This step's conclusion can likewise be taken on trust: its place in the chain is this — it lets us fix the prices first and look at the assignment second, which turns the inner problem into a simple one that separates term by term. Intuitively it holds because with a bilinear objective and a convex feasible region there is no “informational advantage”: it makes no difference who moves first.
Derive it yourself: from the inner maximum of Eq. 22 to the max(0, ·) of Eq. 23
Look at the inner part of Eq. 22: maxx∈[0,1] Σi,j xi,j(si,j − αi − βj). Note that α and β have already been fixed by the outer part. Inside this sum, do the various xi,j still hold one another back?
Think it through first, then look
Not any more. This is the pivotal turn of the whole derivation: the constraints have been “bought out” by the multipliers, and the inner part no longer contains a single equation tying different xi,j together. So this max is separable(separable) — a billion variables each mind their own business and can be maximised one at a time. Why would anyone think of this step in the first place? Because the point of the method of Lagrange multipliers has always been “trade prices for the coupling constraints”, and once the trade is done you always check whether the coupling really is gone; it is a standard move.
So look at a single term: x·c, where c = si,j − αi − βj is a known number and x can be anything in [0, 1]. What value of x makes x·c largest?
Think it through first, then look
Two cases: when c > 0, the larger x the better, so x* = 1 and the maximum is c; when c < 0, the smaller x the better, so x* = 0 and the maximum is 0. This is exactly the paper's sentence “x*i,j = 1 when si,j − αi − βj > 0, and 0 when < 0”. Note one extremely important by-product: although we relaxed x to [0,1], the optimum lands automatically on the endpoints 0 or 1 — a fractional solution never appears at all. That lines up with the total unimodularity conclusion above.
What if c is exactly equal to 0?
Think it through first, then look
Then any x in [0,1] gives the same objective (0 in every case) and the optimum is not unique. The paper deals with it in one clause: ties have measure zero in practice. In plain words: s, α and β are all continuously valued floating-point numbers, and the probability that “three numbers cancel to the last digit” is 0, so engineering can ignore it. But this is a soft spot worth remembering — if the routing scores throw up a lot of ties (because they were quantised to very few levels, say), the assumption stops holding, and the defend-it box at the end of 14.4 comes back to this point.
Add up the maximum max(0, c) of every term, bring along the two constant terms in Eq. 22 that do not depend on x, and what have you got?
Think it through first, then look
Precisely Eq. 23: Σi,j max(0, si,j − αi − βj) + kΣiαi + (mk/n)Σjβj. Note how writing max(0, ·) merges the two cases of the previous step into a single expression: when c > 0 it equals c (corresponding to x* = 1), and when c ≤ 0 it equals 0 (corresponding to x* = 0). A billion 0/1 variables have disappeared, leaving only m + n real variables α and β. That is the entire payoff of duality.
| Symbol | What it is | Intuition |
|---|---|---|
| L(α, β) | What the paper calls the convex dual objective(convex dual objective), depending only on α and β | “the total cost of a pricing scheme”, the lower the better |
| := | The definition symbol: “the notation on the left is defined to be the expression on the right” | giving it a name |
| max(0, s − α − β) | Counts only the part where the margin is positive; anything negative counts as 0 | “count only what the deals that actually happened earned” |
| convex | The graph of the function is like a bowl: the line between any two points is never below the function itself | there is only one “lowest point”, so walking downhill is certain to reach the global optimum |
Convexity is a free gift here: max(0, ·) is a convex function, a pile of convex functions added together is still convex, and adding a linear term keeps it convex. Convex means there is no “false bottom” — you will not walk into a local minimum and get stuck there. This guarantees that the “walk downhill alternately” algorithm in the next section converges to the true optimum.
At the optimum of Eq. 23, token i is assigned to expert j if and only if si,j > αi + βj. Use the canteen analogy to explain what roles αi and βj each play, then answer: if some expert is especially sought-after (many tokens give it high scores), is its βj pushed up or pushed down? Why is that direction the right one?
For a sought-after expert, βj is pushed up. The reason: increase βj by δ, and every currently positive term in Σ max(0, ·) drops by δ, a total drop of δ×(the number of tokens currently selected), while the linear term rises by δ×(mk/n). So the rate of change of L is (mk/n) − (the current load), which is exactly the subgradient in Eq. 27. When the current load far exceeds the target load this rate is negative — raising βj lowers L — so the optimisation naturally pushes it up. Once βj has been pushed up, the criterion si,j > αi + βj becomes harder to satisfy, fewer tokens arrive, and the load comes down. This direction is obviously the right one: congestion raises the price, the price diverts the traffic, until supply and demand balance.
The other way round, a neglected expert (load below target) has a positive rate of change, and its βj is pushed down, even pushed negative — a negative congestion charge is a subsidy, actively pulling tokens in. Being able to go negative is crucial, and 14.6 will show that this is precisely where BIP loses.
Variant: change the objective of Eq. 20 from “maximise the total score” to “maximise the score of the lowest token” (a maximin), and can the whole Lagrangian apparatus still be used? Is the inner max still separable? (Hint: a min is not a sum; it couples all the terms back together again.)
14.4 Appendix C, part three: where the quantile comes from
Now for the prettiest stretch of all. You are going to minimise the L(α, β) of Eq. 23. The paper's method is called exact coordinate minimization(exact coordinate minimization): fix β and solve for the best α, then fix α and solve for the best β, alternating back and forth. The pattern itself is ordinary (think of solving a pair of simultaneous equations by “treating one variable as known” first); what is genuinely special is that — every subproblem here has an exact closed-form solution, with no iteration and no learning rate, one jump straight to the bottom.
Closed-form solution: an answer you can write down directly as a single formula, rather than “run a loop and creep up on it”. A quadratic equation has a formula for its roots, and that is a closed-form solution.
With β fixed, the only part of L that still contains αi depends on row i alone, so the problem decouples completely across tokens — the m tokens each solve their own one-dimensional problem. For token i:
| Symbol | What it is | Intuition |
|---|---|---|
| α | the subscript i has been dropped here; this is the threshold price of the token you are looking at, the only unknown | the one knob to turn |
| si,j − βj | n known numbers: this token's score for each expert, minus that expert's congestion fee | n “price-tagged candidates”, laid out in a row |
| kα | the linear term, with constant slope k | “every notch you raise the threshold costs you k times as much” |
| Σj max(0, · − α) | add up the excess of every candidate that sits “above the threshold” | the lower the threshold, the more excess there is, and the bigger this term |
The paper then gives the key judgement: this objective is piecewise linear in α, with slope k minus the number of margins above α; the minimum is therefore attained when exactly k margins lie above α. That sentence carries a lot, so walk through it on a concrete example.
Derive it yourself: why the minimum sits between the k-th largest and the (k+1)-th largest
Concrete set-up: k = 2, one token with n = 5 experts, and the five “price-tagged candidates” si,j − βj in descending order are 0.9, 0.7, 0.4, 0.2, 0.05. Write g(α) = 2α + Σj max(0, vj − α).
First compute g(1.0), g(0.9), g(0.7). What do you notice?
Think it through, then look
g(1.0) = 2.0 + 0 = 2.0 (α is above every candidate, so all the max terms are 0). g(0.9) = 1.8 + 0 = 1.8. g(0.7) = 1.4 + (0.9−0.7) = 1.6. From 1.0 down to 0.9 the function value drops by 0.2 while α drops by 0.1, so the slope is 2; from 0.9 down to 0.7 the function value drops by 0.2 while α drops by 0.2, so the slope is 1. The slope changed.
Why does the slope go from 2 to 1? Say exactly which term “wakes up” as α crosses 0.9.
Think it through, then look
When α drops from 1.0 to below 0.9, the term
max(0, 0.9 − α)goes from “identically 0” to “equal to 0.9 − α”. That term's slope in α is −1. The total slope used to be the +2 contributed by the linear term alone; add this −1 and it becomes +1.
The general rule: every time α crosses a candidate value on the way down, one more term is “activated” and the total slope drops by 1. So at any position, slope = k − (the number of candidates strictly above α). That is exactly the paper's “slope is k minus the number of margins above α”.Use that rule to list the slope on every interval. Which stretch has slope 0?
Think it through, then look
α > 0.9: 0 above, slope 2−0 = +2.
0.7 < α < 0.9: 1 above, slope +1.
0.4 < α < 0.7: 2 above, slope 0. ← flat
0.2 < α < 0.4: 3 above, slope −1.
0.05 < α < 0.2: 4 above, slope −2.
α < 0.05: 5 above, slope −3.
The slope is monotonically non-decreasing as α grows (+1 every time you cross a candidate), which is exactly the signature of a convex function.For a convex piecewise linear function, where is the minimum? Match the intervals above to “the n-th largest”.
Think it through, then look
The minimum of a convex function is where the slope turns from negative to non-negative. Here the whole stretch on which the slope is exactly 0 is [0.4, 0.7]; every point on it is a minimiser, with minimum value g = 1.6 (check it yourself: g(0.4) = 0.8 + 0.5 + 0.3 = 1.6, g(0.7) = 1.4 + 0.2 = 1.6 — equal, as claimed).
And 0.7 is the k = 2nd largest candidate, 0.4 the (k+1) = 3rd largest. So: the minimisers are any α between the k-th largest and the (k+1)-th largest — exactly what the paper means by “the minimum is attained when exactly k margins lie above α”.The paper says “by convention we take the (k+1)-th largest entry”. Since the whole stretch [0.4, 0.7] consists of minimisers, why single out the leftmost point, 0.4? The convention is not an arbitrary pick — think about the criterion s − α − β > 0 in 14.3 being a strict inequality.
Think it through, then look
Take α = 0.4 (the 3rd largest): the candidates strictly greater than α are 0.9 and 0.7, exactly 2 of them = k; 0.4 itself is equal to α, and under a strict-inequality criterion it is not selected. Take α = 0.7 (the 2nd largest): the only candidate strictly greater than it is 0.9, just 1 = k−1, not enough to fill the quota.
So what the convention does is: make the rule “strictly greater than 0” pick out exactly k experts, not one more and not one fewer, which is precisely the token-side constraint Σj xi,j = k. The paper writes only “by convention” and explains none of this, but this is the real reason it has to be taken that way.Last step: why is “the (k+1)-th largest” the same thing as “the (1 − k/n) quantile”?
Think it through, then look
The quantile quantilep(v) means “sort v from smallest to largest and take the number at the position a fraction p of the way along the sequence”. Among n numbers, the fraction p = 1 − k/n corresponds to the p·n = n − k-th position counting up from the smallest. And “the n−k-th counting up” is “the n − (n−k) + 1 = k+1-th counting down”.
Check it on the example: n = 5, k = 2, p = 1 − 2/5 = 0.6, position 0.6 × 5 = 3, and the 3rd from the bottom is 0.4 — which is exactly the 3rd largest. It matches. So the word “quantile” here is not a loose statistical description; it is simply another way of saying “the n-th largest”.
| Symbol | What it is | Intuition |
|---|---|---|
| si | the i-th row of the score table: token i's scores for all n experts | read along the token axis |
| si − β | the whole row minus the vector of expert thresholds, giving n margins | what is left once the congestion fee is deducted |
| quantile1−k/n | the (1 − k/n) quantile, equivalently the (k+1)-th largest | “the k+1-th counting down from the top” |
| αi* | token i's optimal threshold price | the line that catches exactly k experts |
The expert side is completely symmetric. With α fixed, expert j solves minβ (mk/n)β + Σi max(0, si,j − αi − β). The very same piecewise linear argument: the slope of the linear term is now mk/n, and there are m candidates (the margins of every token in this column), so the minimum is the (mk/n + 1)-th largest entry. And the (mk/n + 1)-th largest of m numbers is exactly the m − mk/n = m(1 − k/n)-th counting up from the smallest — the (1 − k/n) quantile again:
| Symbol | What it is | Intuition |
|---|---|---|
| s:,j | the j-th column of the score table: all m tokens' scores for expert j. The colon means “take everything along this dimension” | read along the expert axis |
| s:,j − α | the whole column minus the vector of token threshold prices, giving m margins | “how much net willingness each person has left for this counter” |
| βj* | expert j's optimal threshold (its congestion fee) | the line that catches exactly mk/n tokens |
Algorithm 1 is just the solver that alternates these two, and it is only six lines: initialise β ← 0; loop T times, each time first sorting s − β in descending order along each row and taking column k+1 as α (this is Eq. 25), then sorting s − α in descending order along each column and taking row mk/n+1 as β (this is Eq. 26); finally return xi,j = 1 if and only if j is in the Top-k of si − β. Note the index notation in the algorithm, [:, k:k+1] and [mk/n : mk/n+1] — those are slices counting from 0, and what they take is exactly “the k+1-th largest” and “the mk/n+1-th largest”, in complete agreement with what was derived above.
A token's margins si,j − βj against 6 experts are, in order, 0.81, 0.12, 0.55, 0.73, 0.30, 0.44, and k = 2. Compute αi* from Eq. 25. Then verify: does the criterion “margin > αi*” pick out exactly 2 experts, and are they exactly the Top-2?
Now the criterion: the margins strictly greater than 0.55 are 0.81 and 0.73, exactly 2 of them, and exactly the Top-2. 0.55 itself does not count (strict inequality), which is precisely what the “take the (k+1)-th largest” convention is for.
Worth feeling out as well: if you wrongly took the k = 2nd largest (0.73), only 0.81 would be strictly greater than it, only 1 expert would be selected, and the token-side constraint would break.
Variant: same set of numbers — if k = 4, what is α*? And a more interesting question: if two of the six margins are tied (say you change 0.44 to 0.55 as well), does “exactly k get selected” still hold? Which assumption that the paper waves through does this correspond to?
Defend it: if I were a reviewer
Your derivation chain has a string of “mild technical assumptions” hanging off it: mk/n divides exactly, ties have measure zero, the minimax theorem lets you swap the order, the relaxation is exact by total unimodularity. But in real training m varies at random, the scores are low-precision floats (bf16 has only 8 mantissa bits, so ties are far more common than you think), and s changes at every step — you have not “solved any of these problems to optimality” at all, you have only taken a few steps on a target that keeps moving. What gives you the right to still call it “exact coordinate minimization”?
A reference defence (put it in your own words first, then look)
Answer in three layers.
Layer one: concede the part that stands. The word “exact” modifies the subproblem, not the whole training process: given the current batch's s and the current β, the α that Eq. 25 gives really is the exact minimiser of Eq. 24 — not an approximation, not one gradient step. That wording is not overstated. As for whether the whole thing converges to a global optimum, the paper never claimed it did — what it claims is “balance within a few steps”, which is an empirical statement about speed.
Layer two: take the assumptions one at a time. The divisibility assumption only affects how tidy the notation in the derivation is; the implementation handles non-integer ranks by interpolation (Appendix D explicitly does a clipped interpolation), so it is not a problem in practice. The tie objection is the most damaging of the lot: under bf16 the routing scores really can repeat in large numbers, and then “exactly k are strictly greater than α” fails and fewer than k experts may be selected. But look at which direction the consequence runs: it would only cause “some tokens picked too few experts”, and the actual implementation computes the threshold and then does a standard Top-k (line 6 of Algorithm 1 is argtopk, not threshold filtering); Top-k has a deterministic tie-breaking rule, so ties do not break “exactly k per token”, they only make it slightly arbitrary which one gets picked. What ties really affect is the precision of the load on the expert side, and that is a deviation of O(number of ties).
Layer three: hand over the real weak spot. The fact that the target keeps moving cannot be defended as “it is still exactly optimal”, only as “that is the design intent”: QB was never trying to solve a static assignment problem, it is trying to supply a threshold that recalibrates itself at every step for a score distribution that keeps drifting. Its value lies precisely in landing in one jump each step, which is how it keeps up with the drift — if it needed dozens of steps to converge, the scores would have moved long before. The paper itself gives no measure of “the gap between the QB solution and the true optimal assignment”, and that is an experiment you could reasonably ask it to add.
14.5 Appendix C, part four: throw away α, keep only β — the most important sentence in the chapter
At this point you have two sets of thresholds, α and β. But when you deploy a model, you cannot go and solve a quantile all over again at every inference step — a user's one-sentence question is only a few dozen tokens, so where would the “distribution over a whole batch of tokens” to take a quantile of come from? If QB depends on quantiles during training and cannot compute quantiles at inference, then the routing behaviour of training and inference will not match and the model will fall apart on the spot. This subsection answers exactly that worry, and the answer is beautiful in a slightly unexpected way.
The paper's argument is only two sentences: at the optimum of Eq. 23, x*i,j = 1 if and only if si,j − αi* − βj* > 0; combine that with the token constraint Σj x*i,j = k, and the selected experts are exactly the Top-k of si − β*.
Why does the second sentence hold? Rearrange the criterion: si,j − βj* > αi*. Notice that the left-hand side depends only on the expert, while on the right αi* is one and the same number for all of this token's experts — it is a horizontal line. “The entries above one and the same horizontal line”, plus “exactly k of them are above the line” — what is that if not Top-k? The entire job of αi is to draw that line; and once you know “k are to be picked”, the actual height of the line need not be known at all — just sort and take the first k.
Why this asymmetry is where the whole derivation lands
Routing therefore needs only the expert thresholds β ∈ Rn (equivalently the bias b = −β of Eq. 13), while the token thresholds α ∈ Rm are an intermediate variable tied to the dynamic training batch, and can simply be thrown away. The paper's own word is “discarded”.
Why can they be thrown away? Look at the lengths of the two vectors and it is obvious: β holds only n numbers (the number of experts, about 103), and it is a model-parameter-level object that travels with the model; α holds m numbers (the token count of this batch, in the millions), and it travels with this batch of data, going completely stale the moment you switch batches — it is not part of the model at all. The conclusion of the derivation is exactly this: the one you need to keep is the one you can keep.
The paper calls this property train–inference consistency(train–inference consistency): at deployment, routing is one fixed operation — “subtract a frozen bias from the scores, then take Top-k” — and needs no quantile computation whatsoever. All that elaborate quantile machinery from training leaves behind a residue of just n numbers.
A synthesis question. Suppose someone proposes a “more symmetric” routing scheme: at deployment you keep both α and β, and for each token you filter with its own αi (selecting every expert that satisfies si,j − βj > αi) instead of taking Top-k. Answer three things: (a) where does αi come from at inference? (b) if you force the issue and use the average of some α saved at the end of training as a global threshold, what goes wrong? (c) why does the paper's scheme sidestep all of this? Tie your answer to the original constraint “every expert serves exactly mk/n tokens”.
(a) At inference αi can in fact be computed on the spot — Eq. 25 needs only the n margins in this token's own row, so a single token is enough and no whole batch is required. The scheme is therefore not computationally impossible. This is a detail worth getting straight: α is discarded not because it cannot be computed, but because there is no need to compute it — since its only job is to draw the line that “catches exactly k”, and Top-k accomplishes the same thing directly, storing m extra numbers and doing an extra sort-and-index is pure waste.
(b) Swap in a fixed global threshold (the average of all the αi, say) and the consequence is that the number of experts a token selects is no longer fixed: the common word's highest score is only 0.4, so not a single expert clears the line and it gets routed to 0 experts (degenerate output); the code fragment has two scores of 0.95 and might clear the line with 5 or even 10. The compute swings wildly with the input content, you cannot pre-allocate GPU memory and communication buffers for a batch, and the inference system becomes very hard to build. More fundamentally, in training every token gets exactly k experts while at inference the count becomes variable, so the input distribution the model sees does not match the one it was trained on.
(c) The paper's scheme sidesteps all of this because it encodes the hard constraint “pick k” into the operation itself (Top-k is k by construction), rather than realising it indirectly through a threshold. Back to the original constraints: Eq. 20 has two of them, the token-side Σj x = k and the expert-side Σi x = mk/n. The token-side one is satisfied by the Top-k operation itself, so its multiplier α need not be kept; the expert-side one is satisfied naturally by no operation at all (it is a global property across tokens), so its multiplier β has to stay behind as a bias. The difference in the fates of the two multipliers comes down to one constraint being “a property of each row” and the other “a property of each column”, while inference happens one row at a time.
One thing more: the Expert Threshold routing the paper mentions at the end goes down exactly path (b) (it maintains an EMA threshold and allows the number of experts selected per token to vary), and the paper says explicitly that K3's approach is “related but different”.
Variant: turn it around — is there a model architecture in which the “expert-side constraint” would also be satisfied naturally by an operation on a single token, so that even β need not be kept? (Hint: think about what happens if a token's k experts are required to be “one from each expert group”; in the literature this is called grouped routing. What does it cost?)
14.6 Appendix C, part five: QB and the “sign update” are two ways of walking the same objective
This subsection connects QB to the auxiliary-loss-free balancing(auxiliary-loss-free balancing) you met in the earlier chapters. That method is very plain: if an expert's load is over target, subtract a fixed small amount from its bias; if it is under, add a fixed small amount. Direction only, never by how much. It works but it looks crude, and Appendix C gives it a precise place.
The paper takes the (sub)gradient of the expert-side subproblem that sits under Eq. 26:
| Symbol | What it is | Intuition |
|---|---|---|
| ∂L/∂βj | the partial derivative of the dual objective with respect to expert j's threshold | “if I raise this expert's congestion fee a little, how does the total cost move?” |
| χ(condition) | the indicator function(indicator function): 1 when the condition holds, 0 otherwise | a counter that only ever answers “yes/no” |
| Σi χ(…) | count how many tokens have a positive margin, which is the number of tokens actually routed to expert j | observed load |
| mk/n | the number of tokens each expert ought to serve | target load |
| subgradient | the stand-in for a derivative where a function has no unique one at a “kink”: the slope of any straight line that “props the function up from below” | max(0,·) has a sharp corner at 0, and this fills in the definition at the corner |
Read Eq. 27 out loud and it is plain speech: gradient = target load − observed load. That is the “load error” itself.
Now take one step of SignSGD: a variant of gradient descent that uses only the sign of the gradient, not its magnitude, moving a fixed distance each step. Apply SignSGD to Eq. 27 and the update you get is “if the load is over target, push βj up by a fixed amount; if it is under, push it down by a fixed amount” — which recovers exactly the fixed-step sign update of auxiliary-loss-free balancing (up to a sign convention, since b = −β).
What this unified view explains
The paper says explicitly that this view answers, at one stroke, two questions that until now could only be explained by “it works well in practice”:
First, why QB needs no learning-rate-like hyperparameter. A sign update has to pick a step size — too big and it oscillates back and forth, too small and it never gets balanced, and which step size is right depends on the number of experts, the batch size and the score distribution. QB does not “step” at all: it jumps straight to the exact coordinate minimiser of the same dual objective. Since it lands in one move, the question of “how far to move” never arises. The sign update keeps only the direction of the load error; QB uses all of the information in that error.
Second, why QB reaches balance within a few updates even with close to 103 experts. A sign update moves one fixed little notch per step, so if some expert's bias is far from target it takes many steps; and the more experts there are, the further out “the most extreme one” is, and the more steps are needed. QB puts every expert exactly in place at every step, and the number of experts barely affects it — the iterations that remain are only there to deal with “α and β holding each other in check”.
Compared with BIP: why “you can only push down, never lift up” is fatal
The paper goes on to say that QB is related to BIP. BIP solves the same assignment problem, but with inequality constraints: Σj xi,j ≤ k and Σi xi,j ≤ mk/n. The change looks tiny — “at most k” instead of “exactly k”, which even sounds more permissive — but it sets off a chain reaction:
Inequality constraints induce non-negativity constraints on the multipliers. (This is a standard result of Lagrangian duality: the multiplier of an equality constraint is free to be positive or negative, while the multiplier of a ≤ inequality constraint must be ≥ 0. You can accept this conclusion for now; the intuition is that an inequality only bites when you are “pressed against the ceiling”, so the fine can be charged in one direction only, never paid back.) So both of BIP's updates have to end with a max(0, ·) clip that cuts any negative value down to 0.
The paper's judgement on the consequence is blunt: this can only suppress over-selected experts and cannot promote neglected ones, which in their experiments noticeably slowed the balancing process. Recall the conclusion of that exercise in 14.3 — a neglected expert needs a negative βj (a negative congestion fee = a subsidy) before it can pull tokens over. BIP cutting negatives down to 0 amounts to abolishing the subsidy as an instrument, leaving only “raise the price on the crowded ones”.
Construct a small concrete scenario (pick your own m, n, k and a set of scores) in which “raise the price on crowded experts” on its own cannot reach balance in one step, while allowing negative biases can. Then answer: can BIP balance in the end? If it can, why does the paper say it “noticeably slowed” balancing rather than “cannot balance”?
token1: 0.90, 0.80, 0.01
token2: 0.85, 0.70, 0.01
token3: 0.60, 0.95, 0.01
With all biases 0 initially: token1→A, token2→A, token3→B. The loads are A:2, B:1, C:0, which is unbalanced.
Non-negative biases only (BIP's side): β sits on the “cost” side, so a non-negative βA can only push A's effective score down. Raise βA past 0.9 and both token1 and token2 leave A — but their second choice is B (0.80 and 0.70), not C (0.01). The loads become A:0, B:3, C:0, which is even less balanced. Push B down as well and they run back to A. C's score of 0.01 is last in every ranking, and pushing other people down will never bring its turn round — unless you push A and B both below an effective score of 0.01, meaning βA and βB both above roughly 0.8, which takes a great many fixed-size steps to accumulate (this is exactly the “noticeably slower”).
Negative biases allowed (QB's side): just set βC = −0.7 and C's effective score becomes 0.71, which immediately lifts it to 2nd in token2's ranking (0.85 / 0.70 / 0.71) and to 2nd in token3's (0.60 / 0.95 / 0.71). Combine that with a moderate price rise on A and one or two steps reach A:1, B:1, C:1. The quantile update of Eq. 26 computes this negative value directly, with no trial and error.
Can BIP balance in the end? It can, but only by the detour of “pushing every popular expert below the neglected one”. Because implementations usually centre the mean of the biases, pushing everyone else down is in effect equivalent to lifting this expert up — so it is reachable, just by a much longer path, over many fixed-size steps. That is why the paper says it “noticeably slowed the balancing process” rather than “cannot balance”. The difference is not whether you can get there but how many steps it takes; and in a training run whose distribution drifts at every step, moving slowly amounts to never catching up.
Variant: another relative the paper mentions is Expert Threshold routing, which maintains an EMA threshold and allows the number of experts selected per token to vary. Explain: if a variable count is allowed, at which step does the argument in 14.5 that “Top-k satisfies the token-side constraint by construction” break down? And what new trouble does that create for the inference system?
14.7 Appendix D: estimating a quantile of a million numbers with a thousand boxes
14.4 made clear which quantile QB has to take, 14.5 made clear what is left once you have taken it. Appendix D deals with the engineering problem wedged in between: that quantile cannot be computed directly at all.
Where is the difficulty? The QB update of Eq. 14 requires taking a quantile over the whole training step — for each of the n experts, the (1 − k/n) quantile of the margins si,j − αi. And m is a number of tokens on the order of a million, spread out across the many data-parallel ranks(data-parallel rank) and gradient-accumulation steps(gradient-accumulation step).
Data parallelism and gradient accumulation: when you train a large model, one “training step” has far too many tokens to fit on a single card. What you do is cut them into many pieces: horizontally, a share goes to each of hundreds or thousands of GPUs (data parallelism), and on each card they are cut vertically again into several small-batch forward passes (gradient accumulation). The consequence: the full set of tokens of one training step has never been in any one place at the same time.
To take a quantile exactly you must sort; to sort you must gather all the data in one place. The paper says: collecting O(mn) margins inside the training loop to get an exact quantile is impractical. For a sense of scale: m ≈ 106, n ≈ 103, which is 1 billion numbers, to be hauled across several hundred cards once per layer per step — more communication than the model's own gradients.
The key observation: what you want was never those numbers, it is their distribution
The paper's way out is one very plain sentence: the update never needs the margins themselves, only their per-expert distribution, and a histogram(histogram) can summarise that distribution at a fixed cost.
Histogram: cut the range of values into a number of boxes (bins, bin), then record only “how many numbers landed in each box”. The original million numbers are compressed into a thousand counts. You have thrown away the exact value of every number (all you know is which box it fell into), but you have kept the shape of the overall distribution — and a quantile needs nothing but the shape.
What the histogram is built over: the required bias r
There is a small conversion here. The paper does not build the histogram over the margins directly, but over the required bias(required bias):
| Symbol | What it is | Intuition |
|---|---|---|
| ri,j | “the bias that would put expert j exactly on token i's critical point” | “how much subsidy does this expert need before it just reaches this token's threshold” |
| αi | token i's cutoff (the threshold price), that is, the horizontal line of 14.4 | the height of the line you have to clear |
| −si,j | the score with a minus sign on it — it is exactly this minus sign that reverses the order | the higher the score, the less subsidy it needs |
Why this extra step? Because negating reverses the order of the margins, so the QB target b̂j of Eq. 14 is exactly the (k/n) quantile of r:,j — “take the upper tail” has turned into “take the lower tail”, and a cumulative count naturally counts from small to large, so a single sweep reads the answer off. At the same time the physical meaning of r is more direct: it is the value the bias itself should have, the units line up, and nothing has to be converted.
The binning range: why you need not worry about data running out of the boxes
The first question about any histogram is always “where do the boxes start and stop”. Set the range too small and a lot of data falls outside it and cannot be counted; set it too large and every box is too wide and the precision too poor. The paper gives a range that is exact, and adaptive at every step.
Derive it yourself: why every r lands in [bmin − 1, bmax + 1]
You know the routing score is the output of a sigmoid. So what is the range of si,j?
Think it through, then look
The range of a sigmoid is the open interval (0, 1). So si,j ∈ (0, 1). This one fact is the foundation of the whole bound — if the routing score used some other activation (a softmax multiplied by something, say, or simply the raw logit), the bound would not hold. The paper can give a clean bound because the architecture chose sigmoid.
Now look at αi. It is not just any number — recall 14.4: αi is the (k+1)-th largest of the biased scores in that row. So what form of quantity must αi itself be equal to?
Think it through, then look
αi is itself the biased score si,j′ + bj′ of some expert j′. This is an observation that is very easy to slide past and absolutely central: taking a quantile is “picking one out of the numbers you already have”, not “computing a new number”, so the result has to be one of the original data points. How would anyone have thought of this step in the first place? Because when you need a range for a quantity, the least effortful way is to find out “what it is put together from”, and then bound each part.
Let the smallest of the current biases be bmin and the largest be bmax. Derive the range of αi.
Think it through, then look
αi = si,j′ + bj′, where s ∈ (0, 1) and bj′ ∈ [bmin, bmax]. Smallest possible: s approaching 0 and b taking bmin, giving > bmin. Largest possible: s approaching 1 and b taking bmax, giving < 1 + bmax. So αi ∈ (bmin, 1 + bmax), which is what the paper has.
Last step: ri,j = αi − si,j. Subtract the two ranges. Note that subtraction flips a range round.
Think it through, then look
The smallest value of r occurs at “α smallest and s largest”: > bmin − 1. The largest value of r occurs at “α largest and s smallest”: < (1 + bmax) − 0 = bmax + 1. So ri,j ∈ [bmin − 1, bmax + 1]. That is the binning interval the paper gives.
The easiest mistake to make is “α smallest minus s smallest” — remember that the minus sign turns the range of the number being subtracted upside down.The length of the interval is (bmax − bmin) + 2. Cut it into B uniform bins; the bin width w = ? And why does the paper stress “recompute the range at every step”?
Think it through, then look
w = (bmax − bmin + 2)/B. The reason for recomputing every step: bmin and bmax change as training goes on — when the load is badly imbalanced the biases spread towards the two ends to correct it, and the interval widens with them; once things are balanced the biases pull back together, the interval narrows, the bins get finer and the precision improves by itself. The paper's way of putting it is that the bin width stays “adaptive as the biases spread to correct imbalance”. This is a free adaptive mechanism: it needs no schedule at all, the range is read off the current state.
Accumulation and recovery
The rest of it follows the structure of a training step exactly:
The accumulation phase (zero communication). In every forward pass, each rank scatter-adds its local ri,j values into a per-expert count matrix H ∈ Nn×B, accumulating across all micro-batches, with no communication needed. Scatter-add just means “work out which box each number belongs in, then add 1 to that box's count”. The shape of H is n rows (one per expert) × B columns (B boxes per row).
The reduction phase (one communication). At the end of the step, a single all-reduce adds up the local counts of all ranks element by element to give the global histogram; every rank recovers the quantile from the same pooled counts, so the biases all the ranks compute agree automatically, with no further synchronisation.
The recovery phase. Each expert's histogram counts each token exactly once, so the target rank is exactly the target load q = mk/n, only now taken over the whole training step. What you do is pick out the first bin at which the cumulative count reaches ⌈q⌉, and interpolate linearly inside that bin:
| Symbol | What it is | Intuition |
|---|---|---|
| b̂j | the estimated new bias of expert j | the answer this step is after |
| bmin − 1 | the left endpoint of the whole binning interval | the origin of the axis |
| βj | the index of the selected bin (counting from 0). Note: the β here and the dual variable β of Appendix C are two different things; the paper reuses the same letter | “which box is the answer in” |
| q = mk/n | the target rank (the target load) | “I want the q-th number counting from the smallest up” |
| cj | the cumulative count of all the bins before the selected one | “by the time I get to the door of this box, how many have I counted already” |
| hj | the count inside the selected bin | “how many are crammed into this box” |
| (q − cj)/hj | how many are still missing ÷ how many there are in this bin, giving a ratio between 0 and 1 | “roughly how far into this box does the target sit” |
| clip(·, 0, 1) | force the result into [0, 1] | a safety catch: if the ratio ever comes out of bounds (say hj is tiny and the division runs away), it is not allowed to leave this bin |
| w | the bin width (bmax − bmin + 2)/B | how much length one box stands for |
Read the whole formula out and it says: answer = left end of the interval + (whole boxes + the fraction within the box) × box width. Exactly like “reading a ruler”: first count the centimetres you have passed, then eyeball the remainder inside the centimetre. The bias you get is finally mean-centred the way Eq. 14 does it (subtract the average of all the biases); the point of this step is to remove the degree of freedom “add the same constant to every bias at once” — because Top-k only looks at relative size, and shifting everything together affects no selection at all.
Three properties
First, it is accurate. The paper's argument is: the cumulative counts are exact at the bin boundaries — what a histogram loses is the information inside a bin, the dividing lines between bins are exact, so the judgement “which bin does the q-th number fall in” carries no error whatsoever. Hence the true quantile and its estimate must land in the same bin, and the difference between the two is pinned down by the bin width w. At B = 1000 that is at most a few times 10−3, and the paper says no measurable residual load imbalance can be observed.
Second, it is cheap. The only communication is a single integer all-reduce over nB values per layer per step, independent of m. This is the crucial point: raise the token count from a million to ten million and the communication volume does not move an inch. The paper says that in their configuration this is under 1% of “exchanging the raw margins over the process group on every micro-batch” (which is the natural alternative).
Third, it estimates the right quantity. This one is the easiest to overlook and the deepest of the three: because counts are additive, the global histogram is exactly invariant to “how the tokens are sharded across ranks and accumulation steps”. That is, however you cut the tokens up and whoever you hand them to, the global histogram that comes out of the all-reduce at the end is identical. So what it estimates is the quantile of the pooled global batch(pooled global batch), and not the average of the per-rank quantiles — the paper goes out of its way to add that “the two are generally different”.
The paper ends with one refinement: taking an exponential moving average(exponential moving average, EMA) of the quantiles estimated across steps lowers the batch-to-batch sampling noise and improves load balance further.
(a) Suppose the extremes of the current biases are bmin = −0.4 and bmax = 0.6, with B = 1000. Compute the bin width w, and say how large the error of the quantile estimate can be at most. (b) Suppose n = 1000 and B = 1000; work out how many integers one all-reduce has to send; then compare with the “send the raw margins” scheme: with m = 106, how many numbers does that one have to send? (c) Use these two numbers to say whether the paper's “under 1%” is a conservative or an aggressive statement.
(b) Histogram: nB = 1000 × 1000 = 106 integers, once per layer per step. Raw margins: mn = 106 × 103 = 109 floating-point numbers, and the alternative the paper names exchanges them once per “micro-batch”, so you have to multiply by the number of micro-batches on top of that.
(c) The ratio: 106 / 109 = 0.1%, and that is before the extra factor of “per micro-batch vs per step”, and before the difference in width between “integer vs float”. So the paper's “under 1%” is a fairly conservative way of putting it — by this rough calculation the real ratio may be at 0.1% or lower still.
Note as well the tension between (a) and (b): to make the error smaller you have to raise B, and the communication volume is proportional to nB. B is a precision-communication trade-off knob, but because the communication volume is independent of m, this knob has a very wide range to move in — pushing B up to 10000 is still only 107 integers, far below 109.
Variant: if a violent load imbalance happens in the middle of training and the biases spread out to bmin = −5 and bmax = 5, what does w become? By what factor has the error grown? Does this set up a vicious circle of “the more imbalanced it is the worse the estimate, and the worse the estimate the more imbalanced it gets”? (Hint: think about whether “estimating accurately” still matters here — for an expert that is already badly off, is a bias that is out by 0.012 any different from one out by 0.003?)
The paper says the global histogram estimates “the quantile of the pooled global batch, not the average of the per-rank quantiles — the two are generally different”. Build a concrete numerical example proving that the two really are different: two ranks, 4 numbers on each rank, take the median (the 0.5 quantile). Make the gap in your example between “the average of the two ranks' own medians” and “the median of all 8 numbers together” as obvious as you can. Then explain: why this difference is not theoretical fastidiousness in real training, but something that genuinely goes wrong.
Example one (the simple version): rank A = {0, 0, 0, 0}, median 0; rank B = {10, 20, 30, 40}, median 25 (the average of the middle two). The average of the two ranks' medians = (0 + 25)/2 = 12.5. Merged into {0,0,0,0,10,20,30,40}, the median is the average of the 4th and 5th = (0 + 10)/2 = 5. 12.5 vs 5, out by a factor of 2.5.
Example two (a control, to show where the gap comes from): rank A = {1, 2, 3, 4}, median 2.5; rank B = {2.4, 2.5, 2.6, 2.7}, median 2.55. The average = 2.525. Merged into {1, 2, 2.4, 2.5, 2.6, 2.7, 3, 4} (8 numbers), the median = (2.5 + 2.6)/2 = 2.55. This time the two differ by only 0.025. Comparing the two examples shows that the gap depends entirely on how different the ranks' data distributions are — when the distributions are close the two are almost identical, and when the distributions are far apart they can differ by a factor of several.
Why it goes wrong in real training: the ranks of data parallelism each get a different shard of the data. If the sharding follows data source or language (rank 0 all Chinese web pages, rank 1 all code, say), then the distribution of “which experts are in heavy demand” can differ a great deal from rank to rank — expert A is red hot on the code shard, and nobody wants expert A on the Chinese shard. Compute a quantile on each rank and then average them, and the bias you get is right for nobody; it is the pooled global quantile that is “the bias this whole batch of data actually needs”. Worse, the average of the per-rank quantiles has no optimisation meaning at all — Eq. 26 asks for the quantile of the global set of m margins, the average of medians is not the median of the average, and it is not the solution to any optimisation problem.
The reason the histogram scheme gets this right for free is that counts are additive: add the two ranks' counts box by box and what you get is exactly the histogram you would have built by pouring both sets of data together, not one number more, not one number less. Whereas the operation “quantile” is not additive. Quantities that are additive can be merged correctly by an all-reduce; quantities that are not, cannot — that is a very valuable general principle in distributed computing.
Variant: which other common statistics are “additive”, and can therefore be merged safely by an all-reduce? Which are not? (Try: sum, count, mean, maximum, variance, median, mode. For the mean and the variance, think about what extra you have to send before they can be merged.)
Defend it: if I were a reviewer
You spent two pages in Appendix C arguing that one of QB's advantages is “no learning-rate-like hyperparameter”. But Appendix D opens by introducing a bin count B = 1000, and an optional EMA coefficient on top of that. Isn't this just moving the hyperparameter out of the optimiser and into the estimator? On what grounds do you call yourself hyperparameter-free?
A reference defence (put it in your own words first, then look)
Start by conceding the literal fact: B really is a hyperparameter, and so is the EMA coefficient. Saying “no hyperparameters at all” is inaccurate; the accurate statement is “no learning-rate-like hyperparameter”, and that is exactly the wording the paper uses — “no learning-rate-like hyperparameter”. That qualifier is not a retrofit after the fact, it is there in the original.
Then make clear the difference in nature between the two kinds of hyperparameter, which is the substance of the defence. What is bad about learning-rate-like hyperparameters is: (1) they decide whether it converges at all, and get one wrong and it oscillates or simply never arrives; (2) they have no monotone “bigger is better” or “smaller is better” relation, so you have to search; (3) the right value drifts with scale, with batch size and with the number of experts, so a change of configuration means retuning.
B is nothing like that: (1) it does not affect whether it converges, only the accuracy of the estimate, and the error has an explicit upper bound w = interval length/B; (2) it is monotone — the bigger B is the more accurate, with no risk that “turning it up makes things worse”, the only cost being a linearly growing communication volume; (3) since that volume is independent of m and small to start with, setting B far higher than you need costs almost nothing. A knob with an explicit error bound, monotone, and with negligible cost, is not the same kind of thing as a knob you have to find by trial and error. The paper says B = 1000 is “sufficient”, and puts the order of the error alongside it (a few times 10−3) plus the observation (no measurable residual imbalance); that is a statement you can check.
Finally, hand over the place that really should be pressed: the paper gives no ablation curve for B (the degree of load imbalance at B = 100 / 1000 / 10000), and gives neither the value of the EMA coefficient nor any quantification of what it buys — “can lower the batch-to-batch sampling noise and improve load balance further” is a purely qualitative sentence. A reviewer demanding both sets of numbers would be entirely reasonable.
14.8 Appendix E: why reserving E/R redundant experts per rank is enough
A change of subject, from maths to systems. The problem MoonEP has to solve is this: E experts are spread over R EP ranks(expert-parallel rank), each of which you can roughly picture as “one GPU responsible for a slice of the experts”, and each rank holds E/R experts locally. If the expert a token needs is not local, it takes cross-machine communication — which is slow. The way to ease that is to put some redundant experts(redundant expert) on a rank: copy a hot expert from another rank into the local one, so the work can be done locally and the communication is saved.
But redundant experts take up memory. The system designer has to know in advance: at most how many redundant-expert slots does each rank have to reserve? Reserve too few and you run short; reserve too many and it is pure waste. Appendix E is the answer to that question.
First, state the problem precisely
Write mr(P) for the number of redundant experts placed on rank r under plan P. For a given router output I (that is, the table of “which experts each token in this batch wants”), the planning goal is to minimise the maximum number of redundant experts on any one rank:
| Symbol | What it is | Intuition |
|---|---|---|
| I | the router output: which experts each token in this batch wants | “today's orders” |
| P | a plan: it decides which rank each token is computed on, and which redundant experts sit on each rank | “the production schedule” |
| mr(P) | the number of redundant experts on rank r under plan P | “how many extra sets of moulds this machine has fitted” |
| maxr | take the largest across all ranks | the weakest-link effect: memory has to be reserved for the worst machine |
| minP | pick the best out of all feasible plans | “the worst machine under the best schedule” |
| P* | a specific plan constructed inside the proof (it need not be optimal) | an example that is good enough |
| E, R | total number of experts; number of EP ranks; each rank holds E/R experts locally |
Why this bound is worth proving
Notice that the definition of M(I) contains a minP — a minimum taken over every possible plan. You cannot enumerate all the plans (the number of them is astronomical). And Theorem 1 says: however outrageous the router output is, there exists a plan under which the maximum number of redundant experts is no more than E/R. This is a guarantee that holds uniformly for every I, so the system can allocate memory statically against it and will never run short. The method of proof is construction: build one plan P* that is good enough, and the min can only be at least as good. That middle ≤ in Eq. 28 is exactly this logic.
The proof of Theorem 1: a construction that keeps “filling holes”
This is the proof in this chapter that a reader with no background can most easily follow, so we take it apart in detail. At its core is one lemma:
The key lemma: there exists a plan P* under which every EP rank receives exactly the same number of tokens (S × K), and each rank's remote tokens come from only one other EP rank.
The paper does not redefine S and K here
Appendix E uses the notation S × K straight off without saying what it means. From the context (“every EP rank receives the same number of tokens”, “all S×K×R tokens”) one infers that S should be the number of tokens on each rank and K the number of experts each token selects, so S×K is the target number of token-expert pairs per rank, and multiplying by R gives the global total. This is an inference, not something the paper defines explicitly — keep that in mind as you read.
Derive it yourself: the hole-filling construction, and “why each rank gets filled at most once”
The initial state: every rank handles only its own local tokens (which rank a token lands on depends on which machine it was read in on). Will the ranks be equally loaded at that point? If you were to split the ranks into two classes, how would you split them?
Think it through, then look
Of course they are not equal — on some ranks the tokens happen to use a lot of local experts, on others they do not. The paper's split: a rank whose load is above the balanced value S×K is overloaded, one below it is underloaded. Note one basic fact: the total is conserved, so if anything is overloaded something must be underloaded — they cannot all be overloaded. That fact gets used later.
Now carry out one operation: pick an underloaded rank and an overloaded rank, and migrate tokens from the overloaded one to the underloaded one. How many? The paper's rule here is the key.
Think it through, then look
Exactly up to the balanced value S×K, not one more and not one fewer. This is the soul of the whole construction: not “migrate half”, not “migrate until the overloaded one is exactly balanced”, but always take “fill the underloaded one right up” as the criterion. Why would anyone think of this step in the first place? Because what has to be proved in the end is “each rank's remote tokens come from a single source”, and that means every rank must be “handled” only once — settle it completely in one go and it never has to be touched again.
After the migration, what state does that overloaded rank end up in? How many possibilities are there?
Think it through, then look
Three: (i) still overloaded (it was over by a lot, and after filling this hole it still has surplus); (ii) exactly balanced (the amount it was over by is exactly the size of that hole); (iii) it becomes underloaded (the amount it was over by was not enough to fill the hole, so it got hollowed out too far).
Hold on — is case (iii) possible? If the overloaded rank is over by only 3 tokens while the underloaded rank is short by 10, migrating 10 across drops the overloaded rank below the balanced value. Yes, it happens. The paper says “put it back into the corresponding set accordingly”, which means reclassifying it into the overloaded set or the underloaded set and letting it take part in the later operations.Why must this process terminate? The paper says at most R − 1 fills.
Think it through, then look
Hold on to this sentence: each fill turns one underloaded rank into a balanced one, and it never changes again after that. Why “never changes again”? Because the later operations only pick underloaded ranks to fill and overloaded ranks to draw from, and this rank is now exactly balanced — it belongs to neither set, so it can never be selected again.
So every fill permanently removes one “unresolved rank”. There are R ranks in all, and the last one needs no separate treatment (once all the others are balanced, conservation of the total forces it to be balanced too), so at most R − 1 fills. This is a termination argument of the “monotonically decreasing counter” kind, the standard weapon for constructive proofs of this sort.Now the most important step: why does each rank get filled at most once? (Hint: line the two things up — “gets filled” and “becomes balanced”.)
Think it through, then look
Because the definition of the action “gets filled” is “gets filled to exactly balanced”. A rank is only chosen as a fill target while it is underloaded; the moment it gets filled it becomes balanced; and a balanced rank is never chosen as a fill target again (argued in the previous step). So for any rank at all, “gets filled” happens at most once.
Notice the asymmetry here: a rank can be drawn from many times (an overloaded rank can supply several holes in a row and still be overloaded after each one), but can only be filled into once. And the side we care about is exactly the “filled into” side — because redundant experts exist to handle the remote tokens a rank receives.To finish: given that all of rank r's remote tokens come from the single rank s, why is mr(P*) ≤ E/R?
Think it through, then look
Those remote tokens all come from rank s, so the experts they need are all local experts of s. And rank s has only E/R experts locally. So the number of experts rank r has to copy over is at most E/R (the worst case being that those tokens use every single expert on s). Hence mr(P*) ≤ E/R, and this holds for every r, so maxr mr(P*) ≤ E/R; then, since minP ≤ any particular plan, Eq. 28 follows.
Look back over the whole chain: all the force of the proof comes from that “single source” property, and “single source” comes entirely from the seemingly arbitrary rule “fill it exactly full every time”. That is the beauty of a constructive proof — one unremarkable design choice holds up the entire conclusion.
Take the total number of experts to be E = 896 and the EP size to be R = 16. (Note: the paper does not disclose the EP size R that K3 actually uses; the 16 here is a value this question sets for itself. E = 896 comes from the paper's architecture description — these few pages of the chapter only mention “close to 103 experts”.) (a) How many experts does each rank hold locally? (b) By Theorem 1, at most how many redundant-expert slots does each rank need to reserve? (c) By the tight bound of Theorem 2, ⌈E(R−1)/R2⌉, what is that number? (d) If you reserve according to the bound of Theorem 1, by what factor does each rank's expert memory grow at most?
(d) Each rank holds at most 56 + 56 = 112 experts, which is 2× what it holds with local experts only (56). This is a very important engineering conclusion: in the worst case, the bound of Theorem 1 means the expert memory doubles. But note that this is an upper bound, a guarantee of “never more than”; real router outputs are usually nowhere near this extreme, and actual usage is far lower.
The comparison between (c) and (b) is also worth taking in: 53 and 56 differ by only 3 (a gap of 5.4%), and that is exactly what Theorem 2 is saying — there exists a router output that forces you to have 53 redundant slots ready, so you cannot push the bound of 56 down below 53. Put another way, the cost of doubled memory is basically unavoidable; it is not that the analysis was too crude.
(While you are here, practise the trend as R grows: at R = 32, E/R = 28, while ⌈896 × 31 / 1024⌉ = ⌈27.125⌉ = 28 — the two are already exactly equal. This is what the paper means by “when R is large, E(R−1)/R2 ≈ E/R”.)
Variant: if you were the systems engineer and wanted to hold the redundant memory overhead to within 25% of the local experts, with E = 896 fixed, how should you choose R? What new problem does that choice bring? (Hint: E/R is an absolute count, and the local expert count is also E/R — so in the worst case this ratio is identically 100%, independent of R. To push it down you have to give up the “worst-case guarantee” and reserve on a statistical basis instead. Which shows that although the bound of Theorem 1 is tight, what it tells a real system is “either accept the doubling, or accept running short occasionally”.)
Theorem 2: this bound basically cannot be improved any further
Theorem 1 gives an upper bound. But upper bounds are things that can be very loose — “no human is taller than 100 metres” is also a correct upper bound, it is just useless. So the question has to be answered: is E/R a slack bound? Can a smaller one be proved? Theorem 2 says: no.
The method of proof is to construct a worst case. The paper builds a router output I* like this: the experts on EP rank 0 receive no tokens at all, while all the experts on the other R−1 ranks split every token evenly between them.
The chain of reasoning goes like this:
- All S×K×R tokens are split evenly among E(R−1)/R experts (because only the experts on R−1 ranks are taking delivery, E/R of them per rank). So each expert receives SKR ÷ [E(R−1)/R] = SKR2/[E(R−1)] tokens.
- Under any plan P, rank 0 must receive S×K tokens (the load has to be balanced), and all of them remote — because its own experts received not a single token.
- At the least, how many distinct experts do those SK tokens involve? Each expert can contribute at most SKR2/[E(R−1)] tokens, so you need at least SK ÷ SKR2/[E(R−1)] = E(R−1)/R2 distinct experts.
- The number of experts has to be a whole number, so round up: rank 0 needs at least ⌈E(R−1)/R2⌉ redundant experts. Hence M(I*) ≥ ⌈E(R−1)/R2⌉.
- In the other direction, building a plan with the filling process from the proof of Theorem 1 and migrating tokens expert by expert as a priority (that is, moving all of one expert's tokens across together as a batch wherever possible, rather than piecemeal) keeps the number of redundant experts on every rank within this value. So the two meet with equality.
Because for large R, E(R−1)/R2 = (E/R)·(1 − 1/R) ≈ E/R, the paper's conclusion is: there is no general upper bound significantly smaller than E/R.
A place that is easy to confuse
What Theorem 2 says is not “in real training every rank needs E(R−1)/R2 redundant experts”. What it says is “there exists a router output that would force that many out of you”. Those two sentences are very different: the first is about the typical case, the second about the worst case. In real training the routing has been balanced by QB (which is exactly what 14.2–14.7 was about), and it is far gentler than this manufactured worst case. The real use of Theorem 2 is to mark out “the limit of the analysis”: it tells you to stop trying to prove a tighter general upper bound, because that is futile; if you want a smaller reservation, the only way is to bring in extra assumptions about the routing distribution.
Someone challenges the construction in Theorem 2: “Having the experts on rank 0 receive not a single token is far too artificial. Let me modify it: have the experts on rank 0 receive very few but non-zero tokens, say 1% of what the other experts get. Then rank 0 does not have to rely entirely on remote traffic — can the bound be pushed down that way?” Analyse this modification and explain why it basically cannot save you. Then answer a more fundamental question: what structural fact is this construction really exploiting?
Work it out. Approximately, let the global SKR tokens be split evenly among the “normal” experts, so each normal expert gets about T = SKR/[E(R−1)/R] = SKR2/[E(R−1)] of them. Rank 0 has E/R experts, each receiving 0.01T, so the total local supply ≈ (E/R) × 0.01T = 0.01 × SKR/(R−1) ≈ 0.01 SK (for large R, R/(R−1) ≈ 1).
In other words the local side can supply about 1%, leaving 99% of S×K to come from remote. By the same count, the number of distinct remote experts needed is at least 0.99 × E(R−1)/R2. The bound has dropped by 1% and is otherwise untouched.
Why it cannot save you. Because the lever in this construction is not the extreme value “rank 0 is zero”, it is the mismatch “rank 0's local experts can absorb far less than the load it is obliged to carry”. Changing the 0 to 1% takes the mismatch from 100% to 99% — a change of quantity, nothing more. To genuinely push the bound down, rank 0's local experts have to absorb a substantial fraction of S×K — but then the routing itself is already close to balanced, and it is no longer a worst case.
The structural facts it is really exploiting are two, and both are needed:
(1) the load has to be balanced — this is a hard requirement of MoonEP, and it forces rank 0 to swallow S×K tokens no matter what; it cannot say “there is no work here so I will sit idle”.
(2) there is a ceiling on how much any single expert can supply — because the tokens were split evenly, no one expert can feed rank 0 on its own. This is a pigeonhole-style count: to hold SK tokens with each “hole” (expert) holding at most T, you need at least SK/T holes.
Put the two together and you get the lower bound. Note that (2) is what the “split evenly” assumption is for — if instead you let a handful of experts take the overwhelming majority of the tokens, rank 0 would only have to copy those few hot experts, and the redundancy count would be small. So “split evenly” is not about “fairness”; it is precisely there to manufacture the worst case: spread the demand thin across as many experts as possible, forcing you to copy as many experts as possible.
Variant: construct a best case the other way round — what kind of router output makes M(I) = 0 (not one redundant expert needed)? Then think: between that best case and Theorem 2's worst case, which end is real routing after QB balancing closer to, and why? (Hint: QB balances expert load, but that is a different thing from “aligning tokens with the positions of experts” — the paper does not discuss that layer.)
14.9 Appendix F: XTML — designing the chat format as an extensible markup language
The last appendix looks like the “softest” one, but it is in fact the section in the whole report that best shows engineering judgement.
Chat template: the model only knows one long string of tokens; it does not know “this is the system prompt, this is what the user said, this is what the tool returned”. The chat template is the set of rules that takes a structured conversation (who said what, which tools were called) and flattens it into a string of tokens, plus the rules that go the other way and parse the model's output back into structure. It is the interface protocol between the model and the outside world.
Three design goals
The paper says K3's chat template was redesigned around three goals:
Extensibility(extensibility): new capabilities should be introduced through backward-compatible message formats rather than by changing the template, so that a single template can serve an entire model generation. Why does this matter? Because the moment the template changes, every behaviour already trained in, every downstream parser, every cached context can become invalid. Only by decoupling “adding a feature” from “changing the protocol” can you keep adding things without tearing it all down and starting again.
Low alignment tax(low alignment tax): the format should be learnable from very little supervised data. The paper says this supports one particular pipeline: “a lightly fine-tuned pretrained model goes straight into reinforcement learning”. Put plainly — if the format is strange and hard to learn, you first have to teach the model to speak it using a large amount of hand-annotated data, and that stage is both expensive and damaging to the abilities learned in pretraining (that is the “alignment tax”); if the format is itself close to the natural language in the pretraining data, the model barely needs teaching, and you can skip most of the supervised fine-tuning stage and start RL directly.
Decoding friendliness(decoding friendliness): the structure should support simple encoders, streaming parsers (parsing while generating, without waiting for the whole segment to be produced) and grammar-constraint enforcers (forcing the model to generate only tokens that fit the grammar).
XTML: replacing angle brackets with reserved tokens
XTML: an XML-like markup language, but with XML's angle-bracket syntax replaced by three reserved special tokens: [open], [sep], [close], plus an [end_of_msg] as the generation stop marker.
An element is written [open]tag attr="value"[sep] … [close]tag[sep], and it and XML's <tag attr="value"> … </tag> are isomorphic (the structures correspond exactly), except that every structural boundary is an explicit special token. The paper says this removes the tokenisation ambiguity at element boundaries and simplifies constrained decoding.
What the “tokenisation ambiguity” problem actually is
What the model reads is not characters but tokens — fragments somewhere between a letter and a word. The tokeniser cuts a string into fragments by a set of statistical rules, and how it cuts depends on the context. The very same </think> may be cut into </ + think + > when a full stop comes before it, into </th + ink> when a newline comes before it, and into something else again when it follows Chinese text. So “the same structural boundary” turns into several different token sequences in the model's eyes, and the model has to learn every one of them, and none of them is entirely reliable.
Worse is confusion: if a user message already contains the characters </think> (say the user is asking a question about chat templates), the parser cannot tell whether this is structure or content — a classic route for prompt injection attacks.
Reserved special tokens solve both problems at once: [close] is always the same token id, whatever comes before or after it; and it cannot possibly be produced by encoding ordinary text (in the tokeniser's vocabulary, special tokens are held back separately, and no string is ever cut into one). So there is now an impassable wall between structure and content.
The benefit for constrained decoding is just as direct: to force the model to “close this element now”, you only have to push the probability of every token except [close] down to 0 at sampling time — an operation on one token id, rather than matching a multi-token string pattern.
Messages and regions: a KV-cache-friendly layout
The top-level unit of the context is the message, and messages fall into two kinds by origin:
Input messages(input messages) serialise the request's messages field, covering the four familiar roles system, user, assistant and tool.
Option messages(option messages) translate the request options into instructions the model reads in the context. Their position reflects their scope — the design point in this section most worth learning:
- Global options (tool declarations,
type="tool-declare", and the reasoning-effort setting) appear before all input messages. The paper gives two reasons: they govern the whole session and rarely change; and changing them invalidates the KV cache anyway. - One-shot options (
tool_choice,response_format) are appended after the input messages, so that per-request changes do not destroy the KV cache of the history. - Input option messages(input option message) are the third kind; they are interleaved with the input messages, and are used to add to or override a global option midway through a session.
KV cache: when a Transformer generates, every token it has already processed leaves behind an intermediate result (a key and a value). The next token only has to read these caches; it does not have to recompute everything before it. But a cache is only valid as long as “the prefix has not changed at all” — because the intermediate result at each position depends on all the tokens before it. The moment you change token t, every cache from token t onward is void and has to be recomputed.
What input option messages buy you is dynamically loaded tools(dynamically loaded tools): a tool retrieved or loaded midway through a session is declared by one extra tool-declare message, after which the set of tools available to the model has grown, and the earlier context does not need rebuilding. Think about what it would be like without this mechanism — all the tool declarations sit at the very front, so adding one new tool means changing that front segment, the KV cache of the entire context is void, and a session of several hundred thousand tokens has to be recomputed from scratch.
A customer-service system has three settings it needs to pass to the model: (i) the company's list of tools, changed twice a year; (ii) “this answer must be in JSON format”, which can differ on every request; (iii) halfway through a session, the customer-service system retrieves three new tools based on the user's question and needs to add them on the fly. Following the paper's scheme, say where in the context each of the three settings should go, and work out for each how much KV cache “changing it” voids (describe it roughly as “a fraction of the whole context”).
(i) A global option, at the very front. It is of type tool-declare and governs the whole session. Changing it voids almost all of the cache — but the paper's defence is that changes of this kind are extremely rare anyway (twice a year), and that “changing them invalidates the KV cache anyway” is unavoidable (once the tool list changes, the basis on which the model understands the whole history changes with it; even if you could keep the cache technically, semantically you should not). Low frequency + unavoidable = a legitimate reason to put it at the very front.
(ii) A one-shot option, after the input messages. It is response_format, and it changes from request to request. Sitting at the very back, changing it only voids that small tail segment (a few dozen tokens), and the several hundred thousand tokens of history in front of it stay intact. This is the most critical arrangement of the three: if you put it at the front, then switching the output format on each request would mean recomputing the whole context, and long multi-turn sessions would be dragged to a halt.
(iii) An input option message, interleaved at the current position. It is a tool-declare added midway. What gets voided is the part from the insertion point onward — and the insertion point is “now”, so nothing after it has been generated yet, which makes the amount actually voided almost zero. That is what “the earlier context does not need rebuilding” means: the new tools take effect from this moment on, and not a single token of the earlier history has to move.
Feel out the logic the three share: each kind is placed at “the furthest forward point its change frequency can bear”. The higher the frequency, the further back the position.
Variant: suppose you now add an option called “temperature setting”, which can change on every request and affects only sampling, not semantics. Where should it go under this principle? Then a nastier one: if some option both governs everything globally and changes often (say “the current time”), is this layout still salvageable? (Hint: think about why most systems would rather hard-code the time into the system prompt than update it every turn.)
Channels: two generation modes switched by prefix
The body of an assistant message is organised into channels(channels), a concept the paper says is inspired by OpenAI's Harmony response format. Each of the three channels has its own job: think carries the reasoning trace, response carries the user-visible answer, and tools carries tool calls.
The two generation modes are selected purely by the generation prefix — [open]think[sep] enters thinking mode, [open]response[sep] enters instruct mode — not by different templates. This answers directly to the “extensibility” goal: switching modes does not require touching the template.
K3 supports only preserved thinking(preserved thinking): in thinking mode the think channel is always kept in the history, even when its content is empty. The reason the paper gives is “so the model observes a consistent message structure across turns”. In instruct mode, history messages contain only the response and tools channels.
Why “kept even when empty” matters
If it were sometimes kept and sometimes deleted, the message structure the model sees in the history would come in two shapes. It would first have to work out “does this turn have a think channel at all?” before deciding how to read what comes after. Always keeping it removes that judgement, and the model can treat “assistant message = think + response + tools” as an iron rule. Consistency of structure is worth far more than the handful of tokens you would save. The paper does not discuss the token cost of doing this, and does not discuss why “non-preserved thinking” is not supported (many systems throw away the old reasoning trace on the next turn to save context) — this is a place where the authors made a choice without arguing for it.
Tool calls: typed arguments, and code as a first-class citizen
Each call in the tools channel carries two attributes, tool and index. index is the number of the parallel call within the message; every tool-result message repeats the same tool/index pair, and the results are ordered by the order of their calls, so a result can be paired unambiguously with its call — you never get “three tools called at once and no telling which return is whose”.
Arguments are typed: string arguments appear as raw text, and values of the other JSON types are serialised compactly. Following the shape of Figure 16c, one call looks like this ([open]argument key="code" type="string"[sep] followed directly by the raw code, then [close]argument[sep]). So free-form text such as code becomes a first-class citizen instead of an escaped JSON string.
The paper also mentions a pure-JSON fallback block, covering those inputs whose arguments cannot be decomposed into typed argument blocks; it appears only in input tokens and never in the model's output, and its loss is masked out during training (that is, the model is never trained to generate this block).
Reasoning effort: say it in natural language instead of inventing new syntax
Reasoning effort is exposed as a thinking-effort-type global option message, inserted after the tool declarations and before the input messages. It does not modify the generation prefix and does not expose a token budget; instead it states the requested level in natural language, acting as a generation-constraint instruction. The schema reserves four levels (low, medium, high, max), and K3 supports a subset of them (the paper does not say which ones).
The paper says this representation decouples the effort interface from the template syntax, and aligns directly with effort-conditioned training. More broadly, this is how all option messages are implemented: tool_choice, response_format and thinking-effort are each translated into one short natural-language instruction placed in the context, rather than into dedicated special syntax.
A synthesis question. Suppose the model has to call a python tool, and the argument is this piece of code:if x > 0 and s == "a\nb":\n print("ok")
(a) If you follow the traditional approach and wrap it into a JSON string {"code": "..."}, what extra work does the model have to do while it generates? Point out at least three specific places that need escaping. (b) Explain why this hurts at least two of the three design goals at once. (c) How does K3's typed argument block get around it? (d) Finally, connect this example to the “low alignment tax”: why does “no escaping needed” reduce the amount of supervised data required?
(a) At least three places: ① the
"a\nb" in the code has two double quotes that have to be written as \"; ② the backslash that the code already contains, in \n (two characters in the python source), has to be written as \\n, or JSON will parse it into a real newline; ③ the code's real newlines (the end of each line) have to be written as \n; ④ the quotes in print("ok") have to be escaped as well. Note that the confusion between ② and ③ is the most disgusting part — the same two characters \n, one of which has to turn into four characters, the other of which came from a single newline, and the model has to keep straight “is this backslash from the source or did I add it?”.(b) It hurts two goals. Decoding friendliness is hurt worst — a streaming parser has to run an un-escaping state machine as it takes in tokens, and when it “receives a lone backslash” it cannot tell what the next character will be, so the parse state is undetermined; constrained decoding also gets harder, because the set of legal next tokens depends on the escaping state. Low alignment tax suffers too: the code in the pretraining corpus is overwhelmingly raw rather than JSON-escaped, so “writing escaped code” is a skill the model has not seen much of in pretraining, and it has to be taught specifically with supervised data; teach it too little and it will make mistakes in long stretches of code — and errors of this kind are all or nothing: miss one escape and the whole JSON fails to parse, the entire tool call is void, unlike natural language where “a small mistake is still usable”.
(c) K3 writes the argument as
[open]argument key="code" type="string"[sep] + the raw code + [close]argument[sep]. Because the boundaries are reserved special tokens, they cannot possibly appear inside the code (as covered above: special tokens cannot be produced by encoding ordinary text), so no escaping is needed at all — the code goes in exactly as it is written. This is what the paper means by “free-form text becomes a first-class citizen”.(d) The link to the low alignment tax: what the model has to learn shrinks from “write code + do a character-level conversion at the same time” to “write code, then emit an end marker”. The latter falls almost entirely within the abilities pretraining already gave it (it can write code anyway, and emitting one particular token is learned in one go), so very little supervised data is needed to learn the format. That directly supports the pipeline the paper describes: a lightly fine-tuned pretrained model goes straight into RL — no huge supervised fine-tuning stage is needed to hammer the format into the model, which also avoids the damage that stage does to pretrained ability.
String the three layers together: a format choice that looks like it is only about “writing a few fewer backslashes” reaches down into the complexity and the error rate of the parser, and up into whether the training pipeline can drop a whole stage. That is why the paper treats the chat template as a design problem worth putting in a technical report, rather than an implementation detail.
Variant: the paper says the pure-JSON fallback block “appears only in input tokens and never in the model's output, and its loss is masked out during training”. Argue for why this design decision is necessary: what would happen if the loss were not masked out? (Hint: what would the model learn? And does that contradict the “low alignment tax” of (d)?) Then think further: given all that, why keep the fallback block at all?
Chapter summary
Five appendices, one common thread: behind every “we did it this way and it works better” in the main text there is either an equation you can derive, or a bound you can prove, or a trade-off whose cost you can state.
Appendix B answers “how do you cap a multiplication without destroying the shape of the activation function”. The answer is to replace Swish's linear factor with β tanh(·/β), keep the sigmoid factor, and treat both branches the same way. Eq. 18 guarantees a first-order match to SwiGLU near the origin and pointwise recovery as β → ∞; Eq. 19 gives an input-independent hard upper bound β₁β₂ = 100 (β₁ = 4, β₂ = 25). The advantage over hard clamping is that it keeps a non-zero gradient away from the saturation boundary — with hard clamping the derivative in the saturated region is identically 0, and neurons die permanently.
Appendix C turns Quantile Balancing from a trick back into the exact solution of a problem. The starting point is the maximum fractional balanced assignment of Eq. 20; the relaxation to [0,1] is exact because of the total unimodularity of the bipartite b-matching polytope; introducing multipliers writes it as a max–min (Eq. 21), swapping the order gives Eq. 22, and the inner problem is separable and attained at max(0, ·) (Eq. 23). Coordinate minimisation of the dual objective is piecewise linear in both directions, with slope “the target count minus the number above the threshold”, so the minimiser is pinned between the k-th largest and the (k+1)-th largest, and taking the (k+1)-th largest is exactly the (1−k/n) quantile (Eq. 25, Eq. 26) — the token axis and the expert axis take the same quantile, which is where the method gets its name. The most important conclusion is that asymmetry: routing needs only the expert threshold β (that is, the bias b = −β); the token threshold α is an intermediate variable tied to the training batch and can be thrown away; so at deployment time routing is just “subtract the frozen bias, then take Top-k”, with no quantile computation at all. Finally, Eq. 27 shows that the expert-side subgradient is exactly “target load minus observed load”, and taking one SignSGD step on it recovers precisely the fixed-step sign update of auxiliary-loss-free balancing — the sign update uses only the direction of the error, while QB jumps straight to the exact minimiser of the same objective, which explains why QB has no learning-rate hyperparameter and why it can balance in a few steps even facing nearly 10³ experts. BIP uses inequality constraints, and the non-negativity they induce puts a max(0, ·) clip on both updates, so it can only push down and never lift up, which slows balancing markedly.
Appendix D solves the engineering problem of “the quantile has to be taken over millions of tokens, across ranks and across accumulation steps”. The key observation is that the update needs only the distribution and not the data itself, so you build a per-expert histogram of the required bias r = α − s; negating reverses the order, and the QB target is exactly the (k/n) quantile of r. The binning range [bmin − 1, bmax + 1] follows from two facts — sigmoid scores fall in (0,1), and “α is itself some biased score” — and it is recomputed every step, so it adapts. With B = 1000 bins, the error is pinned by the bin width at a few times 10⁻³. Three properties: accurate (the cumulative counts are exact at the bin boundaries), cheap (one all-reduce of nB integers per layer per step, independent of m, under 1% of the alternative), and estimating the right quantity (counts are additive ⇒ the global histogram is exactly invariant to how the data is sharded ⇒ what is estimated is the quantile of the pooled global batch, rather than the average of the per-rank quantiles, and those two generally differ).
Appendix E puts a bound on how much redundant expert capacity MoonEP has to reserve. Theorem 1: M(I) ≤ E/R always holds, proved by constructing a scheme that repeatedly “fills an underloaded rank exactly full” — each filling makes one rank permanently balanced, so at most R−1 of them are needed; each rank is filled at most once, so its remote tokens have a single source, and that source has only E/R experts locally. Theorem 2: construct a routing output where “rank 0 receives no tokens at all and the remaining experts share them evenly”, and a pigeonhole count gives ≥ ⌈E(R−1)/R²⌉, which for large R is about E/R, so no significantly smaller general upper bound exists. The engineering consequence is that in the worst case expert memory doubles, and that cost cannot be dodged.
Appendix F covers the XTML chat template. Three goals: extensible (through backward-compatible message formats rather than template changes, one template serving an entire model generation), low alignment tax (the format is easy to learn, supporting a direct move into RL after light fine-tuning), and decoding-friendly. The means is to replace the angle brackets with the three reserved special tokens [open]/[sep]/[close], which removes tokenisation ambiguity and simplifies constrained decoding. The layout principle is “what does not change first, what changes often last”: global options → input messages → one-shot options, with input option messages insertable midway to support dynamically loaded tools without rebuilding the context. An assistant message is divided into the three channels think / response / tools, and the two modes are switched purely by the generation prefix; K3 supports only preserved thinking. Tool calls are paired by tool/index, arguments are typed, and code goes in as raw text rather than as an escaped JSON string. Reasoning effort is made into a natural-language global option message (four levels low/medium/high/max, of which K3 supports a subset), which is also how all option messages are implemented.
The gaps worth remembering. Appendix B gives no comparison curve at all for the smooth cap vs hard clamping, and does not explain how the numbers β₁ = 4 and β₂ = 25 were chosen. Appendix C's “ties have measure zero” is not harmless in low-precision floating point, and the paper waves it through; nor does it give any measure of the gap between the QB solution and the true optimal assignment. Appendix D gives no ablation for the number of bins B, describes the EMA refinement only qualitatively, and uses the same letter for the dual variable β of Appendix C and the “index of the selected bin” β here. Appendix E does not define S and K (you can only infer them from the context), and does not disclose the EP size R that K3 actually uses. Appendix F does not say which of the four effort levels K3 actually supports, does not discuss the token cost of preserved thinking, and does not argue why non-preserved thinking is not supported.
第15章 从论文到代码:把知识变成可迁移的能力
前面十四章解决的是「看懂」。但看懂一篇论文和能做出点什么之间,还隔着一层:你得知道这些东西在真实世界里长什么样、放在哪里、怎么读。这一章做两件事——把论文点名的每一个开源项目做成索引,以及教你一套读陌生仓库的通用方法。最后给五个从一小时到几周的练手任务。
学完这一章你应该能做到
- 说出论文里每一个被点名的开源项目分别对应论文的哪一部分,并知道去哪里找它
- 解释「懂公式」和「能实现」之间具体差在哪些东西上,至少举出四项
- 拿到一个完全陌生的仓库时,说得出接下来六步做什么、先打开哪个文件
- 独立完成本章练手清单的前两项,并让两种实现的数值对上
- 算出跑一个 2.78 万亿参数模型到底需要多少显存,并据此判断自己该走哪条路
本章所有链接的可靠性声明,请先读这一段
这一章里出现的每一个网址,都是从论文(2026 年 7 月版本)的正文脚注或参考文献里逐字抄下来的,没有一个是本站补的、猜的或者凭记忆写的。但是——本站没有访问过其中任何一个地址,无法核实它们现在是否还能打开。开源仓库改名、迁移、转为私有、被删除都是常事,论文写下地址和你现在点击之间已经过去了一段时间。如果链接打不开,正确的反应是拿项目名去搜索引擎找,而不是认为本站写错了。
更重要的一条:论文只给了仓库地址,没有给任何文件名、函数名、行号。所以本章凡是涉及「代码里的某个具体位置」,一律写成「论文未指明具体位置,需自行在仓库中定位」,并告诉你该搜什么关键词。任何一个具体的路径或函数名如果出现在这里,那都会是编造——所以你不会在这一章看到它们。
15.1 一句话,和它背后的几百行
论文 §5.1.1 里有这么一句:他们开发了 FlashKDA,一个基于 CUTLASS 的分块内核,把块内计算和跨块状态传播重叠起来;这个内核把工作拆成「token 并行的若干阶段」和「一个头并行的递推」,两部分各自独立调度、独立调优;它同时服务训练和推理预填充,并且作为 flash-linear-attention 的一个后端被自动分发。
四行字。你在第2章已经把式 1 到式 6 全推过一遍了,公式本身你是懂的。但请诚实地问自己:现在让你写出这个内核,你写得出来吗?
写不出来。不是因为你不懂公式,而是因为公式里根本没有下面这些东西:
第一,分块边界。式 4 把一个 chunk 内的输出拆成「跨块项」和「块内项」,数学上干净利落。但序列长度不是 chunk 大小的整数倍怎么办?最后那个残缺的块怎么处理?一个 batch 里不同样本长度不同,是补齐(padding)还是变长打包(varlen)?这三个问题在论文里一个字都没有,在代码里是三段分支。
第二,数值精度。论文倒是讲了一半——§2.1.1 明说式 4 里的 1/Γ1→C 这个重缩放会「无界增长、在有限精度下溢出」,所以 Kimi Linear 要在对数空间算相对衰减、还要把 chunk 再切成 16-token 的小块;K3 则换成有下界的 sigmoid 参数化(式 5),让累积对数衰减落在 (−80, 0)、倒数小于 e80、留在 BF16 动态范围内。这已经是论文里少见的、把工程细节写进正文的段落了。但即便如此,哪一步该用 FP32 累加、哪一步可以留在 BF16、指数在算之前要不要先减最大值——依然只有代码知道。
第三,内核启动配置。一个 GPU 内核要决定:多少个线程块、每块多少线程、共享内存分多大、循环展开几层、用不用异步拷贝。这些数字不影响正确性,只影响速度——但影响的幅度可以是好几倍。论文说 FlashKDA 的两部分「各自独立调度与调优」,这句话背后是一张调参表。
第四,分发策略。论文自己就点了三种执行状态:训练/预填充用分块内核(§5.1.1)、超长序列用 SM 级的上下文并行(§5.1.1)、解码用另一套完全不同的融合内核(§5.4.2)。也就是说,同一个 KDA,代码里至少是三份实现,外加一层「什么时候调用哪一份」的分发逻辑。
那还有必要读论文吗
有,而且必须先读。代码告诉你「是什么」,只有论文告诉你「为什么不是别的」。§2.1.1 那段关于对角块的讨论就是最好的例子:Kimi Linear 要为每个对角小块做显式的位置对计算,K3 因为衰减有下界,所有因果小块都能直接走稠密的 Tensor Core 矩阵乘。你在代码里只会看到后者——一段平平无奇的矩阵乘。前者那条被放弃的路、以及放弃的理由,只在论文里。先读论文建立「为什么」,再读代码补上「怎么做」,反过来的效率低得多。
下面五件事,哪些是论文式 1–式 6 里明确写了的,哪些是必须去代码里才能知道的?
① 状态 St 的形状是 dk×dv
② 状态张量在显存里是行优先还是列优先存放
③ 衰减 αt 的取值范围是 (egmin, 1)
④ chunk 大小 C 取多少
⑤ Tril 操作把严格上三角置零、保留对角
chunk_size 或 BT 这类命名——但具体在哪个文件,论文未指明,本站也未核实。变式:论文 §5.4.1 说 KDA 的物理缓存块被迫做到 1024–6144 token。这个数是「数学对象」还是「实现选择」?如果是实现选择,为什么论文要把它写进正文?
15.2 索引:论文点名的每一个开源项目
下面三张表把论文里出现过的、带地址或带明确项目名的开源资源全部列出来。「论文给的地址」一列全部是逐字照抄,论文只给了名字或只给了会议出处的,这一列写「论文未给地址」。
| 论文里的概念 | 项目名 | 论文给的地址 | 论文出处 | 对应论文哪部分 |
|---|---|---|---|---|
| K3 模型权重本身 | Kimi-K3 | https://huggingface.co/moonshotai/Kimi-K3 | 第 1 页脚注 1 | 摘要里「我们发布完整的 Kimi K3 模型权重」 |
| KDA 的分块内核 | FlashKDA | https://github.com/MoonshotAI/FlashKDA | 参考文献 [14],§5.1.1 引用 | 式 1、式 3–4 的分块形式;训练与预填充内核 |
| 完美负载均衡的专家并行 | MoonEP | https://github.com/MoonshotAI/MoonEP | 第 19 页脚注 3 | §5.2.1,以及附录 E 的 E/R 冗余专家上界 |
| microVM 智能体沙箱 | AgentENV | https://github.com/kvcache-ai/AgentENV | 第 22 页脚注 4 | §5.3.2,暂停/恢复、Fork、快照三种沙箱生命周期操作 |
| K3 自己写的 GPU 编译器 | MiniTriton | https://github.com/MoonshotAI/minitriton | 第 33 页脚注 5 | §7 案例研究「GPU 编译器开发」 |
| K3 自己设计的推理芯片 RTL | nano-kpu | https://github.com/MoonshotAI/nano-kpu | 第 33 页脚注 6 | §7 案例研究「芯片设计」,4 mm²、100 MHz、INT4 |
| KDA 的上下文并行实现 | 提交在 FLA 上游的 PR | 论文以链接文字「FLA PR #691」给出,未在正文里逐字写出完整 URL;需自行到 flash-linear-attention 仓库按编号 691 查找 | 第 18 页脚注 2 | §5.1.2 KCP,式 17 的前缀扫描分解 |
为什么 AgentENV 的地址在 kvcache-ai 名下
论文原文说 AgentENV 是「与我们的合作伙伴共同开发」的(developed in collaboration with our partners),脚注给的地址也确实不在 MoonshotAI 组织下。这是论文自己写的,不是本站的推断。至于合作伙伴是谁、组织归属如何,论文没说。
| 论文里的概念 | 项目名 | 论文给的地址 | 论文出处 | 对应论文哪部分 |
|---|---|---|---|---|
| 线性注意力算子库(FlashKDA 挂进去当后端) | flash-linear-attention (FLA) | https://github.com/fla-org/flash-linear-attention | 参考文献 [139],§5.1.1、§5.1.2、§4.2.4 引用 | KDA 的 Triton 参考实现;FlashKDA 作为其后端自动分发;也是内核任务的题源 |
| 专家并行通信库(MoonEP 的对照对象) | DeepEP | https://github.com/deepseek-ai/DeepEP | 参考文献 [147],§5.2.1 引用 | §5.2.1,论文称最坏情况下 DeepEP 需 S×K×R 通信缓冲,MoonEP 只需固定的 S×K |
| 跨 PP rank 的激活远程卸载 | Mooncake Transfer Engine | 论文未给地址(参考文献只给 arXiv:2407.00079) | 参考文献 [96],§5.2.2 引用 | §5.2.2「跨 PP rank 平衡激活」 |
| 集合通信 | NCCL | https://developer.nvidia.com/nccl | 参考文献 [82],§7 引用 | §7 MiniTriton 的分布式原语建在 NCCL 上;§5.1.2 的 all-gather |
| 稠密矩阵乘基线 | cuBLAS | https://developer.nvidia.com/cublas | 参考文献 [22],§7 引用 | §7,MiniTriton 的 tensor-core GEMM 在最大形状上接近 cuBLAS |
| 深度学习框架(数值参考基线) | PyTorch | 论文未给地址(参考文献只给 arXiv:1912.01703) | 参考文献 [89],§7、§4.2.4 引用 | §4.2.4 每个内核任务都配一份 PyTorch 参考实现;§7 MiniTriton 与 torch eager 对比 |
| 图编译基线 | torch.compile / PyTorch 2 | 论文未给地址(只给 DOI 10.1145/3620665.3640366) | 参考文献 [5],§7 引用 | §7,MiniTriton 在核心基准套件几何平均上超过 torch.compile |
| 大模型训练框架 | Megatron-LM | 论文未给地址 | 参考文献 [81] | §5.2 流水线并行等并行策略的来源之一 |
| MoE 训练扩展 | Megatron Core MoE | https://arxiv.org/abs/2603.07685 | 参考文献 [137],§5.2.1 引用(ECHO) | §5.2.1,作为「预设冗余专家数或设 token 上限」的对照方案 |
| GPU 内核编程语言 | Triton | 论文未给地址(只给 MAPL 2019 会议出处) | 参考文献 [122],§7 引用 | §4.2.4 内核任务的编程方式之一;§5.1.1 的「Triton 参考实现」;§7 MiniTriton 是「Triton 风格」编译器 |
| 分块编程模型 | TileLang | https://arxiv.org/abs/2504.17577 | 参考文献 [129],§4.2.4 引用 | §4.2.4 内核任务覆盖的编程方式之一 |
| 内核编程 DSL | ThunderKittens | https://openreview.net/forum?id=0fJfVOSUra | 参考文献 [110],§4.2.4 引用 | §4.2.4 内核任务覆盖的编程方式之一 |
| FlashKDA 的底座 | CUTLASS | 论文未给地址,参考文献里也没有对应条目,只在 §5.1.1 正文里作为形容词出现(CUTLASS-based) | §5.1.1 | FlashKDA 的实现基础 |
| 其余被点名的 GPU 编程方式 | CUDA、CuTe DSL、Gluon | 论文未给地址(正文只列了名字) | §4.2.4 | 内核任务套件覆盖的编程方式 |
| 轻量虚拟化(AgentENV 的隔离层) | Firecracker | 论文未给地址(只给 NSDI 2020 会议出处) | 参考文献 [3],§5.3.2 引用 | §5.3.2,用 microVM 换取容器给不了的隔离度与保真度 |
| 沙箱镜像格式 | OverlayBD(论文引的是 DADI 那篇) | 论文未给地址(只给 USENIX ATC 2020 会议出处) | 参考文献 [68],§5.3.2 引用 | §5.3.2,配合自定义 ublk 驱动与 P2P 传输实现亚秒级启动 |
| 编译器基础设施 | MLIR | 论文未给地址(只给 DOI 10.1109/CGO51591.2021.9370308) | 参考文献 [64],§7 引用 | §7,MiniTriton 的 warp 级标注与优化层 |
| 芯片综合用的标准单元库 | Nangate45 Open Cell Library | https://si2.org/open-cell-library/ | 参考文献 [80],§7 引用 | §7 芯片设计案例,nano-kpu 用它做时序收敛 |
| 草稿模型方法 | EAGLE-3 | https://arxiv.org/abs/2503.01840 | 参考文献 [71],§4.1.4 引用 | §4.1.4,把预训练的 MTP 层微调成 EAGLE-3 式草稿模型,训练时展开七步 |
| 优化器(原始提出) | Muon | https://kellerjordan.github.io/posts/muon/ | 参考文献 [53],§2.5、§3.3 引用 | §2.5 Per-Head Muon;§3.3 训练配方 |
| 优化器(规模化版本) | Muon is Scalable for LLM Training | https://arxiv.org/abs/2502.16982 | 参考文献 [73],§5.2.2 引用 | §5.2.2,P2P 版 Muon 正交化对照的「朴素做法」出处 |
| KCP 的思想来源 | Context Parallelism for DeltaNet | https://yywangcs.notion.site/DeltaNet-2a9fc9f5d8058013a498f34e0b25bd52 | 参考文献 [142],第 18 页脚注 2 | §5.1.2,论文说 KCP 的构造建立在 DeltaNet 上下文并行之上 |
| 解码时状态回滚(并发的独立工作) | ReplaySSM | https://tridao.me/blog/2026/replayssm/ | 参考文献 [25],§5.4.2 引用 | §5.4.2,论文说「只缓存投影输入、片上重建」这一设计被并发工作独立提出 |
| MoE 解码内核的设计来源 | WarpDecode | https://cursor.com/blog/warp-decode | 参考文献 [12],§5.4.2 引用 | §5.4.2,Stable LatentMoE 的解码内核建在其 token 中心设计之上 |
| 低比特数据格式 | Microscaling(MXFP4/MXFP8 的来源) | 论文未给 URL 字段(只给 arXiv:2310.10537) | 参考文献 [103],§4.1.4 引用 | §4.1.4,专家权重量化到 MXFP4、激活用 MXFP8 |
| 草稿模型的损失函数 | LK Losses | https://arxiv.org/abs/2602.23881 | 参考文献 [104],§4.1.4 引用 | §4.1.4,式 16 直接优化接受率的负对数 |
| 在线 softmax 归并 | Online normalizer calculation for softmax | https://arxiv.org/abs/1805.02867 | 参考文献 [79],§2.2、§5.4.2 引用 | Block AttnRes 把块间与块内部分和用在线 softmax 归并 |
| 白盒 RL 环境能实例化的主流 harness | Kimi CLI | https://www.kimi.com/code | 参考文献 [56],§4.2.1 引用 | §4.2.1 统一白盒 RL 环境 |
| 同上 | Claude Code | https://docs.anthropic.com/en/docs/claude-code | 参考文献 [15],§4.2.1 引用 | §4.2.1 |
| 同上 | Codex | https://github.com/openai/codex | 参考文献 [20],§4.2.1 引用 | §4.2.1 |
| 同上 | OpenClaw | https://docs.openclaw.ai/ | 参考文献 [86],§4.2.1 引用 | §4.2.1 |
| 同上 | Hermes Agent | https://hermes-agent.nousresearch.com/docs/ | 参考文献 [44],§4.2.1 引用 | §4.2.1 |
| 响应格式规范 | OpenAI Harmony | https://github.com/openai/harmony | 参考文献 [85](本站未逐页定位其正文引用处) | 与 §F 的 XTML 聊天模板属同类问题 |
| K3 的官方发布说明 | Kimi K3 博客 | https://www.kimi.com/blog/kimi-k3 | 参考文献 [60] | 模型发布本身 |
| 基准 | 论文给的地址 | 论文出处 | 用在哪 |
|---|---|---|---|
| Terminal-Bench | 论文未给地址(只给 arXiv:2601.11868) | 参考文献 [78] | §6.1.1 编码轴 |
| Toolathlon(The Tool Decathlon) | https://toolathlon.xyz/introduction | 参考文献 [119](另有 [69] 只给 arXiv:2510.25726) | §6.1.1 智能体轴 |
| SWE-Marathon | https://www.swe-marathon.org/ | 参考文献 [117] | §6.1.1 编码轴 |
| OSWorld-Verified | https://xlang.ai/blog/osworld-verified | 参考文献 [136] | §6.1.1 智能体轴 |
| OSWorld 2.0 | https://arxiv.org/abs/2606.29537 | 参考文献 [143] | §6.1.1 智能体轴 |
| DeepSWE | https://deepswe.datacurve.ai/ | 参考文献 [31] | §6.1.1 编码轴 |
| FrontierSWE | https://www.frontierswe.com/ | 参考文献 [35] | §6.1.1 编码轴 |
| ProgramBench | https://www.vals.ai/benchmarks/programbench | 参考文献 [95] | §6.1.1 编码轴 |
| PostTrainBench | https://posttrainbench.com/ | 参考文献 [94] | §6.1.1 编码轴 |
| JobBench | https://job-bench.github.io/ | 参考文献 [52](另有 [70] 只给 arXiv:2605.26329) | §6.1.1 智能体轴 |
| Agents' Last Exam | https://agents-last-exam.org/leaderboard | 参考文献 [4](另有 [115] 给 https://arxiv.org/abs/2606.05405) | §6.1.1 智能体轴 |
| GDPval | https://arxiv.org/abs/2510.04374 | 参考文献 [90] | §6.1.1 智能体轴 |
| Humanity's Last Exam | https://arxiv.org/abs/2501.14249 | 参考文献 [93] | §6.1.1 推理与知识轴 |
| BrowseComp | https://arxiv.org/abs/2504.12516 | 参考文献 [131] | §6.1.1 智能体轴 |
| τ³-Banking | https://taubench.com/blog/tau-knowledge.html | 参考文献 [1] | §6.1.1 智能体轴 |
常见误解:参考文献里的 URL 等于「这个项目是开源的」
不成立。表 3 里有好几条指向的是排行榜页面或公司博客,不是代码仓库——你能看到分数,不一定能看到题目,更不一定能拿到评测脚本。第14章讲过为什么这件事对「可复现」很致命。同样地,表 2 里 Firecracker、OverlayBD、Triton、Megatron-LM 这些确实是开源项目,但论文只给了论文出处,没给仓库地址——本章按铁律照实标注,你要用就自己去搜项目名。
你想做三件事:(a) 看看 KDA 的上下文并行到底怎么写的;(b) 搞明白「完美负载均衡最多只要 E/R 个冗余专家」这个结论在工程上怎么落地;(c) 找一份能用来对拍你自己写的 KDA 的参考实现。分别该去哪个项目?论文分别在哪里给的地址?
https://github.com/MoonshotAI/MoonEP。现在你自己查 (a) 和 (c)。https://github.com/fla-org/flash-linear-attention。(b) MoonEP,第 19 页脚注 3。(c) 有两条路:FLA 里的 Triton 参考实现(§5.1.1 说 FlashKDA「大幅超过 Triton 参考实现」,所以它存在于 FLA 中),以及 §4.2.4 说每个内核任务都配一份 PyTorch 参考实现——后者是论文描述的内部训练数据构造方式,论文没说这份 PyTorch 参考实现被开源。所以现实中可用的是前者。三处的具体文件与函数名,论文均未指明。变式:如果你只想读懂 §5.3.2 里「暂停一个沙箱时它不占内存也不占 CPU」是怎么做到的,你会去表 1 还是表 2 找?为什么这个问题可能在两个项目里都找不到答案?
15.3 从式 1 到一个内核:中间隔着什么
假设你现在真的打开了一份 KDA 的实现。屏幕上是几百行你看不懂的东西。这一节告诉你该去找什么。
第一件事:找状态张量。 式 1 里的 St 是 dk×dv 的矩阵,但代码里它一定不是二维的——至少还要带上 batch 维和 head 维。所以你要找的是一个四维或五维张量,其中有两个维度是 dk 和 dv。找到它,你就找到了整段代码的锚点:所有对它的读写,就是式 1 的全部。
第二件事:找 chunk 循环。 式 3、式 4 的分块形式意味着代码里必然有一层沿 chunk 索引 t 走的串行循环,循环体内部是大块的并行计算。找到这层循环,你就把代码切成了「串行骨架」和「并行血肉」两半。
第三件事:找 Diag(αt) 去哪了。 式 1 里它是一个对角矩阵乘法。但没有人会真的构造一个 dk×dk 的对角矩阵再去做矩阵乘——那是 dk 倍的浪费。代码里它一定退化成了逐元素乘(broadcast 一个长度 dk 的向量)。这是「数学写法」和「代码写法」之间最经典的一次翻译,看懂这一处,你就懂了这类翻译的套路。
第四件事:找 UT 变换那个下三角求解。 式 4 需要 U[t] 和 W[t],它们来自 UT 变换。论文把推导「留给 Kimi Linear」了,没在正文展开。代码里这一步通常表现为一个对下三角矩阵的求逆或前向替换,形状是 C×C(C 是 chunk 大小)。这也是整段代码里最难读的一块。
第五件事:找 1/Γ 的重缩放。 §2.1.1 明说式 4 用累积衰减的倒数给每个 chunk 里的 key 重新缩放,而这个倒数会无界增长。所以代码里这一处附近,大概率有一个 clamp、一个 log-space 计算,或者一次显式的类型提升到 FP32。找到它,你就找到了整份实现里数值最脆弱的地方——顺便你也验证了论文 §2.1.1 那段讨论不是空话。
这句「各自独立调度与调优」值得多说一句。它的言外之意是:这两部分的最优配置根本不一样。token 并行那部分工作量大、规则、能填满 GPU;头并行那部分是串行链条,唯一的并行度来自「有多少个注意力头」,头数一旦不够,GPU 就会大面积空转。把它们塞进同一个内核、用同一套启动参数,必然要牺牲其中一边。拆开,才能各自最优,还能让一边的计算盖住另一边的等待。
先读参考实现,不要一上来读优化实现
论文自己给了你路线图:FLA 里有一份 Triton 参考实现,FlashKDA 是另一套基于 CUTLASS 的优化实现。这两者的可读性差一个数量级。参考实现的目标是「算对」,结构大致跟着公式走;优化实现的目标是「算快」,公式的形状会被内存布局、流水线、Tensor Core 的形状要求揉得面目全非。先把参考实现读到能和式 1、式 3、式 4 一一对上,再去看优化实现做了哪些变形——那时候每一处变形你都能问出「这是为了绕开什么」。
这一节的定位
上面五条是「该去找什么」的方法论,不是「在哪一行」的导航。论文没有给出任何文件名、函数名或行号,本站也没有访问过这些仓库。所以请把上面的每一条都当作搜索的起点:拿 chunk、state、decay、cumsum、tril、recurrent 这类词在仓库里搜,比在目录树里瞎逛快得多。搜到之后能不能对上式 1,由你自己判断——那正是这一章想训练的能力。
论文说 FlashKDA「作为 flash-linear-attention 的一个后端被自动分发」。仅凭这句话,你能推断出 FLA 的代码里存在什么样的结构?再推一步:如果哪天 FlashKDA 在某台机器上跑不了(比如显卡架构不支持),从用户角度看会发生什么?
变式:把「自动分发」改成「用户手动指定后端」,上面三条推断哪几条还成立?这个改动对使用者和对维护者分别有什么好处和坏处?
15.4 读一个陌生仓库的六步法
这一节不依赖任何具体项目。你以后读任何看不懂的代码,都可以照着走。
一、先读测试,不要先读实现。 这是最反直觉、也最有用的一条。实现回答「怎么做」,而你现在缺的是「做什么」——测试文件恰好回答后者:这个函数吃什么、吐什么、什么情况算对。而且测试里通常躺着一份最小可运行例子,比 README 更新、比文档诚实。找测试的方法:在仓库里搜 test,或看有没有叫 tests 的目录。
二、先跑起来,再读。 哪怕只是把 README 里的第一条命令跑通。跑不通的代码你读一百遍也只是在读文本;跑通了,你就获得了一个可以随便改、随便加打印的活物。能改、能观察,理解速度会提高一个数量级。
三、找参考实现或 naive 版本。 几乎所有高性能项目都保留了一份「慢但正确」的实现,用来验证快的那份没写错。那一份才是给人读的。论文自己就点出了这个模式:FLA 里的 Triton 参考实现之于 FlashKDA,§4.2.4 里的 PyTorch 参考实现之于每一个内核任务。搜关键词:naive、reference、ref、eager、torch。
四、用对拍思维。 你在第2章的实验室里已经亲手做过:同一个 KDA,一边用式 1 的逐步递推算,一边用式 3–4 的分块并行算,两边结果的差在 1e-16 量级——那不是巧合,那是数学等价性的数值证据。在真实项目里这件事叫单元测试。反过来用:想搞懂一个复杂实现在干什么,就自己写一个朴素版本去和它对拍。能对上,说明你的理解是对的;对不上,差异出现在哪一步,就精确地指出了你误解了哪一步。这是理解一个算法最快的路径,没有之一。
五、从数据形状入手。 读不懂一段代码时,别硬啃逻辑,先搞清楚进去的张量是什么形状、出来的是什么形状。深度学习代码的绝大部分复杂度都在形状变换上:转置、reshape、切块、广播。把形状标在纸上,代码会突然变得可读。实操上就是到处插打印语句,或者在调试器里停下来看。
六、把 git log 和 blame 当文档用。 「这一行为什么要这么写」这个问题,注释经常骗你(写完就不再更新),提交信息不会——它记录的是当时那个人为了解决什么问题才加的这一行。看到一行莫名其妙的 clamp、一个魔法数字、一个看似多余的类型转换,先去查它的提交历史,往往一句话就解释清楚了。
打个比方
读陌生代码像走进一座没有导游的大博物馆。「读懂全部」等于从第一间展厅开始逐件看——三小时后你在第二间展厅,累到什么都记不住。正确做法是先在门口地图上定一个目标(我要看那幅画),直奔过去,路上顺带瞥见的其它东西就是白赚的。
类比失效处:博物馆的展品彼此独立,代码不是——你要看的那一行往往依赖三个你没看过的东西。所以「打靶」的过程通常是递归的:为了回答一个问题,你会被迫先回答两个更小的问题。这不是走弯路,这就是路本身。
「我写的朴素实现和仓库里的优化实现对拍通过了,所以我理解对了。」请构造一个反例:对拍通过,但你的理解其实是错的。至少给出两种不同机制的反例。
(1) 输入没覆盖分支。序列长度总是 chunk 的整数倍,尾块逻辑从未被触发;或者 batch 里样本长度全相同,变长打包的路径从未走过。
(2) 容差掩盖差异。你用 1e-3 的容差,而两份实现在数值上的真实分歧是 1e-4 量级——比如一边在 FP32 里累加、一边在 BF16 里累加。这在小规模测试上无害,放到 100 万 token 的序列上误差会累积到肉眼可见。第2章那个 1e-16 之所以有说服力,正因为它贴着 double 的机器精度,几乎没有掩盖空间。
(3) 两边错得一样。如果你的朴素实现是照着优化实现「翻译」出来的,而不是照着式 1 独立写出来的,那你复制的是同一个误解。对拍要有意义,两份实现必须来源独立。
(4) 常数被抵消。比如你把 gmin 理解错了,但恰好测试里 Ah 初始化为 0,两个错误互相抵消。
结论:对拍是证伪工具不是证实工具。通过只说明「还没被抓到」。想让它更有力,就往极端输入上打:长度不整除、单 token、超长序列、全零输入、极端衰减值。
变式:如果两份实现对拍不通过,误差在 1e-2 量级。你怎么定位是哪一步出的问题?(提示:想想二分法能不能用在「计算步骤」上,而不只是用在数组上)
你在某份 KDA 实现里打印出一个中间张量,形状是 (B, H, N, C, C),其中 B 是 batch、H 是头数、N 是 chunk 个数、C 是 chunk 大小。仅凭这个形状,你能猜出它对应式 3–式 4 里的哪一个量?为什么不可能是状态 S?
变式:如果你看到的形状是 (B, H, N, C, dk),候选有哪些?如果是 (B, H, N, dk, dv) 呢?后者最可能是什么,它为什么会占那么多显存?
15.5 动手清单:五个练手项目
下面五个任务从「一小时」跨到「几周」。每个都有明确的完成标准——没达到标准就不算做完,不要自我安慰。
| # | 做什么 | 为什么有价值 | 完成标准 | 前置技能 |
|---|---|---|---|---|
| 1 | 用纯 Python 或 NumPy 实现论文式 1 的朴素递推。随机造 q, k, v, α, β,按 t 逐步更新 St。不到 30 行。 | 逼你把式 1 的每个符号落到一个具体变量上。Diag(αt) 到底怎么写、ktkt⊤ 是外积不是内积——这些只有动手才会发现自己原来没想清楚。 | 能打印出 St 随 t 的变化(比如每步的 Frobenius 范数),并观察到:把所有 α 调小,范数会衰减得更快。 | 会写循环、会 NumPy 的 outer 和矩阵乘 |
| 2 | 实现式 3–式 4 的分块并行形式,和第 1 步对拍。 | 这是全站最核心的一次「同一个东西的两种算法」。分块形式是所有高性能线性注意力实现的骨架,写通它,你就能读懂真实内核的结构。 | 对任意 chunk 大小(包括不整除序列长度的),与第 1 步的最大绝对误差在 1e-12 以下(用 float64)。注意「任意」——尾块必须处理对。 | 任务 1,加上对第2章式 3、式 4 的理解 |
| 3 | 实现论文式 13–式 14 的 Quantile Balancing,和定步长符号更新 bj(t+1) = bj(t) + γ·sign(·) 对比收敛速度。 | 让你亲眼看到「一步解出来」和「一小步一小步试出来」的差别有多大。这是第10章的核心论点,读一百遍不如自己画一张收敛曲线。 | 复现出这个现象:QB 几步之内负载就均衡了,定步长符号更新需要几十步;而且符号更新的步数强烈依赖 γ 取多少,QB 没有这个旋钮。 | 会算分位数(numpy.quantile)、理解 Top-k 路由 |
| 4 | 实现附录 E 的填充构造:随机生成路由输出,按论文的构造分配冗余专家,验证所需冗余专家数不超过 E/R。 | 把一个存在性证明变成一段能跑的代码。证明说「一定存在可行解」,代码逼你回答「那怎么把它找出来」——这正是 §5.2.1 说的「离线用整数规划求精确解、在线用 GPU 规划内核逼近」的动机。 | 跑一千组随机输入,零违反(每一组的冗余专家数都不超过 E/R,且每个 rank 恰好收到 S×K 个 token)。 | 任务 3,加上第10章附录 E 的构造思路 |
| 5 | 最难:把 K3 权重下载下来跑起来(哪怕量化后在多卡上);或者退一步——读懂 FLA 里的 KDA 内核,改一个参数(比如 chunk 大小)看性能怎么变。 | 前者让你直面「万亿参数」这四个字的物理含义;后者让你第一次把「改代码」和「看到数字变化」连起来,这是所有性能工作的起点。 | 前者:模型能对一个提示输出连贯文本。后者:能画出「chunk 大小 → 运行时间」的曲线,并解释拐点为什么在那里。 | 前者:多卡环境、推理框架、量化工具链。后者:任务 2 + 会用 GPU 计时 |
关于第 5 项,我要诚实地泼一盆冷水
论文 Table 1 写明 K3 总参数 2.78T。就算按 4 比特量化算,每个参数半个字节,光是权重就要 2.78×1012 × 0.5 ≈ 1.39×1012 字节,也就是约 1.4 TB。这还没算 KV 缓存、KDA 状态、激活和运行时开销。按单卡 80 GB 的常见规格算,仅装下权重就需要十几张卡,而且必须是能高速互联的那种。
绝大多数人没有这个硬件,这不丢人,这是事实。更现实的三条路:一是本表的任务 1–4,全部可以在一台笔记本上做完,而且它们训练的是同一套核心能力;二是读代码——FLA 的参考实现在 CPU 上就能跑通小规模例子;三是用别人托管的推理服务去观察模型行为(比如长上下文表现),把「部署」和「理解」这两件事分开。
顺带说清楚:本站没有下载过这份权重,也没有验证过 HuggingFace 上那个地址现在是否可访问、里面文件的实际大小是多少。上面 1.4 TB 是按论文给的参数量和 4 比特估算出来的,不是实测值。
综合题。(a) 用论文 Table 1 的数字,估算 K3 在 BF16(每参数 2 字节)和 MXFP4(约每参数 0.5 字节)两种精度下,权重各占多大空间。(b) 论文 §4.1.4 说只把专家权重量化到 MXFP4,注意力投影、latent MoE 投影、共享专家、路由器都保持更高精度。这个选择会让 (a) 的估算偏高还是偏低?(c) 再往回连:为什么论文要在后训练阶段就开始做量化感知训练(QAT),而不是训练完了再量化?
(a) BF16 约 5.6 TB,MXFP4 约 1.4 TB,差 4 倍。
(b) 偏低。1.4 TB 是「全部参数都按 0.5 字节」算的,但论文明说非专家部分保持更高精度,所以真实占用比 1.4 TB 大。大多少取决于非专家部分占总参数的比例——论文没有给出这个比例,所以只能定性地说「偏低」,给不出准确数字。这正是「按论文估算」和「实测」的差距所在。
(c) 三层理由。第一,如果训练时用高精度、部署时才压到 4 比特,模型从没见过量化带来的扰动,性能会掉;QAT 让模型在训练中就适应这种扰动。第二,论文明说 QAT 从 SFT 阶段一直贯穿到 RL。第三也是最关键的:RL 阶段 rollout(产生数据的那个模型)和训练(更新参数的那个模型)如果精度方案不同,等于用 A 模型的数据去更新 B 模型,这是一种训练–推理失配,会污染整个 RL 信号。论文用「消除失配」这个说法点了这一层。
把三问串起来看:一个纯粹的部署优化(省 4 倍显存),倒逼了后训练流程的改造(QAT 前移)——这就是本站反复讲的「架构决定会在系统层留下债」的又一个实例,只不过这次债是反向传导的:部署约束反过来改写了训练流程。
变式:假设有人只把注意力投影量化到 4 比特、专家权重保持 BF16,其它不变。省下的显存大概是多少量级?这个方案为什么几乎没有意义?(提示:Table 1 说 K3 有 896 个路由专家、每专家 MoE 隐层 3072)
本章说「先读测试,不要先读实现」。请构造一个反例:一个真实存在的场景,在那里先读测试反而更慢、更容易误导人。至少两种。
(1) 根本没有测试。论文配套的研究代码经常如此。这时候替代方案是找 README 里的示例、找
examples/ 目录、或者直接找 __main__ 入口。(2) 快照式测试(只断言「输出和上次一样」)。它保证了不回归,但完全不描述语义,读它等于读一堆哈希值。
(3) 测试本身比实现还复杂。高性能内核的测试常常带着一整套参数化框架、随机形状生成器、自定义容差策略——为了读懂测试,你得先读懂测试框架,成本高于直接读实现。
(4) 测试只覆盖端到端,不覆盖你关心的那一层。整个模型的输出对了,不代表你能从中看出 KDA 内部某一步在干什么。
修正后的说法:先找「最小可运行例子」,测试通常是它最好的载体,但不是唯一载体。找不到就退回到 examples、README 命令、或者自己写一个五行的调用。真正的原则不是「读测试」,是「先拿到一个能跑的最小输入输出对」。
变式:反过来,什么样的项目里「先读测试」的收益最大?(提示:想想接口复杂、语义微妙、边界情况多的那类库——比如一个日期时间库,或者一个变长序列打包工具)
15.6 只有代码能教你的三件事
第一,数值精度是实实在在的工程约束,不是脚注。 论文里已经给了你一角:§2.1.1 说重缩放因子会溢出,所以要在对数空间算、要切 16-token 小块;§2.1.1 末尾还提到,为了纠正 flash attention 里的有偏舍入误差,K3 把注意力输出在训练中保持 FP32,这让片上占用翻倍,于是又得重新设计内核让它和 KV 暂存缓冲重叠。你看,一个精度决定,直接改写了内核的内存布局。而代码里这类东西的密度,比论文高得多——每一个 clamp、每一次 .float()、每一个 eps,背后都可能有一次线上事故。
第二,内存布局决定性能,而它在公式里完全不可见。 §5.4.1 有一段特别好的例子:论文说在一个缓存页内,所有头的状态按头连续存放,这样每个头的字节流是自包含的,可以成为跨节点传输的最小单位;于是当预填充节点和解码节点采用不同的张量并行度时,重排可以在传输路径上完成,GPU 侧零重排。这整段推理里没有一个公式,但它决定了系统能不能做预填充/解码分离。「怎么摆」和「算什么」是两个独立的自由度,论文只写后者。
第三,你会开始能估算「论文里一句话的工程代价」。 §5.2.1 那句「MoonEP 保留了 DeepEP 的整体计算流程,另外引入了冗余专家的在线规划与迁移」——读代码之前,这是一句平淡的描述;读过之后你会知道,「在线规划」意味着要写一个在 GPU 上跑的规划内核、要保证它的开销可以忽略、要保证它永远不越过 E/R 上界;「迁移」意味着专家权重要在 rank 之间搬,搬的时机要和前向反向的流水线对齐。这种估算能力是判断一篇论文「哪里最有价值」的关键——通常最有价值的地方,恰恰是描述最平淡的那一句,因为作者自己觉得那是脏活,不好意思展开。
答辩:如果我是审稿人
你说 K3 是「开放」的,论文摘要也说发布了完整模型权重。但训练数据没公开、训练代码没公开、数据配比没公开、超参没给全。那这算开源吗?如果不算,为什么整篇论文和整个社区都在用「open」这个词?
参考防守(先自己组织语言再看)
分三层答。第一层,承认事实。论文用的词是「open weights」和「we release the full Kimi K3 model weights」,指的就是权重。至于训练数据、训练代码、完整超参,论文确实没有承诺发布,也确实没有给出。所以如果按「开源软件」的传统标准(拿到源码就能自己重建产物),K3 不满足——你拿不到能重建这个模型的东西。
第二层,说清楚这个词为什么还是有意义的。权重开放解锁的能力是真实的:你可以本地部署、可以微调、可以做可解释性研究、可以在上面做安全评估而不必求人。论文的定位是「开放前沿」(open frontier)——它主张的是「前沿能力现在在所有人触手可及的范围内」,不是「你可以复现这个训练过程」。这两个主张强度差很多,别混为一谈。而且这一章的表 1 显示,他们确实开源了一批真代码:FlashKDA、MoonEP、AgentENV、MiniTriton、nano-kpu,还往 FLA 上游提了 PR。这不是空口说开放。
第三层,指出真正该批评的地方。批评应该精准打在「用『开源』这个词去收割『可复现』的信誉」这一点上,而不是笼统地说「不算开源」。具体来说:论文的评测结果无法被独立复现(数据不公开,你无法排除数据污染);缩放定律的结论无法被检验(配比和超参不全);而 §7 那句「一个早期的 K3 检查点已经在承担我们自己的内核优化工作」尤其需要追问——这类任务的数据很可能回流进了训练。这些才是「不完全开放」造成的实质性损失。至于权重本身开不开放,反倒是这里面最不重要的一环。
一句话防守:「开放权重」是一个准确的描述,「开源」是一个被滥用的词;正确的做法不是争论标签,而是逐项列出「什么公开了、什么没公开、没公开的那部分让哪些结论无法被检验」。
答辩:如果我是审稿人(第二场)
这一章反复说「读代码能学到论文学不到的东西」。那反过来——什么时候读代码是浪费时间?给我三种情况,别说漂亮话。
参考防守(先自己组织语言再看)
情况一:你还没搞清楚「为什么」。没读懂式 1 就去读 KDA 内核,你看到的只是一堆张量操作,无法判断哪一步是数学要求、哪一步是性能妥协、哪一步是历史包袱。这时候读代码不是学习,是抄写。正确顺序永远是先论文后代码。
情况二:你要回答的是「值不值得」而不是「怎么做」。比如「KDA 相比全注意力在 100 万上下文下省了多少」——这是个设计权衡问题,论文的图表、消融、缩放曲线才是答案来源。代码回答不了它,代码只告诉你某个特定实现跑多快,那是被十几个无关变量污染过的数字。
情况三:这段代码是给机器读的,不是给人读的。高度优化的内核、自动生成的代码、编译器 IR,可读性本来就不在它们的设计目标里。硬读这类代码的投入产出比极低。这时候正确做法是读它对应的参考实现,或者读它的测试,或者读作者写的设计文档——本章表 1 里那句「先读参考实现」就是这个道理。
再加一条隐藏情况:代码可能已经过时。论文写于某个时间点,仓库一直在往前走。你读到的实现可能已经不是论文描述的那一版了。这时候 git log 比代码本身更有价值——它能告诉你论文那一版在哪个提交上。
一句话防守:代码是「怎么做」的唯一权威,但它对「为什么这么做」「值不值得这么做」几乎沉默。用错工具比不用工具更浪费时间。
本章小结
这一章做了三件事。一是给出距离感:论文 §5.1.1 描述 FlashKDA 用了四行字,而这四行字背后至少藏着分块边界、数值精度、内核启动配置、多套实现的分发策略四类东西——「懂公式」和「能实现」之间的距离,恰好就是论文省略掉的那部分。二是建索引:三张表逐字收录了论文里出现的地址,包括 Moonshot 自己开源的六项(K3 权重、FlashKDA、MoonEP、AgentENV、MiniTriton、nano-kpu,外加提给 FLA 上游的 PR #691)、三十来个被点名的第三方项目、以及十几个评测基准。三是给方法:先读测试、先跑起来、找参考实现、对拍、看形状、查 git log,外加最重要的一条——带着一个具体问题进去,答完就停。
关于可靠性,请把这两条一起记住。第一,本章所有 URL 都是从论文里逐字抄的,一个都没有补、没有猜;第二,本站没有访问过其中任何一个,也没有在任何仓库里定位过任何文件、函数或行号——凡是需要具体位置的地方,本章给的都是「搜什么关键词」而不是「打开哪个文件」。这个区分不是谨慎过头,它本身就是这一章想教的东西:把「我知道的」和「我以为我知道的」分开,是读代码和读论文共通的第一项基本功。
最后,表 4 那五个任务里,前四个都能在一台普通笔记本上做完,它们训练的能力和第五个是同一套。1.4 TB 那盆冷水泼的是硬件,不是你。
Chapter 15 · From paper to code: turning knowledge into transferable ability
The previous fourteen chapters were about “understanding it”. But between understanding a paper and being able to build something there is still a layer: you have to know what these things look like in the real world, where they live, and how to read them. This chapter does two things — it turns every open-source project the paper names into an index, and it teaches you a general method for reading an unfamiliar repository. At the end there are five hands-on tasks, running from an hour to a few weeks.
After this chapter you should be able to
- Say which part of the paper each open-source project it names corresponds to, and know where to go and find it
- Explain exactly what lies between “understanding the formula” and “being able to implement it”, naming at least four things
- When handed a completely unfamiliar repository, say what the next six steps are and which file to open first
- Finish the first two items on this chapter's hands-on list on your own, and get the numbers from the two implementations to line up
- Work out how much GPU memory it actually takes to run a 2.78 trillion parameter model, and use that to judge which route you should take
A statement about how reliable every link in this chapter is — read this paragraph first
Every web address in this chapter was copied word for word from a footnote in the body of the paper (the July 2026 version) or from its reference list; not one was added, guessed at, or written from memory by this site. But — this site has not visited any of these addresses, and cannot verify whether they still open today. Open-source repositories get renamed, moved, turned private and deleted all the time, and some time has passed between the paper writing an address down and you clicking on it now. If a link does not open, the right response is to take the project name to a search engine, not to conclude that this site got it wrong.
A more important point: the paper gives only repository addresses, no file names, no function names, no line numbers. So wherever this chapter touches on “some specific place in the code”, it always says “the paper does not specify the location; you have to find it in the repository yourself”, and tells you what keywords to search for. Any concrete path or function name appearing here would be fabrication — which is why you will not see any in this chapter.
15.1 One sentence, and the several hundred lines behind it
There is a sentence in §5.1.1 of the paper that runs like this: they developed FlashKDA, a chunkwise kernel built on CUTLASS that overlaps intra-chunk computation with cross-chunk state propagation; this kernel splits the work into “several token-parallel phases” and “one head-parallel recurrence”, and the two parts are scheduled and tuned independently of each other; it serves training and inference prefill at the same time, and is dispatched automatically as a backend of flash-linear-attention.
Four lines of text. In Chapter 2 you already worked through Equations 1 to 6 yourself, so the formulas themselves you understand. But ask yourself honestly: if you had to write this kernel now, could you?
You could not. Not because you do not understand the formulas, but because the formulas simply contain none of the following:
First, chunk boundaries. Equation 4 splits the output inside a chunk into a “cross-chunk term” and an “intra-chunk term”, which is mathematically clean and tidy. But what if the sequence length is not an integer multiple of the chunk size? What do you do with that last, incomplete chunk? Different samples in one batch have different lengths — is it padding, or variable-length packing (varlen)? The paper says not one word about these three questions; in the code they are three branches.
Second, numerical precision. The paper does cover half of this — §2.1.1 says outright that the rescaling by 1/Γ1→C in Equation 4 will “grow without bound and overflow in finite precision”, which is why Kimi Linear has to compute relative decay in log space and cut each chunk further into 16-token sub-chunks; K3 instead switches to a sigmoid parameterisation with a lower bound (Equation 5), which puts the cumulative log decay in (−80, 0), keeps the reciprocal below e80, and stays inside BF16's dynamic range. That is already one of the paper's rare paragraphs that puts an engineering detail into the body text. Even so: which step should accumulate in FP32, which step can stay in BF16, whether to subtract the maximum before taking the exponential — only the code knows.
Third, kernel launch configuration. A GPU kernel has to decide: how many thread blocks, how many threads per block, how much shared memory to allocate, how many levels of loop unrolling, whether to use asynchronous copies. These numbers do not affect correctness, only speed — but the size of that effect can be several times over. The paper says the two parts of FlashKDA are “scheduled and tuned independently”; behind that sentence sits a table of tuning parameters.
Fourth, the dispatch strategy. The paper itself names three execution regimes: training/prefill uses the chunkwise kernel (§5.1.1), extremely long sequences use SM-level context parallelism (§5.1.1), and decoding uses a completely different fused kernel (§5.4.2). Which is to say: for one and the same KDA there are at least three implementations in the code, plus a layer of dispatch logic for “when to call which one”.
Then is there still any point in reading the paper
There is, and you have to read it first. Code tells you “what it is”; only the paper tells you “why it is not something else”. That discussion of diagonal sub-chunks in §2.1.1 is the best example: Kimi Linear has to do an explicit position-pair computation for every diagonal sub-chunk, whereas in K3, because the decay has a lower bound, all causal sub-chunks can go straight through dense Tensor Core matrix multiplications. In the code you will only ever see the latter — an unremarkable matrix multiplication. That abandoned road, and the reason for abandoning it, exist only in the paper. Read the paper first to establish the “why”, then read the code to fill in the “how”; the other way round is far less efficient.
Of the five things below, which are written down explicitly in Equations 1–6 of the paper, and which can only be learned by going to the code?
① The state St has shape dk×dv
② Whether the state tensor is stored row-major or column-major in GPU memory
③ The decay αt takes values in (egmin, 1)
④ What value the chunk size C takes
⑤ The Tril operation zeroes the strictly upper triangle and keeps the diagonal
chunk_size or BT — but which file it is in, the paper does not specify and this site has not verified.Variant: §5.4.1 of the paper says KDA's physical cache blocks are forced up to 1024–6144 tokens. Is that number a “mathematical object” or an “implementation choice”? If it is an implementation choice, why did the paper put it into the body text?
15.2 An index: every open-source project the paper names
The three tables below list every open-source resource that appears in the paper with an address or with an explicit project name. The “address the paper gives” column is copied word for word throughout; where the paper gives only a name or only a conference citation, that column says “the paper gives no address”.
| The concept in the paper | Project name | The address the paper gives | Where in the paper | Which part of the paper it goes with |
|---|---|---|---|---|
| The K3 model weights themselves | Kimi-K3 | https://huggingface.co/moonshotai/Kimi-K3 | p. 1, footnote 1 | the abstract's “we release the complete Kimi K3 model weights” |
| KDA's chunkwise kernel | FlashKDA | https://github.com/MoonshotAI/FlashKDA | reference [14], cited in §5.1.1 | Equation 1 and the chunkwise form of Equations 3–4; the training and prefill kernel |
| Expert parallelism with perfect load balancing | MoonEP | https://github.com/MoonshotAI/MoonEP | p. 19, footnote 3 | §5.2.1, and the E/R upper bound on redundant experts in Appendix E |
| The microVM agent sandbox | AgentENV | https://github.com/kvcache-ai/AgentENV | p. 22, footnote 4 | §5.3.2, the three sandbox lifecycle operations: pause/resume, fork and snapshot |
| The GPU compiler K3 wrote itself | MiniTriton | https://github.com/MoonshotAI/minitriton | p. 33, footnote 5 | the §7 case study “GPU compiler development” |
| The inference chip RTL K3 designed itself | nano-kpu | https://github.com/MoonshotAI/nano-kpu | p. 33, footnote 6 | the §7 case study “chip design”, 4 mm², 100 MHz, INT4 |
| KDA's context parallelism implementation | a PR submitted upstream to FLA | the paper gives it as the link text “FLA PR #691” and does not write the full URL out word for word in the body; you have to go to the flash-linear-attention repository yourself and look up number 691 | p. 18, footnote 2 | §5.1.2 KCP, the prefix-scan decomposition in Equation 17 |
Why AgentENV's address sits under kvcache-ai
The paper's own text says AgentENV was “developed in collaboration with our partners”, and the address the footnote gives is indeed not under the MoonshotAI organisation. That is what the paper itself writes, not an inference by this site. As for who the partners are and how the ownership works, the paper does not say.
| The concept in the paper | Project name | The address the paper gives | Where in the paper | Which part of the paper it goes with |
|---|---|---|---|---|
| Linear-attention operator library (FlashKDA plugs into it as a backend) | flash-linear-attention (FLA) | https://github.com/fla-org/flash-linear-attention | reference [139], cited in §5.1.1, §5.1.2 and §4.2.4 | KDA's Triton reference implementation; FlashKDA is dispatched automatically as its backend; also where the kernel tasks get their problems |
| Expert-parallel communication library (what MoonEP is compared against) | DeepEP | https://github.com/deepseek-ai/DeepEP | reference [147], cited in §5.2.1 | §5.2.1; the paper says that in the worst case DeepEP needs an S×K×R communication buffer while MoonEP needs only a fixed S×K |
| Remote offloading of activations across PP ranks | Mooncake Transfer Engine | the paper gives no address (the reference list gives only arXiv:2407.00079) | reference [96], cited in §5.2.2 | §5.2.2, “balancing activations across PP ranks” |
| Collective communication | NCCL | https://developer.nvidia.com/nccl | reference [82], cited in §7 | §7, MiniTriton's distributed primitives are built on NCCL; the all-gather in §5.1.2 |
| Dense matrix multiplication baseline | cuBLAS | https://developer.nvidia.com/cublas | reference [22], cited in §7 | §7, MiniTriton's tensor-core GEMM comes close to cuBLAS at the largest shapes |
| Deep learning framework (numerical reference baseline) | PyTorch | the paper gives no address (the reference list gives only arXiv:1912.01703) | reference [89], cited in §7 and §4.2.4 | §4.2.4, every kernel task comes with a PyTorch reference implementation; §7, MiniTriton compared against torch eager |
| Graph compilation baseline | torch.compile / PyTorch 2 | the paper gives no address (only DOI 10.1145/3620665.3640366) | reference [5], cited in §7 | §7, MiniTriton beats torch.compile on the geometric mean over the core benchmark suite |
| Large-model training framework | Megatron-LM | the paper gives no address | reference [81] | one of the sources of the parallelism strategies in §5.2, pipeline parallelism among them |
| MoE training extension | Megatron Core MoE | https://arxiv.org/abs/2603.07685 | reference [137], cited in §5.2.1 (ECHO) | §5.2.1, as the comparison scheme that “presets a number of redundant experts or sets a token cap” |
| GPU kernel programming language | Triton | the paper gives no address (only the MAPL 2019 conference citation) | reference [122], cited in §7 | one of the programming approaches for the kernel tasks in §4.2.4; the “Triton reference implementation” in §5.1.1; in §7 MiniTriton is a “Triton-style” compiler |
| Tile-based programming model | TileLang | https://arxiv.org/abs/2504.17577 | reference [129], cited in §4.2.4 | one of the programming approaches the §4.2.4 kernel tasks cover |
| Kernel programming DSL | ThunderKittens | https://openreview.net/forum?id=0fJfVOSUra | reference [110], cited in §4.2.4 | one of the programming approaches the §4.2.4 kernel tasks cover |
| What FlashKDA is built on | CUTLASS | the paper gives no address, and there is no matching entry in the reference list either; it appears only as an adjective in the body of §5.1.1 (CUTLASS-based) | §5.1.1 | the implementation base of FlashKDA |
| The other GPU programming approaches named | CUDA, CuTe DSL, Gluon | the paper gives no address (the body only lists the names) | §4.2.4 | programming approaches the kernel task suite covers |
| Lightweight virtualisation (AgentENV's isolation layer) | Firecracker | the paper gives no address (only the NSDI 2020 conference citation) | reference [3], cited in §5.3.2 | §5.3.2, using microVMs to buy the degree of isolation and fidelity containers cannot give |
| Sandbox image format | OverlayBD (what the paper cites is the DADI paper) | the paper gives no address (only the USENIX ATC 2020 conference citation) | reference [68], cited in §5.3.2 | §5.3.2, working with a custom ublk driver and P2P transfer to achieve sub-second startup |
| Compiler infrastructure | MLIR | the paper gives no address (only DOI 10.1109/CGO51591.2021.9370308) | reference [64], cited in §7 | §7, MiniTriton's warp-level annotation and optimisation layer |
| Standard cell library for chip synthesis | Nangate45 Open Cell Library | https://si2.org/open-cell-library/ | reference [80], cited in §7 | the §7 chip design case study; nano-kpu uses it for timing closure |
| Draft model method | EAGLE-3 | https://arxiv.org/abs/2503.01840 | reference [71], cited in §4.1.4 | §4.1.4, fine-tuning the pretrained MTP layer into an EAGLE-3-style draft model, unrolled seven steps during training |
| Optimiser (the original proposal) | Muon | https://kellerjordan.github.io/posts/muon/ | reference [53], cited in §2.5 and §3.3 | §2.5 Per-Head Muon; §3.3 the training recipe |
| Optimiser (the scaled-up version) | Muon is Scalable for LLM Training | https://arxiv.org/abs/2502.16982 | reference [73], cited in §5.2.2 | §5.2.2, the source of the “naive approach” that P2P Muon orthogonalisation is compared against |
| Where KCP's idea comes from | Context Parallelism for DeltaNet | https://yywangcs.notion.site/DeltaNet-2a9fc9f5d8058013a498f34e0b25bd52 | reference [142], p. 18 footnote 2 | §5.1.2; the paper says KCP's construction builds on DeltaNet context parallelism |
| State rollback during decoding (concurrent, independent work) | ReplaySSM | https://tridao.me/blog/2026/replayssm/ | reference [25], cited in §5.4.2 | §5.4.2; the paper says the design of “cache only the projection inputs and rebuild on chip” was proposed independently by concurrent work |
| Where the MoE decoding kernel's design comes from | WarpDecode | https://cursor.com/blog/warp-decode | reference [12], cited in §5.4.2 | §5.4.2, Stable LatentMoE's decoding kernel is built on its token-centric design |
| Low-bit data format | Microscaling (where MXFP4/MXFP8 come from) | the paper gives no URL field (only arXiv:2310.10537) | reference [103], cited in §4.1.4 | §4.1.4, expert weights quantised to MXFP4, activations in MXFP8 |
| The draft model's loss function | LK Losses | https://arxiv.org/abs/2602.23881 | reference [104], cited in §4.1.4 | §4.1.4, Equation 16 directly optimises the negative log of the acceptance rate |
| Online softmax merging | Online normalizer calculation for softmax | https://arxiv.org/abs/1805.02867 | reference [79], cited in §2.2 and §5.4.2 | Block AttnRes merges the cross-block and intra-block partial sums with online softmax |
| Mainstream harnesses the white-box RL environment can instantiate | Kimi CLI | https://www.kimi.com/code | reference [56], cited in §4.2.1 | §4.2.1, the unified white-box RL environment |
| Same as above | Claude Code | https://docs.anthropic.com/en/docs/claude-code | reference [15], cited in §4.2.1 | §4.2.1 |
| Same as above | Codex | https://github.com/openai/codex | reference [20], cited in §4.2.1 | §4.2.1 |
| Same as above | OpenClaw | https://docs.openclaw.ai/ | reference [86], cited in §4.2.1 | §4.2.1 |
| Same as above | Hermes Agent | https://hermes-agent.nousresearch.com/docs/ | reference [44], cited in §4.2.1 | §4.2.1 |
| Response format specification | OpenAI Harmony | https://github.com/openai/harmony | reference [85] (this site has not located page by page where the body cites it) | the same kind of problem as the XTML chat template in §F |
| K3's official release notes | the Kimi K3 blog | https://www.kimi.com/blog/kimi-k3 | reference [60] | the model release itself |
| Benchmark | The address the paper gives | Where in the paper | Where it is used |
|---|---|---|---|
| Terminal-Bench | the paper gives no address (only arXiv:2601.11868) | reference [78] | §6.1.1 coding axis |
| Toolathlon (The Tool Decathlon) | https://toolathlon.xyz/introduction | reference [119] (there is also [69], which gives only arXiv:2510.25726) | §6.1.1 agent axis |
| SWE-Marathon | https://www.swe-marathon.org/ | reference [117] | §6.1.1 coding axis |
| OSWorld-Verified | https://xlang.ai/blog/osworld-verified | reference [136] | §6.1.1 agent axis |
| OSWorld 2.0 | https://arxiv.org/abs/2606.29537 | reference [143] | §6.1.1 agent axis |
| DeepSWE | https://deepswe.datacurve.ai/ | reference [31] | §6.1.1 coding axis |
| FrontierSWE | https://www.frontierswe.com/ | reference [35] | §6.1.1 coding axis |
| ProgramBench | https://www.vals.ai/benchmarks/programbench | reference [95] | §6.1.1 coding axis |
| PostTrainBench | https://posttrainbench.com/ | reference [94] | §6.1.1 coding axis |
| JobBench | https://job-bench.github.io/ | reference [52] (there is also [70], which gives only arXiv:2605.26329) | §6.1.1 agent axis |
| Agents' Last Exam | https://agents-last-exam.org/leaderboard | reference [4] (there is also [115], which gives https://arxiv.org/abs/2606.05405) | §6.1.1 agent axis |
| GDPval | https://arxiv.org/abs/2510.04374 | reference [90] | §6.1.1 agent axis |
| Humanity's Last Exam | https://arxiv.org/abs/2501.14249 | reference [93] | §6.1.1 reasoning-and-knowledge axis |
| BrowseComp | https://arxiv.org/abs/2504.12516 | reference [131] | §6.1.1 agent axis |
| τ³-Banking | https://taubench.com/blog/tau-knowledge.html | reference [1] | §6.1.1 agent axis |
A common misreading: a URL in the reference list means “this project is open source”
It does not follow. Several entries in Table 3 point to leaderboard pages or company blogs, not code repositories — you can see the scores, but not necessarily the questions, and still less reliably the evaluation scripts. Chapter 14 explained why this is fatal for “reproducibility”. In the same way, Firecracker, OverlayBD, Triton and Megatron-LM in Table 2 really are open-source projects, but the paper gives only a citation, not a repository address — this chapter marks that down as it is, by the iron rule; if you want to use them, go and search the project name yourself.
You want to do three things: (a) see how KDA's context parallelism is actually written; (b) work out how the conclusion “perfect load balancing needs at most E/R redundant experts” is realised in engineering terms; (c) find a reference implementation you can check your own KDA against. Which project should you go to for each? And where does the paper give each address?
https://github.com/MoonshotAI/MoonEP. Now look up (a) and (c) yourself.https://github.com/fla-org/flash-linear-attention. (b) MoonEP, p. 19, footnote 3. (c) There are two routes: the Triton reference implementation in FLA (§5.1.1 says FlashKDA “substantially outperforms the Triton reference implementation”, so it exists inside FLA), and the PyTorch reference implementation that §4.2.4 says comes with every kernel task — the latter is the internal training-data construction the paper describes, and the paper does not say this PyTorch reference implementation was open-sourced. So the one actually available in practice is the former. For all three, the paper specifies neither file nor function names.Variant: if all you want is to understand how §5.3.2 gets “a paused sandbox takes neither memory nor CPU”, would you look in Table 1 or Table 2? And why might the answer be missing from both projects?
15.3 From Equation 1 to a kernel: what lies in between
Suppose you have actually opened up an implementation of KDA. On the screen are several hundred lines you cannot read. This section tells you what to go looking for.
First thing: find the state tensor. The St in Equation 1 is a dk×dv matrix, but in the code it will certainly not be two-dimensional — at the very least it carries a batch dimension and a head dimension as well. So what you are looking for is a four- or five-dimensional tensor, two of whose dimensions are dk and dv. Find it and you have found the anchor of the whole piece of code: every read and write of it is the whole of Equation 1.
Second thing: find the chunk loop. The chunkwise form of Equations 3 and 4 means the code must contain a serial loop running along the chunk index t, with large blocks of parallel computation inside the loop body. Find that loop and you have cut the code into two halves: the “serial skeleton” and the “parallel flesh”.
Third thing: find where Diag(αt) went. In Equation 1 it is a multiplication by a diagonal matrix. But nobody would really build a dk×dk diagonal matrix and then do a matrix multiplication — that wastes a factor of dk. In the code it will certainly have degenerated into an element-wise multiply (broadcasting a vector of length dk). This is the most classic single translation between “how maths is written” and “how code is written”; once you see through this one place, you know the pattern for translations of this kind.
Fourth thing: find that lower-triangular solve in the UT transform. Equation 4 needs U[t] and W[t], which come from the UT transform. The paper “leaves the derivation to Kimi Linear” and does not spell it out in the body. In code this step usually shows up as an inversion of, or a forward substitution on, a lower-triangular matrix of shape C×C (C is the chunk size). It is also the hardest piece of the whole code to read.
Fifth thing: find the rescaling by 1/Γ. §2.1.1 says outright that Equation 4 rescales the keys inside each chunk by the reciprocal of the cumulative decay, and that this reciprocal grows without bound. So somewhere near this place in the code there is very likely a clamp, a log-space computation, or an explicit promotion to FP32. Find it and you have found the numerically most fragile place in the whole implementation — and along the way you have verified that the discussion in §2.1.1 of the paper is not empty talk.
That phrase “scheduled and tuned independently” is worth another sentence. What it implies is that the optimal configuration for the two parts is simply not the same. The token-parallel part has a large, regular workload and can fill the GPU; the head-parallel part is a serial chain whose only parallelism comes from “how many attention heads there are”, and once there are not enough heads, large parts of the GPU sit spinning. Stuff them into the same kernel with the same launch parameters and you must sacrifice one side. Split them apart and each can be optimal — and the compute on one side can cover the waiting on the other.
Read the reference implementation first; do not start with the optimised one
The paper hands you the roadmap itself: there is a Triton reference implementation in FLA, and FlashKDA is a separate optimised implementation built on CUTLASS. The readability of the two differs by an order of magnitude. The reference implementation aims to “compute the right thing”, and its structure roughly follows the formulas; the optimised implementation aims to “compute fast”, and the shape of the formulas gets kneaded out of all recognition by memory layout, pipelining and the shape requirements of Tensor Cores. Read the reference implementation until you can line it up one-to-one with Equations 1, 3 and 4, and only then look at what deformations the optimised implementation makes — by then you can ask of every one of them “what is this working around?”
What this section is for
The five items above are a method for “what to go looking for”, not navigation to “which line”. The paper gives no file names, no function names and no line numbers, and this site has not visited these repositories. So treat every item above as a starting point for a search: searching the repository for words like chunk, state, decay, cumsum, tril, recurrent is far quicker than wandering around the directory tree. Whether what you find lines up with Equation 1 is for you to judge — and that is exactly the ability this chapter wants to train.
The paper says FlashKDA is “dispatched automatically as a backend of flash-linear-attention”. From that sentence alone, what kind of structure can you infer exists in FLA's code? Then take it one step further: if one day FlashKDA cannot run on some machine (say the GPU architecture is not supported), what happens from the user's point of view?
Variant: change “automatic dispatch” into “the user specifies the backend by hand”. Which of the three inferences above still hold? What are the benefits and drawbacks of that change, for the user and for the maintainer respectively?
15.4 Six steps for reading an unfamiliar repository
This section does not depend on any particular project. Any time you meet code you cannot follow, you can work through these steps.
1. Read the tests first, not the implementation. This is the most counter-intuitive one and also the most useful. The implementation answers “how it is done”, and what you are missing right now is “what it does” — and the test files answer exactly that: what this function eats, what it spits out, what counts as correct. Tests also usually have a minimal runnable example lying in them, more up to date than the README and more honest than the docs. How to find the tests: search the repository for test, or look for a directory called tests.
2. Get it running first, then read. Even if all you do is get the first command in the README to work. Code you cannot run is still only text however many times you read it; once it runs, you have a living thing you can change at will and add print statements to at will. Being able to change it and watch it raises your rate of understanding by an order of magnitude.
3. Find the reference implementation or the naive version. Almost every high-performance project keeps a “slow but correct” implementation around, used to check that the fast one is not wrong. That is the one written for people to read. The paper points the pattern out itself: the Triton reference implementation in FLA plays that role for FlashKDA, and the PyTorch reference implementation in §4.2.4 plays it for every one of the kernel tasks. Keywords to search for: naive, reference, ref, eager, torch.
4. Think in cross-checks. You have already done this with your own hands in the Chapter 2 lab: the same KDA, computed one way by the step-by-step recurrence of Equation 1 and the other way by the chunkwise parallel form of Equations 3–4, the difference between the two sides on the order of 1e-16 — that was not a coincidence, that was numerical evidence of a mathematical equivalence. In a real project this thing is called a unit test. Use it in reverse: to work out what a complicated implementation is doing, write a naive version yourself and cross-check it against that one. If they agree, your understanding is right; if they do not, the step where the difference shows up points precisely at the step you have misunderstood. This is the fastest route to understanding an algorithm, with nothing in second place.
5. Start from the shapes of the data. When you cannot follow a piece of code, do not chew on the logic; work out first what shape the tensors going in have and what shape comes out. Nearly all of the complexity in deep learning code sits in shape transformations: transposes, reshapes, chunking, broadcasting. Write the shapes down on paper and the code suddenly becomes readable. In practice this means inserting print statements everywhere, or stopping in a debugger and looking.
6. Use git log and blame as documentation. On the question “why is this line written this way”, comments often lie to you (written once and never updated again); commit messages do not — they record what problem the person at the time was solving when they added that line. When you see an inexplicable clamp, a magic number, a type conversion that looks redundant, go and check its commit history first; one sentence often clears it up.
An analogy
Reading unfamiliar code is like walking into a large museum with no guide. “Understand all of it” means starting in the first room and looking at every item in turn — three hours later you are in the second room, too tired to remember anything. The right move is to fix a target on the map at the entrance (I want to see that painting), head straight for it, and count whatever else you glimpse on the way as free.
Where the analogy breaks down: the exhibits in a museum are independent of each other, and code is not — the line you want to look at usually depends on three things you have not looked at. So the “target shooting” is usually recursive: to answer one question you are forced to answer two smaller ones first. That is not a detour, that is the road itself.
“My naive implementation cross-checks clean against the optimised implementation in the repository, so my understanding is right.” Construct a counterexample: the cross-check passes, but your understanding is in fact wrong. Give counterexamples with at least two different mechanisms.
(1) The input does not cover the branch. The sequence length is always a multiple of the chunk, so the tail-chunk logic is never triggered; or the samples in the batch all have the same length, so the variable-length packing path is never taken.
(2) The tolerance masks the difference. You use a tolerance of 1e-3, while the real numerical disagreement between the two implementations is on the order of 1e-4 — say one side accumulates in FP32 and the other in BF16. That is harmless on a small-scale test; put it on a sequence of 1 million tokens and the error piles up until it is visible to the naked eye. The reason that 1e-16 in Chapter 2 carries weight is precisely that it sits right against the machine precision of double, leaving almost no room to mask anything.
(3) Both sides are wrong in the same way. If your naive implementation was “translated” from the optimised one rather than written independently from Equation 1, then what you copied is the same misunderstanding. For a cross-check to mean anything, the two implementations have to come from independent sources.
(4) A constant cancels out. Say you have gmin wrong, but the test happens to initialise Ah to 0, and the two errors cancel each other.
Conclusion: a cross-check is a tool for falsifying, not for confirming. Passing only says “it has not been caught yet”. To make it stronger, aim at extreme inputs: lengths that do not divide evenly, a single token, very long sequences, an all-zero input, extreme decay values.
Variant: suppose the two implementations fail the cross-check, with an error on the order of 1e-2. How do you locate which step the problem is in? (Hint: think about whether bisection can be applied to “computation steps” and not only to arrays.)
In some KDA implementation you print an intermediate tensor and its shape is (B, H, N, C, C), where B is the batch, H the number of heads, N the number of chunks and C the chunk size. From the shape alone, can you guess which quantity in Equations 3–4 it corresponds to? And why can it not be the state S?
Variant: if the shape you see is (B, H, N, C, dk), what are the candidates? And if it is (B, H, N, dk, dv)? What is the latter most likely to be, and why does it take up so much memory?
15.5 A hands-on list: five practice projects
The five tasks below run from “an hour” to “a few weeks”. Each has an explicit completion standard — if you have not met the standard it is not done, and do not console yourself otherwise.
| # | What to do | Why it is worth it | Completion standard | Prerequisite skills |
|---|---|---|---|---|
| 1 | Implement the naive recurrence of Equation 1 of the paper in pure Python or NumPy. Make up random q, k, v, α, β and update St step by step in t. Under 30 lines. | Forces you to land every symbol of Equation 1 on a concrete variable. How Diag(αt) is actually written, that ktkt⊤ is an outer product and not an inner product — you only discover that you had not thought these through by doing it. | You can print how St changes with t (the Frobenius norm at each step, say), and observe that turning all the α down makes the norm decay faster. | Can write a loop; know NumPy's outer and matrix multiply |
| 2 | Implement the chunkwise parallel form of Equations 3–4 and cross-check it against step 1. | This is the site's most central instance of “two algorithms for the same thing”. The chunkwise form is the skeleton of every high-performance linear attention implementation; get it working and you can read the structure of a real kernel. | For any chunk size (including ones that do not divide the sequence length), a maximum absolute error against step 1 below 1e-12 (in float64). Note the word “any” — the tail chunk has to be handled correctly. | Task 1, plus an understanding of Equations 3 and 4 from Chapter 2 |
| 3 | Implement the Quantile Balancing of Equations 13–14 of the paper and compare its convergence speed against the fixed-step sign update bj(t+1) = bj(t) + γ·sign(·). | Lets you see with your own eyes how large the difference is between “solve it in one step” and “feel your way there one small step at a time”. This is the central claim of Chapter 10, and reading it a hundred times is worth less than drawing one convergence curve yourself. | Reproduce this phenomenon: QB balances the load within a few steps, while the fixed-step sign update needs dozens; and the number of steps the sign update takes depends strongly on the value you give γ, while QB has no such knob. | Can compute quantiles (numpy.quantile); understand Top-k routing |
| 4 | Implement the filling construction of Appendix E: generate router outputs at random, assign redundant experts by the paper's construction, and verify that the number of redundant experts needed never exceeds E/R. | Turns an existence proof into code that runs. The proof says “a feasible solution definitely exists”; the code forces you to answer “so how do you find it” — which is exactly the motivation behind what §5.2.1 calls “solve it exactly offline with integer programming, approximate it online with a GPU planning kernel”. | Run a thousand sets of random inputs with zero violations (in every set the number of redundant experts stays within E/R, and every rank receives exactly S×K tokens). | Task 3, plus the idea behind the construction in Appendix E from Chapter 10 |
| 5 | The hardest: download the K3 weights and get them running (even quantised, across several cards); or take a step back — read the KDA kernel in FLA until you understand it, change one parameter (the chunk size, say) and watch how performance changes. | The first makes you face head on what the words “trillion parameters” physically mean; the second connects “change the code” to “watch a number change” for the first time, and that is where all performance work starts. | The first: the model produces coherent text for a prompt. The second: you can draw the “chunk size → run time” curve and explain why the knee is where it is. | The first: a multi-card environment, an inference framework, a quantisation toolchain. The second: task 2 + being able to time things on a GPU |
On item 5, this site owes you an honest bucket of cold water
Table 1 of the paper states K3's total parameters as 2.78T. Even reckoned at 4-bit quantisation, half a byte per parameter, the weights alone come to 2.78×1012 × 0.5 ≈ 1.39×1012 bytes, that is about 1.4 TB. That does not yet count the KV cache, the KDA state, activations and runtime overhead. At the common spec of 80 GB per card, fitting the weights alone takes a dozen or more cards, and they have to be the kind with a high-speed interconnect.
The overwhelming majority of people do not have this hardware. That is not shameful, it is a fact. Three more realistic routes: first, tasks 1–4 in this table, all of which can be finished on a single laptop, and which train the same core set of abilities; second, read code — the FLA reference implementation will run small-scale examples on a CPU; third, use someone else's hosted inference service to observe the model's behaviour (long-context performance, say), keeping “deployment” and “understanding” as two separate things.
To be clear in passing: this site has not downloaded these weights, and has not verified whether that HuggingFace address is reachable right now or what the actual size of the files there is. The 1.4 TB above is estimated from the parameter count the paper gives and 4 bits, it is not a measured value.
A composite question. (a) Using the numbers in Table 1 of the paper, estimate how much space the weights take at each of two precisions: BF16 (2 bytes per parameter) and MXFP4 (about 0.5 bytes per parameter). (b) §4.1.4 of the paper says only the expert weights are quantised to MXFP4; the attention projections, the latent MoE projections, the shared experts and the router are all kept at higher precision. Does that choice make the estimate in (a) too high or too low? (c) Connect it back one step further: why does the paper start quantisation-aware training (QAT) during post-training rather than quantising once training is finished?
(a) BF16 about 5.6 TB, MXFP4 about 1.4 TB, a factor of 4 apart.
(b) Too low. The 1.4 TB is worked out as “every parameter at 0.5 bytes”, but the paper says plainly that the non-expert parts are kept at higher precision, so the real footprint is larger than 1.4 TB. How much larger depends on what share of the total parameters the non-expert parts are — the paper does not give that share, so all you can say is the qualitative “too low”, with no accurate number behind it. This is exactly where “estimated from the paper” and “measured” part ways.
(c) Three layers of reason. First, if you train at high precision and only squeeze down to 4 bits at deployment, the model has never seen the perturbation quantisation brings and performance drops; QAT lets the model adapt to that perturbation during training itself. Second, the paper says plainly that QAT runs from the SFT stage all the way through to RL. Third and most important: in the RL stage, if rollout (the model that produces the data) and training (the model whose parameters get updated) use different precision schemes, that amounts to updating model B with data from model A, which is a train–inference mismatch and contaminates the whole RL signal. The paper points at this layer with the phrase “eliminating the mismatch”.
Put the three parts together: a purely deployment-side optimisation (a factor of 4 saved in memory) forced a rebuild of the post-training pipeline (QAT moved earlier) — this is one more instance of “an architectural decision leaves debt at the system layer”, which this site keeps coming back to, except that this time the debt propagates backwards: a deployment constraint rewrote the training pipeline.
Variant: suppose someone quantises only the attention projections to 4 bits and keeps the expert weights in BF16, everything else unchanged. Roughly what order of magnitude of memory would that save? Why is this scheme close to pointless? (Hint: Table 1 says K3 has 896 routed experts and an MoE hidden size of 3072 per expert.)
This chapter says “read the tests first, not the implementation”. Construct a counterexample: a scenario that really exists, in which reading the tests first is slower and more likely to mislead you. At least two.
(1) There are no tests at all. Research code released alongside a paper is often like this. The fallback then is to look for the examples in the README, look for an
examples/ directory, or go straight for the __main__ entry point.(2) Snapshot tests (which only assert “the output is the same as last time”). They guarantee no regression, but they describe no semantics at all; reading them is like reading a pile of hashes.
(3) The test is more complicated than the implementation. Tests for high-performance kernels often come with a whole parameterisation framework, a random shape generator, a custom tolerance policy — to read the test you first have to read the test framework, which costs more than reading the implementation directly.
(4) The tests only cover end to end, not the layer you care about. The output of the whole model being right does not mean you can see from it what one step inside KDA is doing.
The corrected version: look first for a “minimal runnable example”; tests are usually the best carrier for one, but not the only carrier. If you cannot find one, fall back to examples, to README commands, or to writing a five-line call yourself. The real principle is not “read the tests”, it is “get hold of a minimal working input–output pair first”.
Variant: the other way round — in what kind of project does “read the tests first” pay off most? (Hint: think about the kind of library with a complicated interface, subtle semantics and many edge cases — a date-time library, say, or a variable-length sequence packing tool.)
15.6 Three things only code can teach you
First, numerical precision is a real engineering constraint, not a footnote. The paper has already shown you a corner of it: §2.1.1 says the rescaling factor overflows, so it has to be computed in log space and cut into 16-token sub-chunks; the end of §2.1.1 also mentions that, to correct the biased rounding error in flash attention, K3 keeps the attention output in FP32 during training, which doubles the on-chip occupancy, so the kernel then has to be redesigned to overlap it with the KV staging buffers. There you have it: one precision decision directly rewrote the memory layout of a kernel. And the density of this kind of thing in code is far higher than in the paper — behind every clamp, every .float(), every eps there may be a production incident.
Second, memory layout decides performance, and it is completely invisible in the formulas. §5.4.1 has a particularly good example: the paper says that within a cache page the state of all heads is stored contiguously head by head, so that each head's byte stream is self-contained and can be the smallest unit of cross-node transfer; then, when the prefill nodes and the decode nodes use different tensor parallel degrees, the relayout can be done on the transfer path, with zero reshuffling on the GPU side. There is not one formula in that whole line of reasoning, but it decides whether the system can do prefill/decode disaggregation. “How to lay it out” and “what to compute” are two independent degrees of freedom, and the paper writes only the second.
Third, you will start to be able to estimate “what one sentence in the paper costs in engineering”. That sentence in §5.2.1 — “MoonEP retains DeepEP's overall computation flow and additionally introduces online planning and migration of redundant experts” — before you read code, it is a flat description; after you have, you know that “online planning” means writing a planning kernel that runs on the GPU, guaranteeing that its overhead is negligible, guaranteeing that it never crosses the E/R upper bound; and “migration” means expert weights have to be moved between ranks, with the timing of the move aligned to the forward and backward pipeline. This ability to estimate is the key to judging “where a paper is most valuable” — usually the most valuable place is precisely the sentence described most flatly, because the authors themselves think of it as dirty work and are too embarrassed to expand on it.
Defend it: if I were a reviewer
You say K3 is “open”, and the paper's abstract says the full model weights are released. But the training data is not public, the training code is not public, the data mixture is not public, and the hyperparameters are not given in full. So does this count as open source? If it does not, why do the whole paper and the whole community use the word “open”?
A reference defence (put it in your own words first)
Answer in three layers. Layer one, concede the facts. The words the paper uses are “open weights” and “we release the full Kimi K3 model weights”, and what they refer to is the weights. As for the training data, the training code and the full hyperparameters, the paper genuinely does not promise to release them, and genuinely does not give them. So by the traditional standard of “open source software” (with the source in hand you can rebuild the artefact yourself), K3 does not qualify — you cannot get hold of anything that would let you rebuild this model.
Layer two, be clear about why the word still means something. The capabilities that open weights unlock are real: you can deploy locally, you can fine-tune, you can do interpretability research, you can run safety evaluations on it without having to ask anyone. The paper positions itself as an “open frontier” — what it claims is “frontier capability is now within everyone's reach”, not “you can reproduce this training process”. Those two claims differ a great deal in strength; do not run them together. And Table 1 in this chapter shows that they did open-source a body of real code: FlashKDA, MoonEP, AgentENV, MiniTriton, nano-kpu, plus a PR filed upstream to FLA. This is not openness in words only.
Layer three, point at what actually deserves criticism. The criticism should land precisely on “using the word ‘open source’ to harvest the credibility of ‘reproducible’”, rather than saying loosely “it does not count as open source”. Concretely: the paper's evaluation results cannot be independently reproduced (the data is not public, so you cannot rule out data contamination); the scaling law conclusions cannot be checked (the mixture and the hyperparameters are incomplete); and that sentence in §7, “an early K3 checkpoint is already carrying our own kernel optimisation work”, especially needs pressing on — the data from that kind of task has very likely flowed back into training. These are the substantive losses that “not fully open” causes. Whether the weights themselves are open is, of all of this, the least important link.
The one-line defence: “open weights” is an accurate description and “open source” is an abused word; the right move is not to argue about the label but to list item by item “what is public, what is not, and which conclusions the non-public part makes impossible to check”.
Defend it: if I were a reviewer (round two)
This chapter keeps saying “reading code teaches you things the paper cannot”. So the other way round — when is reading code a waste of time? Give me three cases, and no fine words.
A reference defence (put it in your own words first)
Case one: you have not worked out the “why” yet. Go and read the KDA kernel without having understood Equation 1 and all you see is a pile of tensor operations, with no way to tell which step is a mathematical requirement, which is a performance compromise and which is historical baggage. Reading code at that point is not learning, it is transcription. The right order is always paper first, code second.
Case two: the question you are answering is “is it worth it” rather than “how do you do it”. Take “how much does KDA save against full attention at a context of 1 million” — that is a design trade-off question, and the source of the answer is the paper's charts, ablations and scaling curves. Code cannot answer it; code only tells you how fast one particular implementation runs, and that is a number contaminated by a dozen or more irrelevant variables.
Case three: this code is written for machines to read, not for people. Highly optimised kernels, generated code, compiler IR — readability was never among their design goals. The return on forcing your way through code like that is extremely low. The right move then is to read its corresponding reference implementation, or read its tests, or read the design document the authors wrote — that line “read the reference implementation first” in Table 1 of this chapter is exactly this point.
One hidden case on top of those: the code may already be out of date. The paper was written at one point in time, and the repository keeps moving forward. The implementation you are reading may no longer be the version the paper describes. At that point git log is worth more than the code itself — it can tell you which commit the paper's version sits on.
The one-line defence: code is the sole authority on “how it is done”, but it is nearly silent on “why it is done this way” and “whether it is worth doing this way”. Using the wrong tool wastes more time than using no tool.
Chapter summary
This chapter did three things. One, it gave you a sense of the distance: §5.1.1 of the paper describes FlashKDA in four lines, and behind those four lines sit at least four kinds of thing — chunk boundaries, numerical precision, kernel launch configuration, and the dispatch strategy across several implementations — the distance between “understanding the formula” and “being able to implement it” is exactly the part the paper leaves out. Two, it built an index: three tables record, word for word, the addresses that appear in the paper, including the six things Moonshot open-sourced itself (the K3 weights, FlashKDA, MoonEP, AgentENV, MiniTriton, nano-kpu, plus PR #691 filed upstream to FLA), some thirty third-party projects named in it, and a dozen or so evaluation benchmarks. Three, it gave you a method: read the tests first, get it running first, find the reference implementation, cross-check, look at the shapes, check git log, plus the most important one of all — go in with one specific question, and stop once it is answered.
On reliability, hold these two together. First, every URL in this chapter was copied from the paper word for word; not one was filled in, not one was guessed. Second, this site has not visited any of them, and has not located any file, function or line number in any repository — wherever a specific location would be needed, what this chapter gives is “which keyword to search for” and not “which file to open”. This distinction is not caution taken too far; it is itself what this chapter is trying to teach: separating “what I know” from “what I think I know” is the first piece of basic technique that reading code and reading papers have in common.
Finally, of the five tasks in Table 4, the first four can all be finished on an ordinary laptop, and the abilities they train are the same set as the fifth one trains. That 1.4 TB bucket of cold water is thrown at the hardware, not at you.
第16章 终极闯关:跨章综合、总答辩与研究课题
前面十五章是拆解,这一章是重装。这里的题一道也不局限在单章之内——它们要求你把架构、训练、系统、评测四条线同时握在手里。做完这一章,你就不只是「读过」这篇论文了。最后一节是研究模式:两个开放课题,我会明确告诉你哪个是真实的未解问题、哪个只是对你而言的未知领域。
通关标准
- 八道综合题,每题必须做对变式才算过
- 三场总答辩,你要在不看参考防守的情况下先自己组织一遍
- 最后的「结业自查表」上,每一条都能不看材料说清
- 选一个研究课题,写出你的入手方案(不需要完成,需要方案)
16.1 第一关:因果链复原
这一关考的是全站最重要的思维方式——每一个架构决定都会在系统层留下一笔债。
下面是 K3 的四个架构层决定。请为每一个追踪出至少两处它在论文第 5 节(基础设施)或第 4.1.4 节(部署感知后训练)里造成的具体后果,并指明在本站哪一章。
① 每个 block 用 3 层 KDA + 1 层 Gated MLA
② 93 层用 Attention Residuals 而非标准残差
③ 896 个路由专家、每 token 激活 16 个
④ 训练上下文长度 100 万 token
① 3 KDA + 1 Gated MLA
· §5.1.1(第10章):KDA 递推串行 → 天真实现下 SM 在状态传播时闲置 → FlashKDA 用 CUTLASS 把块内计算与跨块状态传播重叠;另外为长上下文预填充做设备内 SM 级上下文并行。
· §5.1.2(第10章):delta 规则把依赖 token 的矩阵 Mt 作用在传入状态上 → 局部片段的效果取决于进入它的状态 → 普通线性注意力「从零算再求和」的做法失效 → 必须发明 KCP,把片段效果拆成「累积转移」和「从零生成的状态」两个可本地计算的量,用一次固定大小 all-gather 加前缀扫描重建。
· §5.4.1(第11章):MLA KV 缓存按 token 分页、随长度增长;KDA 状态固定大小、每请求一份。两者必须在同一个边界上一起恢复才能复用前缀 → 统一分页布局 + 解耦哈希粒度与物理块粒度 + 三个并发一致性机制。
· §5.4.2(第11章):KDA 解码时状态原地更新 → MTP 推测解码验证失败时状态已越过最后一个被接受的 token、无法回滚 → 只缓存投影输入、在片上重建被接受 token 的状态。
② Attention Residuals
· §2.2 本身(第4章):完整形式是 O(Ld) 显存 + 流水线并行下的跨阶段通信 → 这就是 Block AttnRes(切成 8 块,降到 O(Nd))存在的全部理由。
· §5.2.2(第10章):块表示在边界层生成一次、后续层共享、直接驻留 GPU;整个 AttnRes 计算被 checkpointing 包住,使每层为反向保存的激活与标准残差架构完全相同;流水线并行用基于缓存的通信,只增量传输新块、微批次一结束就释放。
· §5.4.2(第11章):两阶段调度(块间 pass + 块内 online-softmax 归并);预填充时用序列并行避免在每个 TP rank 上重复实体化块表示;解码时块间内核放旁路 stream,块内内核融进前面的 TP all-reduce。
③ 896 专家 / 激活 16
· §2.3.3(第5章):近 10³ 个专家超出了定步长符号偏置更新的良好工作范围 → Quantile Balancing。
· §5.2.1(第10、14章):专家并行下 token 负载不均衡 → MoonEP 用动态冗余专家做完美均衡,并证明每 rank 至多 E/R 个冗余专家;完美均衡进一步带来静态形状(消除逐层主机同步)和固定 S×K 通信缓冲区(而非 S×K×R)。
· §4.1.4(第8章):2.78 万亿参数在部署时装不下 → MXFP4 量化专家权重、MXFP8 激活,且从 SFT 阶段起做 QAT,RL 时 rollout 与训练共享同一量化方案以消除训练–推理不匹配。
· §5.4.2(第11章):小批量解码时 group GEMM 退化成内存受限的权重流式读取 → 基于 WarpDecode 的 token 中心内核,warp 再细分成 lane team,权重布局离线置换以降低运行时反量化开销。
④ 100 万上下文
· §3.4(第7章):NoPE + 四阶段渐进课程(8K→64K 预训练,256K→1M 冷却)+ 长文档上采样 + 合成需要跨全长注意的任务。
· §5.1.2(第10章):单卡放不下 100 万 token 的激活 → 上下文并行 → 对 KDA 就是 KCP。
· §5.3.1(第11章):1M 上下文的 RL 里,要为下一轮持久保存的 rollout KV 缓存与训练所需内存互相争抢 → 外部 KV 缓存池(写回而非写穿)+ 训练状态卸载到 NVMe + 自动限流调度器。
· §5.4.3(第11章):生产流量里每请求成本跨三个数量级 → 缓存感知亲和调度(一致性哈希钉两个集群)+ 基于预算的准入控制。
如果你答出了三分之二以上,你已经真正读懂了这篇论文的结构。这篇报告的第 2 节和第 5 节不是两个独立的部分,而是同一组决策的正面和背面。
变式:现在反向做一次。论文 §5.2.2 提到「用 Mooncake Transfer Engine 把激活远程卸载到其他流水线并行 rank 的内存」。请反推:是哪一个更上游的决定导致了「激活在各 PP rank 间分布不均」?(提示:不是架构决定,而是一个并行策略的决定,与流水线的预热阶段有关。)再问:这个不均衡与 §5.2.3 里「把 ViT 计算塞进流水线气泡」利用的是同一个现象吗?
16.2 第二关:数字对账
论文里散落着很多数字。下面四组数字之间存在可验证的一致性关系或明显的张力。请逐组分析。
(a) 表 1:层数 93;注意力层构成「69 KDA + 24 MLA」;§2.1 说「每个 block 是 3 个 KDA 层后跟 1 个 Gated MLA 层」,且「骨干末尾额外加一个 Gated MLA 层」。
(b) §2.2 说「把层分成 8 个块,每块 12 层,产生一个不完整的末块,算上词嵌入层共 9 个块」。
(c) 表 1:路由专家 896,每 token 激活 16,总参数 2.78T,激活参数 104.2B。
(d) §7 案例:AttnRes 内核延迟从 283.6 ms 降到 114.4 ms;图 14 标注 K3 相对 FLA Triton 基线加速 +59.7%。
(a) 完全自洽,而且能反推出论文没写的数字。
设有 B 个完整 block。每个 block = 3 KDA + 1 MLA = 4 层。加上末尾额外的 1 个 Gated MLA:
总层数 = 4B + 1 = 93 → B = 23
KDA 层 = 3 × 23 = 69 ✓
MLA 层 = 23 + 1 = 24 ✓
论文从未写出「23 个 block」这个数,但它可以被完整反推。这也解释了 69/24 = 2.875 ≠ 3 的「矛盾」:3:1 是块内比例,全局比例被末尾那一层拉低了。
(b) 字面读会出矛盾,需要修正理解。
8 × 12 = 96 ≠ 93。而论文又说「产生一个不完整的末块」。所以正确读法是:前 7 块各 12 层(84 层),第 8 块只有 9 层(84 + 9 = 93)。再加上词嵌入层作为一个信息源,共 9 个源。这与式 10 里 b0 = h1(词嵌入永远是一个源)一致。
但请注意:「末块是 9 层」是推断,论文没有明写。另外,AttnRes 的 12 层块边界与注意力的 4 层 block 边界是否对齐(12 是 4 的倍数,看起来像是刻意的),论文完全没有说明。
(c) 可以做一致性检查,但无法精确核对。
激活参数 / 总参数 = 104.2B / 2780B ≈ 3.75%。而 896 个专家里激活 16 个,专家部分的激活比例是 16/896 ≈ 1.79%。两者不等是正常的——因为激活参数里还包含了所有非专家部分(注意力投影、共享专家、路由器、词嵌入),这些是每个 token 都要过的。
从「3.75% > 1.79%」可以定性推断:非专家部分在激活参数里占了可观的份额。但要精确核对需要知道每个模块的参数量,论文没有给出足够的分解信息。这是一个可以做定性检查、无法做定量核对的关系。
(d) 高度吻合,但定义未给出,只能算合理对账。
283.6 → 114.4 ms。若把「加速」定义为节省的时间比例:(283.6 − 114.4)/283.6 = 59.66%,与图 14 标的 +59.7% 几乎完全一致。
但若定义为加速倍数减一:283.6/114.4 − 1 = 148%,就对不上了。
所以可以推断图 14 的纵轴用的是前一种定义(时间节省比例)。但论文没有给出这个定义的公式,这只是一次成功的反推,不是论文明说的。读到任何标着「speedup」的图,先确认它的定义——这两种定义在文献里都有人用,差别巨大。
这道题的意义:技术报告里的数字不是孤立的装饰,它们之间有可验证的约束。养成「随手对账」的习惯,你会发现三类东西:被验证的自洽(提高你对全文的信任)、可反推的隐含信息(比如 23 个 block)、真实的表述含糊(比如 8×12 和 speedup 的定义)。
变式:再做一组。论文 §5.3.2 说全程创建了 51,219,741 个沙箱、跨 1,505,678 个镜像;又说「暂停的沙箱不消耗内存或 CPU 资源,而等待模型推理可占沙箱生命周期高达 98%」;还说实现了「高达 6.5× 的内存超额分配比」。(a) 平均每个镜像对应多少个沙箱?(b) 「98% 的时间可暂停」和「6.5× 超额分配」这两个数字之间,是互相支撑还是互相冗余?如果一个沙箱 98% 的时间都不占内存,那超额分配比理论上可以达到多少?6.5× 相对这个理论值说明了什么?
16.3 第三关:设计决策
现在你是架构师。你要为一个新场景设计模型,约束如下:
· 上下文长度只需要 32K(不是 1M)
· 部署在单张 80GB 显卡上,不允许多卡
· 主要用途是高并发的短对话客服,每个请求 2K token 以内,QPS 极高
· 允许你从 K3 的设计里任意取舍
请逐条判断:KDA、Gated MLA 的 3:1 混合、Block AttnRes、896 专家的 LatentMoE、MXFP4 量化、推测解码、前缀缓存——这七样各自保留、修改还是砍掉?每一条给出理由。
2.78T × 0.5 字节 ≈ 1.39 TB,是 80GB 的 17 倍。所以模型规模必须大幅缩小,896 专家是不可能的。
再算第一个约束:32K 上下文下,第0章那道题的算法给出 KV 缓存 = 46.5 KB/token × 32K ≈ 1.5 GB。这在 80GB 里完全可以接受。
所以 KDA 的核心收益(省 KV 缓存)在这个场景下……你自己接着推。
KDA — 大幅缩减比例,甚至砍掉。 它的收益是「KV 缓存不随长度增长」。32K 下 KV 缓存约 1.5 GB(按第0章的算法),在 80GB 里微不足道。而 KDA 的代价(串行递推、需要专门内核、上下文并行要重新发明、推测解码无法回滚、前缀缓存粒度冲突)一样也不会少。
但有一个反对意见你必须考虑:高 QPS 意味着要同时装下几百个请求的 KV 缓存。1.5 GB × 200 并发 = 300 GB,就装不下了。所以真正的判断依据不是「上下文多长」,而是「并发数 × 单请求缓存」。这个场景下 KDA 可能仍然有价值,只是理由从「支持长上下文」变成了「支持高并发」。能识别出这个转折,是这道题的核心考点。
3:1 混合 — 保留,但比例需要重新搜索。 3:1 是在 1M 上下文下定的。32K 下 MLA 层便宜得多,可以放宽到 1:1 甚至全 MLA。论文没有给任何比例消融,所以这只能靠自己重新实验。
Block AttnRes — 保留,且更划算。 它的开销是 O(Nd) 显存和跨阶段通信,而跨阶段通信在单卡场景下完全消失(没有流水线并行)。它的收益(深度方向的选择性信息检索)与上下文长度无关。这是七项里性价比变得最高的一个。不过要注意:模型变小了,层数变少,AttnRes 的收益本身也会缩水(论文引用的 N≈8 是在更深的网络上)。
896 专家 LatentMoE — 必须大幅缩减,但保留 MoE 结构。 参数装不下,专家数要砍一到两个数量级。但 MoE 的核心收益(总参数与计算量脱钩)在高 QPS 场景下依然极有价值——你希望模型知识多但每 token 便宜。另外,单卡意味着没有专家并行,于是 MoonEP 和负载均衡的跨卡动机消失;但卡内各专家 token 数的倾斜依然存在(论文 §5.2.1 的「Expert-GEMM 调度」那一段讲的正是这个),所以 Quantile Balancing 仍有意义——它服务的是「专家训练是否充分」,而不只是「卡间负载」。
MXFP4 — 保留,且是最该保留的。 单卡 80GB 的硬约束下,量化直接决定了你能装多大的模型。而且论文的 QAT 做法(从 SFT 起就量化、RL 时 rollout 与训练共享量化方案)解决的是「训练–推理不匹配」,这个问题与场景无关。
推测解码 — 保留,但收益可能下降。 它摊薄的是「每个 token 都要把权重流一遍」的带宽成本。但注意:高 QPS 意味着大批量,而大批量下带宽成本本来就被摊薄了(多个请求共享一次权重读取)。所以推测解码在低并发时收益最大、高并发时收益缩水。这个场景下它可能不是优先项。论文 §5.4.2 恰好提到「这个代价在在线服务典型的大批量下会占主导」,说的是状态快照的代价,但同一个「大批量」逻辑也适用于推测解码的收益评估。
前缀缓存 — 保留,且极其重要。 客服场景有大量共享的系统提示(人设、规则、知识库摘要),这部分前缀在所有请求间完全相同。前缀缓存能让这部分只算一次。而且如果你砍掉了 KDA,前缀缓存的实现会简单一个数量级——§5.4.1 那一整节的复杂度(统一分页布局、解耦哈希粒度、三个并发一致性机制)全部来自 KDA 状态与 MLA 缓存的粒度冲突。这是「砍掉 KDA」这个选项一个被低估的收益。
这道题真正想训练的能力:不是记住 K3 怎么做的,而是能把每个设计还原成「它在解决什么压力」,然后判断新场景下这个压力还在不在。注意其中至少三处,正确答案与直觉相反:KDA 的价值来源从「长上下文」转移到了「高并发」;推测解码的收益随并发上升而下降;砍掉 KDA 的最大收益之一是前缀缓存变简单。
变式:换一个场景。约束改成:上下文 1M,但只做单用户、低并发的离线文档分析,延迟不敏感(可以跑一整晚),有 8 张卡。请重做这七项判断,并明确指出哪几项的结论与上一题相反、为什么。
16.4 第四关:证据审计
请从整篇论文中挑出你认为证据最薄弱的三个主张,对每一个说明:论文声称了什么、给了什么证据、缺什么证据、以及需要什么样的实验才能真正支撑它。
要求:三个主张必须来自不同的章节(不能都挑评测部分)。
① §3.2「约 2.5× 的整体缩放效率提升」——打包型。
· 声称:KDA + AttnRes + Stable LatentMoE + 精炼的数据与训练配方,合起来带来相对 K2 约 2.5× 的缩放效率。
· 证据:图 7 的两条拟合曲线,标注了一处 2.5× 的箭头。
· 缺什么:图上没有坐标轴数值,无法反推任何具体损失;没有任何单项消融,四个因素的贡献完全无法拆分;没有说明这个倍数在整个 FLOPs 区间是否恒定(两条线是否平行未知);也没给拟合的置信区间。
· 需要什么实验:逐项消融的缩放定律——固定其他三项,只改一项,各画一条曲线。这需要四到五组独立的缩放实验,成本很高,但这是唯一能把 2.5× 拆开的办法。
② §2.4「对比预训练作为多模态语言模型的初始化不是必需的」——外推型。
· 声称:MoonViT-V2 从零用下一 token 预测训练,在视觉评测上追平了 SigLIP 初始化的基线,说明在这个规模上对比预训练不是必需的。
· 证据:图 6 的梯度范数曲线(显示 from-scratch 更稳),以及一句「matches the baseline」。
· 缺什么:这两页里没有给出任何评测表或分数,「追平」具体是在哪些评测集上、差多少,读者无从核对;图 6 是过程指标(梯度范数)而非结果指标,没有展示任何一次真实的训练崩溃,也没给两条路的最终损失;结论加了 「at scale」 限定却没给规模下界;这是一次对比,不是多次独立实验。
· 需要什么实验:在至少三个不同规模上各做一组 from-scratch vs SigLIP-init 的对照,报告最终损失和一组固定的视觉评测分数,并给出方差。这样才能支撑「at scale」这个限定,并找出规模阈值。
③ §5 的系统优化「大幅超过 Triton 参考实现」等——定性型,且这是全篇最普遍的问题。
· 声称:FlashKDA「大幅超过 Triton 参考实现」;KDA 解码的验证延迟「次线性增长且低于状态缓存基线」;ViT 计算「基本消除了视觉编码器的有效开销」;统一激活管理器的预取「额外开销可忽略」。
· 证据:全部只有文字,没有一个数字。
· 缺什么:加速比、吞吐提升、内存节省量、开销占比——一个都没有。整个第 5 节(除了沙箱的 133ms/49ms/6.5×/5100 万这几个数)几乎没有性能数据。
· 需要什么实验:其实不需要新实验,只需要把已有的测量报告出来。这些优化显然是被测过的(否则不会上线),只是没写进报告。这是披露问题而非科学问题——但对读者而言效果一样:无法判断哪个优化重要、无法复现、无法在自己的系统里做取舍。
这道题的意义:批判性阅读的终点不是「挑毛病」,而是知道自己能从这篇论文里安全地拿走什么。① 之后你能拿走的是「这套组合在作者的配方下有效」,不能拿走「KDA 值 X 倍」;② 之后你能拿走的是「from-scratch 在这个规模上可行」,不能拿走「对比预训练没用」;③ 之后你能拿走的是「这些优化的设计思路」(这部分论文写得很详细,是真正的贡献),不能拿走任何性能预期。
变式:现在做一次反向审计。请从论文里挑出三个你认为证据最扎实的主张,说明它们为什么扎实。(提示:想想哪些主张是数学上可验证的、哪些是任何人都能独立复现的、哪些有第三方独立评估背书。这三类各挑一个。)
16.5 总答辩:三场
答辩一:这篇论文的核心贡献究竟是什么
我的攻击是:这篇论文里没有一个真正的新想法。KDA 是 Kimi Linear 的小改(换了个衰减函数);AttnRes 是引用别人的工作;LatentMoE 是引用别人的工作;MoE、MLA、Muon、推测解码、EAGLE-3、量化训练全是现成的。所谓的 K3 只是一次规模更大的工程集成。既然如此,它凭什么值一篇 47 页的技术报告?
参考防守(先自己组织语言再看,这场要写出至少三点)
先承认攻击的合理部分:这个描述在组件层面基本准确。论文自己也大方地标注了每个组件的来源(KDA 引用 Kimi Linear,AttnRes 引用 [57],LatentMoE 引用 [32],MLA 引用 DeepSeek-V2,Muon、EAGLE-3、MXFP4 全都引用了原始工作)。如果你的评价标准是「有没有一个全新的数学对象」,这篇论文的得分不高。
但辩护有四层:
第一层:有些「小改」是有实质内容的。下界衰减不是「换个函数」这么简单——它是一条完整的推理链:注意到 1/Γ 会溢出 → 前代靠划分 16-token 小片绕开、但对角小片仍需位置对计算 → 与其绕开不如从源头限制 → 反推出 gmin = −5 使 e80 落在 BF16 内 → 对角路径整个消失。这是「硬件约束反向决定数学形式」的一个干净例子,其价值不在于函数本身,而在于这条推理链可以被迁移到其他递推模型上。Quantile Balancing 同理——它把一个启发式的符号更新,还原成一个可证明的对偶问题的精确坐标极小化,从而解释了「为什么不需要学习率」。把启发式还原成有原理的方法,本身就是贡献。
第二层:MoonEP 的界是一个真正的新定理。附录 E 证明了「每 rank 至多 E/R 个冗余专家总能实现完美均衡」,并证明这个界本质上是紧的。这不是工程调参,这是一个有构造性证明和紧性证明的组合优化结果,而且它改变了系统设计——因为界是可证的,所以可以预留固定槽位、保证训练永不中断,这与 ECHO/UltraEP 那类「预设上限、无解就停」的做法有质的区别。
第三层:集成本身在这个规模上是非平凡的。攻击里「只是工程集成」这个说法,隐含了「集成是容易的」这个前提,而这个前提是错的。论文第 5 节展示的正是:每个组件的选择都会让其他组件变难。KDA + MLA 混合让前缀缓存的粒度对齐成为一个需要三个一致性机制才能解决的问题;896 专家 + 2.8T 参数让 EP 负载均衡从「调优问题」变成「可行性问题」;1M 上下文 + 部分 rollout 让 KV 缓存与训练内存直接冲突。这些问题在小规模下不存在,所以它们的解法也无法从小规模工作里外推。
第四层(最重要):这类报告的价值不在新颖性,而在信息披露。把一个 2.8 万亿参数模型造出来,需要成千上万个决策。绝大多数被造出来的前沿模型什么都不说。这篇报告说了:架构的每个参数、训练课程的分段、RL 的三领域九专家结构、九类 RL 环境的设计、沙箱的三个高层操作、前缀缓存的三个并发失效模式。这些信息对整个领域的价值,与「是否有新数学」是两个正交的维度。
但攻击有一点是站得住的,值得记住:正因为组件都来自别处,「这些组件在 2.8T 规模上是否仍然有效」就成了一个开放问题,而论文没有做任何单项消融来回答它。所以你不能从这篇报告推出「AttnRes 在万亿规模上有用」——你只能推出「作者把它用了,整体结果不错」。
答辩二:开源了什么,没开源什么
论文反复强调 K3 是「世界上第一个开放的 3T 级模型」,把「开放」当作核心贡献之一。我的攻击是:它开放的只是权重——训练数据没有、数据配比没有、训练代码没有、超参数大部分没有、算力规模没有、成本没有。一个我无法复现的模型,「开放」这个词用在它身上算不算过誉?而且,2.78 万亿被称为「3T 级」,这本身是不是也在修辞上占便宜?
参考防守(先自己组织语言再看)
关于「3T 级」:这个攻击成立,但杀伤力有限。2.78T 向上取整到「3T-class」是量级修辞,在学界是常见做法(就像「百亿参数模型」通常不是恰好 100 亿)。它确实在措辞上占了便宜,但「3T 级」作为一个量级描述并不失实,而且论文在表 1 里给出了精确值 2.78T,没有隐瞒。这属于可指出但不必深究的那一类。
关于「开放」:这个攻击更实质,需要分层回应。
第一,要承认「开放权重」 ≠ 「开源」。严格意义上的开源需要能复现整个构建过程。K3 给的是权重,不是配方。论文里能查到的训练细节确实是残缺的:数据配比只说「由小模型消融决定」没给数字;四阶段上下文课程只给了 8K/64K/256K/1M 四个端点,各阶段占多少预算没说;训练用了多少 GPU、多少 token、总成本,一概没有;RL 的关键超参数(λ、τ、σ、R_max)全部缺失。你无法用这篇报告复现 K3。
第二,但「开放权重」本身的价值是真实且巨大的。它意味着:任何人可以下载、微调、部署、审计、做机制可解释性研究、在自己的任务上评测而不受 API 限制、在没有网络的环境里运行。这些事在闭源模型上一件也做不了。而且论文第 6.2.2 节还提到一个具体的后果:Anthropic 和 OpenAI 的前沿模型拒绝网络安全相关任务,导致无法做可比评测——而开放权重的模型可以被独立第三方(论文提到了英国 AISI 和 NIST CAISI)在完全受控的条件下评估。可审计性是开放权重的一个被低估的价值。
第三,「能不能复现」这个标准,对这个规模是不现实的。即使论文公开了全部数据和代码,能复现 2.8 万亿参数训练的机构在全世界不超过十家。所以「可复现」作为评判标准,在这个规模上区分不出任何东西。更有意义的标准是「披露了多少可迁移的知识」——按这个标准,这篇报告的信息密度是相当高的(第 5 节的系统设计几乎每一段都可以被别人拿去用)。
第四,也是最该说的一点:措辞的准确性依然重要。论文用的是 「we release the full Kimi K3 model weights」(我们释放完整的 K3 模型权重)——这个表述是准确的,说的就是权重。摘要里「open frontier」的说法则更松一些。作为读者,正确的做法是以精确表述为准,把宽泛表述当作叙事。这也是第1章教的证据分级的又一次应用。
最后一个诚实的补充:本站没有核实过权重是否真的可下载、许可证条款是什么、有没有使用限制。论文只给了一个 HuggingFace 链接。「开放」到什么程度,最终取决于许可证,而这篇报告没有讨论许可证。这是一个真实的信息缺口。
答辩三:换你来当审稿人
这一场没有参考答案,因为攻击方是你。
请你写出三条给作者的修改意见,每条包含:具体指向论文的哪一节、你认为的问题是什么、以及一个具体可执行的改进建议(不能是「应该做更多实验」这种空话,要说清做什么实验、测什么指标、和什么对照)。
写完之后,再为每一条估计作者拒绝它的最可能理由。
写完你自己的三条之后,再看这里的自查标准
好的审稿意见有三个特征,对照检查你写的:
① 指向具体。「§3.2 的 2.5× 缺少消融」是好的;「实验不够充分」是坏的。
② 建议可执行。「在 1B、7B、30B 三个规模上各做一组 from-scratch vs SigLIP-init 的对照,报告最终验证损失和 MMMU-Pro/OmniDocBench 两个评测的分数与三次运行的标准差」是好的;「应该验证一下这个结论」是坏的。
③ 承认成本。顶级的审稿意见会主动区分「这个实验很便宜,没做说不过去」和「这个实验极贵,理解为什么没做,但请在文中明确声明这个局限」。第二种意见几乎总是会被采纳,而第一种常常引发对抗。
关于「估计作者拒绝的理由」这一步——这不是在教你妥协,而是在训练一个更重要的能力:区分「证据缺口」和「披露缺口」。
比如「§5 全篇没有加速比」,作者最可能的拒绝理由是「这些数字涉及内部基础设施细节」。如果是这样,那这就不是科学问题而是商业决定,你的意见应该改成「请至少给出相对倍数而非绝对数值」——这样绕开了披露顾虑,仍然恢复了可比性。
而「§3.2 的 2.5× 缺少消融」,作者最可能的拒绝理由是「每组消融都要重跑完整的缩放定律实验,成本以百万美元计」。这个理由是真实的。所以更现实的意见是「请在文中明确声明 2.5× 是四个因素的联合效应,各自贡献未知,不应被引用为单个组件的收益」——这几乎零成本,且实质性地提高了论文的诚实度。
如果你的三条意见都能通过这个自查,你已经具备了给这类论文写审稿意见的能力。
16.6 结业自查表
下面每一条都应该能不看材料说清楚。勾不上的,回对应章节。
16.7 研究模式:两个开放课题
学完材料之后,真正的开始是能自己提问。下面给你两个课题。按照本站的约定,我会明确区分它们的性质。
课题一:混合比例的可迁移规律(真实的未解问题)
性质说明
这是一个学界尚无定论的真实开放问题,不是「只有你不知道答案」。据我所知,截至这篇报告,还没有工作系统性地回答「线性注意力与全注意力的最优混合比例如何随模型规模、上下文长度、任务类型变化」。K3 用 3:1,前代 Kimi Linear 也用 3:1,但两篇论文都没有给出比例消融。
问题:给定模型规模 N、目标上下文长度 L、任务分布 D,最优的 KDA:MLA 比例是多少?这个比例有没有可预测的规律?
为什么它重要:如果比例是随规模变化的,那么所有在小模型上验证过的混合架构结论都不能外推——而这正是当前领域的普遍做法。如果比例是恒定的,那需要解释为什么。更进一步:K3 把位置编码的全部责任交给了 KDA(因为 MLA 层用 NoPE),这意味着比例不只影响效率,还影响模型能否感知词序。降低 KDA 比例是否存在一个「位置信息不足」的硬下界?
入手之前,先回答这五个问题
你打算怎么测量「最优」?验证损失?某类下游任务?还是专门设计的探针任务?三者可能给出不同的最优比例——你怎么处理这个分歧?
一点提示(但请先自己想)
验证损失是最省事的,但它对「长程检索」这类能力极不敏感——一个完全丧失长程能力的模型,验证损失可能只差一点点。所以至少需要配一个直接探测长程依赖的指标。经典做法是「大海捞针」类的合成任务,但它太简单,容易饱和。更好的做法是设计需要跨越全长做多跳推理的任务——注意论文 §3.4 说他们合成长上下文数据的办法正是「排列并拼接文档和子任务,使嵌入其中的任务只有靠关注散落在全长的信息才能解决」。这个思路可以直接借来做评测。
你能负担的最大规模是多少?如果只能训到 1B,你怎么论证结论对 100B 有效?
一点提示
诚实的答案是:你不能直接论证。但你可以做三个以上规模点,观察最优比例随规模的趋势(单调上升?下降?平稳?)。趋势本身就是有价值的结果,即使它不能外推到万亿规模。这也是为什么「至少三个规模点」是这类研究的最低门槛——两个点无法区分「平稳」和「缓慢变化」。
NoPE 这个混杂因素怎么处理?如果你降低 KDA 比例导致性能下降,你怎么区分是「记忆容量不够」还是「位置信息不够」?
一点提示
这是这个课题最关键的设计难点。一个可行的拆分办法是做一组对照实验:在低 KDA 比例下,给 MLA 层加回 RoPE。如果加回位置编码后性能恢复,说明瓶颈是位置信息;如果不恢复,说明是记忆容量。能想到这个对照,说明你真正理解了第3章那个 NoPE 依赖的脆弱性。
你需要什么工具?列出来。
一点提示
至少需要:一个支持混合注意力的训练框架(论文提到 KDA 的实现在 flash-linear-attention 的 PR #691,这是一个真实可查的起点);一个能跑多规模缩放实验的算力预算;一组长程探针任务。最缺的通常不是算力而是第三项——现有的长上下文评测大多要么太简单要么不可控。这本身可能就是一篇独立的工作。
如果做完发现「最优比例在你测的所有规模上都是 3:1」,这是好结果还是坏结果?
一点提示
是好结果,而且可能比找到一个复杂规律更有价值——它意味着这个超参数不需要随规模重调,可以直接沿用。负面结果和「无变化」结果在方法论上同样重要,只是不好发表。注意论文 §4.1.3 就报告了一个负面结果(「更细粒度的 top-k 蒸馏目标没有观察到明显优势」),这是这篇报告值得称道的地方之一。
课题二:把「下界衰减」的推理链迁移到别处(对你而言的未知领域)
性质说明
这个课题的方法论是已知的——「用数值范围约束反推参数化形式」这个思路在文献里存在(论文自己就引用了三篇有下界递推门的先前工作)。所以这不是一个学界的未解问题。但把它系统性地应用到一个具体的新模块上,对你而言是一次真实的研究训练,而且完全可以在小规模上独立完成,不需要大算力。
问题:第2章展示了一条完整的推理链:某个计算会溢出 → 定位溢出发生的最坏情形 → 从硬件的数值范围反推参数的边界 → 得到一个新的参数化形式 → 顺带消掉一条慢代码路径。请把这条链应用到另一个模块上。
候选目标(任选其一):
- SiTU-GLU 的 β₁、β₂。论文取 4 和 25,给出输出界 100,但完全没有解释这两个数怎么来的,也没有消融。你能不能反推一条类似的链?(提示:先问「输出界 100 是为了让什么落在什么范围内」——是激活的存储格式?是下一层某个计算的输入范围?为什么门分支和上分支的界差了六倍多?)
- QB 直方图的箱数 B。论文取 1000,说误差被箱宽限住、「至多几个 10⁻³」。你能不能反推:要让残余负载不均衡低于某个阈值,B 至少要多大?这个下界怎么随专家数 n、Top-k、批次大小变化?
- Block AttnRes 的块数 N。论文取 8,引用先前工作说「N ≈ 8 恢复大部分收益」,但没在 K3 规模上验证。你能不能从显存、通信量、推理时状态大小三个约束反推 N 的合理区间?
入手方案:先回答这四个问题
你选的这个参数,它约束的究竟是哪个具体的计算?写出那个计算的表达式。
做法提示
这一步做不出来,后面全免谈。第2章里这一步的答案是「式 4 里的 K/Γ」——极其具体。如果你只能说「某个地方会溢出」,说明你还没定位到。把论文里所有出现这个参数的公式找出来,逐个问「这里的值域是什么」。
最坏情形是什么?给出一个能让这个计算取到极值的具体输入。
做法提示
第2章里的最坏情形是「16 步全部用最大遗忘」,得到累积对数衰减 −80。注意这个推理用到了一个结构性事实(16-token 小片)——你的目标模块里对应的结构性事实是什么?如果没有,你可能需要先补一个(比如「假设批次大小为 M」)。
硬件那一侧的约束是什么?BF16 的 3.4×10³⁸?还是别的(FP8 的范围?共享内存的大小?Tensor Core 的形状要求?)
做法提示
不同的约束会给出完全不同的答案。注意论文 §4.1.4 说专家权重用 MXFP4、激活用 MXFP8——如果你的目标模块处在量化路径上,那么相关的数值范围是 FP8 甚至 FP4 的,比 BF16 窄得多。这可能才是 SiTU-GLU 那个界 100 的真正来源,值得去查。
如果你的反推得到的边界和论文取的值不一致,你会怎么解释?
做法提示
三种可能,你要能区分:① 你漏掉了一个约束(最常见);② 论文的取值有安全余量(第2章那条链里 e80 相对 BF16 上限就留了约四个数量级的余量,这显然是刻意的);③ 这个值根本不是从数值约束来的,而是调出来的经验值——这也是一个有价值的发现,它意味着论文的「设计」叙事在这一点上是事后合理化的。
能得出 ③ 这个结论并且敢说出来,说明你已经具备独立评估这类工作的能力了。
结语
你现在应该已经不需要这个网站了。
这十六章教的东西,最终可以压缩成三个习惯。第一,读到任何一个设计,先问「它在解决什么压力,又欠下了什么债」——这是第1章的三维度框架,也是第16章第一关的全部内容。第二,读到任何一个数字或结论,先问「这是可验证事实、作者实验,还是设计主张」——这是第1章的证据分级,也是第12章整章的方法论。第三,读到任何一个「我们发现 X 更好」,先问「对照组是什么,缺什么实验」——这是第16章第四关。
这三个习惯与 Kimi K3 无关。下一篇技术报告出来时,你可以用同一套工具把它拆开。
Chapter 16 · The final gauntlet: cross-chapter synthesis, the closing defences, and research topics
The previous fifteen chapters took things apart; this one puts them back together. Not one question here stays inside a single chapter — they ask you to hold all four threads at once: architecture, training, systems, evaluation. Once you have worked through this chapter you will no longer merely have “read” this paper. The last section is research mode: two open topics, and I will tell you plainly which one is a genuinely unsolved problem and which is only unknown territory for you.
What counts as passing
- Eight synthesis questions, and for each one you only pass by getting the Variant right
- Three closing defences, which you have to put together yourself before looking at the reference defence
- Every line of the final “graduation checklist” is something you can explain without the material in front of you
- Pick one research topic and write out your plan of attack (you do not have to finish it; you do have to have a plan)
16.1 Stage one: reconstructing the causal chain
This stage tests the most important habit of mind on the whole site — every architectural decision leaves a debt at the systems level.
Below are four of K3's architecture-level decisions. For each one, trace at least two concrete consequences it caused in §5 of the paper (infrastructure) or in §4.1.4 (deployment-aware post-training), and say which chapter of this site covers it.
① 3 KDA layers + 1 Gated MLA layer per block
② 93 layers using Attention Residuals rather than standard residuals
③ 896 routed experts, 16 activated per token
④ a training context length of 1 million tokens
① 3 KDA + 1 Gated MLA
· §5.1.1 (Chapter 10): the KDA recurrence is serial → in a naive implementation the SMs sit idle while the state propagates → FlashKDA uses CUTLASS to overlap the within-chunk computation with the cross-chunk state propagation; it also does SM-level context parallelism inside a device for long-context prefill.
· §5.1.2 (Chapter 10): the delta rule applies a token-dependent matrix Mt to the incoming state → what a local segment does depends on the state that enters it → the ordinary linear-attention move of “compute from zero, then add up” breaks → KCP had to be invented, splitting a segment's effect into two locally computable quantities, the “accumulated transition” and the “state generated from zero”, and rebuilding the result with one fixed-size all-gather plus a prefix scan.
· §5.4.1 (Chapter 11): the MLA KV cache is paged by token and grows with length; the KDA state is fixed-size, one per request. The two have to be restored together at the same boundary before a prefix can be reused → a unified paged layout + decoupling hash granularity from physical block granularity + three concurrency-consistency mechanisms.
· §5.4.2 (Chapter 11): during KDA decoding the state is updated in place → when MTP speculative decoding fails verification the state has already moved past the last accepted token and cannot be rolled back → cache only the projected inputs and rebuild the state of the accepted tokens on chip.
② Attention Residuals
· §2.2 itself (Chapter 4): the full form costs O(Ld) memory plus cross-stage communication under pipeline parallelism → that is the entire reason Block AttnRes exists (cut into 8 blocks, down to O(Nd)).
· §5.2.2 (Chapter 10): the block representations are produced once at the boundary layer, shared by the later layers and kept resident on the GPU; the whole AttnRes computation is wrapped in checkpointing, so the activations each layer saves for the backward pass are exactly the same as in a standard residual architecture; pipeline parallelism uses cache-based communication, transferring only new blocks incrementally and freeing them the moment the micro-batch ends.
· §5.4.2 (Chapter 11): a two-stage schedule (an inter-block pass + an intra-block online-softmax merge); at prefill, sequence parallelism avoids materialising the block representations on every TP rank; at decode, the inter-block kernel goes on a side stream and the intra-block kernel is fused into the TP all-reduce in front of it.
③ 896 experts / 16 activated
· §2.3.3 (Chapter 5): close to 10³ experts is beyond the range where a fixed-step sign update to the bias works well → Quantile Balancing.
· §5.2.1 (Chapters 10 and 14): token load is uneven under expert parallelism → MoonEP achieves perfect balance with dynamic redundant experts, and proves at most E/R redundant experts per rank; perfect balance then buys static shapes (removing the per-layer host synchronisation) and a fixed S×K communication buffer (rather than S×K×R).
· §4.1.4 (Chapter 8): 2.78 trillion parameters will not fit at deployment time → quantise the expert weights to MXFP4 and the activations to MXFP8, do QAT from the SFT stage onwards, and during RL have rollout and training share one quantisation scheme so the train–inference mismatch disappears.
· §5.4.2 (Chapter 11): at small batch sizes the group GEMM in decoding degenerates into memory-bound streaming of weights → a token-centric kernel based on WarpDecode, with warps subdivided into lane teams and the weight layout permuted offline to cut the runtime dequantisation cost.
④ a context of 1 million tokens
· §3.4 (Chapter 7): NoPE + a four-stage progressive curriculum (8K→64K in pre-training, 256K→1M in the cooldown) + upsampling long documents + synthesising tasks that require attending across the full length.
· §5.1.2 (Chapter 10): a single card cannot hold the activations for 1 million tokens → context parallelism → which for KDA means KCP.
· §5.3.1 (Chapter 11): in RL at a 1M context, the rollout KV cache that has to be kept for the next round and the memory training needs fight each other → an external KV cache pool (write-back rather than write-through) + offloading training state to NVMe + a scheduler that throttles automatically.
· §5.4.3 (Chapter 11): in production traffic the cost per request spans three orders of magnitude → cache-aware affinity scheduling (consistent hashing pinning two clusters) + budget-based admission control.
If you got more than two thirds of these, you have genuinely understood how this paper is built. §2 and §5 of this report are not two independent parts; they are the front and the back of one set of decisions.
Variant: now do it in reverse. §5.2.2 of the paper mentions “using the Mooncake Transfer Engine to offload activations remotely into the memory of other pipeline-parallel ranks”. Work backwards: which further-upstream decision caused “activations to be distributed unevenly across PP ranks”? (Hint: not an architectural decision, but a decision about parallelism strategy, to do with the warm-up phase of the pipeline.) Then ask: is that imbalance the same phenomenon that §5.2.3 exploits when it “stuffs the ViT computation into the pipeline bubbles”?
16.2 Stage two: reconciling the numbers
Numbers are scattered all through the paper. Between the four groups below there is either a checkable consistency relation or an obvious tension. Analyse them one group at a time.
(a) Table 1: 93 layers; the attention layers break down as “69 KDA + 24 MLA”; §2.1 says “each block is 3 KDA layers followed by 1 Gated MLA layer”, and “one extra Gated MLA layer is added at the end of the backbone”.
(b) §2.2 says “the layers are split into 8 blocks of 12 layers each, which leaves an incomplete final block, and counting the token embedding layer there are 9 blocks in all”.
(c) Table 1: 896 routed experts, 16 activated per token, 2.78T total parameters, 104.2B activated parameters.
(d) The §7 case study: AttnRes kernel latency falls from 283.6 ms to 114.4 ms; Figure 14 labels K3 as +59.7% faster than the FLA Triton baseline.
(a) Fully self-consistent, and it lets you recover a number the paper never wrote down.
Let there be B complete blocks. Each block = 3 KDA + 1 MLA = 4 layers. Add the extra Gated MLA at the end:
total layers = 4B + 1 = 93 → B = 23
KDA layers = 3 × 23 = 69 ✓
MLA layers = 23 + 1 = 24 ✓
The paper never writes the number “23 blocks”, but it can be recovered in full. This also explains the “contradiction” that 69/24 = 2.875 ≠ 3: the 3:1 ratio holds inside a block, and the global ratio is pulled down by that last layer.
(b) Read literally it comes out contradictory, and the reading has to be corrected.
8 × 12 = 96 ≠ 93. And the paper does say “which leaves an incomplete final block”. So the right reading is: the first 7 blocks have 12 layers each (84 layers) and the 8th block has only 9 (84 + 9 = 93). Add the token embedding layer as one more information source and there are 9 sources. That agrees with b0 = h1 in Equation 10 (the token embedding is always a source).
But note: “the final block has 9 layers” is an inference; the paper does not write it. And whether the 12-layer block boundaries of AttnRes line up with the 4-layer block boundaries of attention (12 is a multiple of 4, which looks deliberate) is something the paper says nothing about at all.
(c) You can do a consistency check, you cannot verify it exactly.
activated parameters / total parameters = 104.2B / 2780B ≈ 3.75%. Meanwhile 16 of the 896 experts are activated, so the activation fraction of the expert part is 16/896 ≈ 1.79%. The two being different is normal — because the activated parameters also include everything that is not an expert (the attention projections, the shared expert, the router, the token embedding), and every token has to pass through those.
From “3.75% > 1.79%” you can infer qualitatively that the non-expert part takes up a sizeable share of the activated parameters. But verifying it exactly would need the parameter count of each module, and the paper does not give enough of a breakdown. This is a relation you can check qualitatively and cannot check quantitatively.
(d) A close match, but the definition is never given, so it counts only as a plausible reconciliation.
283.6 → 114.4 ms. If “speedup” is defined as the fraction of time saved: (283.6 − 114.4)/283.6 = 59.66%, almost exactly the +59.7% Figure 14 is labelled with.
But if it is defined as the speedup factor minus one: 283.6/114.4 − 1 = 148%, and it no longer matches.
So you can infer that the vertical axis of Figure 14 uses the first definition (fraction of time saved). But the paper gives no formula for that definition — this is only a successful reverse-engineering, not something the paper states. Whenever you meet a figure labelled “speedup”, check its definition first — both definitions are in use in the literature, and they differ enormously.
The point of this question: the numbers in a technical report are not isolated decoration; there are checkable constraints between them. Get into the habit of reconciling them as you read and you will find three kinds of thing: self-consistency that checks out (which raises your trust in the whole text), implicit information you can recover (23 blocks, say), and genuine vagueness (8×12, and the definition of speedup).
Variant: do one more group. §5.3.2 of the paper says 51,219,741 sandboxes were created in all, across 1,505,678 images; it also says “a paused sandbox consumes no memory or CPU resources, and waiting on model inference can take up to 98% of a sandbox's lifetime”; and it says a “memory over-subscription ratio of up to 6.5×” was achieved. (a) How many sandboxes per image on average? (b) Do the two numbers “98% of the time can be paused” and “6.5× over-subscription” support each other, or do they duplicate each other? If a sandbox takes no memory 98% of the time, what over-subscription ratio is reachable in theory? What does 6.5× tell you against that theoretical value?
16.3 Stage three: design decisions
Now you are the architect. You have to design a model for a new setting, under these constraints:
· the context length only needs to be 32K (not 1M)
· it is deployed on a single 80GB card; multiple cards are not allowed
· the main use is high-concurrency short-dialogue customer support, each request under 2K tokens, at very high QPS
· you may take or leave anything from K3's design
Go through them one at a time: KDA, the 3:1 mix with Gated MLA, Block AttnRes, the 896-expert LatentMoE, MXFP4 quantisation, speculative decoding, prefix caching — for each of these seven, do you keep it, change it or cut it? Give a reason for each.
2.78T × 0.5 bytes ≈ 1.39 TB, 17 times 80GB. So the model has to be far smaller, and 896 experts is impossible.
Now the first constraint: at a 32K context, the method from that question in Chapter 0 gives a KV cache of 46.5 KB/token × 32K ≈ 1.5 GB. Inside 80GB that is perfectly acceptable.
So KDA's core benefit (saving KV cache) in this setting... you carry on from there.
KDA — cut its share sharply, or cut it altogether. Its benefit is that “the KV cache does not grow with length”. At 32K the KV cache is about 1.5 GB (by the method in Chapter 0), which is negligible inside 80GB. And KDA's costs (a serial recurrence, a dedicated kernel, context parallelism that has to be reinvented, speculative decoding that cannot roll back, the prefix-cache granularity conflict) get no smaller at all.
But there is one objection you have to weigh: high QPS means holding the KV caches of several hundred requests at the same time. 1.5 GB × 200 concurrent = 300 GB, which does not fit. So what actually decides this is not “how long the context is” but “concurrency × cache per request”. In this setting KDA may still be worth having, only the reason has changed from “it supports long context” to “it supports high concurrency”. Spotting that switch is the core of what this question tests.
The 3:1 mix — keep it, but the ratio has to be searched again. 3:1 was settled at a 1M context. At 32K the MLA layers are far cheaper, so it can be relaxed to 1:1 or even all-MLA. The paper gives no ablation over the ratio at all, so this can only be redone by experiment yourself.
Block AttnRes — keep it, and it becomes better value. Its cost is O(Nd) memory and cross-stage communication, and the cross-stage communication disappears completely on a single card (there is no pipeline parallelism). Its benefit (selective retrieval of information along the depth direction) has nothing to do with context length. Of the seven, this is the one whose value for money rises the most. But note: the model is smaller and has fewer layers, so AttnRes's own benefit shrinks too (the N≈8 the paper cites was on a deeper network).
The 896-expert LatentMoE — has to shrink a great deal, but keep the MoE structure. The parameters will not fit, so the expert count has to come down by one or two orders of magnitude. But MoE's core benefit (total parameters decoupled from computation) is still extremely valuable at high QPS — you want a model that knows a lot and is cheap per token. Also, a single card means no expert parallelism, so the cross-card motivation for MoonEP and load balancing disappears; but the skew in token counts across experts within a card is still there (the “Expert-GEMM scheduling” paragraph in §5.2.1 of the paper is about exactly this), so Quantile Balancing still means something — what it serves is “whether the experts get trained enough”, not only “load across cards”.
MXFP4 — keep it, and it is the most essential one to keep. Under the hard constraint of a single 80GB card, quantisation directly decides how big a model you can fit. And the paper's QAT approach (quantise from SFT onwards; during RL have rollout and training share the quantisation scheme) solves the “train–inference mismatch”, which is a problem independent of the setting.
Speculative decoding — keep it, but the benefit may fall. What it spreads out is the bandwidth cost of “streaming the weights once for every token”. But note: high QPS means large batches, and at large batches that bandwidth cost is already spread out (many requests share one read of the weights). So speculative decoding pays off most at low concurrency and shrinks at high concurrency. In this setting it may not be a priority. §5.4.2 of the paper happens to say “this cost dominates at the large batch sizes typical of online serving” — it is talking about the cost of the state snapshot, but the same “large batch” logic applies when you judge what speculative decoding is worth.
Prefix caching — keep it, and it matters enormously. A customer-support setting has a large shared system prompt (persona, rules, a summary of the knowledge base), and that prefix is exactly the same across every request. Prefix caching lets it be computed once. And if you have cut KDA, the prefix cache becomes an order of magnitude simpler to implement — the complexity of that whole §5.4.1 section (the unified paged layout, the decoupled hash granularity, the three concurrency-consistency mechanisms) comes entirely from the granularity conflict between the KDA state and the MLA cache. That is an underrated benefit of the “cut KDA” option.
What this question is really training: not remembering how K3 did it, but being able to reduce every design to “which pressure it relieves”, and then judge whether that pressure is still there in the new setting. Note at least three places where the right answer runs against intuition: the source of KDA's value moves from “long context” to “high concurrency”; the benefit of speculative decoding falls as concurrency rises; and one of the biggest gains from cutting KDA is that prefix caching gets simpler.
Variant: change the setting. The constraints become: a 1M context, but only single-user, low-concurrency offline document analysis, latency-insensitive (it can run all night), with 8 cards. Redo the seven judgements and say clearly which of them come out opposite to the previous question, and why.
16.4 Stage four: auditing the evidence
Pick out of the whole paper the three claims you think have the weakest evidence, and for each one say: what the paper claims, what evidence it gives, what evidence is missing, and what kind of experiment would be needed to really support it.
Requirement: the three claims must come from different sections (you cannot take them all from the evaluation part).
① §3.2, “about a 2.5× improvement in overall scaling efficiency” — bundled.
· The claim: KDA + AttnRes + Stable LatentMoE + a refined data and training recipe together give about 2.5× the scaling efficiency of K2.
· The evidence: two fitted curves in Figure 7, with one arrow labelled 2.5×.
· What is missing: the figure has no numbers on its axes, so no concrete loss can be recovered from it; there is no ablation of any single item, so the contributions of the four factors cannot be separated at all; nothing says whether the factor stays constant across the whole FLOPs range (whether the two lines are parallel is unknown); and no confidence interval is given for the fit.
· What experiment is needed: a scaling law per ablated item — hold the other three fixed, change one, and draw a curve for each. That takes four or five independent scaling experiments and costs a great deal, but it is the only way to take the 2.5× apart.
② §2.4, “contrastive pre-training is not necessary as an initialisation for a multimodal language model” — extrapolated.
· The claim: MoonViT-V2, trained from scratch with next-token prediction, matched the SigLIP-initialised baseline on the vision evaluations, which shows contrastive pre-training is not necessary at this scale.
· The evidence: the gradient-norm curves in Figure 6 (showing from-scratch is steadier), plus one sentence, “matches the baseline”.
· What is missing: these two pages give no evaluation table and no scores, so the reader has no way to check which evaluation sets “matched” refers to, or by how much; Figure 6 is a process metric (gradient norm) rather than an outcome metric, it shows no actual training collapse, and it gives no final loss for either path; the conclusion is qualified with “at scale” but no lower bound on scale is given; and this is one comparison, not several independent experiments.
· What experiment is needed: at at least three different scales, run a from-scratch vs SigLIP-init comparison, report the final loss and a fixed set of vision evaluation scores, and give the variance. Only that supports the “at scale” qualifier, and it locates the scale threshold.
③ The systems optimisations in §5, “substantially outperforms the Triton reference implementation” and the like — qualitative, and this is the most widespread problem in the paper.
· The claims: FlashKDA “substantially outperforms the Triton reference implementation”; the verification latency of KDA decoding “grows sublinearly and stays below the state-caching baseline”; the ViT computation “essentially eliminates the effective overhead of the vision encoder”; prefetching in the unified activation manager has “negligible extra overhead”.
· The evidence: words only, all of it, without a single number.
· What is missing: speedup factors, throughput gains, memory saved, the share taken by overhead — not one of them. The whole of §5 (apart from the sandbox figures 133ms/49ms/6.5×/51 million) has almost no performance data.
· What experiment is needed: no new experiment, in fact — the measurements they already have just need to be reported. These optimisations were clearly measured (or they would not have gone into production); they simply were not written into the report. This is a disclosure problem rather than a science problem — but for the reader the effect is the same: you cannot tell which optimisation mattered, you cannot reproduce it, and you cannot make trade-offs in your own system.
The point of this question: the end of critical reading is not “finding fault” but knowing what you can safely take away from this paper. After ①, what you can take away is “this combination works under the authors' recipe”, not “KDA is worth X times”; after ②, “from-scratch is viable at this scale”, not “contrastive pre-training is useless”; after ③, the design thinking behind these optimisations (which the paper does write out in detail, and which is the real contribution), not any expectation about performance.
Variant: now run the audit in reverse. Pick out of the paper the three claims you think have the most solid evidence and say why they are solid. (Hint: think about which claims are checkable mathematically, which are reproducible independently by anyone, and which are backed by an independent third-party evaluation. Pick one of each.)
16.5 The closing defences: three of them
Defend it 1: what the core contribution of this paper actually is
My attack: there is not one genuinely new idea in this paper. KDA is a small change to Kimi Linear (a different decay function); AttnRes is somebody else's work, cited; LatentMoE is somebody else's work, cited; MoE, MLA, Muon, speculative decoding, EAGLE-3 and quantised training are all off the shelf. What is called K3 is only an engineering integration at larger scale. If so, what earns it a 47-page technical report?
A reference defence (put it in your own words first; for this round write down at least three points)
First concede the part of the attack that is fair: at the component level that description is basically accurate. The paper itself is generous about marking where each component came from (KDA cites Kimi Linear, AttnRes cites [57], LatentMoE cites [32], MLA cites DeepSeek-V2, and Muon, EAGLE-3 and MXFP4 all cite the original work). If your criterion is “is there a brand-new mathematical object”, this paper does not score high.
But the defence has four layers:
Layer one: some of the “small changes” have real content. Lower-bounded decay is not as simple as “swapping in another function” — it is a complete chain of reasoning: notice that 1/Γ overflows → the previous generation got around it by splitting into 16-token sub-chunks, but the diagonal sub-chunks still needed pairwise position computation → rather than get around it, limit it at the source → work backwards to gmin = −5, which puts e80 inside BF16 → and the diagonal path disappears entirely. This is a clean example of “a hardware constraint deciding the mathematical form in reverse”, and its value lies not in the function itself but in the fact that this chain of reasoning can be carried over to other recurrent models. Quantile Balancing is the same — it reduces a heuristic sign update to the exact coordinate minimisation of a provable dual problem, and thereby explains “why no learning rate is needed”. Reducing a heuristic to a principled method is itself a contribution.
Layer two: MoonEP's bound is a genuinely new theorem. Appendix E proves that “at most E/R redundant experts per rank can always achieve perfect balance”, and proves the bound is essentially tight. This is not engineering tuning; it is a combinatorial optimisation result with a constructive proof and a tightness proof, and it changes the system design — because the bound is provable, fixed slots can be reserved and training can be guaranteed never to stop, which differs in kind from the ECHO/UltraEP approach of “set an upper limit in advance, and halt if there is no solution”.
Layer three: the integration itself is non-trivial at this scale. The phrase “only an engineering integration” in the attack carries the premise that integration is easy, and that premise is wrong. What §5 of the paper shows is precisely this: every component you choose makes the other components harder. Mixing KDA + MLA turns granularity alignment in the prefix cache into a problem that takes three consistency mechanisms to solve; 896 experts + 2.8T parameters turn EP load balancing from a “tuning problem” into a “feasibility problem”; a 1M context + partial rollout puts the KV cache in direct conflict with training memory. None of these problems exist at small scale, so their solutions cannot be extrapolated from small-scale work either.
Layer four (the most important): the value of a report like this is not novelty, it is disclosure. Building a 2.8 trillion parameter model takes thousands upon thousands of decisions. The overwhelming majority of frontier models that get built say nothing at all. This report says: every parameter of the architecture, the segmentation of the training curriculum, the three-domain nine-expert structure of RL, the design of nine kinds of RL environment, the three high-level sandbox operations, the three concurrency failure modes of the prefix cache. The value of that information to the whole field is a dimension orthogonal to “is there new mathematics”.
But one point in the attack does stand, and is worth remembering: precisely because the components all come from elsewhere, “do these components still work at 2.8T scale” becomes an open question, and the paper runs no ablation of any single item to answer it. So you cannot conclude from this report that “AttnRes is useful at trillion scale” — you can only conclude that “the authors used it and the overall result is good”.
Defend it 2: what was opened up and what was not
The paper says over and over that K3 is “the world's first open 3T-class model”, treating “open” as one of its core contributions. My attack: what it opens is only the weights — no training data, no data mixture, no training code, most hyperparameters absent, no compute scale, no cost. For a model I cannot reproduce, is the word “open” not overpraise? And is calling 2.78 trillion “3T-class” not itself taking advantage of the rhetoric?
A reference defence (put it in your own words first)
On “3T-class”: this attack lands, but it does limited damage. Rounding 2.78T up to “3T-class” is order-of-magnitude rhetoric, and common practice in the field (much as “a ten-billion-parameter model” is usually not exactly 10 billion). It does take advantage of the wording, but “3T-class” as a description of the order of magnitude is not false, and the paper gives the exact value 2.78T in Table 1 rather than hiding it. This belongs to the class of things worth pointing out but not worth pressing.
On “open”: this attack has more substance, and needs a layered answer.
First, concede that “open weights” ≠ “open source”. Open source in the strict sense means being able to reproduce the whole build process. K3 gives the weights, not the recipe. The training details you can look up in the paper really are incomplete: the data mixture is only said to be “decided by small-model ablations”, with no numbers; the four-stage context curriculum gives only the four endpoints 8K/64K/256K/1M, and says nothing about how much of the budget each stage took; how many GPUs training used, how many tokens, the total cost — none of it is there; the key RL hyperparameters (λ, τ, σ, R_max) are all missing. You cannot reproduce K3 from this report.
Second, but the value of “open weights” by itself is real and enormous. It means anyone can download, fine-tune, deploy, audit, do mechanistic interpretability research, evaluate on their own tasks without API limits, and run it in an environment with no network. Not one of those things can be done with a closed model. And §6.2.2 of the paper mentions a concrete consequence: the frontier models from Anthropic and OpenAI refuse cyber-security-related tasks, which makes comparable evaluation impossible — whereas an open-weight model can be assessed by independent third parties (the paper mentions the UK AISI and NIST CAISI) under fully controlled conditions. Auditability is an underrated value of open weights.
Third, the standard of “can it be reproduced” is unrealistic at this scale. Even if the paper published all the data and code, no more than ten institutions in the world could reproduce a 2.8 trillion parameter training run. So “reproducible” as a criterion distinguishes nothing at this scale. A more meaningful standard is “how much transferable knowledge was disclosed” — by that standard the information density of this report is quite high (almost every paragraph of the systems design in §5 is something somebody else can take and use).
Fourth, and the point most worth making: precision of wording still matters. What the paper uses is “we release the full Kimi K3 model weights” — that statement is accurate, and what it says is weights. The “open frontier” phrasing in the abstract is looser. As a reader, the right move is to go by the precise statement and treat the broad one as narrative. This is one more application of the grading of evidence taught in Chapter 1.
One last honest addition: this site has not verified whether the weights really can be downloaded, what the licence terms are, or whether there are usage restrictions. The paper gives only a HuggingFace link. How “open” it is depends in the end on the licence, and this report does not discuss the licence. That is a real gap in the information.
Defend it 3: your turn to be the reviewer
This round has no reference answer, because the attacker is you.
Write out three revision requests for the authors. Each one contains: which section of the paper it points at, what you think the problem is, and a concrete, actionable suggested improvement (not empty phrases like “more experiments should be done” — say which experiment, which metric, and against which control).
Then, for each one, estimate the most likely reason the authors would refuse it.
Write your own three first, then look at the self-check standard here
A good review comment has three features; check yours against them:
① It points somewhere specific. “The 2.5× in §3.2 has no ablation” is good; “the experiments are not thorough enough” is bad.
② The suggestion is actionable. “At three scales, 1B, 7B and 30B, run a from-scratch vs SigLIP-init comparison, and report the final validation loss and the scores on the MMMU-Pro and OmniDocBench evaluations together with the standard deviation over three runs” is good; “this conclusion should be verified” is bad.
③ It acknowledges cost. The best review comments deliberately separate “this experiment is cheap, and not doing it is indefensible” from “this experiment is extremely expensive, I understand why it was not done, but please state the limitation explicitly in the text”. The second kind is almost always accepted; the first often provokes a fight.
On the step of “estimating the reason the authors would refuse” — this is not teaching you to compromise; it is training a more important ability: telling an evidence gap from a disclosure gap.
Take “§5 gives no speedup figures anywhere”: the authors' most likely reason to refuse is “those numbers involve internal infrastructure details”. If so, this is not a scientific problem but a commercial decision, and your comment should become “please at least give relative factors rather than absolute values” — which steps around the disclosure worry and still restores comparability.
Whereas for “the 2.5× in §3.2 has no ablation”, the authors' most likely reason to refuse is “every ablation means rerunning the full scaling-law experiment, at a cost counted in millions of dollars”. That reason is real. So the more realistic comment is “please state explicitly in the text that the 2.5× is the joint effect of four factors, that the individual contributions are unknown, and that it should not be cited as the gain from any single component” — which costs almost nothing and raises the honesty of the paper substantially.
If all three of your comments pass this self-check, you already have the ability to write review comments on a paper like this.
16.6 The graduation checklist
You should be able to explain every line below without looking at the material. Anything you cannot tick, go back to the chapter it names.
16.7 Research mode: two open projects
Once you have worked through the material, the real beginning is being able to ask your own questions. Here are two projects. Following this site's convention, I will be explicit about what kind of thing each of them is.
Project one: a transferable law for the mixing ratio (a genuinely unsolved problem)
What kind of problem this is
This is a real open problem that the field has not settled, not a case of “you are the only one who does not know the answer”. As far as I know, as of this report no work has systematically answered “how does the optimal mixing ratio of linear attention to full attention change with model scale, context length and task type”. K3 uses 3:1, and its predecessor Kimi Linear also used 3:1, but neither paper gives an ablation on the ratio.
The question: given model scale N, target context length L and task distribution D, what is the optimal KDA:MLA ratio? Is there any predictable law behind that ratio?
Why it matters: if the ratio changes with scale, then every conclusion about hybrid architectures that was verified on small models cannot be extrapolated — and extrapolating them is exactly what the field does as a matter of course. If the ratio is constant, that needs an explanation. Further still: K3 hands all responsibility for positional encoding to KDA (because the MLA layers use NoPE), which means the ratio affects not only efficiency but whether the model can sense word order at all. Is there a hard floor on lowering the KDA ratio, set by “not enough positional information”?
Before you start, answer these five questions
How do you plan to measure “optimal”? Validation loss? Some class of downstream task? Or a purpose-built probe task? The three may well give different optimal ratios — how will you handle the disagreement?
A hint (but think for yourself first)
Validation loss takes the least effort, but it is extremely insensitive to abilities like “long-range retrieval” — a model that has completely lost its long-range ability may come out only slightly worse on validation loss. So you need at least one metric that probes long-range dependency directly. The classic approach is a synthetic “needle-in-a-haystack” task, but that is too easy and saturates quickly. Better is to design tasks that require multi-hop reasoning across the full length — note that §3.4 of the paper says their way of synthesising long-context data is exactly to “arrange and concatenate documents and sub-tasks so that a task embedded inside them can only be solved by attending to information scattered across the full length”. You can borrow that idea directly for evaluation.
What is the largest scale you can afford? If you can only train up to 1B, how will you argue that the conclusion holds at 100B?
A hint
The honest answer is: you cannot argue it directly. But you can run three or more scale points and watch the trend in the optimal ratio as scale grows (rising monotonically? falling? flat?). The trend is itself a valuable result, even if it does not extrapolate to trillion scale. This is also why “at least three scale points” is the minimum bar for this kind of study — two points cannot tell “flat” apart from “changing slowly”.
How will you handle the NoPE confound? If lowering the KDA ratio makes performance drop, how do you tell “not enough memory capacity” from “not enough positional information”?
A hint
This is the single hardest design point in the project. One workable way to pull them apart is a set of control experiments: at a low KDA ratio, put RoPE back into the MLA layers. If performance recovers once positional encoding is back, the bottleneck is positional information; if it does not recover, it is memory capacity. Being able to think of this control means you have genuinely understood how fragile that NoPE dependency from Chapter 3 is.
What tools do you need? List them.
A hint
At minimum: a training framework that supports hybrid attention (the paper mentions that KDA's implementation is in PR #691 of flash-linear-attention, a real starting point you can go and check); a compute budget that can run scaling experiments at several sizes; a set of long-range probe tasks. What you will usually be shortest of is not compute but the third item — most existing long-context evaluations are either too easy or not controllable. That could well be a separate piece of work in its own right.
If you finish and find that “the optimal ratio is 3:1 at every scale you tested”, is that a good result or a bad one?
A hint
It is a good result, and possibly more valuable than finding some complicated law — it means this hyperparameter does not need retuning as scale changes and can simply be carried over. Negative results and “no change” results matter just as much methodologically, they are only harder to publish. Note that §4.1.3 of the paper does report a negative result (“no clear advantage was observed from a finer-grained top-k distillation target”), which is one of the things this report deserves credit for.
Project two: carry the “lower-bounded decay” chain of reasoning somewhere else (unknown territory for you)
What kind of problem this is
The methodology of this project is known — the idea of “working back from numerical-range constraints to a parameterisation” exists in the literature (the paper itself cites three earlier works with lower-bounded recurrent gates). So this is not an unsolved problem for the field. But applying it systematically to one specific new module is a piece of real research training for you, and it can be done entirely on your own at small scale, with no large compute.
The question: Chapter 2 laid out one complete chain of reasoning: some computation will overflow → locate the worst case in which the overflow happens → work back from the hardware's numerical range to a bound on the parameter → arrive at a new parameterisation → and clear away a slow code path along the way. Apply that chain to another module.
Candidate targets (pick any one):
- β₁ and β₂ in SiTU-GLU. The paper takes 4 and 25, giving an output bound of 100, but never explains at all where those two numbers came from, and gives no ablation. Can you work back along a similar chain? (Hint: start by asking “the output bound of 100 is there to keep what inside what range” — the storage format of the activations? the input range of some computation in the next layer? why do the bounds on the gate branch and the up branch differ by more than a factor of six?)
- The number of bins B in the QB histogram. The paper takes 1000 and says the error is held down by the bin width, “at most a few times 10⁻³”. Can you work back the other way: for the residual load imbalance to stay below some threshold, how large does B have to be at least? How does that lower bound change with the number of experts n, Top-k and the batch size?
- The number of blocks N in Block AttnRes. The paper takes 8 and cites earlier work saying “N ≈ 8 recovers most of the gain”, but does not verify it at K3 scale. Can you work back from three constraints — GPU memory, communication volume and the size of the state at inference time — to a sensible range for N?
How to start: answer these four questions first
The parameter you picked — which specific computation is it actually constraining? Write out the expression for that computation.
A hint on how to do it
If you cannot do this step, there is no point in any of the rest. In Chapter 2 the answer to this step was “the K/Γ in Equation 4” — extremely specific. If all you can say is “something somewhere overflows”, you have not located it yet. Find every formula in the paper in which this parameter appears, and ask of each one: what is the range of values here?
What is the worst case? Give a concrete input that drives this computation to its extreme.
A hint on how to do it
The worst case in Chapter 2 was “all 16 steps forgetting as hard as they can”, which gives a cumulative log-decay of −80. Note that this reasoning used a structural fact (the 16-token sub-chunk) — what is the corresponding structural fact in your target module? If there is not one, you may have to supply one first (something like “assume a batch size of M”).
What is the constraint on the hardware side? BF16's 3.4×10³⁸? Or something else (the range of FP8? the size of shared memory? the shape requirements of the Tensor Cores?)
A hint on how to do it
Different constraints give completely different answers. Note that §4.1.4 of the paper says expert weights use MXFP4 and activations use MXFP8 — if your target module sits on the quantisation path, the numerical range that matters is FP8's or even FP4's, far narrower than BF16's. That may well be the real origin of that bound of 100 in SiTU-GLU, and it is worth going to check.
If the bound you work back to does not agree with the value the paper takes, how will you explain that?
A hint on how to do it
Three possibilities, and you have to be able to tell them apart: ① you missed a constraint (the most common); ② the paper's value has safety margin in it (in that chain in Chapter 2, e80 leaves about four orders of magnitude of headroom against the BF16 ceiling, which is clearly deliberate); ③ the value does not come from a numerical constraint at all, but is an empirical value that was tuned — that is a valuable finding too, because it means the paper's “design” narrative is, on this point, rationalisation after the fact.
Reaching conclusion ③ and daring to say it out loud means you already have what it takes to evaluate work like this on your own.
In closing
By now you should not need this site any more.
What these sixteen chapters teach can in the end be compressed into three habits. First, whenever you read a design, ask first “what pressure is it solving, and what debt did it take on” — that is the three-dimension framework from Chapter 1, and it is the whole of gate one in Chapter 16. Second, whenever you read a number or a conclusion, ask first “is this a verifiable fact, an experiment of the authors, or a design claim” — that is the evidence grading from Chapter 1, and it is the methodology of the whole of Chapter 12. Third, whenever you read a “we found X to be better”, ask first “what is the control group, and which experiment is missing” — that is gate four in Chapter 16.
These three habits have nothing to do with Kimi K3. When the next technical report comes out, you can take it apart with the same set of tools.