固定跑完全部样本并不总是必要:明显胜出、明显退化或已可确认差异很小的实验,都可以提前结束。关键不是普通地中途查看结果,而是预先定义检查次数、置信区间和停止规则,使整体误判概率仍然受控。
固定样本量浪费在哪里
常见的 LLM 评测流程会先选定 1000 条数据,再让基线版本和候选版本全部执行完。这样容易管理,却会在两类实验中浪费成本:候选提示词前 200 条已经稳定退化,剩余调用只是重复确认;或者两个版本几乎没有差异,却仍要消耗完整预算。
直接每隔几十条查看一次普通的 95% 置信区间也不可取。假设每次检查都有 5% 的误报概率,反复查看并在第一次显著时停止,会提高整个实验的误报概率。这就是多次窥视问题:单次区间可信,不代表经过多次选择后的结论仍然可信。
本文采用较容易审计的方案:预先列出所有检查点,把总显著性水平分配给这些检查,并用 Hoeffding 不等式构造有界观测的区间。它比一些专门的置信序列更保守,但实现简单,不依赖正态分布假设,适合作为工程基线。
把评测结果变成成对观测
让基线和候选版本回答同一条样本,再由人工、规则或固定版本的裁判模型比较。将结果编码为:
| 比较结果 | 数值 |
|---|---|
| 候选胜出 | 1 |
| 平局 | 0.5 |
| 基线胜出 | 0 |
这些观测都位于 [0, 1]。总体均值记为 p,效果量定义为 p - 0.5:正数偏向候选,负数偏向基线。成对比较还能抵消一部分样本难度差异,但前提是两个版本使用同一批、同一顺序的数据。
在第 n 个样本处检查时,Hoeffding 区间半径为:
radius = sqrt(log(2 / alphaLook) / (2n))
若计划检查 K 次,可令每次的 alphaLook = alpha / K。根据并集界,任意一次区间漏掉真实均值的概率不超过总预算 alpha。这不是最紧的边界,却解决了普通 95% 区间反复查看的问题。
先定义停止边界
评测开始前需要确定三类结论:
| 状态 | 停止条件 | 含义 |
|---|---|---|
better | 效果区间下界大于最小改进量 | 候选至少改善到值得发布的程度 |
worse | 效果区间上界小于最小退化量的负值 | 候选出现有实际意义的退化 |
equivalent | 整个区间进入等效范围 | 差异小到当前业务不关心 |
inconclusive | 达到最大样本仍未触发边界 | 证据不足,不等于没有差异 |
例如,minEffect = 0.03 表示只有成对胜率效果超过 3 个百分点才算有实际意义;equivalenceMargin = 0.02 表示 [-0.02, 0.02] 内可视为近似等效。两者之间留下缓冲区,可以避免把统计显著但业务价值很小的变化直接标记为胜出。
还应设置 minSamples。它不是修复统计错误的魔法,而是避免在极少样本、数据排序不均或服务刚启动时做出运营决策。检查间隔也必须预先确定,不能看到曲线接近边界后临时增加检查次数。
TypeScript 完整实现
下面的程序只使用 JavaScript 标准 API,可保存为 sequential-evaluator.ts,使用 npx tsx sequential-evaluator.ts 运行。示例中的数据由固定种子生成,仅用于演示评估器行为,不代表真实模型收益。
type Status = 'better' | 'worse' | 'equivalent' | 'inconclusive';
type Config = {
alpha: number;
minSamples: number;
maxSamples: number;
checkEvery: number;
minEffect: number;
equivalenceMargin: number;
};
type Result = {
status: Status;
samples: number;
mean: number;
effect: number;
lowerEffect: number;
upperEffect: number;
};
function checkpoints(config: Config): number[] {
const points: number[] = [];
for (let n = config.minSamples; n < config.maxSamples; n += config.checkEvery) {
points.push(n);
}
if (points.at(-1) !== config.maxSamples) points.push(config.maxSamples);
return points;
}
function evaluate(outcomes: number[], config: Config): Result {
if (outcomes.length < config.maxSamples) {
throw new Error('Provide at least maxSamples outcomes');
}
if (outcomes.some((value) => value < 0 || value > 1)) {
throw new Error('Every outcome must be between 0 and 1');
}
const looks = checkpoints(config);
const alphaPerLook = config.alpha / looks.length;
let sum = 0;
let nextLook = 0;
for (let n = 1; n <= config.maxSamples; n++) {
sum += outcomes[n - 1];
if (n !== looks[nextLook]) continue;
const mean = sum / n;
const radius = Math.sqrt(Math.log(2 / alphaPerLook) / (2 * n));
const lowerEffect = Math.max(0, mean - radius) - 0.5;
const upperEffect = Math.min(1, mean + radius) - 0.5;
const common = { samples: n, mean, effect: mean - 0.5, lowerEffect, upperEffect };
if (lowerEffect > config.minEffect) return { status: 'better', ...common };
if (upperEffect < -config.minEffect) return { status: 'worse', ...common };
if (
lowerEffect >= -config.equivalenceMargin &&
upperEffect <= config.equivalenceMargin
) {
return { status: 'equivalent', ...common };
}
nextLook++;
if (n === config.maxSamples) return { status: 'inconclusive', ...common };
}
throw new Error('Unreachable');
}
function seededRandom(seed: number): () => number {
let state = seed >>> 0;
return () => {
state = (1664525 * state + 1013904223) >>> 0;
return state / 2 ** 32;
};
}
const config: Config = {
alpha: 0.05,
minSamples: 100,
maxSamples: 2000,
checkEvery: 25,
minEffect: 0.03,
equivalenceMargin: 0.02
};
const random = seededRandom(42);
const outcomes = Array.from(
{ length: config.maxSamples },
() => (random() < 0.68 ? 1 : 0)
);
console.log(evaluate(outcomes, config));
生产环境不应先生成全部结果再传入。可以保留相同的累计和与检查逻辑,在每批模型调用完成后更新;一旦触发停止状态,就取消尚未提交的任务。示例使用数组,是为了让核心统计规则可以独立运行和测试。
如何避免错误结论
首先,样本应当随机抽取或至少随机排序。如果前 100 条全是简单问答,后面才出现长文本,提前停止得到的只是排序偏差。若评测集包含语言、业务线等明显分层,应在各层内抽样,并保证每个检查点都有合理构成。
其次,裁判本身的随机性也属于观测过程。应固定裁判版本、提示词和采样参数,并随机交换两个答案的展示顺序,降低位置偏差。若同一答案反复送给裁判再挑选有利结果,原有置信保证不再成立。
再次,不要在运行中修改边界。临时降低 minEffect、扩大等效范围或增加检查点,本质上都是根据数据改规则。确需调整时,应把本轮标记为探索实验,再用新规则和独立样本确认。
最后,Bonferroni 加 Hoeffding 通常较保守。调用成本很高、实验量很大时,可以研究经验 Bernstein 置信序列或基于下注过程的 anytime-valid 方法,但需要同时验证公式适用条件和数值实现。不要为了更早停止而退回普通正态区间反复窥视。
总结
序贯评估的价值不是保证每次都提前结束,而是在证据已经充分时停止继续购买重复信息。一个可审计的实现需要做到:预先固定最大样本和检查点;对多次检查控制整体错误率;设置最小样本保护、实际效果阈值和等效区间;把达到预算但未越界明确记录为 inconclusive。
这套方法适合模型版本、提示词和工作流的成对比较。它不能修复偏置数据、失真的裁判或运行中修改规则的问题,但能在这些前提得到控制后,缩短明显实验的反馈周期,并让提前停止具有可解释的统计依据。
评论