代码生成也许不总靠更大模型:一个简单自蒸馏方法带来的提醒

今天 Hacker News 上还有一篇很值得记的论文,标题很朴素,甚至有点故意低调,叫《Embarrassingly Simple Self-Distillation Improves Code Generation》。它讲的事情也确实简单:不用老师模型,不用 verifier,不用强化学习,模型只拿自己采样出来的代码答案,再对这些答案做一次普通的监督微调,代码生成能力就能明显提升。

这听上去几乎有点反直觉。一个模型靠自己的输出再训练自己,为什么会变强,而不是把已有缺陷放大一遍。论文给出的实验结果却相当醒目。以 Qwen3-30B-Instruct 为例,LiveCodeBench v6 上的 pass@1 从 42.4% 提升到了 55.3%,而且提升主要集中在更难的题目上。作者还说,这种方法不仅对一个模型有效,在不同尺寸的 Qwen 和 Llama 模型上也都有泛化。

我觉得这篇论文真正有意思的地方,不只是"又一个分数提升",而是它在提醒我们一件事:代码生成问题的瓶颈,可能并不全在于模型知道得不够多,而在于模型在生成时怎样在"精确"与"探索"之间分配概率质量。很多时候,模型不是完全不会做,而是在解题路径上被一堆看似合理但最终错误的 token 分支带偏了。于是你看到的现象就会是,模型能写出像样的代码、也懂大概思路,可 pass@1 还是不够高。

这篇论文把问题描述成一种"precision-exploration conflict",我觉得这个提法很值得注意。代码任务和普通聊天不一样,它对正确性更苛刻。自然语言回答里,一个近似表达、一个换种说法,往往问题不大;但在代码里,一个变量名、一个边界条件、一个循环终止条件错了,整个答案就会从正确掉到错误。因此,生成代码不是单纯追求多样性,也不是单纯追求保守,而是要在不同上下文里动态调整:该收紧的时候收紧,该保留探索的时候保留探索。

简单自蒸馏之所以有效,论文的解释是,它会在这种上下文相关的分布调整上帮模型"校形"。哪里需要更精确,它就压低那些分散注意力的尾部选项;哪里仍需要探索,它又不会把多样性完全抹平。换句话说,这不是模型突然学会了全新的知识,而更像是它学会了在做题时少走一些自己原本爱走的弯路。

这件事对实际做 AI coding 产品的人有什么启发。第一,别太快把性能问题都归结为"模型还不够大"。在很多任务上,后训练、采样策略、轨迹筛选、蒸馏数据构造,可能比再堆一点预训练算力更划算。第二,代码生成是少数特别适合这类方法的领域,因为它天然有可执行、可判定、可比较的输出结构。你不一定需要一个庞大的奖励模型,很多时候只要把模型自己在不同温度、不同 truncation 条件下的输出用得更聪明,就能榨出明显收益。第三,这也再次说明 coding model 的竞争不只是底座模型竞争,而是整条生成与后处理流水线的竞争。

更深一层看,这篇论文还触到一个常被忽略的问题。我们现在谈模型进步,常常默认路径是更大的数据、更长的训练、更高的算力预算。但如果一些相对便宜的后训练配方也能换来非常可观的代码收益,那么未来模型竞争就不只是"大厂烧钱游戏",而可能变成谁更懂任务结构、谁更会设计训练回路。对开源社区来说,这其实是个好消息。因为这类改进更容易被复现、被移植,也更容易在中等规模模型上见效。

当然,简单自蒸馏也不代表万灵药。模型如果本身基础能力不够,拿自己的输出再训一次,可能只是把低质量先验进一步固化。它更像一种放大器:当底座已经具备相当不错的代码理解和解题能力时,这种方法能帮它把正确路径变得更常走,把错误路径变得更少走。但它不能凭空创造不存在的能力。

不过,这已经足够重要了。因为现在很多 coding assistant 的真实体验瓶颈,不在"完全不会写",而在"经常差一点"。少一个越界条件,少一个错误假设,少一次看起来合理却没跑通的实现,用户感受到的质量差距就会非常大。而这篇论文的价值正在于,它提供了一条成本相对低、工程上相对现实的路线,去减少这些"差一点"。

如果把它翻译成更直白的话,这篇论文在说的其实是:模型有时候不是不会,而是太容易被自己带偏。想办法让它少被自己带偏,代码质量就会明显上升。

这也是为什么我觉得它比很多"又大了 2 分"的论文更值得记。它不是在证明 scaling law 继续成立,而是在提醒我们,代码生成的下一波提升,很可能不只是更大的模型,也可能是更聪明的自我整理。