代码数据的策展方法论:当预训练语料中代码占比越来越大,代码数据工程的独特挑战与系统解法
系统解读预训练代码数据策展的全景——从数据源构成、代码专属启发式过滤、多层去重、许可证合规到 LLM 改写增强,覆盖 The Stack v2、Nemotron-Code、OpenCoder、SwallowCode、Stack-Edu 五条生产管线的工程解剖与对比
本期主题:代码数据——预训练语料中最「特殊」的存在
前 22 期我们系统覆盖了预训练数据工程的各个维度——去重、过滤、配比、合成、安全、治理、评估、多模态、token 级操作、管线工程。但有一个品类的数据,我们一直在提及却从未专门剖析:代码。
这不是疏忽。代码数据之所以值得单独一期,是因为它在预训练数据工程中占据了一个极其独特的位置:
第一,代码不只服务于编码任务。 Cohere 团队 2024 年的系统性消融实验(To Code, or Not To Code?)在 470M 到 2.8B 参数的模型上证明:加入代码数据后,自然语言推理提升最高 +8.2%,世界知识提升 +4.2%,生成质量胜率提升 +6.6%——代码是通用模型的「推理增强剂」。Petty 等人(How Does Code Pretraining Affect Language Model Task Performance?, TMLR 2025)进一步用控制实验建立了因果关系:代码配比越高,组合性任务(语义解析、数学推理)表现越好,但会损害语言结构敏感和世界知识密集型任务。
第二,代码数据的策展规则和通用文本截然不同。 代码有语法约束、许可证合规要求、仓库级依赖、跨文件引用、高度重复(fork 和复制粘贴)——这些特性意味着文本数据的过滤、去重、质量评估方法不能直接搬用。
第三,各家对代码配比的「押注」越来越大。 Llama 3 在 15T token 的预训练数据中大幅提升了代码占比;DeepSeek-V3 的 14.8T token 中包含了大量代码和数学数据;StarCoder2 专门在 3.3T+ 代码 token 上训练。当代码从预训练的「配角」变成「主角」之一,代码数据工程的质量直接决定模型的天花板。
本期,我们系统解剖代码数据策展的全链路——从数据源、过滤、去重、许可证、质量评估到 LLM 改写增强——并对比五条具有代表性的生产管线。
一、代码数据源:GitHub 之外还有什么?
代码预训练数据的来源比想象中多元。以下是主流管线使用的数据源全景:
| 数据源类型 | 代表项目 | 规模量级 | 特点 |
|---|---|---|---|
| GitHub 公开仓库 | The Stack v2(Software Heritage) | 67.5TB / 619 种语言 | 覆盖面最广,但质量参差不齐 |
| Common Crawl 中的代码 | Nemotron-CC-Code | 427.9B tokens | 网页中的代码片段,覆盖教程和文档嵌入代码 |
| 代码问答 | Stack Overflow、GitHub Issues | 数十 B tokens | 自然语言-代码对齐的天然来源 |
| Jupyter Notebooks | StarCoder2Data | 24.1B tokens | 代码+文本交错,适合推理链学习 |
| Git Commits | StarCoder2Data | 随仓库历史 | 代码变更语义,编辑行为建模 |
| 合成代码 | Nemotron-Code、SwallowCode | 161-174.9B tokens | LLM 生成/改写,质量可控 |
| 技术文档/教程 | OpenCoder(RefineCode) | 待定 | 代码相关文本的召回,增强理解 |
关键观察:纯 GitHub 代码虽然量大,但质量长尾极重——大量仓库是学生作业、配置文件、自动生成代码。近两年的趋势是:以 GitHub 为主体,辅以网页代码提取、合成代码、代码问答,构建多来源混合语料。
NVIDIA 的 Nemotron-Pretraining-Code-v1 是一个标志性案例:747.4B tokens 的精选 GitHub 代码 + 174.9B tokens 的合成代码问答数据(覆盖 11 种语言,由 Mixtral-8x22B 生成),两条管线互补。
二、代码专属的启发式过滤:为什么文本过滤规则不够用
代码数据的过滤是整个策展流程中最体现「领域知识」的环节。OpenCoder(ACL 2025)做了目前最系统的工作——发布了第一个专门为代码设计的启发式过滤框架。
文本过滤 vs 代码过滤的差异
| 维度 | 通用文本过滤 | 代码过滤 |
|---|---|---|
| 长度 | 过短/过长文档 | 空文件、过短函数、超长自动生成文件 |
| 语言检测 | 自然语言识别 | 编程语言识别(语法检测) |
| 质量信号 | perplexity、教育价值 | 语法正确性、注释比例、命名规范、圈复杂度 |
| 重复模式 | 近似文档重复 | fork 重复、模板代码、配置文件、自动生成 |
| 有害内容 | 色情/暴力/偏见 | API 密钥、密码、PII、恶意代码 |
| 结构化特征 | 无 | AST 可解析性、import 深度、函数/类结构 |
OpenCoder 的启发式规则体系
OpenCoder 的过滤框架包含两层:
第一层:代码质量信号计算
- 行级统计:平均行长度、最大行长度、空行比例
- 注释信号:注释行比例、注释长度
- 代码结构:import 语句数量、函数/类定义密度
- 字符分布:字母/数字/特殊字符比例
第二层:基于规则的过滤
- 对不同文件类型(代码文件 vs 数据文件 vs 配置文件)应用不同阈值
- 过滤条件示例:平均行长度 > 阈值(可能是压缩或混淆代码)、注释占比过低(可能缺乏可读性)
对实践的启示:如果你在构建代码预训练数据管线,OpenCoder 的过滤框架代码(github.com/OpenCoder-llm/opc_data_filtering)是目前最好的起点。它是第一个把代码过滤的「隐性知识」显式化为可复现管线的开源项目。
三、代码去重:比文本去重更复杂的原因
代码去重面临几个文本领域不存在的挑战:
3.1 重复的多样形态
代码重复的四种形态(按检测难度递增):
Level 0:精确重复(Exact Duplicate)
→ 同一文件出现在多个 fork 中
→ 解法:哈希比对(SHA-1/MD5),极快
Level 1:近似重复(Near-Duplicate)
→ 变量重命名、格式调整、注释修改
→ 解法:MinHash + LSH(BigCode 方案)
Level 2:语义重复(Semantic Duplicate)
→ 不同实现但功能等价
→ 解法:AST 结构比对 / 代码嵌入
Level 3:模板/样板代码(Boilerplate)
→ 框架生成的脚手架代码、配置模板
→ 解法:启发式规则 + 黑名单
3.2 各管线的去重策略对比
| 管线 | 精确去重 | 近似去重 | 额外策略 |
|---|---|---|---|
| The Stack v2 | ✅ SHA-1 文件级 | ✅ MinHash LSH | 仓库级去重(同源 fork 合并) |
| Nemotron-Code | ✅ | ✅ 模糊去重 | 与 BigCode 对齐的严格方案 |
| OpenCoder | ✅ | ✅ | 文件级 + 仓库级 双层去重 |
| StarCoder2Data | ✅ | ✅ | 基于 Software Heritage 的归档去重 |
对实践的启示:代码去重务必做到至少两层——文件级精确去重 + MinHash 近似去重。如果计算资源允许,仓库级去重(识别 fork 和镜像仓库)能进一步消除 30%+ 的冗余。BigCode 项目的 MinHash 近似去重实现 是社区标准。
四、许可证过滤:代码策展的「法律红线」
许可证合规是代码数据策展中独有的、不可回避的工程维度。通用文本数据虽然也有版权问题,但代码的许可证体系更加结构化和严格。
4.1 许可证分类与过滤策略
代码许可证光谱(从宽松到严格):
宽松许可证(Permissive) Copyleft 许可证
├── MIT ├── GPL-2.0
├── Apache-2.0 ├── GPL-3.0
├── BSD-2-Clause ├── AGPL-3.0
├── BSD-3-Clause └── LGPL-*
├── ISC
└── Unlicense
中间地带
├── MPL-2.0(弱 Copyleft)
└── CDDL-1.0
无许可证
└── 默认版权保护,不可自由使用
各管线的许可证策略差异显著:
| 管线 | 策略 | 详情 |
|---|---|---|
| The Stack v2 | 保留所有 | 发布时标注许可证,由用户自行过滤 |
| StarCoder2Data | 宽松过滤 | 基于 BigCode 许可证白名单 |
| Nemotron-Code | 更严格过滤 | 在 BigCode 基础上收紧许可证集合 |
| OpenCoder | 宽松优先 | 倾向保留 permissive 许可证 |
4.2 LiCoEval:当合规问题从训练数据蔓延到模型输出
LiCoEval(ICSE 2025,北大)揭示了一个令人不安的事实:对 14 个主流 LLM 的评估发现,0.88% ~ 2.01% 的生成代码与现有开源代码存在「显著相似性」,而绝大多数模型无法提供准确的许可证信息。Copyleft 许可证(如 GPL)下的代码合规表现尤其差。
4.3 SynPrune:检测代码是否被用于训练
SynPrune(AAAI 2026,同济大学)提出了一种语法感知的成员推断攻击方法,专门用于检测特定代码是否出现在模型的预训练数据中。核心思路:编程语言中许多 token(括号、分号、关键字搭配)是语法强制要求的,模型对这些 token 的高置信度并不能说明它「见过」该代码——应当剪枝这些 token 后再做推断。
对实践的启示:许可证过滤不是可选项。建议至少采用 BigCode 的宽松许可证白名单作为基线,如果面向商业部署,参考 Nemotron-Code 的更严格策略。同时建立代码生成阶段的合规检测机制——SynPrune 和 LiCoEval 提供了可操作的方法论。
五、重点论文解读
1. “Arctic-SnowCoder: Demystifying High-Quality Data in Code Pretraining” (arXiv 2024, Snowflake)
核心发现:
Arctic-SnowCoder-1.3B 仅用 555B tokens 通过三阶段渐进式数据精炼完成预训练,在 BigCodeBench 上达到同规模 SOTA,超越参数量 2x 以上的 StarCoderBase-3B。
三阶段方法论是代码数据策展的一个精华样本:
| 阶段 | Token 量 | 方法 |
|---|---|---|
| Phase 1:通用预训练 | 500B | 标准质量代码,基础过滤+去重+去污染 |
| Phase 2:高质量继续预训练 | 50B | BERT 风格质量标注器筛选,正例来自高质量代码+指令数据 |
| Phase 3:合成增强 | 5B | Llama-3.1-70B 以 Phase 2 数据为种子生成合成数据 |
数据量逐阶段递减(500B → 50B → 5B),质量逐步提升——100:10:1 的漏斗比例。
最关键的洞察:
高质量代码数据的关键不在于其「绝对质量」,而在于与下游应用分布的对齐程度。
这意味着代码数据策展不应追求单一的「通用高质量」标准,而应根据目标任务反向设计质量定义。对话式编码助手需要的「高质量」和系统编程模型需要的「高质量」可能截然不同。
对实践的启示:
如果你的计算预算有限(< 1T tokens),三阶段渐进精炼比一次性用大量中等质量数据训练更有效。核心是:先用大量标准数据建立基础分布,再用少量高对齐数据修正方向,最后用合成数据补盲点。
2. “OpenCoder: The Open Cookbook for Top-Tier Code Large Language Models” (ACL 2025)
核心贡献:
OpenCoder 不只是一个代码 LLM——它是目前开放程度最高的代码 LLM 项目,同时发布了模型、数据、管线代码和消融实验结果。论文将构建顶级代码 LLM 的关键要素归纳为三个:
- 代码优化的启发式规则:专门为代码设计的清洗和去重规则(上文已详述)
- 代码相关文本的召回:从大规模文本语料中召回与代码相关的文档(教程、API 文档、技术博客),这一步至关重要——它让模型不仅学「怎么写代码」,还学「为什么这么写」
- 高质量合成数据:在退火和 SFT 阶段使用合成数据
消融实验的关键发现:
通过严格的消融实验,OpenCoder 证明了上述三个要素中每一个都对最终性能有不可替代的贡献。特别是,代码相关文本的召回——这个容易被忽视的步骤——对代码理解和推理能力的提升显著。
GitHub Star 过滤的局限性:OpenCoder 做了一个直觉挑战的实验——训练了两个 1.5B 模型,一个用原始数据,一个用 GitHub Star ≥ 5 过滤后的数据。结果发现,Star 过滤并未带来一致的性能提升。这说明 Star 数量作为代码质量的代理指标是不可靠的——很多高质量的工具代码、科学计算代码 Star 极低,而很多高 Star 仓库(如 awesome-list)对预训练并无价值。
对实践的启示:
代码数据策展不能只看代码本身。召回代码相关的自然语言文本(README、文档、教程、技术问答)并加入训练混合,是一个投入产出比极高的操作。OpenCoder 的管线代码是可直接复用的参考实现。
3. “Rewriting Pre-Training Data Boosts LLM Performance in Math and Code” — SwallowCode (ICLR 2026, 东京工业大学)
核心发现:
SwallowCode 提出了一种激进的范式转换——不丢弃低质量代码,而是用 LLM 改写来提升质量(transform-and-retain)。
四阶段管线:
| 阶段 | 方法 | 作用 |
|---|---|---|
| Stage 1 | 语法验证 | 过滤无法解析的 Python 代码 |
| Stage 2 | Pylint 风格过滤 | 筛除严重违反编码规范的片段 |
| Stage 3 | LLM 改写(风格一致性) | 强制代码遵循统一编码风格 |
| Stage 4 | LLM 改写(自包含化 + 算法优化) | 将片段转化为自包含的、算法高效的示例 |
在固定 500 亿 tokens 的持续预训练预算下,SwallowCode 对 Llama-3.1-8B 的提升:
- HumanEval pass@1:+17.0
- HumanEval+ pass@1:+16.1
相比 Stack-Edu(同样是高质量代码数据集),SwallowCode 的优势极其显著。
为什么 transform-and-retain 优于 filter-and-discard?
论文的消融实验揭示了每个管线阶段的增量贡献——LLM 改写阶段带来最大增益。核心原因是:过滤策略面临质量-数量的根本矛盾——越严格的过滤得到越高质量的数据,但剩余数据量越少,多样性越低。改写策略打破了这个矛盾:保留数量和多样性的同时提升质量。
论文团队选择改写而非从零合成的理由也值得注意:已有研究(Chen et al., 2024)表明从零生成的合成代码存在分布退化风险,改写真实代码能保留分布多样性。
对实践的启示:
如果你有足够的 LLM 推理预算(SwallowCode 用 Llama-3.1-70B 做改写),transform-and-retain 是目前代码数据策展的最优策略——它在 HumanEval 上的提升比任何纯过滤方法都大。关键的改写管线:先统一风格(降低噪声),再自包含化(提升单文件可学性)。
4. “Stack-Edu” (HuggingFace, 2025)
核心方法:
Stack-Edu 将 FineWeb-Edu 的「教育价值过滤」范式引入代码领域,从 The Stack v2(StarCoder2Data)中筛选出 125B tokens 的高教育价值代码:
- 用 Llama-3-70B-Instruct 对 50 万代码片段进行 0-5 分教育价值评分(合成标注)
- 针对 15 种编程语言分别训练 StarEncoder 分类器
- 保留评分 ≥ 3(Java 使用 ≥ 2)的代码
结果:在 MultiPL-E 基准上,全部 15 种编程语言都优于原始 StarCoder2Data。
语言分布(按 token 量):
| 语言 | Token 量 | 语言 | Token 量 |
|---|---|---|---|
| Java | 42.1B | JavaScript | 11.1B |
| Python | 21.8B | C | 11.1B |
| C++ | 16.0B | PHP | 9.07B |
| Markdown | 14.0B | SQL | 9.62B |
对实践的启示:如果你需要一个开箱即用的高质量代码数据集作为基线,Stack-Edu 是当前最佳选择。但 SwallowCode 的实验表明,如果计算预算允许,在 Stack-Edu 基础上再做 LLM 改写能进一步显著提升——两种方法是互补而非替代的关系。
六、五条管线的工程对比
将上述研究综合起来,我们可以画出代码数据策展管线的完整全景和横评:
| 维度 | The Stack v2 | OpenCoder | Nemotron-Code | SwallowCode | Stack-Edu |
|---|---|---|---|---|---|
| 数据源 | Software Heritage | GitHub + Web(RefineCode) | GitHub | The Stack v2 (Python) | StarCoder2Data |
| 规模 | 67.5TB / 619 语言 | 960B tokens | 747.4B + 174.9B 合成 | ~16.1B tokens | 125B tokens |
| 许可证策略 | 全量标注,用户自选 | 宽松优先 | BigCode 基础上更严格 | 继承上游 | 继承上游 |
| 去重 | SHA-1 + MinHash | 文件级 + 仓库级 | 精确 + 模糊 | 继承上游 | 继承上游 |
| 质量过滤 | 无(原始语料) | 代码专属启发式规则 | 启发式(OpenCoder 风格) | Pylint + 语法检查 | LLM 教育价值评分 |
| LLM 增强 | 无 | 退火阶段合成 | 合成代码 QA(174.9B) | 全量 LLM 改写 | 无 |
| 核心创新 | 规模与开放性 | 可复现的完整管线 | GitHub + 合成双管线 | transform-and-retain | 教育价值过滤 |
| 开源程度 | ✅ 数据 + 管线 | ✅ 全套(模型+数据+管线+消融) | ✅ 元数据 + 合成数据 | ✅ 数据+管线+prompt | ✅ 数据 + 分类器 |
代码数据策展方法的演进路线:
2022 ─── 原始语料 ──→ 基础过滤 ──→ 去重 ──→ 训练
The Stack v1
2024 ─── 原始语料 ──→ 启发式过滤 ──→ 多层去重 ──→ 许可证过滤 ──→ 训练
The Stack v2 / StarCoder2Data
2025a ─── 原始语料 ──→ 代码专属启发式 ──→ 多层去重 ──→ 许可证过滤 ──→ 合成增强 ──→ 训练
OpenCoder / Nemotron-Code
2025b ─── 原始语料 ──→ 质量评分过滤 ──→ 训练
Stack-Edu(教育价值分类器)/ Arctic-SnowCoder(BERT 质量标注器)
2026 ─── 原始语料 ──→ 语法检查 ──→ 风格过滤 ──→ LLM 全量改写 ──→ 训练
SwallowCode(transform-and-retain)
七、代码配比:多少才够?
代码在通用模型预训练数据中应占多大比例?这是一个尚无定论但极其重要的工程决策。
已知的数据点
| 模型 | 总训练量 | 代码信号 |
|---|---|---|
| Llama 3 | 15T tokens | 大幅提升代码占比(具体未公开) |
| DeepSeek-V3 | 14.8T tokens | 包含大量代码和数学 |
| StarCoder2-15B | 3.3T+ tokens | 纯代码+代码相关文本 |
| Qwen-2.5 | 18T tokens | 代码和数学数据增强 |
Petty et al. (TMLR 2025) 的因果分析
这项控制实验提供了目前最严谨的配比指导:
- 代码比例 ↑ → 组合性任务(语义解析、数学) ↑
- 代码比例 ↑ → 语言结构敏感任务 ↓
- 代码比例 ↑ → 世界知识任务 ↓
这意味着不存在一个普适的最优代码配比——它取决于你的目标能力侧重。一个推理增强型模型可能需要 20-30% 的代码;一个知识密集型模型可能只需要 10-15%。
对实践的启示:代码配比的决策不能拍脑袋。建议使用 DataDecide 类方法(参见 Vol.18),在小规模代理实验中用不同代码配比训练,观察目标基准上的 Scaling Laws,再外推到全量训练。
本期思考
代码数据正在经历文本数据十年前走过的路——从「有就行」到「必须精心策展」。但代码有自己的节奏。
文本数据的策展演进是:规模 → 质量过滤 → 去重 → 配比优化 → 合成增强。代码数据的演进路线类似但多了两个维度:许可证合规和语法结构利用。前者是法律约束,后者是技术机遇——代码的结构化特性意味着我们可以用 AST、类型系统、编译器反馈等文本数据不具备的信号来评估和改进数据质量。
SwallowCode 的 transform-and-retain 范式推测(标注为推测)会成为未来 2-3 年代码数据策展的主流方向——因为 GitHub 上公开的高质量代码终究是有限的,而 LLM 改写能把低质量代码「升级」成高质量训练数据,本质上拓展了可用数据的边界。但这也带来一个元问题:用 LLM 改写的代码训练新的 LLM,长期是否会导致代码分布的同质化?这值得持续关注。
对于正在构建代码数据管线的团队,我的建议是一个分层决策框架:
- 必做项:许可证过滤 + 精确去重 + 基础启发式过滤(OpenCoder 框架)
- 强烈建议:MinHash 近似去重 + 代码相关文本召回 + 教育价值分类器(Stack-Edu 风格)
- 如果计算预算允许:LLM 改写增强(SwallowCode 风格)+ 合成代码 QA 生成
- 持续监控:PII 检测 + 许可证合规审计(SynPrune 风格) + 代码质量漂移检测
下期预告:预训练数据的 Scaling Laws 再审视——当数据量逼近互联网总量的天花板,Scaling Laws 的预测还可靠吗?