跳到主要内容

那些在你没留意时变简单的评估集

· 阅读需 10 分钟
Tian Pan
Software Engineer

你在 18 个月前编写了这套评估集(eval set)。那时它是一个非常有用的工具:低价模型的得分是 71%,更好的模型得分是 84%,而当出现回归(regression)时,分数会下降并被察觉。这套测试套件在 CI 中赢得了一席之地。于是你不再关注它了。

今天再运行它,每个候选模型的得分都是 96、97、98。新版本的得分与旧版本相同。你怀疑表现较差的模型与你认为更好的模型得分也一样。仪表盘上的数字依然显示为绿色,检查依然通过,但它实际上什么也没告诉你。你的评估集并没有坏。它只是变简单了——因为底层的模型变强了——而没人在意它失去区分度的那个瞬间。

这就是评估饱和(eval saturation),这不仅是你可能遇到的失效模式,更是任何静态测试套件在足够长的时间跨度下必然走向的终局。一个所有模型都能通过的测试,已经不再是测试了。

饱和是预期的结果,而非意外

将饱和视为一种物理规律而非 Bug 会更有帮助。公开基准测试(benchmarks)的轨迹已经说明了这一点。HumanEval 和 MBPP——曾是默认的代码衡量标准——现在前沿模型在 Pass@1 上的得分已接近 99% 和 94%。MMLU 和 MMLU-Pro 在 88% 以上已功能性饱和,顶级模型之间的差距比测量噪声还要小。SuperGLUE 在发布后大约一年内就触及了天花板。一项关于基准测试平台期的系统研究发现,基准测试的年龄和规模可以预测其饱和,而人们认为会有所帮助的保护措施——如私有测试集、封闭式回答格式——其保护效果有限。

其机制很简单。基准测试通过将得分分布在一定范围内来进行区分。当被测群体进步时,分数会堆积在最大值附近。范围被压缩了。超过某一点后,你在统计上就无法区分最好的模型和次好的模型,因为两者的得分都是 97%,且差异在抽样误差范围内。这就是天花板效应(ceiling effect),它摧毁了你真正想要的唯一属性:区分“优秀”与“卓越”的能力。

你的内部评估集也遵循同样的物理规律。它比 MMLU 更小且更具针对性,这意味着它饱和的速度更快,而不是更慢——你编写了几百个案例来反映你在 2024 年所知道的失效模式,而从那以后模型已经吸收了其中的大部分。区别在于没有报刊为你这套测试发布排行榜,所以没有什么能迫使你注意到这一点。公开基准测试至少会在退役时引发关注,而你的测试只会在不断通过的 CI 任务中悄无声息地变得平庸。

一个万物皆可通过的基准测试已无法衡量任何事物

饱和评估集的危险之处不在于它没用,而在于它极其自信地没用。它依然会产生一个数字。这个数字依然很高。流水线依然显示为绿色。发布流程中的每一个信号都显示模型没问题,而这个信号现在已经脱离了现实。

具体来说,以下是饱和的测试套件无法再做到的事情:

  • 为候选模型排名。两个模型的得分都是 97% 和 97%。你只能像抛硬币一样随机发布一个并称之为决策。
  • 捕捉回归。一个破坏了 1% 行为的变更将 97% 的得分降至 96%,这与运行间的波动无法区分。于是回归问题被发布了。
  • 为模型升级提供依据。新模型的成本更高。你的评估显示它的表现完全一致。你要么支付了过高的费用,要么错过了一个真正的改进,而你没有任何证据。
  • 为回滚辩护。生产环境中的某些环节感觉变差了,但理应判定争议的评估集却报告没有变化。

分数在任何单一案例上都没有撒谎。它测试的每一个案例都是正确的。它的谎言源于遗漏——因为它完全由模型已经“成长”并超越的问题组成,所以它报告的总和是在一个不再有任何有趣事情发生的各种能力空间里取的平均值。你正在用一把最小刻度比你关心的任何差异都要大的尺子进行测量。

此外还有一个更隐蔽的代价。全绿的评估套件是一张不在场证明。当测试套件通过时,团队就会停止观察,因为观察的成本很高,而仪表盘显示一切正常。饱和的评估集用真实的关注度换取了虚假的信心。这比没有评估更糟糕,因为没有评估至少能让你保持警惕。

你需要的区分度存在于长尾难例中

信号去哪了?它转移了。区分“优秀”模型与“卓越”模型的案例不在你的测试套件中——它们存在于你从未添加的长尾难例(hard tail)中。

把你的评估集看作一个难度分布。当你编写它时,大部分案例分布在模型认为真正具有挑战性的区间,并带有一个由难例组成的细长尾部。18 个月的模型进步让整个分布向左平移了。曾经具有挑战性的区间现在变得微不足道。细长的长尾难例现在是唯一仍在发挥作用的部分——但它太细了,无法产生稳定的分数。你的 300 个案例中,大多数在每次运行时贡献的信息量为零,因为每个模型都能做对。你实际有效的评估集是那 12 个没人能完全攻克的难例,而它们被淹没在 288 个噪声案例中。

这重新定义了修复方法。你不需要一个规模更大的评估集。一千个简单案例的饱和速度与一百个完全一样。你需要一个难度更高的评估集——具体来说,你需要不断补充长尾难例,使难度分布始终保持在当前模型能力的边界上。测量工具必须紧跟被测物体。一个为室温校准的温度计在烤箱里无法告诉你任何有用的信息,而模型已经进入了烤箱。

长尾难例恰恰也是决定你产品成败的地方。没有人会因为 Agent 没能完成一个简单的案例而流失,因为模型能搞定这些。用户流失是因为模糊的指令、对抗性的输入,或者是第四步取决于对第二步误读的多步任务。而这些案例,恰恰是饱和的评估集不再包含的。

将评估集视为不断演进的产物

解决这个问题的思维转变是,停止将评估集视为一次性构建的固定资产,而是将其视为具有维护负担的动态产物——就像如果不加照看就会腐烂的代码一样。有三种实践可以保持它的生命力。

从生产环境中挖掘新案例。 你最丰富的鲜活难题来源已经在免费提供:每一次点踩、每一次无声的放弃、每一位重新组织语言并重试的用户,都是现实递给你的带标签的失败。关键在于捕捉每一个案例及其产生的上下文——输入、模型版本、检索到的文档、实际输出——并将其转换为永久的评估案例。一个务实的起点是使用 50 个真实的生产失败案例作为测试集的种子,并在每次修复 Bug 时添加一个案例,这样每次事故都能永久性地提升基准。生产环境中的失败是你所能获得的最优质的评估数据,因为从定义上讲,它们就是你现有测试集未能捕捉到的案例。

刻意添加对抗性和合成的疑难案例。 等待生产环境暴露每一个弱点既太慢又太冒险。按难度分层,并主动编写处于难度顶端的案例:模糊的指令、矛盾的上下文、用户提供内容中的提示注入(prompt-injection)尝试、长程任务。一些团队在评估时生成候选案例,这样测试集就不会在多次运行之间被悄悄“背诵”。目标是保持一个可用的评分梯度——这个差距要足够大,使得 1% 的退化在噪声之上依然清晰可见。

淘汰模型已经解决的案例。 这是团队最容易忽略的一步,也是保持核心指标具有意义的关键。当一个案例在六个月内被所有模型都正确回答时,它就不再提供有效信号,而成了虚增总分的纯粹负载。将已解决的案例移入一个成本较低的独立回归层——仍然运行它们,仍然捕捉灾难性的倒退——但将它们从核心评分中剔除,以便该数字仅反映“必争之地”。即使回归存档在增长,评估集的核心也应该变得更小、更难

检验你的测试集是否具有生命力的方法很直接:当新模型发布时,你的评估集得出的分数是否与旧模型不同?如果是,说明工具仍然有效。如果每个模型得分都一样,你测量的不是模型——你测量的是测试案例的陈旧程度。

本周行动指南

调取你测试过的各模型版本的最近几次评估运行记录。绘制得分曲线。如果曲线在顶部持平,那么你拥有的并不是一个合格的评估集——而是一个恰好在报告“正常”的损坏仪器。

然后做三件事。找到你的“硬尾部”(hard tail):即模型仍然存在分歧或失败的案例,并确认这些案例的数量足以影响总分。开启一条从生产失败案例到测试集的流水线,并为每个案例附带完整的上下文。最后,设置一个周期性的日历提醒——每季度一次比较合理——来审计难度、淘汰已解决的案例并注入新的难题。

模型会不断变得更好。这是你唯一可以确信的事。一个没有随之变得更难的评估集,并不是一个慢慢磨损的安全网——而是一个已经失效的安全网,它只是挂在那里,看起来完好无损,直到有东西从中坠落的那一刻。

References:Let's stay in touch and Follow me for more thoughts and updates