如果你一直在关注代码专用大语言模型 (LLMs) 的爆发式增长,你可能看过各种排行榜。每周似乎都有一个新的开源模型宣称在 HumanEval 等基准测试上能够匹敌 GPT-4。看起来我们正处于自动编程的黄金时代。

但这里有一个陷阱。如果你拿这些评分极高的模型,在竞争性编程网站上较新、较鲜活的问题上进行测试,它们的表现往往会崩盘。这是为什么呢?

根据北京邮电大学和美团的研究人员发表的一篇引人入胜的新论文,答案是数据泄露 。 许多模型本质上是在“背诵测试答案”,因为它们的训练数据已被基准测试题污染。

在这篇深度文章中,我们将探讨论文《How Do Your Code LLMs Perform? Empowering Code Instruction Tuning with Really Good Data》。我们将揭示研究人员如何检测这种泄露,他们如何清洗数据,以及最重要的是,他们开发的用于筛选高质量、多样化且复杂数据的新颖方法——XCoder

问题所在: 当 100% 准确率成为坏兆头

要理解这篇论文的贡献,我们首先需要看看代码指令微调 (Code Instruction Tuning) 的现状。这个过程是指获取一个预训练的基础模型 (如 LLaMA 或 StarCoder) ,并在一对对“指令-代码解决方案”上对其进行微调。目的是让模型学会遵循用户的请求。

这项任务的数据集通常来自两个来源:

  1. 蒸馏 (Distillation) : 要求一个强模型 (如 GPT-4) 生成编程问题和解决方案 (例如 Code Alpaca, WizardCoder) 。
  2. 现实世界数据: 挖掘 GitHub 提交记录或 StackOverflow (例如 OctoPack) 。

当研究人员在流行的开源数据集 (如 Magicoder-Evol-Instruct 或 Code-Feedback) 上训练模型时,他们注意到了一些可疑的现象。

图 1: 左图显示了不同基准测试上的性能比较,右图显示了数据去污染后的不同结果。Magicoder Evol-Instruct 和 Code-Feedback 在 HumanEval 上可能存在数据泄露。

图 1 (左) 所示,像 Magicoder-Evol-Instruct 这样的模型在 HumanEval (一个标准的、较旧的基准测试) 上取得了令人难以置信的高分。然而,它们在 LiveCodeBench 上的表现却大幅下降,LiveCodeBench 是一个通过持续从竞赛中收集新问题来防止污染的基准测试。

这种差异表明,这些模型未必“聪明”;它们可能只是在训练过程中见过 HumanEval 的题目。

衡量泄露: TLI 指标

为了证明这一点,作者引入了一个名为测试泄露指数 (Test Leakage Index, TLI) 的指标。

TLI 量化了训练数据与测试数据的重叠程度。它的工作原理是为两个数据集生成 n-gram (文本序列) ,并计算相似度分数。高 TLI 意味着模型实际上是在测试集上进行训练。

表 2: 三个存在数据泄露的数据集及其清洗后版本在 HumanEval 和 LiveCodeBench 上的性能比较。TLI 衡量了训练集在 HumanEval 上的数据泄露程度。清洗后的大小和性能变化以红色突出显示。

表 2 揭示了问题的严重性。像 Magicoder-Evol-Instruct 这样的数据集,其 TLI 高达 43.2 , 与约 5.0 的“干净”基线相比,这简直是天文数字。

当研究人员清洗数据 (移除泄露的样本) 后,HumanEval 上的性能骤降 (例如,从 68.3% 降至 65.9% 或更低) ,而在未见过的 LiveCodeBench 上的性能则保持稳定。这证实了所谓的“最先进”结果很大程度上是由数据泄露造成的幻觉。

解决方案: 定义“好”数据

一旦移除了泄露的数据,我们还剩下一个巨大的潜在训练数据池——来自各种开源集合的超过 250 万个样本。在所有这些数据上进行训练既昂贵又低效。

这篇论文的核心贡献是一种系统性的数据修剪方法。作者提出,“好”的代码指令数据必须满足三个维度:

  1. 复杂性 (Complexity) : 问题不应是微不足道的。
  2. 质量 (Quality) : 代码解决方案必须正确且健壮。
  3. 多样性 (Diversity) : 数据集不应只是 10,000 个“打印 Hello World”的变体。

他们将这三个维度结合到一个流程中,创建了 XCoder , 这是一个在高度精选的数据子集上训练出来的模型。

图 2: 我们的数据选择方法示意图。

图 2 展示了工作流程。让我们详细分解他们如何量化每个维度。

维度 1: 指令复杂性

为了衡量复杂性,研究人员没有依赖像长度这样的简单启发式方法。相反,他们训练了一个专门的复杂性评分器 (Complexity Scorer)

他们利用了“Evol-Instruct” (进化指令) 的概念,即使用 ChatGPT 迭代地使简单指令变得更复杂。通过跟踪一个样本经历了多少轮进化,他们创建了一个带标签的数据集,用于训练一个能够预测任何给定指令复杂性分数的模型。

维度 2: 响应质量 (单元测试方法)

这可能是他们方法中最具创新性的部分。在通用文本生成中,“质量”是主观的。但在编程中,代码要么能运行,要么不能。

然而,仅仅检查代码是否编译通过是不够的。它需要解决描述的具体问题。研究人员开发了一个单元测试模型 (Unit Test Model)

图 8: 单元测试模型的输入和输出案例。

图 8 所示,他们专门训练了一个模型 (微调过的 LLaMA3-70B) 来阅读问题描述并生成一套单元测试 (验证代码逻辑的断言) 。

在数据选择过程中,对于每一对训练样本 \((Instruction, Code)\),系统会:

  1. 将指令输入单元测试模型以生成测试用例。
  2. 针对这些测试用例执行提供的代码。
  3. 根据通过率计算质量分数。

这作为一个严格的过滤器。如果一个训练样本包含的代码实际上没有解决指令中的问题,它将获得低分并被丢弃。

为什么要用模型来写单元测试? 你可能会想,大模型写测试是否足够可靠。作者对此进行了分析,发现扩大模型规模能显著提高测试生成的准确性。

图 4: 不同规模训练的单元测试模型生成测试用例时的准确性比较。我们还额外评估了 GPT-4 生成测试用例的能力。

图 4 显示,他们的 70B 单元测试模型达到了近 79% 的准确率 , 足以匹敌 GPT-4。这种可靠性使他们能够信任对数百万数据样本的自动评分。

维度 3: 指令多样性

最后,如果所有样本在语义上都是相同的,那么拥有复杂且正确的代码也没用。为了确保广度,研究人员采用了基于多样性的采样 (Diversity-based Sampling)

他们将指令嵌入到向量空间中。在选择数据时,只有当样本与池中已选的例子足够“遥远” (不相似) 时,才会迭代地添加该样本。这确保了模型接触到广泛的编码概念和问题类型。

实验: XCoder 有效吗?

研究人员应用这种选择策略创建了 XCoder 数据集,并在其上训练了基于 LLaMA3 的模型。结果令人印象深刻,特别是在数据效率方面。

表 3: 使用 XCoder 数据与其他主流数据在 HumanEval 和 LiveCodeBench 上的性能比较。所有模型均基于 LLaMA3-8B-Base 训练并使用贪婪解码。

表 3 突出了主要发现:

  • 效率: 仅用 40k 样本训练的 XCoder 在困难且无污染的 LiveCodeBench 上,表现优于使用 100k 样本的 Magicoder-Evol-Instruct
  • 泛化性: 它在 BigCodeBench 上也取得了最先进的结果。
  • 无泄露: 虽然它没有达到受污染模型在 HumanEval 上那种人为虚高的分数,但它在各个榜单上的表现是诚实且稳健的。

数据扩展实验 (在图像组中显示为表 7) 进一步强化了这一点。使用 10k 样本的 XCoder 达到了与使用 160k 样本的随机采样相当的性能。这证实了“质量胜于数量”的假设。

图 3: XCoder 与其他主流模型在 LiveCodeBench 上的性能比较。

当扩展到 70B 参数模型时, XCoder-70B (如图 3 所示) 定位为目前最好的开源代码模型之一,在 LiveCodeBench 的 Easy-Pass@1 指标上与 GPT-4 和 Gemini Pro 1.5 等专有巨头紧密竞争。

高质量数据的解剖

这篇论文最具教育意义的方面之一是回顾性分析。在让算法选出最好的 160k 样本后,研究人员回过头来查看这些样本来自哪里。这告诉了我们什么样的构建数据方法实际上是有价值的。

图 5: 不同数据源对 XCoder 的贡献比例…

图 5 提供了所选数据的详细分类:

  1. 复杂性 (图表 a) : Code-Feedback 数据集 (涉及多轮细化) 是复杂指令的主要来源。这表明涉及“调试”或“改进”代码的数据比简单的一次性生成对认知要求更高。
  2. 质量 (图表 b) : OctoPack (源自真实的 Git 提交) 和 StarCoder2-Self-Align 对质量贡献显著。这是合理的: 提交到 GitHub 的代码通常能运行,而 Self-Align 数据集通常包含执行过滤器。
  3. 多样性 (图表 c & d) : 这是最惊人的发现。在多样性采样之前,合成数据集占主导地位。但在多样性采样之后 (图表 d) , OctoPack (现实世界数据) 的份额从 2% 飙升至前列。

洞察: 合成数据 (由 GPT-4 生成) 在复杂性质量方面表现出色,但往往是重复的。现实世界数据 (来自人类) 虽然混乱且有时简单,但提供了覆盖编程场景长尾所需的多样性

结论

论文《How Do Your Code LLMs Perform?》对 AI 编程领域是一个至关重要的现实检验。它揭露了抬高基准分数的猖獗的数据泄露问题,并提供了一套稳健的方法来修复它。

对于学生和从业者来说,结论很明确:

  • 信任,但要验证: 对 HumanEval 的分数保持怀疑。查看 LiveCodeBench 或最近的、无污染的基准测试。
  • 数据修剪是关键: 你不需要数百万行数据。你需要的是干净复杂且经过验证的数据。
  • 执行的力量: 使用模型编写单元测试来验证训练数据是一种强大的技术,超越了简单的文本相似度。
  • 混合来源: 最好的结果来自于复杂的合成指令与多样化的现实世界示例的融合。

XCoder 证明,只要有正确的数据策略,我们可以训练出更小、更高效的模型,它们真正理解代码,而不仅仅是死记硬背。