Kimi K3 被拿来做标题的数字是 2.8 万亿参数。这是整份报告里最不值得看的一个数字。
值得看的是:它的架构是围绕一个跟规模无关的问题组织起来的——信息在哪里流不动? 答案分三部分:沿序列、沿深度、沿宽度,每一条各配一套机制。
同样的算力,scaling 效率大约是 Kimi K2 的 2.5 倍。这个数来自团队自己拟合的 scaling law 曲线,不是第三方复现,所以请把它当成他们的主张来读。但支撑它的那几套机制足够具体、足够可被质疑,这才是这份报告值得花时间的原因。
本文精读 Kimi K3 技术报告(Moonshot AI)的架构部分。我们之前写过长程 Agent 的设计,这一篇在技术栈上更靠下一层。
三个方向,而不是一个数字
Transformer 的每一层都在三个方向上混合信息。跨 token,让第 90 万个位置能影响第 1 个位置;跨深度,让第 90 层能用上第 3 层注意到的东西;跨通道,让特征可以重新组合。
大部分扩规模的工作是把三个方向一起变大。K3 把它们拆开,各给一套机制:
- 序列——混合注意力:每 3 层 Kimi Delta Attention 配 1 层 Gated MLA。
- 深度——Attention Residuals:每一层直接对前面所有层的输出做注意力,而不是只继承一个累积状态。
- 宽度——Stable LatentMoE:896 个路由专家,每个 token 唤醒 16 个。
隐藏维度一点没变。K2 是 7168,K3 还是 7168。不管什么变大了,反正不是一层的宽度。
序列:四分之三的层不再读全文
标准注意力每来一个新 token,都要把前面整段重读一遍。到一百万 token 的时候,压垮系统的就是这笔账。
K3 把这件事拆开做。3 层 KDA 维护一个固定大小的运行状态——与其说是重读原文,不如说是记笔记——后面跟 1 层 Gated MLA 做完整的全局注意力。这个模式一直重复,最后再额外补一层 MLA,保证最后一层永远看得到全部。93 层里:69 层 KDA,24 层 MLA。
「固定大小」才是关键。状态不随序列增长,所以它撑不爆。但它同时是有损的,所以每四层要有一层全局注意力,把笔记丢掉的东西找回来。
然后是一个二阶效应。因为这个递归状态自带衰减——越近的 token 天然越清晰——位置信息就顺带有了。于是 K3 的全局注意力层完全不加位置编码。没有 RoPE,也就没有什么需要重新缩放的东西。
这意味着它扩到一百万 token,不需要动任何位置编码。这些年业界为扩上下文攒下的那一堆插值技巧,在这里一个都用不上——因为根本没有编码可供插值。
一个下界,删掉了一整条 GPU 代码路径
这是我们最喜欢的一段,而它小到很容易被翻过去。
递归状态一边走一边遗忘。要按块高效地算这件事,就得除以累积衰减,而累积衰减是一堆小于 1 的数连乘。放任不管,你就是在除以一个任意接近 0 的数。
上一代的处理办法是把每个块再切成 16 个 token 的小块,并在对数空间里算。这管用,但对角线上的小块仍然要一对一对位置地算——一条又慢又特殊、用不上 Tensor Core 的路径。
K3 的解法只是改了一行参数化:给对数衰减加一个下界,每一步最多只能忘到还剩 0.67%,不许再少。
顺着推下去。有了这个下界,16 个 token 的小块上,累积对数衰减落在 (−80, 0) 区间内。倒数因此小于 e80 ≈ 5.5 × 1034,稳稳待在 BF16 约 3.4 × 1038 的范围里。什么都不会溢出。于是对角块可以和其它块用同一种稠密矩阵乘法。
那条特殊路径不是被优化了,是没有了。
把因果反过来读会更有意思:是硬件的动态范围决定了可接受的区间,区间决定了这个常数,常数决定了激活函数必须有下界。是数值精度选择了数学形式,而不是反过来。
深度:从接力赛变成群聊
93 层深下去,标准的残差流就是一场接力赛。第 50 层只收到第 49 层交给它的那一个累积状态。第 1 到 48 层各自注意到了什么,早就被加进那个状态里,再也拆不出来了。
论文的说法是:这和 RNN 在时间维上的瓶颈是同一个——而那个瓶颈业界早就解决了,用的是注意力。Attention Residuals 把同一套解法搬到深度上:每一层带一个可学习的伪 query,对前面所有层的输出做注意力,自己决定读谁。
照字面做,代价是深度上的平方级计算,更麻烦的是要把每一层的输出都留在显存里,在流水线并行下还要跨阶段传输。所以 K3 用的是分块版本:93 层切成每 12 层一组,组内求和,组间做全注意力。开销从每层降到每组,推理时的状态也被限住了。
宽度:896 个专家,醒着 16 个
MoE 的做法是养一个大池子,每个 token 只唤醒其中几个。K2 是 384 里选 8,K3 是 896 里选 16——稀疏度 56。
把池子扩到这个程度会撞坏两件事,而报告对这两件事都写得异常直白。
通信。 常规 MoE 里每个被选中的专家都要收到完整宽度的 token,所以流量随选中数量线性增长。LatentMoE 把两者解耦:路由专家在一个只有模型宽度一半的紧凑隐空间里工作,另有两个全宽的共享专家处理每个 token 都需要的部分。池子可以变大,而线上的通信量不跟着涨。
稳定性。 在这个稀疏度下,路由分支变成了接近四次连乘的矩阵链,激活值会炸。两个补丁:在专家聚合和上投影之间插一个 RMSNorm;换一个新的激活函数 SiTU-GLU,用带缩放的 tanh 给 SwiGLU 的两个乘子各加一个软上限,让它们在低精度下都跑不飞。
负载均衡。 第三个补丁是最值得偷的。让大约 900 个专家保持负载均匀,意味着每一步都要调整每个专家的偏置。标准做法是按误差方向以固定步长挪一下,结果不是震荡就是跟不上。K3 改成直接求解:把 top-k 换成 top-(k+1),多出来的那一项本身就是这个 token 的准入门槛。有了这些门槛,某个专家在给定偏置下会拿到多少 token 就是单调的,于是命中目标负载的那个偏置,无非是 margin 的一个分位数。一次前向搞定,没有步长要调。
在真实规模下,这个分位数横跨所有 rank 上的数百万个值,所以他们用直方图来估:每个 rank 统计自己的 bin,一次 all-reduce 求和,再从池化后的计数里读出分位数。计数是可加的,所以无论 token 怎么分片,估出来的都等价于整个 batch 的分位数,代价只是每个专家几百个 bin。
账单落在服务栈上
这些都不是免费的。报告最诚实的部分是基础设施那一章,代价就落在那里。
固定大小的递归状态存起来便宜、传起来也便宜,但它是串行更新的,而且不能简单相加。这两个性质各自带来一堆工作:
- 把序列切到多个设备上。 普通线性注意力可以让每个设备从零算出自己的局部状态,再求和。但 KDA 会把一个与 token 相关的转移作用在入态上,所以求和是错的。解法是把每一段拆成「累积转移」和「从零起算的状态」两个量——这两个是可以组合的——再用前缀扫描加一次固定大小的 all-gather 恢复每个设备的入态。
- 跨请求复用前缀。 一半的缓存是按 token 分页的,另一半是每个请求一份定长状态,而一次命中要求两者能在同一个边界上同时恢复。他们的答案是把粒度解耦:按 512 个 token 做哈希,按 1024–6144 分配物理块,递归状态只在哈希端点的一个稀疏子集上存快照。
- 投机解码。 状态是原地更新的,草稿被拒时没法回滚。他们改成缓存投影后的输入——比状态本身小得多——然后在片上重建。
这三件事的模式,和那个衰减下界是同一个,只是方向相反:架构选定了一种表示,而表示反过来规定了系统层要做的活。
报告悄悄放弃的一个行业惯例
K3 是原生多模态的,而它的视觉编码器是用 next-token prediction 从零训出来的。没有 SigLIP 初始化,没有对比预训练——而那才是标准配方,团队自己上一代模型用的也是它。
给出的理由不是效果,是稳定性:对比初始化的编码器在联合优化时梯度范数一直偏高、还频繁尖刺,从零训的那个则全程平稳。视觉评测的成绩打平。
正因为打平,这个发现反而比赢了更有分量。如果从零训更好,你会说那是个更好的配方。它只是打平——所以结论是:在这个规模上,一个被业界当成必选项的步骤,其实只是可选项。
如果你在这些模型上跑 Agent,这意味着什么
我们做的是多 Agent 桌面客户端,所以我们盯的不是谁在榜首,而是一次长程任务跑下来还付不付得起。
真正要紧的数字不是上下文窗口有多大,而是一百万 token 服务起来要多少钱。四分之三的层带的是定长状态,所以随对话增长的那部分缓存,只有同深度全注意力模型的四分之一。BrowseComp 上,报告给出的是 91.2%、每个任务大约 2 美元——约为最接近的闭源分数的一半成本,比最高推理档下的 Claude 系低一个数量级。
对一个要跑几百次工具调用的 Agent 来说,这个比值直接决定了一个任务值不值得开始。过去读起来像纯研究的架构工作,现在直接体现在「这次长任务在经济上合不合理」上。
我们从中拿走了什么
两件事,都可迁移。
第一是提问方式。信息在哪里流不动? 会导向和 我们还能做多大? 完全不同的工作——而且它可分解,这正是三套机制能被分别开发、分别度量的原因。
第二是那个衰减下界。一个在表达能力上几乎不花钱的约束,从内核里移除了一整条特殊路径。不是更快的路径,是没有路径。这种交换的机会远比实际被抓住的多,而且只有同时握着数学和硬件的人才看得见。
报告和权重都开在 GitHub 上。架构那一章八页,值得慢慢读一遍。
