数据工程观察 · Vol.23

代码数据的策展方法论:当预训练语料中代码占比越来越大,代码数据工程的独特挑战与系统解法

系统解读预训练代码数据策展的全景——从数据源构成、代码专属启发式过滤、多层去重、许可证合规到 LLM 改写增强,覆盖 The Stack v2、Nemotron-Code、OpenCoder、SwallowCode、Stack-Edu 五条生产管线的工程解剖与对比

code data code curation The Stack OpenCoder SwallowCode Nemotron-Code Stack-Edu license filtering code deduplication code quality 代码数据 策展方法论 许可证合规 合成代码 代码配比

本期主题:代码数据——预训练语料中最「特殊」的存在

前 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-Code427.9B tokens网页中的代码片段,覆盖教程和文档嵌入代码
代码问答Stack Overflow、GitHub Issues数十 B tokens自然语言-代码对齐的天然来源
Jupyter NotebooksStarCoder2Data24.1B tokens代码+文本交错,适合推理链学习
Git CommitsStarCoder2Data随仓库历史代码变更语义,编辑行为建模
合成代码Nemotron-Code、SwallowCode161-174.9B tokensLLM 生成/改写,质量可控
技术文档/教程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:高质量继续预训练50BBERT 风格质量标注器筛选,正例来自高质量代码+指令数据
Phase 3:合成增强5BLlama-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 的关键要素归纳为三个:

  1. 代码优化的启发式规则:专门为代码设计的清洗和去重规则(上文已详述)
  2. 代码相关文本的召回:从大规模文本语料中召回与代码相关的文档(教程、API 文档、技术博客),这一步至关重要——它让模型不仅学「怎么写代码」,还学「为什么这么写」
  3. 高质量合成数据:在退火和 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 2Pylint 风格过滤筛除严重违反编码规范的片段
Stage 3LLM 改写(风格一致性)强制代码遵循统一编码风格
Stage 4LLM 改写(自包含化 + 算法优化)将片段转化为自包含的、算法高效的示例

在固定 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 的高教育价值代码:

  1. 用 Llama-3-70B-Instruct 对 50 万代码片段进行 0-5 分教育价值评分(合成标注)
  2. 针对 15 种编程语言分别训练 StarEncoder 分类器
  3. 保留评分 ≥ 3(Java 使用 ≥ 2)的代码

结果:在 MultiPL-E 基准上,全部 15 种编程语言都优于原始 StarCoder2Data。

语言分布(按 token 量):

语言Token 量语言Token 量
Java42.1BJavaScript11.1B
Python21.8BC11.1B
C++16.0BPHP9.07B
Markdown14.0BSQL9.62B

对实践的启示:如果你需要一个开箱即用的高质量代码数据集作为基线,Stack-Edu 是当前最佳选择。但 SwallowCode 的实验表明,如果计算预算允许,在 Stack-Edu 基础上再做 LLM 改写能进一步显著提升——两种方法是互补而非替代的关系。


六、五条管线的工程对比

将上述研究综合起来,我们可以画出代码数据策展管线的完整全景和横评:

维度The Stack v2OpenCoderNemotron-CodeSwallowCodeStack-Edu
数据源Software HeritageGitHub + Web(RefineCode)GitHubThe Stack v2 (Python)StarCoder2Data
规模67.5TB / 619 语言960B tokens747.4B + 174.9B 合成~16.1B tokens125B 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 315T tokens大幅提升代码占比(具体未公开)
DeepSeek-V314.8T tokens包含大量代码和数学
StarCoder2-15B3.3T+ tokens纯代码+代码相关文本
Qwen-2.518T 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,长期是否会导致代码分布的同质化?这值得持续关注。

对于正在构建代码数据管线的团队,我的建议是一个分层决策框架:

  1. 必做项:许可证过滤 + 精确去重 + 基础启发式过滤(OpenCoder 框架)
  2. 强烈建议:MinHash 近似去重 + 代码相关文本召回 + 教育价值分类器(Stack-Edu 风格)
  3. 如果计算预算允许:LLM 改写增强(SwallowCode 风格)+ 合成代码 QA 生成
  4. 持续监控:PII 检测 + 许可证合规审计(SynPrune 风格) + 代码质量漂移检测

下期预告:预训练数据的 Scaling Laws 再审视——当数据量逼近互联网总量的天花板,Scaling Laws 的预测还可靠吗?