我们把 Kimi K3 的 896 个专家逐个测了一遍,发现中文和代码是“敌人”。
结论先放在这里:在 Kimi K3 的 896 个路由专家里,中文任务最依赖的那一批,和 Python 代码最依赖的那一批,重叠率只有 17.8%。而如果你从 896 个里随机抓两次,期望重叠率是 27%。也就是说,它们不只是”不太一样”,是低于随机水平的负相关——中文用得越多的专家,代码越用不到。
这个数字不是我们坐下来想验证的假设,是被逼出来的。
一、先说为什么要动专家
Kimi K3 是 2.78T 总参数、104B 激活的原生多模态 MoE,896 个路由专家、每 token 激活 16 个,配上 Stable LatentMoE 框架和 1M 上下文。Moonshot 从 SFT 阶段就上了量化感知训练,权重以 MXFP4 出厂——所以它在磁盘上是 1.56 TB,而不是 BF16 的 5.6 TB。
1.56 TB 已经是压缩过的结果了,这是麻烦所在。目前最大的 Apple Silicon 统一内存是 M3 Ultra Mac Studio 的 512 GB。要把这个模型塞进去,需要平均每个权重 ≤1.38 bit。而 K3 的专家本来就已经是 4 bit 的真实信息量了——你再往下压,压的是 Moonshot 已经压过一轮的东西,二次量化的质量完全不可知。
有个数字能说明专家在这个模型里的分量:97.94% 的参数是路由专家。KDA 注意力占 1.10%,共享专家 0.44%,MLA 注意力 0.20%,embedding 加 lm_head 只有 0.08%。任何试图”不动专家”的省内存方案,能动的加起来才 2%。
所以剩下的路只有一条:扔掉一部分专家。
二、REAP,以及它藏起来的那个参数
Cerebras 的 REAP 给出的方法很直接:对每个专家 j,在一个校准集上统计
S_j = 平均( 路由门控 g_j(x) × ‖专家输出 e_j(x)‖₂ )
按这个显著性分数逐层排序,留下最重要的,让路由器重新归一化。
看起来这是个纯粹的工程操作。但注意公式里那个 x——它来自校准语料。整个方法有一个没写在名字里的输入:你拿什么文本去测。
这件事的后果很不对称。校准语料里充分出现的领域,对应专家会被打高分留下来;语料里没出现的领域,对应专家的显著性接近零,会被安静地剪掉。没有报错,没有警告,转换脚本一路绿灯,模型正常加载、正常生成通顺的文本——直到有人用它没见过的那门语言写了一句话。
三、我们差点就这么干了
第一版校准语料我们用了 C4 的 pooled multilingual 配置。名字里带 multilingual,看起来该有的都有。
实测了一下前 200 篇文档:97% 是拉丁字母。这基本上就是又来了一份英语。
在这套语料里,CJK 占比 0.03%。如果直接拿它去跑 REAP,中文相关的专家会因为几乎没被激活过而全部落在显著性排序的尾部,然后在剪枝时消失。我们会得到一个各项指标都正常、代码能力也不错、但中文能力被静默切除的模型,而且大概率要等到发布之后才有人发现。
修掉这个问题不难——用具名的分语言配置替换 pooled 配置,给中文一个明确的份额(最后落在语料的 11% 左右,实际消耗的前缀里占 14%)。真正值得记下来的是:这是靠”测一下”发现的,不是靠”想一下”发现的。
顺带还踩到两个小坑。一是各来源必须交错采样而不是拼接——校准脚本只读前 seqs × seqlen 个 token,拼接的语料等于只用排在最前面的那个来源做了全部校准。二是 C4 的 zh 分片里混了一些双重编码的乱码文档,它们能通过朴素的”含 CJK 字符”检测,得用 CJK 占比阈值才滤得掉。
四、既然要测,不如测清楚
修完语料之后有个自然的追问:领域和专家之间,到底是不是真的存在对应关系?
这个问题不用额外跑一遍。校准阶段本来就要把整个语料推过每一层,只要给每个 token 打上来源标签,用一次融合的 (来源, 专家) scatter,就能在同一遍里累积出按语料来源分开的显著性。之后比较各来源的 top-N 专家集合即可。
以 top-242(对应 73% 剪枝率)计算,两次随机抽取的期望重叠是 27%。实测:
领域对
重叠率
相对随机
Python 代码 ↔ 多语言代码
57.2%
2.1×
德语 ↔ 西班牙语
59.3%
2.2×
德语 ↔ 英语网页
56.5%
2.1×
中文 ↔ 日语
42.8%
1.6×
中文 ↔ Python 代码
17.8%
0.66×——低于随机
三个簇清清楚楚:代码是一簇,欧洲语言是一簇,CJK 是一簇。中文和日语互相拉扯(1.6×),德语西班牙语英语几乎互为同一批专家(2.1–2.2×)。而中文和代码,是这张表上唯一一个掉到随机水平以下的组合。
顺带一个实用推论:既然一次带标签的校准就存下了各来源的显著性,那么做定向构建不需要重新校准——把你想要的那几个 bucket 加起来就行。
五、它真的会兑现,而且是双向的
重叠率是权重空间里的统计量,不等于能力。所以我们做了五个构建,除了校准目标之外其他全部相同,同样的 prompt、greedy 解码:
构建
显著性保留率
中文
代码
混合语料
59.1%
通顺,但十几个 token 后回绕到 prompt
正确
REAP-80(179 专家)
~50%
陷入硬重复
正确
英语+代码
68.4%
完全崩溃
正确
纯中文
79.8%
不重复,但空泛
完全崩溃
中文+代码
69.3%
最好——正确且具体,不重复
正确
“英语+代码”那一行的中文输出退化成了整句自我重复,几乎没有信息量;而”纯中文”那一行在代码 prompt 上直接卡在函数的 docstring 上循环,一行实现都没写出来。剪掉一个领域的专家,这个领域就死掉;留住它,这个领域就变好;把两个领域并起来,两个都保得住。
这条结论对使用者的意义比对研究者的更大:你不是在挑一个剪枝率,你是在挑一份语料,而语料决定了这个模型还剩下什么能力。
六、这个证据有多强
必须说清楚:上面这张表是每个领域一条 prompt、24 个 token、greedy 解码。这不是严格的评测。
它之所以可信,是因为它和两个独立的测量互相印证——专家重叠率(权重空间)和显著性保留率(校准统计),三者指向同一个结论。但要真正立住这个论断,需要的是每个领域多条 prompt 加上留出集困惑度。
之所以要把这段话写进来,是因为我们已经在这件事上错过一次:更早的一版记录把”定向校准”判为无效,依据是单条代码 prompt——而那条 prompt 过度确定,所有构建都吐出一模一样的 token,本来就区分不出任何东西。那个结论是错的。同样的怀疑态度,也应该施加在现在这个正面结果上。
还有一处方法论上的松动值得摆出来:K3 的 LatentMoE 会对合并后的专家输出做 RMSNorm,再经 up_proj 回到残差流。这意味着专家的绝对范数在下游被部分归一化掉了。好消息是这让 K3 大概率比标准 MoE 更抗剪枝;坏消息是 REAP 公式里那个 ‖e_j(x)‖ 测的东西被部分抵消了——这个判据也许该改写成”相对贡献”而不是绝对范数。另外,K3 的 2 个共享专家每个 token 都会触发,任何剪枝都动不到它们,这给了一个谁也拿不走的能力底线。
七、还有一堵墙,剪枝翻不过去
把这套东西跑起来之后,速度是 0.14–0.20 tok/s。
不是”有点慢”,是没法交互。
原因不在剪枝率上。每生成一个 token 要读约 87 GB 权重,其中 61 GB 是非专家张量——它们只占 2% 的参数量,但每个 token 都要完整过一遍。一个 350 GB 的构建(还剩 160 GiB 空闲)实测 0.20,451 GB 的构建实测 0.16,增益几乎与体积严格成反比。这是内存带宽的天花板,不是分配器问题,任何剪枝比例都救不了。
所以这套工作的价值不在”让 Mac Studio 能跑 K3”——它能加载,但不能用。价值在于它把一个平时看不见的东西量化了出来:MoE 模型的能力是按领域分块存在专家里的,而剪枝是一次不会报错的能力删除操作。 你在选校准语料的那一刻,就已经在决定这个模型将来会在什么地方突然变哑。
热门跟贴