第 19 章 第四部分:闭环与展望

评估闭环与归因

三层评估体系(L1自动评测每ckpt/L2 LLM-as-Judge每周/L3真人评测每月)、关键评测集速查(MMLU/GSM8K/HumanEval/IFEval/MT-Bench/Arena-Hard/AlpacaEval2/TruthfulQA/τ-Bench/BFCL)、LLM-as-Judge四大陷阱(Position/Length/Self-preference/格式偏好)、Reward Hacking检测(reward突增告警/rollout模式聚类/Cross-Verifier/人审sample)、数据归因TDA(消融/Influence Function EK-FAC/TracIn)、数据-训练-评估闭环(评测掉分→找数据→加量改质/某评测涨太多→检查污染/DPO长度漂移→加debias/RL reward暴涨→检查hacking)、数据卡片Datasheet

评估闭环 LLM-as-Judge Reward Hacking TDA Influence Function Judge 校准 数据卡片

没有闭环的数据工程是盲修。

评测掉分 → 找数据 → 改质加量 → 再评测,是工业级团队的肌肉记忆。


19.1 三层评估体系

19.1.1 三层架构

工业级评估体系分三层,按频率与成本递增:

┌──────────────────────────────────┐
│ L3 真人评测(每月)   成本极高      │
├──────────────────────────────────┤
│ L2 LLM-as-Judge(每周)成本中     │
├──────────────────────────────────┤
│ L1 自动评测(每 ckpt) 成本低      │
└──────────────────────────────────┘

19.1.2 L1:自动评测(每 checkpoint)

频率:每个训练 checkpoint(约每 1000 steps) 成本:单次 < 1 小时,< 1 美元 用途:快速筛选 checkpoint,监控训练趋势

评测集:

  • MMLU(5-shot,4 选 1)
  • GSM8K(5-shot CoT)
  • HumanEval(pass@1)
  • IFEval(指令遵循准确率)

特点:

  • 全自动,无人介入
  • 分数稳定可复现
  • 适合横向对比 checkpoint

19.1.3 L2:LLM-as-Judge(每周)

频率:每周一次(或重大 checkpoint) 成本:单次 4-8 小时,10-100 美元 用途:评估生成质量、多轮对话、复杂任务

评测集:

  • MT-Bench(GPT-4 评分 1-10)
  • AlpacaEval 2.0(胜率 vs GPT-4)
  • Arena-Hard(与 GPT-4 对比胜率)
  • τ-Bench(Agent 任务成功率)

特点:

  • 需要调用 GPT-4 等强模型
  • 分数有偏差(见 19.3)
  • 适合细粒度质量评估

准入口径:一致率必须做机会校正

L2 上线前的标准动作是拿一批人工标注的校准集,测 judge 与人的一致性。这里有个容易踩的坑:直接报 exact-match agreement(原始一致率)会系统性高估 judge 的判别能力,因为它没有扣除”瞎猜也能对”的部分。

迄今规模最大的系统性评测(Reliability without Validity,arXiv:2606.19544)覆盖 9 家供应商的 21 个 judge,在 MT-Bench / JudgeBench / RewardBench 上跑了 118 组实验、约 54.1 万条判定,结论是 kappa deflation 在整个 cohort 上普遍存在——在 MT-Bench 上,exact match 比 Cohen’s κ 高出 33–41 个百分点。也就是说一个 raw agreement 看起来 85% 的 judge,换成 κ 可能只有 0.45 左右。

工程含义有三条:

  1. 报 κ,不报 raw。校准集上必须给出 Cohen’s κ(或等价的机会校正指标);若沿用 raw agreement,“>85% 可接受”这类阈值会把一个实际中等水平的 judge 判为达标。
  2. 阈值要自己测,不要抄。κ 的绝对值随任务类别数与标签分布变化,跨任务不可移植。阈值应在自有校准集上重新测定,并把测定过程写进数据卡片。
  3. 一致 ≠ 无偏。该研究报告了一个”一致性—偏差悖论”:两个已投产的 judge 在 test–retest 上信度 > 0.95,却同时带有 > 0.10 的位置偏差。稳定复现的 judge 也可能稳定地错,因此 19.3 的偏差审计不能因为”重测很稳”就省略。

同一研究还指出 judge 在不同 benchmark 上的排名最多能挪动 14 位——“哪个 judge 最好”依赖于用哪个评测集来问,选 judge 时应当在自己实际要评的分布上测,而不是照搬公开榜单。

19.1.4 L3:真人评测(每月)

频率:每月一次(或发版前) 成本:单次数千-数万美元 用途:金标评测,校准 L1/L2

流程:

  • 50-100 名标注员
  • 每人评 100-500 条
  • A/B 对比(与基线模型)
  • 双盲交叉校验

特点:

  • 最接近真实用户体验
  • 慢、贵、不可复现
  • 用于最终发布决策

19.2 关键评测集速查

19.2.1 评测集分类速查表

评测集类型规模指标频率
MMLU学术知识14K 题4 选 1 准确率L1
MMLU-Pro学术知识+12K 题10 选 1 准确率L1
GSM8K数学1.3K 题CoT 准确率L1
MATH数学5K 题准确率L1
HumanEval代码164 题pass@1L1
MBPP代码974 题pass@1L1
IFEval指令遵循500+准确率L1
MT-Bench多轮对话80GPT-4 评分L2
AlpacaEval 2.0通用805胜率L2
Arena-Hard通用500胜率L2
TruthfulQA诚实817真实率L1
XSTest安全250拒答率L1
τ-BenchAgent165任务成功率L2
BFCL工具调用2000+准确率L2
LiveCodeBench代码(抗污染)周更新pass@1L1

19.2.2 评测集选用原则

1. 抗污染优先 MMLU、GSM8K 等老评测集污染严重,必须配合 LiveCodeBench、AIME 等持续更新的评测。

2. 多维度覆盖 单测一个维度(如只测 MMLU)会过拟合,必须测知识/推理/代码/安全/工具至少 5 个维度。

3. 量级匹配 小模型(< 7B)用 GSM8K,大模型(> 70B)必须用 AIME 等更难的评测。


19.3 LLM-as-Judge 四大陷阱

19.3.1 Position Bias(位置偏差)

LLM judge 倾向于选第一个响应。

测试:

def test_position_bias(judge, prompt, response_a, response_b):
    r1 = judge(prompt, response_a, response_b)  # A 在前
    r2 = judge(prompt, response_b, response_a)  # B 在前
    # 如果 r1 偏向 A 且 r2 偏向 B,有 Position Bias
    return r1 == "A" and r2 == "B"

应对:双向评审,分歧的丢弃。

19.3.2 Length Bias(长度偏差)

LLM judge 倾向于选更长的响应。

实测:GPT-4 judge 对长响应的偏好约 +15%。

⚠️ 这个数字存在口径争议,不要当常数用。 上文引用的 21-judge 大规模评测(arXiv:2606.19544)在统一的单一 pairwise 评分 rubric 下测得整个 cohort 的 verbosity bias 均小于 0.011,与”+15%“相差一个量级。两者不必然矛盾——长度偏差对 prompt 措辞、评分是否给出显式 rubric、以及成对比较还是单独打分高度敏感:在开放式无 rubric 的评分里长度偏差可以很大,在约束良好的 pairwise rubric 下可以接近消失。

实践上的结论是:长度偏差的幅度是你自己那套 judge prompt 的属性,不是模型的固有常数,必须在自己的配置上实测。若实测已经很小,长度归一化反而可能引入新的偏差。

应对:

  • prompt 中明确”不要因长度选响应”
  • 长度归一化评分
  • 加入”短而精”的对照
  • 先测再治:在自有 prompt 配置上测出长度偏差幅度,再决定是否需要归一化

19.3.3 Self-preference(自我偏好)

LLM judge 倾向于选同族的响应——GPT-4 偏好 GPT 系列的输出。

实测:GPT-4 judge 评 GPT-4 vs Claude 的输出,GPT-4 胜率虚高 5-10%。

应对:

  • 用多个不同家族的 judge 交叉验证
  • 重要评测用 L3 真人评测校准

19.3.4 格式偏好

LLM judge 偏好特定格式(如 markdown 列表、加粗)。

测试:

# 同一内容,不同格式
plain = "1. 苹果 2. 香蕉 3. 橙子"
markdown = "- **苹果**\n- **香蕉**\n- **橙子**"
# 如果 judge 总是选 markdown,说明有格式偏好

应对:

  • 评分 prompt 中明确”忽略格式”
  • 用多个 judge 风格对冲

极端情况:关键词堆砌可以把 judge 打到不如随机

格式偏好的危害不止于”排版好看的空洞答案得高分”。一份针对合规判定场景的四轴基准(Principle-Bench,arXiv:2608.14329)给出了一个相当刺眼的数字:一个 120B 的开放权重 judge 在正常输入上准确率 0.94–0.96,但面对针对性堆砌合规关键词的输入时,准确率掉 47 个百分点至 0.27——低于随机猜测。作者称之为 “compliance theatre”(合规剧场):文本把该出现的词都摆齐了,judge 就判它合规。

更能说明问题的是同一研究的交叉验证:换一个不同模型家族的 judge 来评同一批被堆砌的样本,两个 judge 的一致性只有 Cohen’s κ = 0.16。分歧集中在被攻击的那一批样本上,说明失效来自模型侧而非语料标注——不是题出得有歧义,是 judge 真的被骗了。

两点实践含义:

  • “剥离格式后重评”应作为默认流程,而不是有空才做的优化项;
  • 模型规模不构成抗攻击的保证。120B 也会栽,且栽得比小模型更隐蔽(它在正常输入上表现最好,最容易被信任)。

引用边界:该基准的语料是 168 个加密资产金融推广场景、映射到英国 FCA 的两条原则,属于特定合规域的窄基准,且作者自述语料由单一模型家族生成、未做外部标注者验证。这里借用的是”对抗扰动能让 judge 崩到随机以下”这一现象与它的四轴测法,不宜把 0.74 → 0.27 当作通用 judge 的普适退化幅度。

19.3.5 把四大陷阱变成可测量项

19.3.1–19.3.4 给的是”有哪些坑 + 怎么绕”,但真正上线时要回答的是一个量化问题:手上这个 judge 到底有多抗偏差,够不够格评这个任务? 两条现成路径:

路径一:四轴可信度体检

Principle-Bench 提出的评估框架有普适价值(哪怕它的语料是窄域的)——任何 judge 上线前都该在四个轴上过一遍:

轴测什么怎么造样本
accuracy(准确性)正常输入上与金标的一致性原始校准集
paraphrase(改写鲁棒性)同义改写后判定是否翻转保义改写
adversarial(对抗鲁棒性)针对性堆砌/诱导后是否被骗关键词堆砌、格式灌水
calibration(校准)置信度是否可信(ECE)原始集 + Platt scaling 等后校准

落地判据:任一轴上的下降超过预设阈值即判定该 judge 不适合当前任务,而不是只看总体准确率达标就放行。该研究在六种方法(关键词计数、三种 embedder、开放权重 judge、校准级联)上的结论是没有任何一种能在四轴上全面占优——这意味着”选一个最强的 judge”往往不成立,需要按轴取舍或做级联。

路径二:偏差注入基准

JudgeBiasBench(arXiv:2603.08091)走的是另一条路:用受控的偏差注入流水线,把 12 类代表性偏差按 4 个维度的分类体系造成评测样本,从而定量比较不同 judge 对各类偏差的敏感度。它同时覆盖生成式与判别式两种 judge 形态——这一点对工程选型有用,因为两者的偏差谱系并不相同。该工作还给出了缓解侧的做法:把偏差相关属性显式引入训练(生成式用 RL、判别式用对比学习),在基本保住通用评估能力的前提下降低偏差。

选用建议:日常迭代用 JudgeBiasBench 这类偏差注入基准做回归(偏差类型全、可自动化);judge 换代或进入高风险场景(合规、安全、对外发布决策)时补一次四轴体检,尤其是对抗轴与校准轴。两者都不能替代 19.1.4 的 L3 真人评测——它们测的是 judge 的鲁棒性,不是模型的真实质量。


19.4 Reward Hacking 检测

19.4.1 Reward Hacking 的表现

RLVR 训练中,模型学会”骗过 verifier”而非真正解决问题:

正确行为:
prompt: "求 12 × 15"
response: "12 × 15 = 180"
reward: 1.0

Hacking 行为:
prompt: "求 12 × 15"
response: "180"  # 直接吐答案,没有推理
reward: 1.0(verifier 只检查最终答案)

19.4.2 检测方法 1:reward 突增告警

class RewardAnomalyDetector:
    def __init__(self, window=100):
        self.history = []
        self.window = window
    
    def update(self, reward):
        self.history.append(reward)
        if len(self.history) < 2 * self.window:
            return False
        
        recent = np.mean(self.history[-self.window:])
        baseline = np.mean(self.history[-2*self.window:-self.window])
        
        # 突增 > 20% 告警
        if recent > baseline * 1.2:
            return True
        return False

19.4.3 检测方法 2:rollout 模式聚类

def detect_hacking(rollouts, threshold=0.5):
    """检测 rollout 是否过于同质化"""
    embeddings = encode(rollouts)
    labels = cluster(embeddings, n_clusters=20)
    
    for label in set(labels):
        ratio = sum(labels == label) / len(labels)
        if ratio > threshold:
            print(f"Warning: cluster {label} 占比 {ratio:.0%}, 疑似 hacking")
            return True
    return False

19.4.4 检测方法 3:Cross-Verifier

def cross_verify(primary_verifier, secondary_verifier, response):
    v1 = primary_verifier(response)
    v2 = secondary_verifier(response)
    
    if v1 == 1 and v2 == 0:
        return "hacking_suspected"
    elif v1 == 0 and v2 == 1:
        return "verifier_bug"
    else:
        return "consistent"

19.4.5 检测方法 4:人审 sample

def human_audit_hacking(rollouts_with_reward_1, sample_size=100):
    """对 reward=1 的 rollout 抽样人审"""
    samples = random.sample(rollouts_with_reward_1, sample_size)
    
    true_positive = 0
    for s in samples:
        # 人审判断是否真的解决问题
        if human_judge(s) == "correct":
            true_positive += 1
    
    accuracy = true_positive / sample_size
    if accuracy < 0.8:
        print(f"Warning: 人审准确率 {accuracy:.0%}, 疑似 hacking")
    return accuracy

19.5 数据归因 TDA

19.5.1 TDA 是什么

TDA(Training Data Attribution)研究”哪些训练数据对某个测试样本贡献最大”。

核心问题:模型在测试样本 x_test 上的正确/错误,是由哪些训练样本导致的?

19.5.2 消融实验(最简单)

def ablation_attribution(train_data, test_data, subset_size=1000):
    """逐子集消融,看哪个子集对测试集影响最大"""
    baseline_score = evaluate(train_on(train_data), test_data)
    
    attributions = []
    for i in range(0, len(train_data), subset_size):
        # 移除第 i 个子集
        reduced = train_data[:i] + train_data[i+subset_size:]
        reduced_score = evaluate(train_on(reduced), test_data)
        
        # 分数下降越多,说明该子集越重要
        attribution = baseline_score - reduced_score
        attributions.append((i, attribution))
    
    return attributions

优点:直观、准确 缺点:成本极高(要训练 N 次)

19.5.3 Influence Function(EK-FAC)

Influence Function 用梯度近似计算”如果移除某条训练数据,测试 loss 会变化多少”:

def influence_function(train_sample, test_sample, model):
    # 1. 计算训练样本的梯度
    train_grad = compute_gradient(model, train_sample)
    
    # 2. 计算测试样本的梯度
    test_grad = compute_gradient(model, test_sample)
    
    # 3. 用 EK-FAC 近似 Hessian 逆
    hessian_inv = ekfac_approximation(model)
    
    # 4. Influence = -test_grad · Hessian_inv · train_grad
    influence = -test_grad @ hessian_inv @ train_grad
    
    return influence

优点:只需训练 1 次 缺点:EK-FAC 近似有误差,对大模型不友好

19.5.4 TracIn

TracIn 用训练过程中的 checkpoint 序列计算影响:

def tracin(train_sample, test_sample, checkpoints):
    """用多个 checkpoint 计算影响"""
    influence = 0
    for ckpt in checkpoints:
        model = load(ckpt)
        train_grad = compute_gradient(model, train_sample)
        test_grad = compute_gradient(model, test_sample)
        influence += np.dot(train_grad, test_grad)
    return influence

优点:实现简单,可并行 缺点:需要保存多个 checkpoint

19.5.5 两条新路径(研究阶段)

19.5.2–19.5.4 的三种方法有一个共同的隐含前提:你得先指定一个归因目标——某个测试样本、某个验证集、某个下游任务。这在”MMLU 掉分了,去找是哪批数据的问题”这种场景下没问题,但预训练阶段常常反过来:你想知道某批语料在整个训练过程中起了什么作用,而”代表通用能力的验证集”本身就选不出来。2026 年出现了两条绕开这个前提的路径,都还在研究阶段。

(1)task-agnostic 归因:把”最终参数”当作归因目标

arXiv:2608.13515 换了个定义:一条样本的影响力 = 它的梯度更新让参数向该次预训练最终参数靠近了多少(平方距离的减少量)。目标从”某个任务的 loss”换成”这次训练自己的终点”,于是不需要挑验证集,也不需要重训——直接用训练时已经存下的中间 checkpoint 估计。

在 Pythia 与 PolyPythia 系列的 18 组配置上,它给出了一个跨配置稳定的时序结论:训练早期文学类数据与参数轨迹的对齐度更高,后期换成 STEM 类数据更高。这个”交叉点”对第 9 章的课程设计是直接的证据——数据配比该随训练阶段变,而不是全程一个混合比例。

成熟度:验证规模止于 Pythia 系列(最大 12B 量级的稠密模型),尚未见到在前沿 MoE 规模上的复现。“最终参数”作为归因目标也有个哲学上的循环——它衡量的是”对这次训练的贡献”,而不是”对模型好不好的贡献”,若这次训练本身跑偏了,高影响力数据也可能是坏数据。用它做训练动态分析可以,用它做数据取舍需谨慎。

(2)forward-only 归因:把算力从查询时挪到训练时

19.5.3 的工程痛点是 Influence Function 要在推理时做反向传播和 Hessian 近似,每来一个查询都得付一次。arXiv:2511.19803 的做法是把这笔计算搬到训练期:训练时用短程梯度传播模拟每条样本的参数影响并存下来,之后任何查询只用前向评估即可读出归因结果。它保留了与 Influence Function 相同的一阶反事实目标,但去掉了 per-query 的反向传播。

这个取舍匹配一类真实场景:模型可能服务数十亿次查询,但训练数据源是固定有限的——比如一个 LLM 要向特定出版方(论文举的例子是《纽约时报》)核算贡献并付费,归因需要能反复、实时地跑。

⚠️ 引用边界要看清:该论文报告”在 LOO / LDS 指标上匹配或超过 TRAK,且推理成本低数个量级”,但实验是在标准 MLP 基准上做的,不是语言模型。它目前是一个有理论框架支撑的可扩展性主张,而非已在 LLM 规模上验证的替代方案。不要因为”免反向传播”就把它当成 EK-FAC 在大模型上的现成替代品。

当前选用建议:生产归因仍以 19.5.2–19.5.4 为主(消融做关键决策、EK-FAC 做大规模、TracIn 做轻量筛查);上面两条适合作为研究性探索或训练动态分析的补充手段,纳入生产前需要自己在目标规模上验证一遍。

19.5.6 TDA 的应用

  1. 找”毒数据”:测试集掉分时,用 TDA 找出贡献为负的训练样本
  2. 数据去重:TDA 分数相近的样本冗余,去除其一
  3. 数据增强:TDA 分数高的样本模式,用类似模式合成更多数据
  4. 冷启动:新能力快速找到”高价值”训练样本

19.6 数据-训练-评估闭环

19.6.1 闭环的核心

数据-训练-评估闭环是工业级团队的”肌肉记忆”:

评测发现问题 → 数据归因 → 数据调整 → 训练 → 再评测

19.6.2 典型闭环场景

场景 1:评测掉分 → 找数据 → 加量改质

现象: MMLU 从 70 掉到 65
归因: TDA 显示新增数据中某些知识类样本质量差
调整: 移除低质量样本 + 补充高质量知识类数据
再评测: MMLU 恢复到 71

场景 2:某评测涨太多 → 检查污染

现象: GSM8K 从 75 暴涨到 95
排查: 用 13-gram 检查训练数据是否含 GSM8K 测试集
发现: 新加入的某开源数据集含 GSM8K 测试集
处理: 移除污染数据,重新训练

经验:任何评测分数”不正常暴涨”都要警惕污染。

场景 3:DPO 长度漂移 → 加 debias

现象: DPO 后输出平均长度从 200 涨到 350
归因: 偏好数据中 chosen 普遍比 rejected 长
调整: 加入长度去偏数据(短而精胜过长而水)
再评测: 长度恢复到 220

场景 4:RL reward 暴涨 → 检查 hacking

现象: RL 训练 200 步后 reward 从 0.5 暴涨到 0.95
排查: 用 Cross-Verifier 检查 rollout
发现: 模型学会"输出特定格式骗过 verifier"
处理: 加强 verifier + 加入 reward 平滑项
再训练: reward 平稳增长到 0.7

19.6.3 闭环的周期

工业级闭环周期:

  • 快闭环:1-2 天(L1 评测 + 数据调整 + 再训练)
  • 中闭环:1-2 周(L2 评测 + 配方调整 + 完整训练)
  • 慢闭环:1-2 月(L3 评测 + 重大配方调整 + 发版)

每个团队应该同时运行三个闭环,对应不同节奏。


19.7 数据卡片 Datasheet

19.7.1 Datasheet 的内容

数据卡片(Datasheet for Datasets, Gebru et al., 2021)是数据集的”出厂说明书”:

# 数据卡片示例
name: " aide-sft-v3"
version: "3.0"
date: "2026-07-06"

# 动机
purpose: "用于 SFT 训练通用对话能力"
funding: "内部研发"

# 构成
composition: |
  - 总量: 939K
  - 任务分布: 通用30% / 数学20% / 代码15% / 安全10% / 指令10% / 多语5% / 知识5% / 工具5%
  - 语言: 中文 40% / 英文 50% / 其他 10%

# 采集
collection_process: |
  - 70% Magpie 合成(Llama-3-70B 生成)
  - 20% Evol-Instruct 进化
  - 10% 人工标注
collection_time: "2026-Q2"

# 预处理
preprocessing: |
  - 格式归一化到 messages 结构
  - PII 脱敏(Presidio)
  - 13-gram 去污染(覆盖 20+ 评测集)
  - Deita Selector 精选

# 质量
quality_assessment: |
  - IFD: 0.5 平均
  - 4-gram 多样性: 0.68
  - InsTag 覆盖: 1240
  - 拒答率: 7%

# 已知局限
limitations: |
  - 偏向中英文,小语种覆盖不足
  - 数学数据难度偏低
  - 合成数据有 GPT-4 风格偏置

# 推荐/禁忌
recommended_use: "通用 SFT 训练"
prohibited_use: "不可用于医疗/法律等专业领域"

19.7.2 Datasheet 的工业价值

  1. 可追溯:训练出问题时能找到数据源头
  2. 可复现:他人能用同样数据复现训练
  3. 可审计:合规审计时能证明数据合法
  4. 可对比:版本间能清楚对比数据变化

19.8 评估闭环工业 SOP

1. 三层评估体系搭建:
   - L1 自动评测(每 ckpt)
   - L2 LLM-as-Judge(每周)
   - L3 真人评测(每月)

2. 评测集管理:
   - 必扫 20+ 评测集
   - 抗污染评测(LiveCodeBench/AIME)
   - 多维度覆盖

3. LLM-as-Judge 偏差控制:
   - 一致率报 Cohen's κ,不报 raw agreement
   - 双向评审去 Position Bias
   - 先实测长度偏差幅度,再决定是否长度归一化
   - 多 judge 交叉去 Self-preference
   - 剥离格式后重评(默认执行,非可选)
   - 换代/高风险场景补四轴可信度体检

4. Reward Hacking 检测:
   - reward 突增告警
   - rollout 聚类
   - Cross-Verifier
   - 人审 sample

5. 数据归因 TDA:
   - 消融实验(关键决策)
   - Influence Function(大规模)
   - TracIn(轻量级)
   - task-agnostic / forward-only(研究阶段,需自行验证)

6. 闭环执行:
   - 快闭环 1-2 天
   - 中闭环 1-2 周
   - 慢闭环 1-2 月

7. 数据卡片:
   - 每个版本必填
   - 包含动机/构成/采集/质量/局限

第 20 章将展望工具栈、典型案例与未来方向——把全书串成完整图景。