[基模杂谈]代码预训练数据管线经验杂谈
Jasaxion / 2026-09-28
随着蒸馏实验的不断发展,现在越来越多的研究发现数据产生的价值越大越模型架构等,把数据做好模型能力有稳步的提升。代码数据预训练的来源和流程,优化路径包括:去重、模型打分、合成数据与过程数据融合,助力模型性能提升。
写在前面:显卡负责计算,数据负责决定算什么
这篇文章起源于我之前在 JD 参与 JoyAI-LLM-Flash 预训练过程中的一些经验记录。这里不展开未公开的数据配方和内部实验,而是把这段时间关于代码预训练的思考,与公开论文、数据集和工程实践放在一起,重新梳理一遍。
我越来越认同一句不太严谨、但很有工程现场感的话:一个好的基座模型,离不开“数据 + Infra”。显卡当然重要,不过把同一份样板代码训练十遍,显卡只会认真地帮你算十遍,不会主动弹窗提醒:“老板,这个我是不是昨天见过?”
严格说,模型能力是数据分布、模型结构、训练目标、计算预算、优化策略和评测共同作用的结果。本文不打算证明架构不重要,而是讨论一个更实际的问题:当模型和预算大体确定以后,我们还能怎样通过数据,让每一份算力花得更明白?
我的关注点也因此从“又收到了多少 TB”逐渐转向几件事:收进来的究竟是什么;模型实际看了多少次;样本是否对应想要的能力;涨分来自有效学习,还是碰巧把考卷也收了进来。
一、误区:TB、token 和“训练过”不是一回事
代码数据讨论最容易发生的误会,是大家都在说“规模”,却根本没在数同一种东西。
| 资产 | 实际问题 | 常见误读 |
|---|---|---|
| 抓取与存储字节 | 下载、解压或存储了多少数据? | 把压缩体积、源码字节、重复出现的字节混为一谈 |
| 文件条目与唯一内容 | 某文件出现在哪些仓库?有多少不同正文? | 把目录清单条目当成已下载且唯一的源文件 |
| 唯一语料 tokens | 去重、筛选后有多少可用内容? | 忽略 tokenizer、数据版本和过滤范围 |
| 累计训练 tokens | 经过采样和重复,模型一共看了多少? | 把多轮训练消耗当成独立新数据 |
| 有效监督 tokens | 多少位置实际计算了训练损失? | 把 padding、仅作上下文的内容也算成等量监督 |
例如,Seed-Coder 的 GitHub 文件语料约有 1T unique tokens,但整个预训练累计消耗 6T tokens,还包含代码相关网页、数学相关网页以及后续阶段的数据。Phi-1 的约 7B-token 语料也不意味着只训练 7B:论文报告多轮训练后累计略超 50B tokens。
把 unique tokens 和 consumed tokens 混用,会同时误判数据覆盖、重复次数和训练成本;把所有输入位置与 loss positions 混用,又会把两种长上下文配方比较成苹果对梨。
本文后面出现的数字,尽量说明它是“原料池”“训练子集”还是“累计消耗”。跨论文的 token 数如果没有统一 tokenizer,就先当量级参照,不急着算精确增长倍数。
二、经典 The Stack 三代,不只是越来越大,也是在重新定义“可用”
2026年代码数据的主线是”去掉人的手工规则,加上可验证的信号“。
流水线已经收敛成一个共同栈:抓取–>精确去重–>跨语言近去重–>最少量规则前置–>模型打分/分类器筛选–>分阶段配比–>仓库级打包+FIM;
而真正拉开差距的三件事是:合成数据的形态(改写型 vs 教科书型vs可验证型) 、软件演化过程数据(commit/PR/issue) 、以及 midtraining的时机与配比。
开源代码数据:Stack 三代
| 版本 | 时间 | 原始规模 | 训练切分 | 语言 | 关键变化 |
|---|---|---|---|---|---|
| Stack v1 | 2022 | 6.4TB | ≈200B Token | 358 | 首次把“许可合规的大规模代码”做成公共品;opt-out 机制 |
| Stack v2 | 2024 | 67.5TB | ≈550B Token | 600+ | 基于 Software Heritage, SWHID 溯源;但内容需二次获取 |
| Stack v3 | 2026.07 | 113.7TB / 224M 仓库 / 约 439 亿文件 | ≈4.9T Token / 15.9TB /173 M 仓库 | 713(train) / 770(full) | 直爬 GitHub、内容内联、跨语言去重、修掉v2的去重缺陷 |
Stack v1:给公开代码贴上来源、许可和退出入口
The Stack 最初论文介绍的是约 3.1 TB、30 种编程语言的宽松许可源码集合;后来 v1 系列扩展,才出现我们熟悉的 6.4 TB、358 种语言等回顾口径。不能把后续版本的规模直接贴到最初那篇论文上。
它的基本思路可以这样理解:

这里有两个经常被省略的细节。
第一,GHArchive 是仓库发现线索,不是源代码正文仓库。论文从事件记录中取得约 2.21 亿个仓库名,实际下载约 1.37 亿个仓库;全量获取与最终许可子集,显然不是同一个统计对象。[1,§3]
第二,许可分类不是一次运行后永久正确。v1 数据卡记录,早期被归入 permissive 的 MPL、EPL、LGPL,后来因属于弱 copyleft 被调整出相应许可范围。[2] 这个历史很值得记住:自动识别和人工政策都可能变化,保留许可证据与版本,比只保留一个布尔值 is_safe=true 更重要。
v1 也不只是发布数据。它在 350M 模型、Python 子集上比较近去重方案;宽松许可设置下,HumanEval pass@1 从 10.99 到 13.94,MBPP 从 11.60 到 15.94。[1,表5–6] 这支持“在该设置下去重改善效果”,却不能直接推出今天任何规模、任何语言都应使用相同阈值。
v1 的意义可以概括为:公开代码不再只是一个巨大的下载目录,而开始成为带有来源和治理机制的研究资产。
Stack v2:从独立软件档案里取数据,但档案不等于训练集
The Stack v2 借助 Software Heritage(SWH)的软件归档图。SWH 使用内容寻址结构组织文件、目录、版本与快照,适合去重存储、追踪来源和定位历史软件对象。[3][4]
不过,SWH 保存历史,不等于 The Stack v2 把全部历史都拿来训练。 StarCoder2 论文明确写道,它为主源码提取选择主分支的最新 revision。档案馆里有历年报纸,你去借最新一期,并不等于把档案馆搬回了家。[3,§2.1]
v2 的核心流程是:

数据卡报告约 3.28B 个唯一文件、67.53 TB,来自约 104.2M 个 GitHub 仓库。[4] 相比 v1,语言识别和文件树许可传播也更系统:不仅看文件扩展名,还使用 go-enry 等工具;许可信息通过目录结构关联到文件。[3]
v2 的一个现实门槛是内容访问。公开数据集主要给出 SWHIDs 等标识,正文需要按 Software Heritage 的批量访问安排获取。 [4] “拿到了数据集”可能只是拿到了一叠提货单,提货本身还需要授权和工程工作。
这也解释了为什么 v2 的不同数字总让人困惑:
- v3 的回顾表写 v2 训练子集约 550B tokens。
- StarCoder2 论文所述全语言代码训练集合是 775B+ unique tokens。
- 加入其他来源后的整体训练集合约 900B+ unique tokens。
- 模型实际累计训练则达到 3.3–4.3T tokens。[3][4][6]
Stack v3: 正文更容易拿到了,但“开箱可读”不等于“无条件可训”
BigCode 社区发布 The Stack v3,这是迄今最大的开放代码数据集,包含114 TB 原始数据、 15.9 TB 处理后数据、约 5T 去重tokens,涵盖 770 种编程语言和2.24亿仓库,完全开放且无限制许可。相比2024年的v2(68 TB raw /550B tokens / 618种语言),V3 在规模、语言覆盖和使用便利性上大幅提升,并重新爬取了截至2025 年8月的最新 commit。
数据全集: https://huggingface.co/buckets/HuggingFaceCode/stack-v3-full
2026 年 7 月出现的 The Stack v3 重新直接抓取 GitHub 默认分支快照;官方说明抓取于 2025-08-07 完成,不包含完整 Git 历史。训练版把 UTF-8 正文放进 files[].content,按仓库分组,使用上明显更方便。[6]
初版约 4.9T tokens;截至本次核查,官方 v3.1 修订已将其更正为约 3.6T。 Changelog 的原因是分区错误造成精确重复文件泄漏,而不是发现了四分之一代码“质量不好”。
固定修订 1f61b735bc0a5698345ce2196730f24bfa467f33 的训练统计为:
| 统计项 | 该修订报告值 |
|---|---|
| 仓库记录 | 172,823,131 |
| 保留文件条目 | 2,267,483,994 |
| 源码字节 | 11,501,524,769,560,约 11.50 TB |
| 编程与标记等语言类别 | 713 |
| 估算 tokens | 3,611,282,968,225,约 3.61T |
| 用于估算的 tokenizer | Qwen/Qwen3-Coder-Next-Base |
以上是官方统计,不是本文下载全库后重算。该修订 README 开头仍残留 15.9 TB,而版本对照表与结构化统计已更新,因此这里采用结构化统计,并把旧数字作为初版历史记录。[6]
算法层面做了去重,不代表分布式落盘、分区和拼接以后,产物里就一定没有重复。 验收最终产物,不要只验收流程图。
v3 还需要分清两种发布物:

官方对 full 报告约 113.7 TB、约 224M 仓库、43.9B 文件条目,但其中约 28.3B 是 stub:二进制、超大文件、无法识别语言等对象只留下了清单信息。[6] 所以“439 亿条目”不是“439 亿份下载即用、彼此唯一的源码”。同样,按仓库分组只描述组织方式;经过过滤与去重,依赖和文件不一定完整。
近去重方面,v3 在所有语言上共同处理,使用 MinHash-LSH 与 Jaccard 估计复核;候选阈值报告为 0.7,簇代表按 stars、forks、宽松许可、仓库较早创建的顺序选择。[6] 这里的“跨语言”是跨语言分区检查文本重复,不等于自动判断 Python 和 Java 是否实现了同一算法。
v3 相比v2优化了什么?
内容内联,一行一个仓库。 v2 存的是Software Heritage 标识符,研究者要拿到真实内容还得另做安排——社区吐槽“数据集很棒,代码在哪儿“。V3 直爬 GitHub 默认分支 (截止2025-08-07),把 UTF-8 源码直接放进数据里,“全仓库预训练”从研究话题变成开箱可用的默认。
去重改成跨语言+实测复核。 V2按语言分别做 MinHash;v3一次跑完所有语言,并且不再信任 LSH 索引,而是实测每一对候选的 Jaccard,把估计重叠 ≥70%的聚成簇。这修掉了v2中“比预期多丢文件“的缺陷,C++ 涨 15x、 TypeScript 7.5x , Rust 7x , Python 4.8x(部分是两年新增,部分是 bug 修复)。
保留规则公开且可调。 每簇保留:最多 star –> fork 数 –> 许可宽松度 –> 最早创建时间。这不是中性规则——它把 GitHub 的流行度动力学烘焙进了语料。stack-v3-full 桶保留簇 ID 与过滤前信号,就是为了让不认同这套政策的团队自己重建。
过滤沿用 StarCoder2 的规则集:字母字符 <25%(汇编用字母数字)、存在>1000字符的行、平均行长>100字符、自动生成标记、 大块编码数据。再用 StarPII把邮箱/密钥/ 姓名/密码/IP 替换为占位符。
真正值得继承的不是“一代比一代大”的单向故事,而是:来源治理、内容可得性、过滤可重建性和产物质量,都在成为数据集的一部分。[1–6]
三、代码数据源:GitHub、SWH、Common Crawl
我曾经很喜欢“GitHub 是活的当下,SWH 是历史,网页是人类对代码的解释”这种概括。它有画面感,但一旦写得太满,就会误导实现。
代码数据来源于GitHub、Software Heritage、 Common Crawl,它们其实是同一批代码的三种视角: GitHub 是活的当下, Software Heritage 是死的全历史,Common Crawl是人类对代码的解释。三者供的不是同一种东西,因此也不能互相替代。[3][4][32]

三个数据源的能力归属(token 占比为 RefineCode 口径)。注意 token 量与能力数量是反相关的:GitHub 供了78%的token 但只撑起基础代码能力,SWH 只占12.5% 却撑起了整个可审计与合规维度。
| 维度 | GitHub | Software Heritage | Common Crawl |
|---|---|---|---|
| 本质 | 活的平台 | 死的档案 | 人类的解释 |
| 提供的资产 | 默认分支源码、issue / PR / commit、star / fork / 许可、CI 状态 | 全量版本历史、SWHID 永久标识、已去重的 Merkle 图、跨 forge(GitLab / PyPI / npm / Debian) | 教程、官方文档、博客、Stack Overflow 问答、API 参考 |
| 不可替代之处 | 唯一的天然过程监督来源:改了什么、为什么改、改完过不过 CI | 唯一能回答“两年前这个文件长什么样”,因此也是唯一能做干净时间切分与作者级 opt-out 的路径 | 唯一的自然语言 ↔ 代码大规模对齐语料 |
| 缺什么 | 没有稳定标识(force push / 删库即失效);许可元数据噪声大;无法回溯历史 | 内容分发受法务约束;函数数据集快照有滞后(v2 用的是 2023-09-06 的图) | 候选占比极低(约 3%)、噪声极大;代码片段常不完整、不可执行 |
| Token 占比(RefineCode) | 755B(78.4%) | 120B(12.5%) | 71B(7.4%) |
三者存在重叠:同一份 README 可能出现在 GitHub、SWH 和网页抓取中。所谓“多源”,可能真的是多种知识,也可能只是同一本说明书换了三个快递箱。
OpenCoder 的 RefineCode 给出了一个不错的量化例子。论文表 2 中的来源是:[7]
| 来源 | tokens |
|---|---|
| GitHub Code | 755B |
| Jupyter Notebooks | 11B |
| The Stack v2 | 120B |
| Processed Common Crawl | 13B |
| Processed SkyPile | 3B |
| Processed FineWeb | 55B |
| Processed AutoMathText | 3B |
合计约 960B。13B、3B、55B 相加是 71B,但它是三个网页子来源的合计。FineWeb 上游来自 Common Crawl ,也不意味着直接来源列可以随意合并;上游和当前数据产品是两个维度。
755B 占得多,并不能证明 GitHub“只提供基础能力”;SWH 的可审计性首先是数据治理属性,不是模型吃下某种 token 后自动学会的一项技能。要讨论来源的边际价值,需要固定预算的替换消融。
许可与 PII:开放数据不等于免检数据
v3 的数据库许可为 ODC-By,原始源码仍需遵守对应许可证;no_license 表示流水线未识别出许可,不是作者主动放弃版权。full 还明确未做 PII 脱敏;train 虽经 StarPII 扫描,官方也说明检测并不完美。[6]
因此,许可政策、退出请求、密钥和个人信息风险,不能等质量评分器打完分再决定。模型认为代码写得漂亮,与我们是否应该处理、训练或再分发它,是两个问题。更稳妥的做法是保存原始许可文件、路径适用范围、识别器版本和所有来源关联,并按使用场景审查。
四、代码数据流水线
这里总结了预训练代码数据的几个过程:采集、精确去重、近似去重、语法过滤、质量打分、配比调度、打包拼接。

| 做什么 | 典型代价 | 2026 年的默认做法 | |
|---|---|---|---|
| 采集 | 直接爬 GitHub 默认分支 / 用 SWH 档案 / 从 CC 抽代码网页 | 存储与带宽;v3 原始 113.7 TB | 直接爬 + 内容内联(v2 只存指针,被吐槽“数据集在哪儿”) |
| 精确去重 | 按内容哈希折叠,每个文件只存一份 | 几乎免费 | 必做,覆盖率最高收益最大 |
| 近去重 | MinHash + LSH 聚类,每簇留一个代表 | CPU 与 shuffle;是流水线最大瓶颈之一 | 跨语言全局聚类 + Jaccard 复核(v2 是按语言分别做) |
| 语法过滤 | Tree-sitter 解析,剔除语法错误文件 | 便宜 | 作为规则前置的一部分,不做深度语义检查 |
| 质量打分 | LLM 标注小样本 → 蒸馏小打分器 → 全语料推理 | 一次推理全语料;1.3B 打分器是主流折中 | 只滤最差的一档(10% 量级),不做“只留 1% 精品” |
| 配比与调度 | 决定各源在各阶段的采样权重 | 需要小模型消融,一次可信消融是 10⁴ GPU-day 量级 | 三阶段:通用 → 中期(高质量/仓库/合成)→ 长上下文 |
| 打包 | 拼接、FIM 注入、去污染 | 低 | 依赖拓扑拼接 + 结构感知 FIM + 多层去污 |
关键是把“算特征”和“做决定”分开。某个文件的模型分数已经花钱算完,阈值从 0.4 调成 0.5,通常不该再把整个模型推理跑一遍。换一个仓库配比,也不应该重新爬 GitHub。Seed-Coder 明确强调模块可单独运行,便于增量扩展与避免全流程重算。[5,§2.1]
至少应记录:输入快照与文件清单、处理代码版本、tokenizer、规则与评分器版本、随机种子、输出摘要,以及每阶段文档数、字节数和 token 数。按语言、来源、长度、仓库类型分桶保存排除样本,比一个漂亮的总保留率更能帮助排障。
DataTrove 官方 MinHash 示例有一句非常朴素、却很重要的警告:最后重读输入删除样本时,输入来源和任务划分必须与生成签名时一致。[30] 如果删除索引基于旧分片的行号,中间却换了输入顺序,那么一个完全正确的去重算法,也能很高效地删错文件。
所以我理解的 Infra,不只是“跑得快”,还包括:能够证明这一轮究竟处理了什么,失败后怎样恢复,参数改变后哪些东西需要重算。 这和最终模型分数一样,是数据资产可信度的一部分。
五、流程 1:去重
精确去重先解决“同一份内容被搬了多少次”
代码世界的重复来源很多:fork、镜像、vendored 依赖、复制的工具脚本、生成代码和不同来源的重复收录。精确内容哈希适合作为第一道基线,但“算法简单”不等于“几乎免费”:全量读取、解压、哈希、shuffle、结果回写和溯源关联都要花成本。[7][30]
还要区分原始字节哈希和规范化后的哈希。统一换行、剔除注释、修改空白,对查重可能有帮助,但不应该悄悄把规范化文本当成原始证据;Python 缩进更不是普通装饰。建议保留不可变原文,另存用于检索的视图与变换版本。
去重是代码数据流水线里最容易被低估的一环。直觉上它是个算法问题(用什么哈希、多少band),但2025-2026年的证据一致指向另一个结论:检测算法已经收敛到一个性价比点,真正決定下游效果的是保留策略——删多少、留哪一份、大簇怎么处置。
MinHash-LSH 的阈值,不是想象中的门禁线
把文档切成 shingle 集合后,Jaccard 相似度为:
$$ J(A,B)=\frac{|A\cap B|}{|A\cup B|}. $$理想的独立 MinHash 下,单个签名位置相同的概率等于真实相似度 $s$。LSH 把签名分成 $b$ 个 band,每个 band 有 $r$ 行;至少一个 band 完全相同,就把这对文档召回为候选:
$$ P(\mathrm{candidate}\mid s)=1-(1-s^r)^b. $$FineWeb 的公开参照是词级 5-gram、112 个 hash、14×8 分组。[8] 在这个固定配置下:
| 真实 Jaccard 相似度 $s$ | 候选命中概率,近似值 |
|---|---|
| 0.70 | 56% |
| 0.75 | 77% |
| 0.80 | 92% |
| 0.85 | 98.8% |
这里变化的是文档对的真实相似度,不是把删除阈值从 0.75 调到 0.85。若另有候选复核阈值,固定候选集下提高阈值,通常会保留更少重复边,而不是删得更多。
需要分别记录四件事:shingle 怎么定义、LSH 怎么召回、候选如何复核、复核后的边如何聚簇。FineWeb 的这版方案直接用 LSH 匹配进行传递聚类;不能把另一个方案的 Jaccard 复核步骤强行写进它的方法。[8]
聚簇还有一个隐藏坑:A 像 B、B 像 C,不保证 A 同样像 C。连通分量不是一个“任意两点都超过阈值”的朋友圈。大簇里可能是真正泛滥的模板,也可能有桥接样本造成链式合并。看最终删除率之前,先抽查边、簇和代表。
命中概率随相似度陡峭上升。这意味着阈值的选择比哈希函数的数量更敏感:从0.75提到 0.85,命中率从 77% 跳到98.8%,被判为重复的文件量差出一个量级。
太容易相似了
文件级还是仓库级,也是代码特有的陷阱,为了保结构而在仓库粒度去重
代码比文本多一个维度:文件不是独立的,它们属于仓库。于是出现了一个诱人的想法——为了保留仓库结构,就在仓库粒度去重。
OpenCoder 的 Python 实验给出了很有说服力的局部证据。相同原始池中约有 4.858 亿文件、1103 万仓库;文件级去重后保留 32.74B tokens,仓库级保留 99.47B,约为前者三倍。仓库级结果再做文件级去重,还能删除约 68B tokens,即 68.4%。在对应 1.5B 模型的 HumanEval/MBPP 实验中,文件级方案更好。[7,§6.1、表9、图8]
故此为了保持结构而牺牲去重,等于把去重作废。
原因并不难理解:两个仓库整体差异很大,不妨碍它们都拷贝了同一个公共依赖。只看整仓库相似度,局部重复会被其他文件稀释。
但结论不是“仓库级上下文没用”,而是“仅用仓库作为判重单元,不足以控制文件冗余”。我会把实现拆成三层:内容存储去重、仓库版本关系保存、训练曝光权重控制。
完全相同正文可以共用存储,但每个 repo/commit/path 关联仍应保留;近重复文件则不能随便替换成另一仓库的代表,否则 API 版本和依赖可能错位。更微妙的是:磁盘上只存一份,不代表训练时只看一遍。如果打包时按一百个仓库关联展开,同一依赖仍可能出现一百次。
因此,“文件去重后恢复仓库”是一条设计方向,不是一条无损定理。可以限制重复内容参与 loss 或限制采样,但重复上下文本身仍消耗算力,也仍影响其他位置的预测。需要看实际训练曝光,而不只是存储压缩率。

文件级完成去重后,在打包时再重建仓库结构,两者应该解耦。
去得越干净,一定越好吗?FineWeb 的答案是“不一定”
FineWeb 曾尝试跨全部 crawl 全局去重,老快照被删掉大量内容,最终留下的数据在同预算训练中却不如每快照独立去重。作者检查发现:经常出现的正常内容被削掉后,广告、关键词列表等相对更容易被放大。 [8]
FineWeb2 则按自然语言跨快照去重,并保留簇大小,随后依据不同簇大小的质量过滤率重新分配重复权重,称为 rehydration。[9] 它不是“N≥1000 一律删除”,也不是“最大的簇训练最多”。其权重依据语言和质量统计变化,而不是一个全局神奇数字。
这两项工作告诉我的不是“重复好”或“重复坏”,而是:去重改变了采样分布;删掉什么与留下什么,同样重要。 删除重复和有意识地重复高价值数据并不冲突,前提是后者是可解释、可测量的选择,而不是文件复制留下的偶然结果。
六、流程 2:质量过滤,规则管底线,模型给信号,实验做裁决
从手工规则走向模型,不等于把人类判断从系统里删除
质量过滤是 2025-2026年变化最剧烈的地方。
2023-2024 年的主流是“人写规则“:StarCoder 的长行/字母占比/编码数据过滤, DeepSeek-Coder 的分语言规则,OpenCoder 的130+条手工规则带自定义权重ß。
2025年之后,头部团队不约而同地改成“用模型打分“。

规则的优势很实在:便宜、可控、可解释。长行、乱码、明显生成标记、大块编码数据,都适合先用规则处理。缺点也明显:表面特征容易与真正想要的质量错位。[3][5][7]
Seed-Coder 举了两个很好的例子:一个温度处理函数注释齐全,却出现 temp < 0 and temp > 1 这种不可能条件;另一个看似大量硬编码数字的数组,实际上用于 LED 图案显示。[5]

前者不是“逻辑错误无法量化”——约束分析和边界测试就可能发现。真正困难的是,为任意真实项目廉价地取得完整规格并验证。后者也不是错误代码,而是告诉我们:脱离使用场景,看起来像垃圾的数字表,可能正在认真工作。
因此,模型评分的价值是补足规则难以表达的软判断,不是给代码颁发“所有输入下都正确”的证书。
Seed-Coder 的“只滤 10%”,但其量大
Seed-Coder 从常见语言抽取 222,066 个文件,让 DeepSeek-V2-Chat 按可读性、模块化、清晰度和可复用性评分。 再把分数归一化,用一个 Llama 2 结构的 1.3B 预训练模型加回归头,训练一轮,扩展到全量评分。[5]
随后过滤最低约 10% 的文件,得到约 1T unique tokens、89 种语言。但在它之前,去重、基础处理与语法检查已经让原始数据体量减少约 98%。两个阶段的计数单位还不完全相同,不能直接相乘得出精确总保留率。[5,§2.2.1]
所以准确的经验是:“对已经处理过的语料,再保守地去掉低分尾部”,而不是“原始 GitHub 九成都是好东西”。
模型偏差也没有消失。Seed-Coder 自己发现,网页评分容易偏爱格式整齐的电商、颜色页面与文档,而低估杂乱但有信息量的论坛,因此还需要分类别调整阈值和采样。[5,§2.2.3] 人工规则少了,人类对目标、标签、类别和评测的选择仍在。
Stack-Edu 与 MIRA:足够好的筛选,可能根本不是同一种“好”
Stack-Edu 的目标更偏向教育性,因此筛选较为激进。它从 StarCoder2Data 的 15 种主要语言中,用 Llama3-70B-Instruct 生成教育性评分,并训练 15 个语言专用 StarEncoder 分类器,每个使用 50 万样本。最终从约 450B tokens 中保留约 125B,仅约 28%。[10]
但这个 28% 不能与 Seed-Coder“保留约九成预处理后文件”直接比较。 两者的筛选目标、原始数据分布、统计分母和训练阶段均不同。更重要的是,Stack-Edu 在 SmolLM2 中的效果实验也并非“仅用这 125B tokens 从零训练”:相关消融是从已经训练 3T tokens 的检查点出发,再进行 200B-token annealing;其中 Python HumanEval 从 20.7 提升到 25.6,C++ MultiPL-E 从 16.7 提升到 24.8。[10,表2]
因此,这里的结论不是“保留比例越低越好”,而是筛选强度应与数据目标和训练阶段匹配。
2026 年的 MIRA 进一步讨论来源感知 rubric:不同数据组先明确该评什么,再做教师打分、学生蒸馏和组内选择。Qwen2.5-Coder-14B 的实验中,25B-token 的 MIRA-Group 在四类任务宏平均上为 64.20,50B raw mixture 为 63.83;但 SWE-Multi 是 36.33 对 40.00,仍有退步。[11,表1]
“半量追平”因此只在该宏平均与后续统一 SFT 设置下成立,不能翻译成所有能力不降、总成本减半。更有意思的是,MIRA-Global 甚至不如同预算随机选择:不同来源的分数未经校准放到一把尺子上,可能比没有这把尺子还糟。
我会怎样给评分器验收?
基于以上证据,我更关心四类检查,而不是先决定评分器一定要多大:
- 标签可靠吗? 人工抽查与执行校准是否覆盖低资源语言、长文件、合法配置和不漂亮但有用的代码?
- 学生真的学到尾部了吗? 总体误差很好,可能只是多数样本集中在中高分;应看来源、语言和评分档的误差。
- 筛完以后分布变了什么? 语言、库、任务、长度和时代分布有没有被顺手洗窄?
- 同预算有没有更好? 至少比无评分基线、来源保持的随机筛选、来源内评分筛选;同时报 unique tokens 与重复次数。
Tree-sitter 也一样:解析成功不是编译成功,错误恢复产生的树不意味着语法合法;解析失败也可能来自新语法、DSL 或解析器不支持。将失败单独分桶,比把它们一律判成低质更可控。
这一节的结论是: “质量”不是样本固有的单个分数,而是样本、目标、上下文和预算之间的关系。 [5][10][11]
七、流程 3:数据类型,commit、PR、issue:把静态结果变成修改能力
静态仓库是软件开发的终态:它记录了结果,却丢掉了规划、调试、返工、重构的过程。2025- 2026年最有价值的新数据源,几乎都来自“把过程找回来“。主要是:commit/PR/issue(重建) —— 的“过程数据” 。
静态仓库快照记录“现在是什么”,演化记录补充“从哪里变来”。但 commit 和 PR 仍是压缩后的结果,不是人类完整的思考录像:失败尝试、临时搜索和被放弃的方案经常没有留下来。
更合理的目标,是重建“意图—旧代码—变更—验证”的对应关系,而不是看到一个 commit 就宣布拥有了推理过程。
commit:从续写下一行,到决定下一刀改哪里
利用 commits 并非 2025 年才开始。2023 年的 OctoPack/CommitPack 已把 commit message 与代码变化用于 instruction tuning;CommitPackFT 选择那些更像自然语言指令的消息。[12] 人类程序员确实在不断贡献指令对,只是有时写得很认真,有时只留下一个足以让后人沉默的“fix”。
Seed-Coder 将这条路线扩到持续预训练:从 140K 个筛选仓库收集 74M commits,仓库门槛包括至少 100 stars、10 forks、100 次提交和 100 天维护活动;去重与预处理后形成约 100B-token 语料。[5,§2.2.2]

输入是 commit message、变更前 README、目录结构及 BM25 检索的五个相关文件,目标是修改路径和代码变化。相比静态续写,它更贴近定位与编辑,但论文没有给出“仅加入 commits、其他全部不变”的完整收益归因,所以不能把最终 SWE 分数单独记到它名下。
PR:先保证修改能重放,再讨论修改是否正确
PR 通常比单条 commit 多一些意图与评审语境。Clean-PR (arXiv 2602.07457, ICML 2026)是目前比较扎实的仓库级编辑数据工作。它从1640万条原始 PR(8.6TB、27.4万个仓库)出发,过滤后只有18.59% 合格——也就是说原始 PR 流的噪声率超过 80%。关键工程决定是:用 Search/Replace 编辑块替代脆弱的 unified diff,并做往返校验,最终得到202万个编辑块、覆盖12种语言。用在 Qwen2.5- Coder-32B 上:先mid-training,再做 agentless 对齐 SFT, SWE-bench Lite +13.6%, Verified +12.3%。[13,表2–4]
注意计数单位:不是 202 万个编辑块;一个训练实例平均包含 4.3 个 Search/Replace blocks。
Clean-PR 的核心工程动作是往返重建:

SEARCH 唯一、区域不重叠、重放结果一致,解决的是“训练目标是否忠实表达真实改动”。它没有自动证明项目可编译、测试全绿或 bug 真正被修好。[13,附录A.4]
简言而之:格式有效、行为验证、评测独立。
mid-training 与 SFT,不是非此即彼
PR数据的最佳落点是 mid-training,不是 SFT
Clean-PR 先做编辑数据 mid-training,再用定位文件、细粒度定位和补丁生成等分步骤样本做 SFT,还加入错误检索产生的干扰上下文。[13] 后一步很像告诉实习生:“文件出现在你面前,不代表它有罪。”
论文在 Qwen2.5-Coder-32B 与简化 Agentless 协议下报告:[13,表6]
| 训练条件 | SWE-bench Lite pass@1 | Verified pass@1 |
|---|---|---|
| 原 Qwen-Coder-32B-Instruct | 10.7% | 18.3% |
| Base + 本文 SFT,无 mid-training | 11.3% | 17.6% |
| Base + StarCoder2-style 17.4B + SFT | 15.7% | 20.4% |
| Base + Clean-PR 17.7B + SFT | 24.3% | 30.6% |
相对第一行,提升是 13.6、12.3 个百分点。它是两阶段训练的组合结果,不是“mid-training 取代 SFT”的证明。17.7B 是语料规模,论文 mid-training 跑两轮,也不应直接当累计训练量。
这篇更有决策价值的局部消融,是 Python 条件下只用 PR 描述与增加关联 issue 的比较:Lite 20.4→22.3,Verified 25.7→27.8。[13,表8] Issue 提供了增量上下文,但 PR 描述本身已经能形成有效监督。
issue:需求视角很重要,但没有垄断“为什么改”
唯一把“需求”和“代码变更“配上对的天然语料
Comnmit 和 PR 给你的是改动本身,issue 给你的是改动的理由。这是它不可替代的地方:一条 issue 里天然含有复现步骤、报错日志、期望行为、以及维护者与报告者之间的多轮澄清,这是“自然语言需求–>代码变更“ 这一配对唯一的天然来源,纯代码语料里根本不存在。今天所有SWE 类任务的题面都是 issue,这不是巧合。
Issue 的优势通常是用户视角:复现步骤、异常日志、期望行为以及维护者的追问。它能补上开发者一句“修复边界情况”背后到底是哪种边界。
但 commit message、PR title/body、review 也可能解释原因。自然语言需求与代码变化并不只存在于 issue,软件工程任务的题面也可能是从 commit 回译、从变异生成,或由真实问题启发而合成。[12][13][17]
把数据源对象和训练产品分开,思路就清楚了:
| 训练产品 | 内容 | 最低需要验证什么 |
|---|---|---|
| 编辑样本 | 描述 + before + edits | 能忠实重建目标状态 |
| 可执行任务 | 题面 + 基线环境 + 隐藏测试 | 任务可运行、测试有效、规定范围内无回归 |
| 交互轨迹 | 观察、定位、编辑、工具调用与反馈 | 终态结果、过程记录与模仿目标有效 |
三类产品都可以使用 commit、PR 和 issue;样本形态决定适合 CPT、编辑 SFT、agent SFT 还是 RL,而不是来源的名字决定训练阶段。
从真实任务到合成环境:SWE-Gym、SWE-smith 与 SWE-Mirror
SWE-Gym 为真实历史问题配环境和测试;SWE-smith 反过来在能运行的仓库里注入缺陷;R2E-Gym 从 commits 回译题面并收集或生成测试。[15][17][41] 这些路线都在解决同一个昂贵环节:让一段代码修改从静态文本,变成今天还能运行、还能给反馈的任务。
更全面的 Agent 打镜像的过程,可以参考文章:基模杂谈swe-环境构建与长程轨迹挖掘
SWE-Mirror 选择复用已有环境,把真实问题的核心逻辑移植到相似目标仓库:[18]

它最终报告 60,671 个验证任务,但不能称每个缺陷都在目标仓库历史中真实发生过:真实的是问题来源,迁移后的任务有合成成分。
最强 32B 模型的 SFT 使用 12,456 条轨迹,其中 6,431 来自 Mirror,6,025 来自 SWE-rebench。在其框架、轮数与上下文设置下,Verified 从 6.2% 到 52.2%,即提升 46.0 个百分点。[18,§3、表6] 这是混合轨迹与完整训练评测方案的结果,不是“给 issue 加一个验证器就涨 46 分”。
更值得借鉴的是验证边界。除了 F2P(失败转通过)和回归检查,作者还检查异常与 flaky 状态;同时对一小批成功镜像任务做人类语义审核,结果并非全部与源问题一致。[18,§2.4] 测试绿了,仍需要问:“它验证的还是不是原来那件事?”
这一节我最想留下的经验是:演化数据的价值来自关联、重建和验证,而不只是数量。任务数、成功轨迹数、编辑块数和 tokens,也必须各算各的。
执行化还会把数据工程变成供应链与隔离问题。安装脚本、测试和依赖都来自不可信代码,不应直接在带生产凭据的宿主机运行。建议使用可销毁、最小权限、受限网络和资源配额的环境,固定镜像摘要、base commit、依赖来源与测试命令,保留失败日志。容器是载体,不是“安全且可复现”的证明;未锁定的依赖和外部服务,足以让昨天通过的任务今天失效。这是工程防护建议,不是对上述论文执行环境的安全认证。
小结:三者对照
commit 和PR是语料,issue 是题库。
- 把issue 当语料喂,你得到的是1%的权重、80%+ 的噪声,外加一份可能污染你所有 SWE 评测的答案纸;
- 把issue 当题面用,配上“‘gold patch使 F2P ≠ 0且 P2F=0“ 这类硬验证,它就是目前最便宜的真实任务来源——SWE-Mirror 的+46.0% 就是这么来的。区别不在数据,在于你有没有验证器;
八、流程 4:合成数据,不是“改写型打败教科书型”的排行榜
合成数据至少有三条可以叠加的轴:是否锚定真实来源、是否教学化表达、是否经过外部可执行检查。
| 路线 | 主要解决的问题 | 最容易引入的偏差 |
|---|---|---|
| 改写型 | 可读性、冗余、自包含性与表达噪声 | 接口漂移、过度规范化、抹掉真实依赖 |
| 教科书/练习型 | 概念讲解、示例密度与难度组织 | 风格单一、题型窄、远离工程长尾 |
| 执行验证型 | 实现与测试是否一致、修复能否闭环 | 测试 oracle 错误、易验证任务偏好、环境成本 |
一个样本可以既是基于真实代码的改写,又带讲解,还经过测试。将它们排成单一优劣榜,反而会遮住真正的设计变量。[14][19][21][22]
SwallowCode:先筛,再改,改得漂亮也可能改错接口
东京科学大学的 SwallowCode(arXiv 2505.02881,2025-05,V4 于2026-03)是改写范式在代码上的代表作,也是少见的开放权重 +开放数据+开放prompt的完整工作流。它的口号是 transform-and-retain(改造并保留), 而不是 filter-out(过滤丟弃)。
SwallowCode 来自 The Stack v2 的 Python 子集,最终约 16.1B tokens。它的流程不是“任何低质量代码都不丢”,而是先用 Python 语法检查、pylint 和注释规则过滤,再执行两阶段改写:[19]

SGCR: 按风格指南重写命名、结构与表达
- 命名更规范
- 结构更清晰
- 表达更一致
SCOR: 改善自包含性、冗余与实现
- 提升自包含性
- 减少冗余
- 优化实现细节
SwallowCode 真正值得关注的地方,不只是“把代码写得更漂亮”,而是它代表了一种不同于强过滤的数据策略:对于已经通过基础质量门槛、但表达或实现质量一般的代码,不直接丢弃,而是尝试通过改写把它转化为可用训练数据。 这与“只保留极少数高质量样本”的路线形成鲜明对比。在代码这种对数据量需求很大的领域,这种 transform-and-retain 思路尤其有吸引力,因为它试图提升已有数据的利用率,而不是单纯缩小数据池。
论文中的主实验也说明,这种改写在特定设置下能够带来明显收益。作者使用 Llama-3.1-8B 进行 50B tokens 的持续预训练,其中代码占 16%,即 8B tokens,其余为多语言文本。在相同训练预算下,与 Stack-Edu 数据条件相比,SwallowCode 在 HumanEval 上提升 17.0 个百分点,在 HumanEval+ 上提升 16.1 个百分点。[19,§3] 因此,这项工作的证据更准确地支持这样一个结论:经过针对性的质量改写,原本不够理想的代码数据可以变得更有训练价值。
但这里不能进一步推导成“改写总比过滤好”。论文同时报告了一个很重要的负结果:在 SGCR 风格改写之后,MBPP 性能反而下降了约 10 个点。[19,§3.3.1、附录I] 作者分析发现,问题并不一定出在代码逻辑本身,而是模型在追求风格规范时,会把题目要求的函数名改成更标准的 snake_case。代码本身可能更整洁了,但外部调用接口已经发生变化,因此无法再满足原来的评测规格。
这个现象揭示了改写型数据一个非常关键的风险: “代码质量变好”与“代码仍然忠实于原始规格”是两个不同的目标。 命名更规范、结构更清晰,并不意味着样本仍然保持相同的 API、函数签名和行为约束。如果只检查改写后的代码是否“看起来更好”,却不验证接口和语义是否保持不变,就可能把原本有效的数据改坏。
因此,从工程上更值得继承的不是“尽可能改写”,而是把两个验收维度分开:一方面评估风格、可读性、自包含性和冗余;另一方面必须单独验证函数签名、接口约定和可执行行为是否与原样本一致。换句话说,改写可以提高数据质量,但前提是不能破坏规格忠实性。
教科书路线不是“纯合成”,Phi-1 的最终成绩也不是只靠预训练
Phi-1 经常被概括为“用高质量教科书数据训练小模型”,但这个说法容易把它的数据构成和训练阶段过度简化。它的预训练数据并不是全部由合成教材组成:模型首先使用约 6B tokens 的筛选真实代码与 StackOverflow 内容,在此基础上再加入 不足 1B tokens 的合成 Python 教材数据;预训练结束后,又使用约 180M tokens 的合成编程练习 进行微调。[14]
因此,Phi-1 的效果不能简单归因于“合成教科书”。从结果也能看出不同阶段的作用:Phi-1 base 在 HumanEval 上约为 29% ,经过后续练习数据微调后才达到 50.6% 。换句话说,最终性能来自一套组合策略,而不是某一种数据形式单独奏效:先筛选真实数据,提高基础语料质量;再用教学化合成数据集中组织概念、解释和示例;最后通过练习式微调,让模型更适应实际编程任务的输入与输出形式。
教科书式数据真正擅长的,是提高单位 token 的“学习密度”。相比直接堆积原始代码,它可以有意识地把一个概念的定义、解释、代码示例和典型用法组织在一起,使模型更容易建立清晰的知识关联。这也是为什么教学化数据即使规模不大,也可能产生较高的训练价值。
但这种优势并不意味着真实代码可以被教材完全替代。真实软件工程中的很多信息天然难以通过“写一本好教材”覆盖,例如稀有 API、历史兼容逻辑、跨文件与跨模块依赖、特定框架约定,以及大量不规则的边界情况。教材通常倾向于展示清晰、典型和自包含的例子,而真实代码分布恰恰包含大量不整洁、不典型却实际存在的长尾模式。
因此,Phi-1 更值得借鉴的并不是“合成数据优于真实数据”,而是:真实数据负责提供分布覆盖,筛选负责提高原料质量,教学化合成负责提高知识密度,练习数据则进一步完成任务对齐。 教学性值得增强,但不应以牺牲真实工程分布为代价。
合成数据比例与训练效率的适用边界,“30% 合成、5–10 倍提速”
《Demystifying Synthetic Data in LLM Pre-training》研究的是英文网页数据与改写、问答、教科书式合成数据的混合,实训规模到 3B 参数、200B tokens,核心评价指标主要是验证损失。[20]
论文发现,在较大训练预算下,某些约三分之一改写数据、三分之二真实网页的混合,可以用更少训练量达到相近验证损失。“5–10×”来自 scaling 结果及其外推,描述的是训练样本效率潜力,不是 GPU 吞吐直接提升五到十倍,也不能等同于把数据生成、验证和失败重试都算进去后的端到端代码训练成本下降。
论文还观察到,33% 教科书式合成的混合优于 67%;但 Phi-4 的训练配方中又包含较高比例的合成与重写数据。[20][23] 这并不矛盾,因为两者的原料、教师模型、策展方式、模型规模、训练预算和评价目标都不同。更重要的是,该论文正文中“约 30% 较优”的概括,与部分精细比例实验并不完全一致;在缺少完整原始 run 表的情况下,不应把 30% 当成通用最优比例。
可验证合成:验证器本身也需要验证
UnitCoder 将真实代码提取、测试生成、执行修复和解释性改写串成闭环;KodCode 则同时生成问题、解答与测试,并通过执行结果和分支覆盖进行筛选。[21][22] 这类工作说明,数据改写和可执行验证并不是两条互斥路线,而是可以组合使用。 但它们的主要实验集中在后训练阶段,因此不能直接用来证明某种预训练合成比例就是最优方案。
需要特别区分的是:覆盖率高不等于测试正确。 branch coverage 达到 100%,只能说明测试走到了所有分支,并不能证明这些测试准确表达了原始需求。一个没有分支的 return x + 1 很容易达到满覆盖;如果真实需求其实是 x + 2,而生成的测试也理解错了需求,那么答案和测试完全可能一起通过,却同时偏离正确规格。
UnitCoder 还暴露出另一个容易忽略的问题:验证链路本身也可能引入评测污染。论文说明,其测试生成器使用 BigCodeBench 的函数和测试进行训练,随后又使用 BigCodeBench 评估下游模型。[21,§4.1] 即使训练时屏蔽输入函数的 loss,也不能据此认为上游生成器完全没有接触过评测信息。这不足以判断污染具体贡献了多少分,但足以说明:去污染不能只检查最终训练集,还要追溯 teacher、verifier、测试生成器和种子数据。
因此,可验证合成真正需要的是分层验收,而不是一个“测试通过”布尔值。除了执行通过率,还应分别记录测试 oracle 的可靠性、覆盖率、误杀与漏检、接口保持率,以及样本从原始代码到测试、改写和最终训练样本的完整谱系。
模型坍塌:关键在递归数据如何进入下一代
所谓“模型坍塌”,主要讨论的是多代模型反复消费前代生成数据时会发生什么。已有研究表明,如果每一代都用合成数据替换原始数据,生成误差会逐代累积,原始分布中的低概率和长尾模式也更容易消失;保留部分真实数据可以明显减轻这种退化。[24]
但这不能简化成“用了合成数据就会坍塌”。后续研究进一步区分了不同的数据更新方式:如果真实数据和历代合成数据都持续累积,并在下一代训练时共同使用,实验中可以保持稳定;如果总训练数据量固定,只从不断扩大的历史数据池中抽取有限子集,则仍会出现较慢的渐进退化。[25] 因此,完全替换、固定比例混合和历史累积,本来就是不同的训练过程,不能用同一个“模型坍塌”结论概括。
一个直观但不严格的比喻是:真实数据像底片,合成数据像修图稿。修图本身并不可怕,危险的是每一轮都扔掉底片,只拿上一轮已经压缩过的信息继续生成下一版。反过来,始终保留底片和历史版本通常更稳健,但不断累积数据也意味着存储与训练计算可能随之增加。
对数据工程而言,更重要的不是争论“合成数据有毒”还是“合成数据安全”,而是记录真实来源是否仍被保留、合成样本来自哪一代模型、各代数据如何混合,以及长尾能力是否持续退化。模型坍塌首先是一个递归数据管理问题,而不是对单轮合成数据的统一判决。
九、流程 5:仓库与长上下文,内容、顺序、位置适配
仓库级训练至少包含三个不同变量:窗口里装了哪些内容;这些内容如何排序;模型是否适应这么长的位置跨度。它们经常一起改变,因此看到分数上升,不能自动宣布某一个是唯一主因。
一般采用拓扑拼接,但仓库内的拼接策略可能被高估了
Granite:长上下文扩展是一套组合配方
IBM 在 Scaling Granite Code Models to 128K Context(arXiv 2407.13739)里给出了目前最完整的配方:用import建 DAG –> 破环 –> 拓扑排序,文档与构建文件置顶、依赖文件次之、 非连通文件按目录树 DFS 排列;<4096 token 的文件降采样到 10%;RoPE $\theta$ 从8K/100K阶梯升到 128K/10M。只花 4B token(原预训练的 0.1%),长上下文任务最高+38%,短上下文只掉约1%。这套配方已开源为 IBM data-prep-kit 的 repo_level_ordering 变换(可在 SORT_BY_PATH 与 SORT_SEMANTIC 间切换)。
Granite 的 128K 上下文扩展并不是只调一个 RoPE 参数,而是建立在已有 3B/8B 代码基座之上的一整套数据与位置适配方案。[26] 在仓库级数据组织上,作者根据 import 关系构建文件依赖图,对其中的环进行处理后得到可排序的 DAG,再按拓扑顺序组织代码;文档和构建文件被优先放置,不连通文件则结合目录遍历进行安排。与此同时,训练还调整了长文档采样分布,并修改 RoPE 的基频参数。
这里有一个容易写错的概念细节:流程是先建立有向依赖图,再处理其中的环,使其变成 DAG,而不是“先建立 DAG,再破环”。两种说法看起来接近,但后者在定义上已经自相矛盾。

拓扑拼接是工程上合理的默认,但别指望它带来质变。真正决定长上下文效果的是位置编码的扩展方式。不同的拼接策略收益几乎一样,主要收益来自 RoPE ABF ($\theta$ 1e4 –> 5e5),且只用了1B token(实际有效72M)就追平数百 B token 的竞品。
论文在特定 RepoQA 设置下报告了明显提升,但需要注意,这一结果来自仓库组织、长度重采样和位置编码适配共同作用。因此,严格来说,它能支持“这套组合方案有效”,却不能单独证明收益主要来自依赖排序,也不能把效果全部归因于 RoPE 参数调整。[26]
IBM 官方的 data-prep-kit 也提供了 repo_level_order 变换,支持基于路径、语义等方式组织仓库级数据。[31] 对工程实践而言,更值得复用的是:明确记录输入结构、排序规则、长度分布和位置适配,并通过对照实验拆分各环节贡献,而不是直接照搬某一组 RoPE theta 参数。
项目级补全:先建立便宜而可靠的项目级上下文组织
《On Pretraining for Project-Level Code Completion》在 OpenCoder-1.5B 上研究 Python 单行补全,并将上下文从 4K 扩展到 16K,对比了多种项目级上下文组织方式。[27] 在统一推理 composer 的条件下,file-level 数据配合 RoPE 适配训练,in-project EM 已达到 45.2;使用 Path Distance 组织仓库级数据后提升到 48.8。相比之下,只修改 RoPE theta 而不进行相应训练,结果仅为 9.8。[27,表3]
论文系统比较了 15 种 repository context composer,即不同的仓库级上下文构造/拼接策略。
比较的策略不只是简单的“文件排序”,而是包括例如:
- File-level:完全不加入跨文件 repository context,作为单文件 baseline;
- Path Distance .py:按目录/路径距离排序,再用代码行 IoU 做次级排序;
- Lines IoU .py:仅按代码内容相似度 IoU 排序;
- Code Chunks:在 Path Distance 的基础上去掉 docstring、comments、imports;
- Half-memory .py:随机 dropout 约 50% 的 context 行;
- Declarations .py:只保留函数、类等声明;
- 另外还有 reversed、irrelevant 等人为扰动版本,用来研究顺序和噪声的影响。
具体来说,他们把 OpenCoder-1.5B 的上下文从 4K 扩展到 16K,比较不同 composer 后,在
inproject 上各 repository-level composer 的 Exact Match 大约在 45.2–48.8 之间,最好与最简单策略只差约 3.6 个点。甚至 File-level + RoPE 长上下文适配 本身就已经达到很强的结果,因此作者认为 RoPE adaptation 是主要驱动因素,context composition 本身的贡献相对次要。
这组结果首先说明:长上下文能力不能靠“改一个 theta”直接获得,位置适配本身需要训练。 在完成这一基础适配之后,不同合理仓库组织策略之间的额外差距反而相对有限。这里更准确的结论是:应先建立一个足够强的 file-level + 长上下文适配基线,再判断更复杂的仓库级组织到底带来了多少增益;这并不意味着真实依赖关系或仓库结构没有价值。
成本口径也需要谨慎。论文脚注中的 72M tokens 对应的是一个特定的 file-level 适配实验,并不是所有仓库级训练都只使用了 72M tokens,更没有计入 OpenCoder 基座此前的预训练成本。[27] 因此,把结果概括成“72M tokens 打败数百 B tokens”会把已有基座的能力当成免费前提,容易高估这一步训练本身的贡献。
另外,还必须区分 training composer 和 inference composer。论文发现,在统一推理布局下,即使训练阶段使用反转顺序或加入部分无关上下文,与正常训练组织方式的差距有时并不大;但如果推理阶段本身也采用带干扰的上下文布局,性能会明显下降。[27] 因此, “训练时排序差距有限”不能被解释成“推理时上下文怎么塞都一样”。 训练组织和推理检索仍然是两个需要分别控制的变量。
OctoLong:从文件排序走向依赖可达性,跨仓库拼接
OctoLong 把问题从“仓库里的文件应该怎么排”进一步推进到“真正相关的依赖能不能进入上下文”。它利用 AST、语言服务器和包管理器递归追踪跨仓库依赖,构造依赖密度更高的长上下文。[28] 相比单纯用长文件填满窗口,这一路线更关注上下文的有效性:调用点所依赖的定义、包实现和关联逻辑,是否能够同时出现在模型可见范围内。

不过,需要区分能够构造多长的数据和模型实际训练到多长的上下文。OctoLong 的数据采集主要围绕 Python 展开,虽然可以生成百万 token 甚至更长的依赖上下文,但论文中的主要训练窗口仍为 128K。完整训练流程包含约 50B tokens 的长上下文阶段,其中约 6.2B tokens 来自跨仓库代码,之后还包括模型合并以及约 10B tokens 的 SFT。[28]
因此,常见的“约 12% 跨仓库数据”只描述 50B-token 长上下文训练阶段内部的组成,不能直接理解为整个训练流程只增加了 12% 的成本。更有说服力的是论文给出的等规模替换实验:在 8B 模型上,将跨仓库数据替换为规模匹配的控制语料后,多项长程代码任务以及部分短代码指标都会回退。[28,表4、附录表9] 这说明收益并不只是来自“多训练了一些 token”,依赖相关内容本身具有额外价值。不过,与官方 Qwen3-8B 相比,完整模型在部分 pass@1 指标上仍然较低,因此也不能把这种数据组织方式理解为对所有代码能力都单调提升。
把这一结果和前面的项目级补全实验放在一起,更合理的工程顺序是分层验证:先完成长上下文的位置适配,建立便宜而可靠的强基线;再通过等规模对照证明相关内容和依赖密度是否真正带来增益;最后才比较更复杂的文件排序与上下文组织策略。
十、流程 6:FIM:改变的是序列组织,不是预测范式
Fill-in-the-Middle(FIM)的核心,是把原始代码切成 prefix、middle 和 suffix,再重新排列,使模型利用左右两侧上下文恢复中间内容。虽然它常被称作一种训练目标,但实现上仍然可以沿用标准的自回归 next-token prediction,并不要求更换模型架构。[29]
| |
实际训练通常还需要专门的 sentinel token,不同模型的具体格式也并不完全兼容。上面的 PSM/SPM 主要描述内容重排方式,而不是完整的输入模板。
1 2 3 4 5 6<fim_prefix> def add(a, b): <fim_suffix> a + b <fim_middle> return
FIM rate 控制的是“多少样本被改写”,不是“挖掉多少内容”
FIM rate 最容易被误解。一个常见的 0.5 通常表示:约一半训练样本被转换为 FIM 形式,而不是每个样本都挖掉 50% 的中间内容。完整配置还需要另外指定 PSM/SPM 比例、切分单位、缺口跨度、结构化与随机掩码比例、哪些 token 计算 loss,以及在哪些数据来源和训练阶段启用。
原始 FIM 工作采用先按字符位置切分、再重新 tokenize 的方式,因此缺口可以落在词或 token 内部,更接近真实编辑器光标的位置。[29] 这里的“字符级切分”只描述切分位置,并不意味着模型使用字符级词表。论文同时保留 prefix、suffix 和 middle 区域的训练 loss,并建议混合使用 PSM 与 SPM,而不是只固定一种排列。
对于 FIM rate,原始实验在特定模型规模和训练预算下观察到:提高到 0.9 时,左到右建模能力没有明显下降,而 1.0 开始出现退化。[29] 这只能说明该实验设置下 FIM 可以占很高比例,不能进一步推出“0.9 对所有模型和任务都安全”。不同代码模型也采用了不同配置,例如 Seed-Coder 在 regular 阶段使用 0.5、continued 阶段降到 0.1,并采用 SPM;DeepSeek-Coder、StarCoder 等则分别使用自己的 PSM/SPM 配方。[5][29][33]
因此,FIM rate、排列形式和阶段调度都应视为需要实验确定的超参数,而不是固定行业规则。
结构化 FIM:让缺口更像真实编辑,但不能只剩结构化缺口
随机 FIM 可以覆盖任意位置,但很多随机缺口并不像真实代码编辑。Structure-Aware FIM 因此利用 AST,将缺口尽量对齐到函数、语句或其他语法子树,使补全任务更接近实际代码块编辑。[34]
但结构化并不意味着应该完全放弃随机缺口。该工作使用约 70% FIM,其中主体采用 AST 对齐,同时仍保留约 10% 的随机 FIM;对于不支持解析或解析失败的代码,也可以回退到随机切分。[34] 作者的消融进一步表明,如果只保留确定性的 AST 模式,模型容易适应这些规则化缺口,却在随机位置补全上明显变差。[34,§7]
这说明两类缺口覆盖的是不同分布:结构化 FIM 提高语义和编辑场景的真实性,随机 FIM 则保持对任意光标位置的覆盖。 真实 IDE 中的用户编辑位置并不会总是恰好落在一个完整 AST 节点边界上,因此二者更适合互补,而不是互相替代。
2026 年的 Function-Aware FIM 又进一步把“挖哪里”升级为“哪些函数值得挖”。它根据函数复杂度和可推断性筛选目标,同时引入教师生成的 rationale、候选实现和 judge 筛选,并继续结合 agent 后训练。[35] 因此,它带来的提升不只来自函数级 mask 本身,还混合了数据选择、合成、蒸馏和后训练信号。其主要实验集中在 Python,跨语言效果仍需要单独验证。

最后,FIM 数据管线还需要一个非常基础但不能省略的验收:经过切分、重排和逆变换后,是否能够逐字节恢复原始文件。 尤其在 Tree-sitter byte offset、Unicode、中文注释和 tokenizer 边界同时存在时,字符索引与字节索引一旦混用,就可能静默地产生错误训练样本。对模型而言,这些错误不会被识别成“数据工程 bug”,它只会把它们当成正常代码认真学习。
十一、流程 7:多语言与低资源:先测响应,再决定配额
代码预训练天生是多语的,但不同语言对规模的响应完全不同. Scaling Laws for Code: Every Programming Language Matters (arXiv 2512.13472)做了1000+次实验、 约336,000 H800卡时, 模型0.2B-14B、数据 1Ttoken, 是目前这个方向最系统的工作.

《Scaling Laws for Code: Every Programming Language Matters》没有把多语言训练简化成“哪种语言多喂、哪种语言少喂”,而是分别研究单语言缩放、跨语言迁移、多语言配对和预算分配。[36] 这几组实验共同说明:不同语言对参数、数据和辅助监督的响应并不相同,因此平均分不能直接替代语言级决策。
单语言缩放:先看每种语言如何响应参数和数据
论文分别对 Python、Java、JavaScript、TypeScript、C#、Go 和 Rust 做单语言训练,在 10 档模型规模和 6 档 token 预算下形成 420 个实验,并用类似下面的 scaling law 拟合验证 loss:
$$ L(N,D)=\left(\frac{N_c}{N}\right)^{\alpha_N} +\left(\frac{D_c}{D}\right)^{\alpha_D}+L_\infty. $$其中 N 表示模型参数量,D 表示训练数据量,拟合结果描述模型和数据继续增加时,预测损失还能下降多少。作者观察到,不同语言的缩放行为存在明显差异,例如 Python 对规模增长更敏感,而 Rust 在其实验分布下更早出现收益趋缓。[36,§3]
但这不能直接解释为“Rust 更简单”或“Python 就应该获得更多预算”。验证 loss 同时受到 tokenizer、数据来源、模板代码比例和翻译语料风格等因素影响,不同语言的绝对 token loss 也未必可以直接横向比较。决定下一批 token 投向哪里,需要结合缩放曲线、当前数据规模和实际任务指标,而不能只看某一个指数。
低资源语言:辅助语言能否比重复数据更有价值?
论文进一步固定 128B tokens 总预算,对比两种方案:一种是 64B 目标语言数据训练两遍,另一种是 64B 目标语言加 64B 辅助语言。[36,§4]
这个对照回答的是一个很具体的问题:当新的目标语言数据已经不足时,加入其他语言,是否比重复现有数据更有效? 它并没有证明辅助语言优于同等规模、全新的高质量目标语言数据;如果后者能够轻易获得,“低资源”问题本身也会弱很多。
跨语言迁移还具有明显方向性。A 能帮助 B,并不意味着 B 对 A 有同等增益;语法、类型系统、标准库和生态越接近,可能共享更多结构,但这种关系仍需要通过实际实验确认。由于论文中部分迁移表格与正文解释存在不完全一致的地方,这里更适合保留“迁移具有方向性”这一结论,而不是把某个精确矩阵直接转化成部署比例。
随机混合与程序配对,是两种不同的训练信号
把多种语言随机混在训练集中,与把同一程序的不同语言实现放在一起,提供的监督并不相同。前者主要共享统计与编程知识,后者则显式暴露不同语言之间的语义对应关系。
论文的多语言配对实验表明,这种对齐监督确实可能改善整体表现,但收益并不会平均落到所有语言上。例如在表 3 的 7B 设置中,报告的五语言平均分从 32.46 提升到 33.79,但 Python 从 34.15 降到 26.22,而 Java 则明显提高。[36,§5、表3]
因此,平均分上涨不能代替逐语言验收。 配对数据可能增强跨语言迁移,也可能改变模型对主力语言的能力分布。与此同时,代码翻译本身还需要检查惯用写法和执行语义;语法能够编译,并不意味着生成的是符合目标语言生态习惯的实现。
配额优化:先定义约束,再求最优分配
在最后的配额实验中,论文使用两个 1.5B 模型、各 400B tokens 进行验证,其中包括 350B 代码和 50B FineWeb-Edu。基线将代码预算在七种语言之间均分,优化方案则在总预算不变的前提下重新分配各语言 token。[36,§6]
这一实验说明,语言配额本身可以成为优化变量,而不是默认均匀采样。但它仍然是特定模型规模、数据池和预算下的受控结果,不能直接外推到任意多万亿 token 的训练。论文不同章节使用的规模范围和部分统计口径也并不完全一致,因此工程上更适合继承优化思路,而不是机械复制某组比例。
对于真正的低资源语言,更稳妥的决策顺序是:先统计 unique 数据量和重复率,再检查过滤器是否对某些语言产生系统性误杀;随后测量辅助语言的有向迁移和配对收益,最后在主力语言最低质量约束下优化预算。SQL、Assembly 以及其他更低资源或领域化语言,也不能直接从这七种通用编程语言的实验得到现成答案。[36,Limitations]
另一项《A More Data-Hungry Regime》也发现,在特定代码训练设置下,更高的 D/N 比例可能继续带来收益,但超过一定范围后性能仍会下降。[37] 因此, “代码更吃数据”描述的是最优预算可能向数据侧移动,而不是数据越多越好。 增加 token 的前提仍然是数据具有足够的信息增量,而不是用重复和噪声把配额填满。
十二、流程8:配比与时机:比例和阶段都需要单独验证
“代码应该占多少”看似是一个比例问题,实际上至少包含四个不同维度:通用模型中代码占多少、代码内部合成数据占多少、不同类型数据在什么阶段引入,以及各阶段最终累计曝光了多少次、对应什么学习率。把这些变量压缩成一句“代码放 30%”,通常会掩盖真正起作用的因素。
Cohere 的《To Code, or Not To Code?》在 470M 模型、200B-token 预算下测试了六档离散代码比例,并发现约 25% 代码时,其非代码综合目标表现较好;但代码专项任务往往偏好更高的代码占比。[38] 这说明代码比例本质上存在任务间权衡,而不能据此推出“25%”或“25–40%”是所有通用模型都适用的固定甜点。尤其该实验并没有覆盖连续比例空间。
比例之外,数据在什么时候出现也可能影响结果。《Midtraining Bridges Pretraining and Posttraining Distributions》v2 的相关代码实验表明,在 70M/160M 模型设置下,目标数据较早引入时可以承受更高权重;如果较晚引入,同样的高权重反而可能损害性能。[39] 但这一现象同时受到累计目标数据曝光量和学习率日程影响,因此不能直接外推成“大模型也存在完全相同的最佳时间窗”。
从 Seed-Coder、OpenCoder、Phi 以及这些受控实验来看,更稳妥的理解是:训练阶段划分首先是一种分布调度方式,而不是固定的三段式模板。 广泛、稳定、成本较低的筛选代码或改写数据可以较早参与训练;而稀缺、昂贵、强任务绑定的数据,例如工具调用轨迹、交互式修复记录或高成本验证样本,是否更适合后置,则需要单独实验确认。[5][7][14][23][39]
工程上,比复制某个比例更可靠的做法,是建立一个小规模二维对照:至少比较“较早/较晚引入”和“较低/较高权重”,同时固定后训练协议。最终不仅报告域内收益,还应同时记录通用能力回退、目标数据累计曝光量、学习率位置和总训练成本。否则,很难判断真正有效的是“引入时机”,还是某个条件只是让模型看到了更多目标数据。
十三、流程 9:去污染与评测:从样本隔离到评测协议控制
代码数据与公开评测之间存在多种潜在重叠路径。除题面和参考答案外,测试用例、题解、相关 issue、修复 patch、镜像仓库,以及由教师模型生成的改写或变体,都可能重新进入训练数据。因此,代码去污染不应只针对最终训练文件做一次字符串匹配,而应同时考虑数据来源、变换过程和评测协议。
语义重复与字面去污染的局限
《Soft Contamination Means Benchmarks Test Shallow Generalization》检查了 OLMo 3 的部分预训练数据和全部微调数据,并通过 embedding 检索候选,再进行语义标注。在其 top-100 候选检查设置下,77.5% 的 CodeForces 题目至少存在一个被判定为语义重复的样本。 [40,§3–4]
这里需要注意统计口径。77.5% 的分母是 benchmark 题目,而不是全部训练样本或全部重复样本,因此不能将其解释为“n-gram 方法漏掉了 77.5% 的污染”。同时,这一比例还受到候选召回范围、embedding 模型和语义标注标准的影响。
该结果更直接支持的结论是:仅依赖字面匹配不足以发现改写、翻译和语义等价形式的重叠。 但语义检索同样只是候选发现机制,而不是能够保证数据完全无污染的判定标准。
13.2 去污染需要覆盖数据生成的多个阶段
更稳妥的做法,是在不同阶段分别控制可能的泄漏来源。
- 来源阶段: 检查 benchmark 仓库及其 fork、镜像、题面、答案、测试和 patch,并将与评测直接相关的来源排除或单独标记。
- 变换阶段: 对原文、翻译、改写、回译题面、生成测试和衍生轨迹保留统一的 lineage,使同一来源的衍生样本在数据划分时保持一致。
- 最终训练集阶段: 再进行 hash、n-gram 和语义相似度检查,以发现经过拼接、重写或二次生成后重新出现的评测内容。
- 评测阶段: 固定 base model、harness、工具 schema、上下文长度、交互轮数、采样次数和测试可见性,避免把推理配置变化误认为模型本身的能力变化。
Clean-PR 同时采用仓库排除、源码哈希、patch 子序列匹配和 issue 相似度检查,正是因为不同方法覆盖的泄漏路径并不相同。[13,附录A.6–A.7] 但这些措施只能说明当前数据集进行了哪些去污染处理,并不能据此证明原始 base model 的历史预训练数据也完全不含相关内容。
时间留出需要明确时间戳的定义
时间切分可以降低公开 benchmark 长期暴露带来的风险,但软件仓库中的“时间”并不是单一变量。同一个任务可能同时包含 issue 创建时间、issue 最后修改时间、PR 创建与合并时间、commit 时间,以及数据实际被抓取或首次观测的时间。
SWE-bench 的构建过程表明,如果题面截断位置处理不当,后续评论中的修复信息可能被带入任务;SWE-rebench 则通过持续收集较新的 issue 和修复,降低固定评测集长期公开造成的暴露风险。[16][41]
因此,自建留出集最好同时保存多个时间戳。仅按 issue 创建时间切分,可能把多年后补充的答案仍归入早期数据;仅按仓库名称隔离,也无法处理 fork、文件复制和跨仓库镜像带来的内容重叠。
更可靠的方案通常需要组合使用内容级去污染、仓库或血缘级隔离,以及时间戳。同时,语义相似本身并不等价于泄题。常见算法和通用实现模式天然会在不同仓库中重复,因此语义检索更适合作为候选发现步骤,再结合来源和任务语义进行复核。
评测配置本身也必须版本化
pass@1、Exact Match、BLEU、验证 loss 和软件工程修复率衡量的是不同能力,不能直接横向替代。即使模型权重不变,增加采样次数、扩大上下文长度、使用更强的检索器,或者向模型暴露更多测试信息,都可能显著改变最终结果。[17][27][28]
因此,一个可比较的实验至少应报告:使用的 base model、训练数据版本、unique tokens 与累计训练 tokens、后训练设置、推理预算、评测切分以及具体 harness。对于 agent 或软件工程任务,还应记录工具接口、最大交互轮数和测试可见性。
换句话说,评测结果并不只由模型权重决定,而是由模型、数据、推理配置和评测协议共同决定。 如果这些条件没有被完整记录,不同实验之间的分数就很难进行严格比较。
十四、如果重新搭管线,我会按什么顺序投入?
第一步,先建立可追踪、可复现的基线。
首先明确任务范围与许可政策,冻结原始数据快照,并保存内容、路径、仓库与版本之间的映射关系。在此基础上,使用稳定 tokenizer、基础规则、精确去重和简单采样,跑通一套可复现的训练与评测流程。[5][30] 后续任何复杂模块,都应相对于这一基线衡量净收益。
第二步,优先消除明显冗余和数据实现问题。
先校准文件级近去重,检查跨来源、跨分区重复,分析异常大簇和最终样本的重复曝光,并统计不同语言的保留率。[6–9] 这些问题通常比高级语义去重更基础:如果分区、loader 或打包过程本身仍在重复数据,继续增加复杂过滤器很难得到可信结论。
第三步,再评估质量评分与过滤强度。
规则过滤、模型评分和随机基线应在尽量一致的来源与训练预算下比较,同时关注低资源语言和合法异常样本的误删情况。公开工作中的 10%、28% 等保留比例,只能说明对应数据源和目标下的实验选择,不能直接作为新项目的默认阈值。[5][10][11]
第四步,将结构化数据与合成数据拆开验证。
编辑重建、可执行任务、教师轨迹、仓库级上下文和 FIM 分别引入了不同监督信号。更稳妥的做法是一次控制一个主要变量,先确认单项增益,再研究组合效果。[13][18][19][35] 尤其当数据选择、蒸馏、mask 策略和后训练同时变化时,不能把最终提升全部归因于其中某一个模块。
第五步,在扩大训练规模之前,先扩大证据。
小模型和数据切片适合筛选方向,但去重收益、长尾覆盖和 scaling 行为仍需要更大规模实验确认。最终比较时,应明确区分等训练 tokens、等训练计算量和端到端总成本,因为这三种口径回答的是不同问题。[8][19][37]
与其给出一组固定参数,更合理的是把公开实验结果作为参考点,并明确哪些量仍需要在本项目中重新验证:
| 决策 | 可参考的公开实验 | 本项目需要重新验证的量 |
|---|---|---|
| 近去重 | FineWeb 的词级 5-gram / 14×8;代码数据采用过不同配置 | 分词方式、候选召回、复核精度、簇结构、最终重复曝光 |
| 文件与仓库组织 | OpenCoder 文件级对照;仓库 manifest 保留 | 局部重复、版本一致性、依赖完整度、仓库级任务收益 |
| 质量评分 | Seed 的 1.3B 回归器、Stack-Edu 教育分类、MIRA 来源感知 | teacher 偏差、尾部误差、来源校准、可用数据量、真实增益 |
| 过滤强度 | Seed 预处理后约删除 10% 文件;Stack-Edu 约保留 28% tokens | 同预算随机基线、重复次数、能力覆盖、保留率敏感性 |
| 合成比例 | 网页改写中约三分之一是已研究设置之一 | 分母定义、生成器、难度、多样性、验证率、端到端成本 |
| 过程数据 | commit 编辑预测;Clean-PR 的 mid-training + SFT | 重建正确性、测试质量、检索误差、harness 匹配、污染 |
| 长上下文 | file-level + 位置适配、路径排序、依赖组织逐层对比 | 有效监督量、相关内容密度、长短任务权衡、训练成本 |
| FIM | PSM/SPM 联合、单一排列、随机/AST/函数级 mask 均有实例 | 格式兼容、FIM rate、mask 定义、光标分布、L2R 回退 |
| 多语言 | 重复基线、辅助语言、平行配对、语言配额优化 | 有向迁移、token 效率、主力语言下限、尺度稳定性 |
| 调度 | 时机与权重联合实验,而非只看最终比例 | 累计曝光、学习率位置、域内收益、通用能力回退 |
| 去污染 | 内容、仓库、时间和合成 lineage 的多层控制 | 候选复核、误杀/漏检、teacher/verifier 污染、新题泛化 |
论文之外的工程实践也有参考价值。例如 OpenCoder 的公开经验强调,规则阈值需要通过反复抽样检查校准,并优先在小模型上完成较多消融后再放大;其他预训练实践也反复指出,HTML 解析、数据格式和低 perplexity 的合成语料都可能产生与下游收益不一致的结果。[42] 这类材料更适合作为工程经验,而不能与独立受控实验等量看待:同一个结论被多次转述,并不会增加独立证据数量。
最后,成本比较也不应只看生成价格。更完整的口径至少包括:
$$ C_{\mathrm{total}}=C_{\mathrm{collect}}+C_{\mathrm{process}} +C_{\mathrm{generate}}+C_{\mathrm{verify}} +C_{\mathrm{train}}+C_{\mathrm{evaluate}}. $$其中生成和验证成本都应包含失败重试,复用成本也只有在数据确实被后续多次使用时才适合摊销。每百万“生成 tokens”的单价较低,并不等价于每百万“去重后、验证通过、最终可用 tokens”的成本较低,更不能直接推出单位下游收益更低。[19][30]
因此,数据管线的投入顺序更适合遵循一个原则:先解决可观测、可复现和明显冗余,再逐步增加评分、结构、合成与复杂调度;每增加一层复杂度,都需要对应的受控证据说明它带来了什么额外收益。
结语:好管线不仅生产数据,也生产对数据的解释
回头看,我对代码预训练数据的理解,已经从“尽可能多地收集、尽可能彻底地清洗”,转向更关注一组更具体的问题:为什么要收这类数据,为什么要删掉另一类,哪些重复是有害的,哪些重复是必要曝光,以及最终为什么相信性能提升确实来自这些数据决策。
The Stack 三代说明,规模之外还需要考虑内容可得性、许可治理与产物质量;OpenCoder 和 FineWeb 表明,去重本身会改变数据分布;Seed-Coder、Stack-Edu 和 MIRA 说明,质量判断必须依赖目标与来源;Clean-PR 和 SWE 系列展示了从静态文本到可执行任务之间不同程度的重建与验证;SwallowCode、FIM 及其结构化变体则进一步表明,改写和数据组织能够提高有效监督,但同时也可能引入接口漂移、分布偏移和新的验证成本。[1–19][29][34][35]
这些工作并没有收敛出一套可以直接复制的统一配方。更有价值的是它们提供了一套分析框架:一项数据操作究竟补充了什么能力,改变了哪些变量,引入了什么成本,是否存在更简单的对照,以及最终评测是否仍与训练过程保持足够独立。
因此,“数据 + Infra”更适合被理解为一种研究与工程方法,而不是替代模型研究的口号:让数据来源、处理过程、训练暴露和评测协议都能够被追踪、复现和修正。训练系统负责把梯度计算出来,而数据工程真正需要回答的问题,是这些梯度是否来自我们想让模型学习的信号。
术语速查
| 术语 | 本文中的含义 |
|---|---|
| unique / consumed tokens | 独立语料中的token量 / 经采样重复后累计训练消耗 |
| manifest / lineage | 数据清单与版本映射 / 原始、变换、合成和训练样本的血缘关系 |
| SWHID | 平台无关的软件对象标识;不是许可凭证或无限下载保证 |
| MinHash / LSH | 估计集合相似度的签名 / 高效召回可能相似对象的索引方法 |
| Jaccard / shingle | 集合交并比 / 用于比较的连续片段,须说明字符、词或其他单位 |
| rehydration | 去重后利用重复簇等信息重新分配训练重复权重 |
| scorer / rubric | 质量评分模型 / 评分维度与标准;不等于功能正确性证明 |
| round-trip verification | 编辑表示应用回旧代码后,能否精确重建目标版本 |
| F2P / P2P / P2F | 指定测试的失败转通过 / 通过保持通过 / 通过转失败 |
| test oracle | 决定输出或行为是否符合要求的判断依据,不只是测试执行器 |
| CPT / mid-training / SFT | 持续预训练 / 基础与后训练之间的过渡训练 / 监督微调;需跟随论文定义 |
| FIM / PSM / SPM | 中间填空数据变换 / 前缀-后缀-中间 / 后缀-前缀-中间 |
| AST / 依赖图 | 语法结构树 / 文件或程序对象之间的静态关系近似 |
| RoPE / theta | 旋转位置编码 / 基频参数;调整参数不等于完成训练适配 |
| EM / pass@1 / BLEU | 字符串精确匹配 / 单样本通过率 / 文本重叠指标,不能互换 |
| 百分点 / 相对提升 | 25%−20%=5个百分点;(25−20)/20=25%相对提升 |
附录:整体流程路线与建议
如果从头搭一条流水线,按这个顺序做:
- 精确去重(内容哈希)
去掉完全重复文件和镜像副本,先消除最明显的数据冗余。 - 文件级 MinHash 近去重
识别轻微改写、格式变化、注释变化导致的近重复代码。 - 语言识别 + Tree-sitter 语法过滤 + License/PII
保证代码语言正确、语法可解析,同时过滤许可风险和敏感信息。 - 训练质量打分器
抽样约 20 万条让强模型打分,再训练轻量 scorer 扩展到全量数据。 - 过滤最低质量档,保留 70%–90%
去掉明显低质量样本,同时避免过度清洗导致数据分布变窄。 - 按来源分四类(文件 / 仓库 / 演化 / 合成)
将不同结构和用途的数据分桶,为后续采样和课程调度做准备。 - 三阶段数据调度,结构化数据后置
先学基础代码能力,再学仓库上下文,最后引入演化和合成类复杂数据。 - 依赖图重建仓库顺序 + 结构感知 FIM
按依赖关系组织仓库文件,并让 FIM 利用函数、文件和跨文件结构信息。 - n-gram 去污 + 语义去污
同时检测表面文本重合和语义近似,降低 benchmark 泄漏风险。 - 时间切分内部留出集
用未来时间段数据构建内部评测集,作为数据策略和 checkpoint 的最终裁决依据。
| 参数 | 推荐取值 | 依据 |
|---|---|---|
| MinHash 参数 | 5-gram、112 hash、14 band × 8 row;阈值按语言样本实测后定 | FineWeb 的性价比点;命中率在 0.75→0.85 之间从 77% 跳到 98.8% |
| 去重粒度 | 文件级;仓库结构在打包阶段重建 | OpenCoder:仓库级去重保留 3× token 但下游更差,且 68.4% 残留可再按文件级去重 |
| 超大簇处置 | N≥1000 的簇权而非保留 | FineWeb2 的簇大小理水化策略 |
| 质量过滤强度 | 保守:只滤最差一档(10% 量级),不做“只留 1% 精品” | Seed-Coder 滤 10%;代码最优 D/N 高于文本(2510.08702) |
| 打分器 | 强 LLM 标注 20 万级样本 → 蒸馏 1B 级打分器 → 全语料推理 | Seed-Coder(1.3B)、Stack-Edu(StarEncoder) |
| 评分维度 | 按数据源分别发现维度,而不是一套固定 rubric | MIRA:半量 token 追平全量 |
| FIM 格式 / 粒度 / 比例 | SPM;结构感知掩码(AST 子树或依赖图选函数);常规 0.5 → 持续预训练 0.1,别用 0.7+ | 2207.14255、2506.00204、2607.12463 |
| 合成数据形态 | 优先改写型;教科书型控制比例 | Meta(2510.01631):1/3 改写 + 2/3 真实提速 5–10×;教科书型 33% < 67% |
| 合成数据比例 | ≈30%(随模型规模与数据预算微调) | Meta 的经验收敛值 |
| 代码在通用预训练中的占比 | 25–40% 是反复出现的甜点;100% 伤世界知识 | Cohere(2408.10914);另有研究显示算术类峰值在 40–50% |
| mid-training 时机 | 早引入 + 高权重;晚引入只能用低权重 | 2510.14865:晚期加大配比无法补偿错过窗口 |
| 长上下文扩成本 | 只需预训练预算的 0.1%–1%;重点是 RoPE θ 阶梯重标定 | Granite 4B token(0.1%);JetBrains 1B token(有效 72M) |
| 去污 | 8–13 gram 必做,补 embedding 语义检索,最好再建时间切分留出集 | n-gram 漏掉 78% 的 CodeForces 语义重复 |
| 去污 n-gram 长度 | 8(s1 / open-r1)、10(Seed-Coder)、13(SmolLM / Llama 系) | 各家实践 |
参考文献与延伸阅读
以下按正文编号整理。论文结果均以所列版本为准;动态数据卡尤其应锁定revision。部分近作尚属预印本,引用表示可核读,不表示本文完成独立复现。
- Kocetkov et al. The Stack: 3 TB of permissively licensed source code. 2022,v1。§3、表3/5/6。论文
- BigCode. The Stack 数据卡。初始与后续版本、许可修订。数据卡
- Lozhkov et al. StarCoder 2 and The Stack v2: The Next Generation. 2024,核读v1,§2–4。正文
- BigCode. The Stack v2 数据卡。数据发布物与访问安排。数据卡
- Zhang et al. Seed-Coder: Let the Code Model Curate Data for Itself. 2025,v2,§2与附录A。正文
- HuggingFaceCode. The Stack v3. 2026。固定修订README · 同修订训练统计 · full说明
- Huang et al. OpenCoder: The Open Cookbook for Top-Tier Code Large Language Models. 2024/2025,v3;来源表亦核对v1原始HTML。正文
- Penedo et al. The FineWeb Datasets: Decanting the Web for the Finest Text Data at Scale. 2024,v2,§3.4、附录E。正文 · 官方长文
- Penedo et al. FineWeb2: One Pipeline to Scale Them All. 2025,v1,§4.3–4.5。正文
- SmolLM2: When Smol Goes Big — Data-Centric Training of a Small Language Model. 2025,v1,表2、附录D。正文 · Stack-Edu
- MIRA: Mid-training Rubric Anchoring for Source-Aware Data Selection. 2026,v2,表1、附录A。预算/checkpoint文字存在不完全一致之处,正文只采用明确表格比较。正文
- Muennighoff et al. OctoPack: Instruction Tuning Code Large Language Models. 2023/2024。论文 · CommitPackFT官方卡
- Pull Requests as a Training Signal for Repo-Level Code Editing(Clean-PR). 2026,v2,表2–9、附录A。正文 · Microsoft Research发表页
- Gunasekar et al. Textbooks Are All You Need. 2023,v2,§2、§5。论文
- Training Software Engineering Agents and Verifiers with SWE-Gym. 2024/2025,v2,§3–4。正文
- SWE-rebench: An Automated Pipeline for Task Collection and Decontaminated Evaluation of Software Engineering Agents. 2025,v2。正文
- R2E-Gym: Procedural Environments and Hybrid Verifiers for Scaling Open-Weights SWE Agents. 2025,v1。正文
- SWE-Mirror: Scaling Issue-Resolving Datasets by Mirroring Issues Across Repositories. 2025,v1,§2–3、表6。PDF
- Rewriting Pre-Training Data Boosts LLM Performance in Math and Code(SwallowCode). 2025/2026,v4,§3、附录G/I。正文
- Demystifying Synthetic Data in LLM Pre-training: A Systematic Study of Scaling Laws, Benefits, and Pitfalls. arXiv记录为2025 v1,正文日期及比例网格有不一致;不据此采用精确最优比例。正文
- UnitCoder: Scalable Code Synthesis from Pre-training Corpora. EMNLP 2025,§3–4。正式论文
- KodCode: A Diverse, Challenging, and Verifiable Synthetic Dataset for Coding. 2025,v2,§2–3。正文
- Phi-4 Technical Report. 2024,表4–5、附录B。正文
- Shumailov et al. AI models collapse when trained on recursively generated data. Nature,2024。论文
- Is Model Collapse Inevitable? Breaking the Curse of Recursion by Accumulating Real and Synthetic Data. 2024,v2。正文
- Scaling Granite Code Models to 128K Context. 2024,v1,§2–3。正文
- On Pretraining for Project-Level Code Completion. 2025,v1,§3、表3–4。正文
- OctoLong: Mid-Training On Cross-Repository Code Contexts Enhances Long-Context Modeling. 2026,v1,§3–5、附录B/C。正文
- Bavarian et al. Efficient Training of Language Models to Fill in the Middle. 2022,v1,§3–5、§8。正文
- Hugging Face. DataTrove。仓库 · MinHash分阶段示例
- IBM / data-prep-kit. Repo Level Order Transform。本次核读dev分支,不作为稳定API承诺。说明
- GitHub Docs. Viewing and understanding files;Software Heritage软件标识说明。GitHub历史查看 · SWHID标准说明
- DeepSeek-Coder,2024,v2,§3.1.2;StarCoder,2023,v2,训练格式小节。DeepSeek-Coder · StarCoder
- Structure-Aware Fill-in-the-Middle Pretraining for Code. 2025,v1,§3、§5–7。正文
- Function-Aware Fill-in-the-Middle as Mid-Training for Coding Agent Foundation Models. 2026,v3,§2–3、§6。正文
- Scaling Laws for Code: Every Programming Language Matters. 2025,v1,§3–6、表3。部分迁移表和规模口径需谨慎解读。正文
- Scaling Laws for Code: A More Data-Hungry Regime. 2025/2026,v2,§3–4。正文
- To Code, or Not To Code? Exploring Impact of Code in Pre-training. 2024,v1,§3.3。正文
- Midtraining Bridges Pretraining and Posttraining Distributions. 2025/2026,v2,§5–6。正文
- Soft Contamination Means Benchmarks Test Shallow Generalization. 2026,v1,§3–4。正文
- Jimenez et al. SWE-bench: Can Language Models Resolve Real-World GitHub Issues? . 2023起始工作,附录A;SWE-smith: Scaling Data for Software Engineering Agents,2025,v2。SWE-bench · SWE-smith
- 社区实践,作为定性经验而非独立实验:crazycth《OpenCoder:顶尖代码大模型的开源实践全指南》、 《LLM Pretrain Data Project》;真中合欢《LLM实践–数据去重:Simhash&Minhash 原理分析&代码实现》;字节跳动Seed官方中文解读。OpenCoder分享 · 预训练实践 · 去重实现讨论 · Seed官方解读