跳到主要内容

你团队的基准测试正在互相欺骗:共享评估基础设施的污染问题

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的红队刚完成了一次越狱扫描。他们发现了三个新型攻击向量,将其整理成文档,并把这些提示词放入共享提示词库,供其他人学习。一周后,安全团队运行基线评估,报告鲁棒性提升了 12%。所有人都在庆祝,却没人问为什么。

实际发生的是:安全团队的基线评估悄悄纳入了红队的攻击提示词。模型并没有变得更健壮——是评估被污染了。你的基准测试现在衡量的是对已知攻击的免疫力,而非对新攻击的泛化能力。

这就是共享评估基础设施污染问题,它比大多数团队意识到的要普遍得多。症状是指标被人为拉高,根本原因是把评估基础设施当生产基础设施来对待——优化了共享和效率,而非隔离性和保真度。

共享评估基础设施毒害结果的三种方式

渠道一:缓存补全泄漏

现代 LLM 推理系统会缓存常见前缀的键值(KV)状态以降低推理延迟。这在生产环境中是正确行为。在共享评估基础设施中,却成了隐患。

当 A 团队运行评估时,他们的提示词会被缓存。当 B 团队的评估随后在同一推理实例上运行时,若提示词前缀重叠,可能会悄悄命中缓存状态。根据缓存策略的不同,B 团队的结果部分由 A 团队的评估运行决定——而非模型的全新推理。

跨租户 KV 缓存共享的研究表明,单凭缓存命中或未命中的模式就能以较高准确率重建其他团队的提示词。即便没有恶意,跨团队的重叠提示词前缀(常见的系统提示模板、共享的指令格式、复用的少样本示例)也会让状态悄悄通过缓存渗漏。

实际后果:在不了解评估运行时缓存状态的情况下,你无法信任在共享推理基础设施上生成的绝对分数。看似 3% 的提升,可能有 1.5% 来自缓存,只有 1.5% 是真实改进。

渠道二:顺序运行污染

第二种污染渠道更为隐蔽,它不依赖缓存,而是依赖时间。

当评估运行在共享基础设施上顺序执行时,一次运行的残留物会持续影响下一次:遗留文件、累积的内存状态、带有残留梯度状态的热启动模型进程、影响后续读取的文件系统条目。这些残留物在评估结果中往往不可见,却影响可复现性。

还有一个确定性幻觉的问题。团队常常以为固定随机种子就能保证跨次运行的可复现输出,但事实并非如此。在采样层,torch.multinomial 等操作在不同批量大小下本质上是不确定的——GPU 调度顺序会改变有效随机性。研究表明,仅仅改变 GPU 数量或评估时的批量大小,即便种子不变,也会让准确率偏移高达 9%。

这意味着两个团队在不同日期、不同队列深度和批量配置下运行"相同"评估、使用"相同"种子,会得到系统性的不同数字。没有哪个结果本身是错的,但它们不可比较,把它们当作可比较的数字正是污染进入的地方。

渠道三:提示词状态渗漏

第三种渠道是组织层面的,而非技术层面的。当供评估框架使用的共享资源——提示词库、数据集注册表、示例存储——在运行之间发生变化且没有显式版本控制时,就会发生渗漏。

回到开篇的红队场景,但更不显眼的版本到处都是:

  • 开发人员在调试时向共享提示词模板的"训练示例"部分添加了对抗样本,然后忘记撤回。
  • 基线提示词在数周内被不同团队成员反复改进,却没有留下任何记录。
  • 共享数据集注册表部署了新版本,因为有人修复了标注错误——而这恰好删除了模型表现较差的样本。

每种情况下,评估框架评估的内容都与上周略有不同,但版本标签没有改变。指标提升了,污染却是隐形的。

为什么"固定种子"和"隔离命名空间"还不够

认识到这一问题的团队通常会实施两种局部缓解措施:确定性种子和基础设施隔离(独立的 Kubernetes 命名空间、独立的队列)。这两种方法单独使用都不够。

固定种子会失效,因为确定性只在单进程、单批次、单 GPU 配置的层面上得到保证。改变其中任何一项——这在共享基础设施中随负载波动而时常发生——你的种子就会产生不同的样本。零温度也无济于事:即便没有采样随机性,模型输出仍取决于浮点累加顺序,而这随硬件和批量大小而变化。"确定性"运行只在相同二进制文件、相同硬件、相同批量大小、相同日期的狭义条件下才是可复现的。

隔离命名空间会失效,因为它隔离的是计算资源,而非数据。如果 A 团队和 B 团队共享提示词注册表、数据集存储,甚至是公共评估框架配置,命名空间隔离仍然允许提示词状态污染通过这些共享数据层流动。

所需要的是在每个可能共享状态的层面进行隔离:计算、缓存和数据。

真正有效的隔离原语

密封评估环境

密封评估环境运行时没有任何共享外部状态。在实践中,这意味着:

  • 每次评估运行使用全新容器:每次评估从已知良好的镜像开始,不携带前次运行的任何状态。这意味着没有热身缓存收益,但也意味着没有污染。
  • 内存中模拟服务:替代原本会在运行期间访问共享数据库或模型注册表的服务。
  • 显式环境快照:在运行开始时捕获确切配置——模型检查点、提示词版本、数据集哈希——并与结果一起存储。

开销是真实的:密封环境比共享基础设施多消耗 10–30% 的计算资源。对于把关模型部署的 CI 式评估运行,这个代价值得支付。对于探索性研究,较轻量的隔离(命名空间 + 缓存清除)可能已足够。

批量无关的确定性采样

LLM 推理基础设施的近期研究引入了一种有原则的确定性解决方案:不依赖种子来控制 torch.multinomial,而是用基于种子哈希函数生成的 Gumbel 噪声来扰动 logits。由于哈希函数以 (input_ids, seed) 为输入并在采样前完成计算,结果与批量大小和调度顺序无关。相同输入搭配相同种子,无论同时运行什么,始终产生相同样本。

这从采样层解决了顺序污染问题。团队现在可以跨不同批量配置的运行进行有意义的比较,"固定种子"真正意味着可复现。

运行范围的提示词状态

对提示词状态渗漏最实用的缓解措施,是让提示词版本在整个评估运行期间显式且不可变。

具体做法:

  • 评估中使用的每个提示词模板在运行开始时固定到特定 Git 提交哈希。
  • 若解析的哈希与预期值不匹配,框架拒绝继续运行。
  • 结果存储时附带完整的提示词哈希清单,而非仅有易读的版本标签。

这意味着提示词变更后,旧评估结果不会被追溯性地作废——它们对其运行所针对的固定版本仍然有效。也意味着静默的注册表更新无法破坏进行中的运行。

团队间的 KV 缓存边界

对于共享推理基础设施、又希望避免密封环境全部开销的团队,选择性缓存隔离是一条中间路线。其思路是监控哪些前缀在团队之间共享,并选择性地清除或隔离跨越团队边界的前缀。这保留了团队内部缓存的收益(实践中占总缓存命中的大部分),同时阻断了污染评估结果的跨团队泄漏。

这种方式的开销很小——与完全共享缓存相比约多 5%——且无需完全分离基础设施就能防止最常见的污染向量。

组织层面:红队数据隔离

技术隔离解决计算和数据层的问题。组织层面需要另一种纪律:防止红队发现污染基线评估。

核心原则是时间分离。基线评估在特定模型版本的红队测试开始之前运行。红队发现存储在隔离注册表中,与基线提示词库分开。将红队提示词推广到共享基础设施(用于训练、防御性评估、示例库)须经过显式审核步骤,该步骤会检查此次推广是否会影响任何现有基线评估数据集。

听起来像繁文缛节,但如果自动化检查,开销很低。在任何合并到共享提示词库之前,预合并钩子会扫描传入提示词与当前基线使用的哈希集合的重叠情况。若有重叠,合并被拦截并显示明确错误。

补充实践是为基线标注其相对于红队发现的时间位置。一个先于某已知越狱发现的基线,与一个晚于该发现的基线,衡量的是不同类型的内容。两者都有效,但没有这个标注就无法比较。

好的评估基础设施是什么样的

将这些原语整合在一起,一个良好隔离的评估基础设施具备以下特性:

每个结果都完全可复现。 给定运行 ID,你可以精确重建运行内容:模型检查点哈希、提示词 SHA 清单、数据集版本、种子和批量配置,以及运行所在硬件。

团队边界在数据层得到强制执行。 提示词注册表、数据集存储和示例库具有显式访问控制和不可变版本引用。团队无法在不留审计痕迹的情况下意外(或故意)通过修改共享资源影响另一个团队的评估。

红队数据永远不会悄悄进入基线评估。 从红队发现到共享库的推广路径受自动哈希扫描把关。基线被标注了其相对于发现前后的时间位置。

缓存状态得到显式管理。 推理基础设施要么使用密封环境(每次运行全新状态),要么使用选择性缓存隔离(强制执行跨团队缓存边界)。选择方式有文档记录,并反映在结果比较方式中。

确定性声明是真实的。 采样使用批量无关的原语。相同种子和配置的多次运行,无论批量大小或队列深度如何,都产生相同输出。

忽视这一问题的代价

基准测试污染不会大声失败,而是通过让你的数字看起来比实际更好而悄悄失败。在干净基准上提升 4% 的模型,在被污染的基准上显示 7% 的提升。被污染的数字赢得了是否部署的内部讨论,而模型在生产中的表现只有 4% 的提升。

最危险的版本不是恶意行为——而是数月来不可见的共享状态积累。提示词库悄悄变化,缓存补全不断积累,红队发现通过非正式渠道传播。没有人做了什么错事,然而你的评估基础设施却逐渐停止衡量你以为它在衡量的东西。

修复这一问题需要以其他工程学科中对受控实验适用的同等严格隔离标准来对待评估基础设施。当测量的完整性依赖于隔离时,隔离不是可选的额外开销——它就是测量本身。


无法告诉你它被什么污染的基准测试,无法告诉你你的模型实际学到了什么。

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