代码有严格语法,有明确结构,有变量、函数、控制流,也有一种接近“逻辑推演”的形式感。 相比大量噪声很高的网页文本,代码看起来更干净、更有秩序。因此,很多人会自然地认为: 模型读了更多代码,应该就能学到更强的推理能力。
但这里其实有一个问题:我们说的“代码”,到底是什么?是真正可以执行的程序? 还是包含代码片段、自然语言解释、数学推导、Markdown、Notebook、教程和问答的混合文本? 如果这些东西都被统一叫作代码,那么当模型推理能力提升时,我们很难判断,起作用的究竟是程序本身, 还是混在其中的结构化解释和推理过程。
在这篇 ICML 2026 论文 What Really Improves Mathematical Reasoning: Structured Reasoning Signals Beyond Pure Code 中,我们想重新把这个问题拆开。
我们的结论并不是“代码没有用”。代码当然有用,尤其是对编程能力。 但如果问题变成“纯代码是否能作为一种通用推理增强剂”,答案就没有那么简单了。 至少在我们的控制实验中,真正对复杂数学推理更有帮助的,并不是纯粹的可执行代码, 而是一类更一般的结构化推理信号。
先把“代码”说清楚
很多关于代码预训练的讨论,最大的问题不是实验不重要,而是“代码”这个概念太粗。 比如,一个 GitHub 仓库里的 Python 函数,显然是代码。但一个 Jupyter Notebook 里, 可能同时有代码、文字解释、公式推导和实验分析。一个 Markdown 文档里, 可能有列表、层级标题、伪代码和问题拆解。一个编程问答页面里, 也可能包含大量自然语言解释和一步步的 reasoning。
这些内容确实和代码有关,但它们不等同于纯代码。更准确地说,它们是代码、自然语言和结构化知识的混合体。 所以我们在论文中专门区分了 Code 和 Code-NL。
Code 指的是相对纯粹的可执行程序片段,比如函数、脚本和算法实现。 它的核心特征是程序语法和可执行性。Code-NL 指的是代码与自然语言混合的数据, 比如教程、Notebook、问答、带解释的代码片段,以及一些结构化网页文本。 它们可能看起来很像代码,但很多时候真正有价值的不是代码语法本身, 而是其中的问题描述、步骤拆解、公式推导和解释过程。
这个区分很关键。因为如果不把它们拆开,我们很容易把“结构化解释文本”的收益, 误认为是“纯代码”的收益。
我们做了什么实验?
我们构建了一个约 10T tokens 的预训练语料,覆盖 Web、Code、Code-NL、Math、Wikipedia、Books 和 Multilingual 等多个领域,并在这个语料上训练不同配置的 MoE 模型。
实验设计的重点是控制变量。我们不是简单地问:加入代码之后模型是否更强。 因为这种问法太粗,结果也很容易被数据混杂影响。我们问的是: 在总训练 token 数固定的情况下,如果移除纯代码数据,再用其他领域数据补齐预算, 模型能力会发生什么变化?同样,如果移除数学数据,又会发生什么变化?
这种设置更接近真实训练中的数据配比问题。因为预训练预算通常是有限的。 加入一种数据,往往意味着减少另一种数据。模型能力不是在无限预算下简单叠加出来的, 而是在有限预算下相互竞争、相互妥协出来的。
发现一:纯代码主要提升编程,不一定提升复杂推理
实验结果里最符合直觉的一点是:移除代码之后,模型的编程能力明显下降。 这说明代码数据对代码能力非常重要,这一点没有争议。
但更有意思的是另一面:在固定训练预算下,加入纯代码并没有稳定提升非编程推理能力。 相反,在一些知识密集型任务,尤其是复杂数学推理任务上,纯代码和数学能力之间出现了竞争。
我们在论文里把这种现象称为 negative coupling,也就是负耦合: 某一类数据提升了模型在本领域的能力,但同时可能挤占另一个领域所需要的学习信号。
这其实很符合预训练的现实。训练数据不是越多种越好,也不是每个领域的数据都能无成本地贡献能力。 模型的参数、优化过程和训练预算都是有限的。当我们增加某一类数据时,它可能改变模型学习的重点, 也可能让另一些能力得不到充分训练。
所以,与其说“代码提升推理”,不如说:代码提升了模型处理程序结构和编程任务的能力; 但它是否提升数学推理,要看代码数据里到底包含什么,以及它替代了什么数据。
发现二:数学和代码也不是简单互补
很多人会觉得,数学和代码都很结构化,所以二者应该互相帮助。但实验结果比这个判断更复杂。
数学数据确实能帮助一些编程任务,尤其是 CodeForces、LiveCodeBench 这类更偏算法和竞赛的问题。 原因也不难理解:这些任务不只是写语法,而是需要抽象建模、符号推理和问题分解。
但数学数据并不总是帮助所有代码任务。对于一些需要理解程序执行过程、输入输出行为和代码语义的任务, 数学数据有时反而会带来干扰。
这说明代码能力和数学能力之间确实有重叠,但它们不是同一个东西。 数学更强调符号关系和推导链条,代码更强调可执行语义和程序行为。 二者共享一部分“结构化思维”,但也有各自独立的能力需求。
这也是我们认为数据标签本身不够精确的原因。仅仅说“代码数据”或“数学数据”并不能解释模型究竟学到了什么。 更重要的是看这些数据内部是否包含可迁移的结构。
真正值得关注的是“推理脚手架”
基于这些观察,我们进一步寻找一种更精确的数据形态。论文中称它为 cognitive scaffolds, 可以理解为“认知脚手架”或“推理脚手架”。
这类数据主要来自数学语料,但它们不是普通数学文本。它们通常有更清晰的中间步骤、 更明显的子目标拆分、更完整的符号操作和更可追踪的推导过程。
普通答案可能只是告诉模型“怎么得到结果”。而推理脚手架更像是在展示“问题是如何被拆开的, 每一步为什么成立,前后步骤如何连接”。
这对于复杂数学任务尤其重要。复杂题目往往不是靠一句自然语言解释就能解决的, 它需要模型在多个中间状态之间保持一致:既要做局部符号操作,又要记住全局目标; 既要处理当前步骤,又要知道这一步和最终结论有什么关系。
我们用一个轻量级 FastText 分类器去识别这类结构化样本。这里有一个容易误解的点: 分类器用代码样本作为结构化文本的代理信号,但最终筛选的是数学语料中的样本。 也就是说,我们不是把代码知识搬到数学里,而是借助代码的结构特征, 找到数学数据中更像“显式推理过程”的部分。
这也是这篇论文最核心的 insight:代码的价值未必只在于代码本身。 它更可能提示我们,什么样的文本结构对推理学习有帮助。
结果:复杂数学推理明显受益
在保持数学数据总预算不变的情况下,我们提高了这类结构化数学样本的比例。 结果显示,复杂数学推理任务获得了明显提升,尤其是在 College Math、MATH、OlympiadBench、 MathBench 等更困难的 benchmark 上。
更重要的是,这种替换基本没有明显破坏编程能力。也就是说,相比粗暴增加某一大类数据, 筛选更有结构的推理样本,可能是一种更温和、更有效的数据优化方式。
当然,它也不是万能的。在 GSM8K、CMath 这类相对简单、很多题可以通过自然语言模式直接解决的任务上, 过强的结构化推理信号反而可能带来一点下降。
这点很重要。它提醒我们,数据优化不是寻找一种“永远正确”的数据, 而是要考虑任务难度、能力目标和训练预算之间的匹配。对简单问题来说, 过度复杂的推理轨迹可能不是帮助,反而是干扰;但对复杂问题来说,显式的步骤结构就变得非常重要。
模型内部也能看到数据的影响
除了 benchmark 分数,我们还分析了 MoE 模型中的专家路由。简单说, MoE 模型会把不同输入分配给不同专家模块。专家路由的变化, 可以在一定程度上反映模型内部如何组织不同类型的能力。
实验发现,移除代码数据后,代码任务中的专家激活模式会发生明显变化; 移除数学数据后,数学任务中的路由模式也会明显变化。 这说明数据配比不仅影响最终分数,也会改变模型内部的计算路径。
相比之下,推理脚手架带来的路由扰动更小、更分散。 它不像是强行把模型推向某个单一领域,而更像是在多个任务之间提供一种比较稳定的结构化信号。
这与我们的观察是一致的:推理脚手架的作用,不是制造一个新的“数学专家”或“代码专家”, 而是帮助模型更好地组织复杂推理过程。
这项研究想说明什么?
如果只用一句话概括,这篇论文想讨论的是:不要把“代码提升推理”这个结论说得太快。
代码确实重要,但代码数据内部并不纯粹。很多被称为代码的数据, 其实包含大量自然语言解释、结构化推导和跨领域知识。过去观察到的推理提升, 可能并不完全来自可执行代码本身,而是来自这些混合数据里的结构化推理轨迹。
因此,未来做预训练数据优化时,问题不应该只是“代码放多少”“数学放多少”“网页放多少”。 更关键的问题是:哪些样本真的携带了可迁移的能力信号?
从这个角度看,大模型训练正在从“更多数据”走向“更懂数据”。 数据来源当然重要,但数据结构可能更重要。一个样本来自 GitHub、arXiv 还是网页, 并不能完全决定它的价值。真正重要的是,它是否把问题拆得清楚, 是否暴露了中间推理过程,是否能帮助模型学习跨步骤、跨领域的结构。
所以,这篇论文并不是要否定代码数据,而是希望把问题说得更精确: 纯代码主要帮助模型学习编程和程序结构;而复杂推理能力更依赖那些显式、可追踪、可迁移的结构化推理过程。
换句话说,让模型更会推理的,可能不是“更多代码”,而是更好的推理脚手架。