每次我们发布新模型,都会有一些同事来到知乎分享背后的故事,此次K2也不例外。有些人觉得这是我们的营销手段,也许是有一点这方面的考虑,但更本质的是,我们都是一群乐于分享的人,而一个模型发布的背后,有太多值得分享的故事了。
对于本人来说,如果第三方发布一个新模型,我会主要关心模型的架构与优化方面的变化,这通常也是Pretrain阶段的主要发力点之二,所以接下来也从这两个方面介绍一下K2。
架构
架构方面,可能会让一些想看“新东西”的读者失望,因为我们大体上是复用了DeepSeek-V3的架构,仅在个别地方有所改动,下图比较清晰地展示了改动之处:

但这里想要说的是,不管是跟DeepSeek-V3的“异”还是“同”,背后其实都有很多思考和实验在支撑着我们的选择。
异
首来说说“异”。MLA部分,我们没有像DeepSeek-V3那样double heads,
同学的回答从Infra角度介绍了这个变化,而对于我来说,我是单纯觉得它有点别扭,可以说double heads是我少有的不认同DeekSeek审美的操作之一。不论如何,最终我们放弃了double heads,这既降低了Infra的压力,又减少了num_heads这个变数,个人认为这是很舒服的变化。然后是MoE部分,我们将num_experts从256升到384,这部分有两个原因,一是为了补回没有double heads带来的损失,二是符合我们测出来的Sparsity的Scaling Law。后者简单来说,就是在一定范围内,固定激活的专家数量,增加总专家数带来的效果收益,大于增加的Infra成本。
还有就是num_experts=384,刚好凑出了1T总参数量这个吉祥的数字,代表着我们开始迈进 1t.ai(可以点击)时代!其实很多时候就是这些简单的美感,支撑我们渡过枯燥的实验过程。
此外,还有一些细微的改动,比如我们将Routed Experts的Scaling Factor从2.5改为了2.827,这个改动很小,大体上不会带来实质变化,但它是我们自己从理论出发推导出来的结果(参考这里),它遵循了我们一贯的原则——每一处继承或者调整,都需要足够的证据,不管是理论的还是实验的。
同
接着再来说说“同”。首先还是MLA,为什么要选择MLA,笔者已经用两篇文章《Transformer升级之路:21、MLA好在哪里?(上)》和《Transformer升级之路:21、MLA好在哪里?(下)》介绍了MLA的优越性,简单来说就是MLA真的很能打。
当然,实际情况还复杂得多,我们内部也一直致力于探索MLA更好的替代品,但是作为我们第一个开源的超大模型,我们还是选择了致敬DeepSeek,复用它的MLA设计。还有MoE部分,我们沿用了DeepSeek-V3的Shared Expert、大Sparsity和Loss-Free负载均衡,这些我们都是经过自己的实验,确认它能要不能提高MoE的训练稳定性,要不能提高MoE的学习效率,最终才确定使用的。
这里特别提一下Sparsity部分,在相当长一段时间内,我们都是在Sparsity=8(64选8)下做实验,直到有一天我们解决了Infra问题后,开始尝试更大的Sparsity,发现收益非常可观,于是开始探索Sparsity的Scaling Law,并且感叹似乎一切都在DeepSeek的预料中(DeepSeek-V3已经是256选8,32的Sparsity;K2则是384选8,48的Sparsity)。
所以,我们最终决定,K2还是先狠狠地致敬DeepSeek了,而不是刻意地标新立异。更多的想法和创意,我们把它们做得更扎实后,在K3、K4再见了。
优化
优化方面,很多读者都应该注意到,我们已经转移到Muon的大本营,然而Muon在超大规模上的实践完全是空白的,所以从Moonlight到K2,实际上也是一场Muon的Scaleup之路上的“大开荒”。
器
几个月前的Moonlight中,我们已经在16B的MoE上验证了Muon,确认了Weight Decay的必要性和Match Adam Update RMS的有效性。但单凭这两点,还不足以将Muon扩展到千亿甚至万亿参数的模型,因为新的拦路虎出现了——MaxLogit爆炸。
对此,我们的解决方法是MuonClip,它是Muon与QK-Clip的组合,技术细节大家可以看文章《QK-Clip:让Muon在Scaleup之路上更进一步》,这里我们尽量多聊一点文章以外的内容。
\text{MuonClip}\quad\left\{\quad\begin{aligned} &\boldsymbol{M}_t = \mu \boldsymbol{M}_{t−1} + \boldsymbol{G}_t \\[8pt] &\boldsymbol{O}_t = \mathop{\text{msign}}(\boldsymbol{M}_t) \underbrace{\times \sqrt{\max(n,m)}\times 0.2}_{\text{Match Adam Update RMS}} \\[8pt] &\boldsymbol{W}_t = \boldsymbol{W}_{t−1} − \eta_t (\boldsymbol{O}_t + \lambda \boldsymbol{W}_{t-1}) \\[8pt] &\left.\begin{aligned} &\text{if }S_{\max}^{(l,h)} > \tau: \\ &\qquad\text{if }\boldsymbol{W} \in \{\boldsymbol{W}_{qc}^{(l,h)}, \boldsymbol{W}_{kc}^{(l,h)}\}: \\ &\qquad\qquad\boldsymbol{W}_t \leftarrow \boldsymbol{W}_t \times \sqrt{\tau / S_{\max}^{(l,h)}} \\ &\qquad\text{elif }\boldsymbol{W} \in \{\boldsymbol{W}_{qr}^{(l,h)}\}: \\ &\qquad\qquad\boldsymbol{W}_t \leftarrow \boldsymbol{W}_t \times \tau / S_{\max}^{(l,h)} \end{aligned}\quad\right\} \text{QK-Clip} \end{aligned}\right.\\
QK-Clip是应对MaxLogit爆炸非常直接有效的方法,而且从事后来看,它的思想也非常简单,但它的出现并没有那么显然易得,甚至可以说非常“突然”。
思
让我们回到K2启动训练的前几天,尽管大家都觉得K2的训练已经迫在眉睫,然而我们仍没找到能杜绝MaxLogit爆炸的方法,我们已经在这个问题上前前后后试了几个月,所有的方法在参数量进一步变大后均以失败告终。我们甚至已经打算尝试“Q、K的weight单独用回Adam优化器”这样的丑陋思路(因为Adam似乎没那么容易炸)。
另一边,笔者隐隐觉得参数裁剪特别是奇异值裁剪能够解决这个问题,但奇异值裁剪并不直观,多数同学不好接受,而且实现复杂度也略高,所以依然是待定状态。在一筹莫展之际,笔者突然醒悟般意识到“MaxLogit本身就是触发Clip的最本质信号”,然后就顺理成章提出了QK-Clip。这个方法相比奇异值裁剪直观太多了,大家很快都理解了它,并开始测试,终于K2的最后一环被打通,K2训练正式启动!期间还经历了从Total Clip到Per-Head Clip的变化,但这都是小插曲了。
QK-Clip的出现,对笔者以及整个团队的冲击都蛮大的。就MaxLogit这个问题而言,我相信各位读者随便都能提出很多可能的解决方案,然而事实就是前几个月我们严重困在了“可能”两个字中,因为对于一个问题,有太多“可能”的解决办法了,没有直接思考“保证能”解决问题的办法,是我们前几个月困境的本质所在。
所以,QK-Clip不仅解决了MaxLogit爆炸这个具体的问题,还给了我们很大启发,在面对一个类似的难题时,我们应该至少要问自己三个问题:
- 这个方法能保证解决这个问题吗?(我们此前的一系列无效尝试)
- 如果不能,有什么方法能保证解决这个问题吗?(QK-Clip)
- 这是解决这个问题的最小改动的方法吗?(Per-Head的QK-Clip)
另外,我们认为QK-Clip的思想,实际上是解决很多训练不稳定问题的“抗生素”,它可以推广成“哪里不稳Clip哪里”,只要我们的监控指标出现了异常,那么就可以考虑构建类似的Clip。这也许不是最优雅的解决方法,但肯定是一个非常直接有效的解决方法。
最后
最后的最后,K2的成功训练,是很多同学共同努力的结果,本文只谈及到了自己相关的非常小的部分,还有很多精彩的内容没有参与和谈及,敬请大家期待我们的Tech Report。