[控场AI]
· 7 分钟阅读· 3,611 字

去污染数据集仍含52道HumanEval题:基准测试污染被低估

去污染数据集仍含52道HumanEval题:基准测试污染被低估

trifero.ai发现HumanEval数据污染导致评分虚高4.3分,但机制并非记忆而是风格泛化。

trifero.ai实验室在收到读者质疑后复查了训练数据,发现即使标注为“去污染”的Magicoder-Evol-Instruct-110K中仍含52道HumanEval题。移除污染样本后,HumanEval pass@1从26.8%降至22.6%。然而,丢分并未集中在被移除的题目上,而是均匀分布在所有HumanEval风格题目中,说明污染效应并非来自答案记忆,而是模型在相似风格题目上的泛化练习被误报为“能力”。此外,MBPP上的运行间方差约4分,与污染效应相当,表明单次运行结果不可靠。研究者建议扫描指令数据、质疑去污染标签、运行随机种子对照,并向厂商追问评测细节。

训练一个360M参数的代码模型,在HumanEval上报告26.8%的pass@1——两天后,一位读者问我们是否检查了指令数据与基准测试的重叠。我们没查过。查完之后,诚实的数字是22.6%。

这是Strife Technologies旗下trifero.ai实验室一次公开的自我纠错。他们发现:即使使用了标注为"去污染"的数据集,HumanEval的164道题中仍有52道潜伏其中。

HumanEval数据污染分析

扫描方法:两道关卡

研究团队对每个指令集做了两项检查:

  • 题目文本:取每道HumanEval文档字符串的前80个字符,归一化空白后比对。
  • 标准答案:去掉注释和文档字符串后,检查答案中13个token的n-gram是否有过半匹配。
数据集样本数发现文档字符串发现答案
evol-codealpaca-v1(实际使用)111,27260题,156例8题,67例
Magicoder-Evol-Instruct-110K("去污染"版)111,18352题,116例8题,66例
Magicoder-OSS-Instruct-75K(实际使用)75,19701题,1例

关键在于:那个被过滤重分发的"去污染"版本,与原始版本仅差89行,却仍然包含60道污染题中的52道。换句话说,换用这个版本也救不了你。

n-gram 匹配是数据污染检测中最常用的一类近似重复检测技术。其基本思路是:将文本切分为连续的 n 个 token 组成的片段(如 13-gram 表示每 13 个连续 token 为一个片段),然后统计候选样本与参考样本之间共享的 n-gram 比例。之所以用 n-gram 而非精确字符串匹配,是因为它能捕捉到轻微的改写、变量名替换或空白差异——但这些改动仍保留了底层的 token 序列结构。如果一篇答案中超过半数的 13-gram 都能在基准标准答案中找到,则几乎可以确定两者存在衍生关系。n 的取值是一个权衡:n 太小(如 3)会因常见短语而产生大量假阳性;n 太大(如 50)则对合理的代码改写过于敏感,容易漏检。13 是一个在代码场景下经验性的折中选择。

为什么过滤器漏掉了它们

这些匹配不是复制粘贴。它们是练习卷形式的基准题:改写措辞、增加额外要求、或者移植到Java,每道都附带完整答案。

比如HumanEval/43的pairs_sum_to_zero,带着原始文档字符串、doctest和可用答案出现,只是加了一条"改为线性时间复杂度"的指令。

这不是收集过程的意外。Evol-Instruct通过反复改写种子题目来扩展数据集,而有些种子就是基准题。精确匹配过滤器抓不住改写——而改写正是整个方法的核心。

Evol-Instruct 是一种通过让大语言模型反复改写种子指令来扩充指令数据集的方法。其流程通常是:给定一条种子指令(可能来自开源数据集或人工编写),让模型生成一条“更深”“更广”或“更复杂”的新指令,同时生成对应的回答。这种迭代演化能快速产生大量多样化样本,但存在一个结构性风险:如果种子池中混入了基准测试题,那么所有由该种子演化出的变体都会继承其核心问题结构,只是换了措辞或增加了约束条件。精确匹配过滤器只能发现与基准题完全一致的文本,而 Evol-Instruct 的产物在字面上已经完全不同——它们共享的是语义和解题结构,不是字符串。因此,一个为精确匹配设计的过滤器在面对演化数据时几乎不产生任何拦截效果。

模型是在背诵答案吗?

不是。这恰恰是最让研究者意外的地方。

他们把每次聊天模型的输出与标准答案用difflib比率比对(归一化空白后按行计算)。背诵答案的得分会超过0.9。44次通过中,最高只有0.73,而且那是一道标准答案只有一行的题。大多数低于0.3。

于是他们移除污染样本重新训练,同时用原始数据、只换随机种子再训一遍,以区分数据效应和重跑效应。

检查点HumanEvalHumanEval+MBPP
Base13.4%11.0%20.2%
Chat,已发布26.8%25.6%38.9%
同数据,seed 226.2%25.6%34.6%
去污染后22.6%21.3%35.0%

去污染版对比已发布版:HumanEval -4.3分,丢8题、得1题,McNemar检验p = 0.039。两次原始数据运行相差不超过1题,所以下降来自数据本身,不是重跑波动。

difflib 是 Python 标准库中用于序列比对的模块,其 SequenceMatcher 可以计算两个序列的相似度比率(范围 0 到 1)。在代码场景下,研究者通常将模型输出与标准答案按行切分后进行比较,比率越高说明两者越接近。如果模型是在逐字背诵训练中见过的答案,比率通常会超过 0.9;而当比率低于 0.3 时,说明输出与标准答案在行级别上几乎没有重叠。需要注意的是,difflib 比率对代码中的空白、注释和变量命名较为敏感,因此在使用前通常会做归一化处理。这一指标的优势在于简单直观,但它无法区分“理解了逻辑后用不同写法实现”和“只记住了部分片段”——这也是研究者结合子集分析来进一步排除记忆机制的原因。

丢分的位置揭示真相

如果是记忆,丢分应该集中在与已移除训练数据匹配的63道题上。但事实并非如此。

子集题数Base已发布Seed 2去污染后
在已移除数据中635141713
不在已移除数据中10117302624

对比两次原始数据运行的均值,去污染模型在两个子集上都低了约4分。

这说明:污染是真实的,但机制不是记忆。在HumanEval风格题目上的练习,普遍提升了模型在HumanEval风格题目上的表现——包括从未出现在数据中的题目。研究者之前把这种提升报告成了"能力"。

McNemar 检验是一种用于配对二分类数据的统计检验方法,特别适合比较同一组样本在两个不同条件下的表现差异。在模型评测中,它常被用来判断两个模型(或同一模型的两个版本)在同一个基准测试上的通过/失败模式是否发生了统计上显著的变化。具体做法是构建一个 2×2 列联表,统计“版本 A 通过且版本 B 失败”和“版本 A 失败且版本 B 通过”的题目数量,然后检验这两个数量是否显著不对称。与简单的通过率差值相比,McNemar 检验的优势在于它关注的是逐题的变化方向,而非总体均值,因此能排除题目难度分布不同带来的干扰。p 值小于 0.05 通常被认为差异显著。本文中 p = 0.039 意味着去污染导致的 4.3 分下降不太可能仅由随机波动造成。

MBPP给出了不同的教训

只有10道MBPP题匹配到已移除数据,而两次原始数据运行相差11题。已发布的38.9%最好被理解为一次幸运的随机种子。MBPP上的运行间方差约为4分,与HumanEval上的污染效应大小相当。

一次运行报告的是一个数字,它同时代表了模型、数据、评测框架和随机种子。

给评测者和厂商的四个建议

扫描你的指令数据。 文档字符串前缀加答案n-gram,几分钟就能跑完,代码已在仓库中开源。

不要把"去污染"当作可信标签。 检查过滤器实际匹配了什么。面对一个通过改写构建的数据集,精确匹配过滤器几乎等于没有过滤器。

跑随机种子对照。 没有这个对照,就无法把4分的数据效应和4分的方差效应分开——而在MBPP上,方差效应反而是更大的那个。

向厂商(包括我们)问三个问题: 去污染方法是什么?评测框架是什么?数字背后跑了几次?

完整报告(含图表、置信区间和McNemar检验)见 trifero.ai/research/elroy,代码与九个章节、所有基准测试完成与失败记录以Apache-2.0协议开源于 github.com/strifero/elroy。

#HumanEval#基准测试污染#代码模型#数据去污染#指令微调#评测透明度#pass@1#Evol-Instruct
分享:

相关推荐