章节导航 第 29 章 / 共 30 章
数据质量评估闭环与迭代
以评估驱动的数据迭代闭环:指标、看板、回归与持续优化。
没有闭环的数据工程是盲修。
评测掉分 → 找数据 → 改质加量 → 再评测,是工业级团队的肌肉记忆。
“You can’t improve what you can’t measure.” —— Peter Drucker
在数据工程中,这句话需要补充一个前提:你还得知道该测什么。
29.1 评估的认知基础:数据好不好,不能只看 Loss
29.1.1 训练 Loss 不等于数据质量
这是一个需要反复强调的认知:预训练 loss 的下降不代表数据质量好。
训练 loss 本质上衡量的是模型对当前数据分布的拟合程度。如果你的训练数据充斥着重复的模板化内容(比如电商网站千篇一律的产品描述),模型的 loss 会下降得很漂亮——因为这些数据的熵很低,容易预测。但这样训练出的模型在开放式生成任务上会表现得索然无味。
反过来,如果你的数据多样性极高、包含大量专业领域的低频知识,训练 loss 可能下降得很慢,但模型的下游表现可能非常好。
因此,评估数据质量需要跳出”盯着 loss 曲线”的思维定式,建立一套多维度的评估体系,并通过下游任务验证来闭环。
29.1.2 数据质量对 Scaling Laws 的量化影响
“数据不好会影响模型表现”是个直觉。但具体影响多大?有没有定量模型?ACL 2025 的一项分析 400 多个模型训练数据的大规模实证研究揭示了一个关键现象——次级缩放(sub-optimal scaling):当训练数据中冗余信息(高数据密度)过多时,增加数据量或模型规模带来的性能提升会显著减速,偏离经典 Scaling Law 预测的幂律曲线。
这给了我们一个有用的心智模型:
- 经典 Scaling Law 假设数据是”足够好的”——只要加量就能提升
- 次级缩放定律 修正了这个假设——数据冗余会让 scaling 变”钝”,你以为加了 2 倍数据应该提升 X%,实际只提升了 0.6X%
- 实践含义:在盯着”数据量够不够”之前,先检查”数据重复度高不高”。后文的重复率统计不只是监控指标,而是直接关系到 scaling 效率的核心变量
这也解释了为什么去重在整个数据管线中的地位如此关键——它不只是”清理噪声”,而是在维护 Scaling Law 的有效性。
29.1.3 从”多指标并列”到”多信号融合”
建立”多维度评估体系”时会遇到一个自然的问题:当你有文本复杂性、信息量、格式规范性、教育价值等七八个质量维度的分数时,如何把它们合成一个决策?
最朴素的做法是设阈值——每个维度都过了就留下。但这要么太严(所有维度都高的数据很少),要么太松(只要有一个维度高就留下)。实际操作中,大量时间花在了调多个阈值的组合上。
FIRE(Flexible Integration of Data Quality Ratings,EMNLP 2025)提出了一种更系统的方案:将多个异构质量评级器对齐到统一的特征空间,通过渐进式数据选择进行迭代精炼。实验表明,FIRE 用不到 37.5% 的数据量即可达到随机选择全量数据的同等性能。
这代表了数据评估的一个范式升级——从”在 dashboard 上看多个指标然后拍脑袋”走向”让框架自动学习多信号的最优融合权重”。监控系统负责采集各维度指标,而 FIRE 这类框架负责将指标转化为数据选择决策。
29.2 评估指标体系:三层架构与评测集
29.2.1 三层评估体系
工业级评估体系分三层,按频率与成本递增:
┌──────────────────────────────────┐
│ L3 真人评测(每月) 成本极高 │
├──────────────────────────────────┤
│ L2 LLM-as-Judge(每周)成本中 │
├──────────────────────────────────┤
│ L1 自动评测(每 ckpt) 成本低 │
└──────────────────────────────────┘
三层并非互斥,而是相互校准:L1 提供高频信号,L2 填补生成质量的评估空白,L3 提供金标并校准前两层。任何一层缺失都会让闭环失真——只有 L1 会漏掉开放式生成的退化,只有 L3 则反馈周期太慢。
29.2.2 L1:自动评测(每 checkpoint)
频率:每个训练 checkpoint(约每 1000 steps) 成本:单次 < 1 小时,< 1 美元 用途:快速筛选 checkpoint,监控训练趋势
评测集:
- MMLU(5-shot,4 选 1)
- GSM8K(5-shot CoT)
- HumanEval(pass@1)
- IFEval(指令遵循准确率)
特点:
- 全自动,无人介入
- 分数稳定可复现
- 适合横向对比 checkpoint
29.2.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 等强模型
- 分数有偏差(详见 29.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 左右。
工程含义有三条:
- 报 κ,不报 raw。校准集上必须给出 Cohen’s κ(或等价的机会校正指标);若沿用 raw agreement,“>85% 可接受”这类阈值会把一个实际中等水平的 judge 判为达标。
- 阈值要自己测,不要抄。κ 的绝对值随任务类别数与标签分布变化,跨任务不可移植。阈值应在自有校准集上重新测定,并把测定过程写进数据卡片。
- 一致 ≠ 无偏。该研究报告了一个”一致性—偏差悖论”:两个已投产的 judge 在 test–retest 上信度 > 0.95,却同时带有 > 0.10 的位置偏差。稳定复现的 judge 也可能稳定地错,因此偏差审计不能因为”重测很稳”就省略。
同一研究还指出 judge 在不同 benchmark 上的排名最多能挪动 14 位——“哪个 judge 最好”依赖于用哪个评测集来问,选 judge 时应当在自己实际要评的分布上测,而不是照搬公开榜单。
29.2.4 L3:真人评测(每月)
频率:每月一次(或发版前) 成本:单次数千-数万美元 用途:金标评测,校准 L1/L2
流程:
- 50-100 名标注员
- 每人评 100-500 条
- A/B 对比(与基线模型)
- 双盲交叉校验
特点:
- 最接近真实用户体验
- 慢、贵、不可复现
- 用于最终发布决策
29.2.5 关键评测集速查
| 评测集 | 类型 | 规模 | 指标 | 频率 |
|---|---|---|---|---|
| MMLU | 学术知识 | 14K 题 | 4 选 1 准确率 | L1 |
| MMLU-Pro | 学术知识+ | 12K 题 | 10 选 1 准确率 | L1 |
| GSM8K | 数学 | 1.3K 题 | CoT 准确率 | L1 |
| MATH | 数学 | 5K 题 | 准确率 | L1 |
| HumanEval | 代码 | 164 题 | pass@1 | L1 |
| MBPP | 代码 | 974 题 | pass@1 | L1 |
| IFEval | 指令遵循 | 500+ | 准确率 | L1 |
| MT-Bench | 多轮对话 | 80 | GPT-4 评分 | L2 |
| AlpacaEval 2.0 | 通用 | 805 | 胜率 | L2 |
| Arena-Hard | 通用 | 500 | 胜率 | L2 |
| TruthfulQA | 诚实 | 817 | 真实率 | L1 |
| XSTest | 安全 | 250 | 拒答率 | L1 |
| τ-Bench | Agent | 165 | 任务成功率 | L2 |
| BFCL | 工具调用 | 2000+ | 准确率 | L2 |
| LiveCodeBench | 代码(抗污染) | 周更新 | pass@1 | L1 |
评测集选用原则:
- 抗污染优先。MMLU、GSM8K 等老评测集污染严重,必须配合 LiveCodeBench、AIME 等持续更新的评测。
- 多维度覆盖。单测一个维度(如只测 MMLU)会过拟合,必须测知识/推理/代码/安全/工具至少 5 个维度。
- 量级匹配。小模型(< 7B)用 GSM8K,大模型(> 70B)必须用 AIME 等更难的评测。
29.2.6 Proxy Benchmark 的选择
不是所有 benchmark 都适合用来评估数据质量。好的 proxy benchmark 应该满足:
- 对数据变化敏感:数据管线的微调能在该 benchmark 上体现出差异
- 评估稳定:多次运行结果波动小
- 覆盖多能力维度:不偏向某个特定领域
- 不容易被”刷”:对过拟合有一定抵抗力
FineWeb 团队的 benchmark 组合(经过实践验证):
| Benchmark | 评估能力 | 数据敏感度 | 稳定性 |
|---|---|---|---|
| HellaSwag | 常识推理 | 高 | 高 |
| ARC-Challenge | 科学推理 | 高 | 中 |
| WinoGrande | 语义理解 | 中 | 中 |
| PIQA | 物理直觉 | 中 | 高 |
| MMLU (5-shot) | 知识广度 | 中 | 中 |
| OpenBookQA | 开放知识推理 | 高 | 中 |
对于中文场景,建议加入:
| Benchmark | 评估能力 | 备注 |
|---|---|---|
| C-Eval | 中文知识广度 | 类似中文 MMLU |
| CMMLU | 中文多学科 | 覆盖更广 |
| CLUE-WSC | 中文指代消解 | 中文语言理解 |
29.2.7 数据统计与可视化:训练前的全貌检查
在正式训练之前,你需要对数据的全貌有清晰的了解。一个好的数据 dashboard 应该覆盖以下维度。
1. 语言分布
"""
数据语言分布统计与可视化
"""
from collections import Counter
from typing import List
def analyze_language_distribution(
lang_labels: List[str],
lang_scores: List[float],
target_distribution: dict = None
) -> dict:
"""分析语言分布并与目标分布对比"""
total = len(lang_labels)
actual_dist = Counter(lang_labels)
actual_pct = {lang: count / total * 100
for lang, count in actual_dist.most_common(20)}
report = {
"total_documents": total,
"unique_languages": len(actual_dist),
"top_20_languages": actual_pct,
}
# 与目标分布对比
if target_distribution:
gaps = {}
for lang, target_pct in target_distribution.items():
actual = actual_pct.get(lang, 0)
gap = actual - target_pct
gaps[lang] = {
"target": target_pct,
"actual": round(actual, 2),
"gap": round(gap, 2),
"status": "OK" if abs(gap) < 2 else ("WARN" if abs(gap) < 5 else "FAIL")
}
report["distribution_gaps"] = gaps
# 低置信度文档比例
low_confidence = sum(1 for s in lang_scores if s < 0.8) / total * 100
report["low_confidence_pct"] = round(low_confidence, 2)
return report
2. 文档长度分布
长度分布是数据质量的重要晴雨表。典型的健康分布应该呈对数正态分布,如果出现异常的双峰或长尾,往往暗示着数据问题。
import numpy as np
def analyze_length_distribution(lengths: list, bins: int = 50) -> dict:
"""分析文档长度分布"""
lengths = np.array(lengths)
stats = {
"count": len(lengths),
"mean": float(np.mean(lengths)),
"median": float(np.median(lengths)),
"std": float(np.std(lengths)),
"min": int(np.min(lengths)),
"max": int(np.max(lengths)),
"p10": float(np.percentile(lengths, 10)),
"p90": float(np.percentile(lengths, 90)),
"p99": float(np.percentile(lengths, 99)),
}
# 异常检测
# 1. 极短文档比例(可能是提取失败)
very_short = (lengths < 50).sum() / len(lengths) * 100
stats["very_short_pct"] = round(very_short, 2)
# 2. 极长文档比例(可能是拼接错误)
very_long = (lengths > 50000).sum() / len(lengths) * 100
stats["very_long_pct"] = round(very_long, 2)
# 3. 可疑的"精确长度"聚集(可能是截断或模板)
length_counts = Counter(lengths)
suspicious_peaks = [
(length, count) for length, count in length_counts.most_common(20)
if count > len(lengths) * 0.01 # 超过 1% 的文档长度完全相同
]
stats["suspicious_length_peaks"] = suspicious_peaks
return stats
3. 质量分数分布
def analyze_quality_distribution(quality_scores: list, labels: list = None) -> dict:
"""分析质量分数分布,可按领域细分"""
scores = np.array(quality_scores)
overall = {
"mean": float(np.mean(scores)),
"median": float(np.median(scores)),
"std": float(np.std(scores)),
"below_0.3": float((scores < 0.3).sum() / len(scores) * 100),
"between_0.3_0.7": float(((scores >= 0.3) & (scores < 0.7)).sum() / len(scores) * 100),
"above_0.7": float((scores >= 0.7).sum() / len(scores) * 100),
}
if labels:
by_domain = {}
for label in set(labels):
domain_scores = [s for s, l in zip(quality_scores, labels) if l == label]
by_domain[label] = {
"count": len(domain_scores),
"mean": round(np.mean(domain_scores), 3),
"median": round(np.median(domain_scores), 3),
}
overall["by_domain"] = by_domain
return overall
4. 重复率统计
def analyze_duplication_stats(
docs: List[str],
n_gram_size: int = 5,
sample_size: int = 10000
) -> dict:
"""快速评估数据集的重复程度:采样 + n-gram 统计"""
import random
if len(docs) > sample_size:
sample = random.sample(docs, sample_size)
else:
sample = docs
# 文档级精确重复
doc_hashes = Counter()
for doc in sample:
doc_hash = hash(doc.strip())
doc_hashes[doc_hash] += 1
exact_dup_rate = sum(c - 1 for c in doc_hashes.values() if c > 1) / len(sample) * 100
# N-gram 重复率
all_ngrams = Counter()
for doc in sample:
tokens = doc.split()
for i in range(len(tokens) - n_gram_size + 1):
ngram = tuple(tokens[i:i + n_gram_size])
all_ngrams[ngram] += 1
total_ngrams = sum(all_ngrams.values())
unique_ngrams = len(all_ngrams)
ngram_dup_rate = (1 - unique_ngrams / total_ngrams) * 100 if total_ngrams > 0 else 0
return {
"exact_document_dup_rate": round(exact_dup_rate, 2),
f"{n_gram_size}-gram_dup_rate": round(ngram_dup_rate, 2),
"unique_ngram_ratio": round(unique_ngrams / total_ngrams, 4) if total_ngrams > 0 else 0,
"total_ngrams_sampled": total_ngrams,
"sample_size": len(sample),
}
29.2.8 数据管线中的质量监控点
一条成熟的数据管线应该在关键节点设置质量监控。以下是推荐的监控点和对应的异常检测规则:
| 管线阶段 | 监控指标 | 正常范围 | 异常信号 |
|---|---|---|---|
| 爬取/下载 | 下载成功率 | >95% | 某个 CC 快照成功率骤降 |
| HTML 提取 | 空文档比例 | <5% | 提取器对某类网页失效 |
| 语言识别 | 低置信度比例 | <15% | 混合语言页面增多 |
| 启发式过滤 | 过滤掉的比例 | 30-60% | 过低=过滤太松,过高=过滤太严 |
| 去重 | 去重率 | 20-40% | 过高=数据源重复度大 |
| 质量过滤 | 保留比例 | 20-50% | 分布发生显著漂移 |
| 最终数据集 | 平均文档长度 | 500-3000 tokens | 突然变短/变长 |
29.3 LLM-as-Judge 的偏差治理
L2 层依赖 LLM-as-Judge,但 judge 本身有系统性的偏差。上线前必须理解并治理四大陷阱。
29.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"
应对:双向评审,分歧的丢弃。
幅度能大到什么程度? 21 个 judge 的 MT-Bench 偏差审计(arXiv:2606.19544,Table 3)给出了全 cohort 的实测分布,位置偏差定义为 (AB/BA 双向评估):最低的 Gemini 2.5 Pro 为 0.002,最高的 Qwen 3 8B 为 0.192,原文的措辞是”span nearly two orders of magnitude”。0.192 意味着接近五分之一的判定由呈现顺序决定,而不是由内容质量决定——这是双向评审属于必做项而非可选优化的实测依据。
同一批数据里还有一条对选型更有用的观察:同家族内部的差距可以极大,Gemini 2.5 Pro(0.002)与 Gemini 2.5 Flash(0.125)相差 70 倍。按品牌或按系列挑 judge 是靠不住的。原文的总体趋势是推理型与前沿档位偏差更低、成本优化档位偏差更高,但这个趋势并不单调——GPT-5.4-mini(0.013)就明显低于 GPT-5.4(0.083),档位高低不能直接换算成偏差大小。
29.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 的属性,不是模型的固有常数,必须在自己的配置上实测。若实测已经很小,长度归一化反而可能引入新的偏差。
该论文给出的四条实践建议里,第 (4) 条写得很直白:在标准 pairwise rubric 下”no longer foreground verbosity bias”。但作者在同一段与 Limitations 里划了两道范围——结论只成立于单一 pairwise 模板与固定的长度差操作化之下,原文是”we do not claim that verbosity bias has been eliminated under arbitrary rubric or task variation”,且 rubric 设计(是否给参考答案、是否要求推理)已知会与偏差轮廓交互。“先测再治”的口径不变,变化的是读者现在对实测会得到什么有了先验:大概率测出来很小,于是结论多半是不归一化。
应对:
- prompt 中明确”不要因长度选响应”
- 长度归一化评分
- 加入”短而精”的对照
- 先测再治:在自有 prompt 配置上测出长度偏差幅度,再决定是否需要归一化
29.3.3 Self-preference(自我偏好)
LLM judge 倾向于选同族的响应——GPT-4 偏好 GPT 系列的输出。实测 GPT-4 judge 评 GPT-4 vs Claude 的输出,GPT-4 胜率虚高 5-10%。
应对:
- 用多个不同家族的 judge 交叉验证
- 重要评测用 L3 真人评测校准
29.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 的普适退化幅度。
29.3.5 把四大陷阱变成可测量项
上述四节给的是”有哪些坑 + 怎么绕”,但真正上线时要回答的是一个量化问题:手上这个 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 换代或进入高风险场景(合规、安全、对外发布决策)时补一次四轴体检,尤其是对抗轴与校准轴。两者都不能替代 L3 真人评测——它们测的是 judge 的鲁棒性,不是模型的真实质量。
路径三:把条目质量拆成两个不同的属性
上面两条测的都是 judge 的抗偏差能力,还有一层常被混为一谈——rubric 条目(或评测题目)自己的质量。CalibratedRubric(arXiv:2607.29252)指出,自动构造 rubric 的常规流程依赖”judge 全体一致”加二元方差过滤,而这两个筛子分不开”可测量”与”有信息量”:一条所有人都能稳定判对的条目可测量性极高,信息量却可以是零,因为它区分不出任何能力差异。它的做法是把两件事拆开估——可测量性用 Beta–Bernoulli 一致性后验估计每个 rubric 的 measurability,信息量用 IRT 下的子模覆盖目标挑选条目、组装紧凑的 rubric bank。实测收益是 JudgmentBench 上与人类金标的 κ 从 0.604 提到 0.743,FinResearchBench 上只需 49 条 rubric 就达到随机选 131 条的相关性目标。
对本章的用处:“这条测得准”与”这条值得测”是两个问题。29.2.6 挑 proxy benchmark 的四条原则里,“评估稳定”对应可测量性,“对数据变化敏感”对应信息量,两者要分开测,一个总体准确率替代不了。
路径四:把三项同时成立的联合概率当作准入指标
前面三条路径都在分别测某一个属性(抗偏差、可测量性、信息量)。但一个 judge 要”可信”,需要三件事同时对同一批复判成立:复现性(同样的输入再判一次还一样)、顺序不变性(把 A/B 对调不影响结果)、准确性(与金标一致)。三者缺一不可,分开测会漏掉它们的交集。
trustworthy verdict rate(T)就是把这三者合成一个量的指标——一次判定同时满足复现、顺序不变、准确这三者的联合概率(arXiv:2610.12083,AACL-IJCNLP 2026 Main)。该研究的压力测试覆盖 6 个前沿模型 × 4 个基准 × 5 种 prompt 格式 × 2 种呈现顺序 × 3 个采样温度 × 每条件 10 次重复,三条实测结论值得记:
- 温度设为 0 也挡不住改判——同一批复判在 temperature=0 下仍会翻转,“设成 0 就确定性了”是错觉;
- 位置对调会翻掉 challenging 任务上的多数判决——顺序不变性是独立的一条轴,不是准确性的附属品;
- 最”确定性”的那个 judge 是靠稳定地重复错误判决拿到完美一致性的,与金标只有 51% 的一致率。
第 3 条正是 29.2.3 已经警告过的”一致性—偏差悖论”的量化形态:重测信度高不能作为放行依据,因为存在”稳定地错”这个失效模式,且它会把一致性指标拉满。T 的另一个用处是能推出位置偏差对准确率施加的理论上界——顺序不变性不达标时,准确率再高也有天花板,这条把 29.3.1 的位置偏差从”要治的毛病”升级成”对准确率有硬性上限的约束”。
由 T 还带出两条可直接执行的改进:
- 把 pairwise 胜率换成 holistic rubric 打分,对可信度的提升大于任何单一 prompt 格式层面的干预——即”改判分范式”比”调 prompt”划算;
- 粒度下探一层:可靠性是逐条样本(item-specific)的属性,不是模型级的。因此准入不应只报一个总体值,而应按 item 分桶看。这是对 29.2.3 末句”哪个 judge 最好依赖用哪个评测集”的再细分——不止要按评测集分,还要按条目分。
29.4 Reward Hacking 检测
RLVR 训练中,模型可能学会”骗过 verifier”而非真正解决问题:
正确行为:
prompt: "求 12 × 15"
response: "12 × 15 = 180"
reward: 1.0
Hacking 行为:
prompt: "求 12 × 15"
response: "180" # 直接吐答案,没有推理
reward: 1.0(verifier 只检查最终答案)
Reward hacking 与数据污染、judge 偏差并称评估闭环的三大”信号失真源”——分数涨了,能力没涨。以下是四种互补的检测方法。
29.4.1 检测方法 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
29.4.2 检测方法 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
29.4.3 检测方法 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"
29.4.4 检测方法 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
29.5 消融实验与数据归因
评估指标告诉你”好不好”,消融与归因告诉你”为什么”。这是从评估走向数据调整的桥梁。
29.5.1 小模型快速验证法
大模型预训练动辄需要数千到数万 GPU·小时。你不可能每次调整一个过滤阈值就重新跑一遍完整训练。因此,业界广泛采用”小模型代理”方案:用一个参数量小得多的模型(通常 400M-1.5B)在你的数据上快速训练,然后评估这个小模型的表现来间接判断数据质量。
基本流程:
数据管线 A ──→ 训练 400M 模型 ──→ 在 proxy benchmark 上评估
↓
数据管线 B ──→ 训练 400M 模型 ──→ 在 proxy benchmark 上评估
↓
比较 A vs B,选优
↓
用最优管线训练大模型
FineWeb 团队正是用这种方法系统化地优化了他们的数据管线。他们训练了超过几十个 1.8B 参数的模型,每个模型训练 28B token(大约需要几天时间),然后在一组精心挑选的 benchmark 上比较不同数据管线的效果。
实操:搭建一个小模型验证流程
"""
小模型快速验证框架
用于对比不同数据管线的效果
"""
import json
from dataclasses import dataclass, field
from typing import List, Dict
@dataclass
class DataPipelineConfig:
"""数据管线配置"""
name: str
description: str
# 关键参数
text_extractor: str = "trafilatura" # 提取器
lang_threshold: float = 0.65 # 语言识别阈值
min_doc_length: int = 200 # 最短文档长度
max_doc_length: int = 100000 # 最长文档长度
dedup_threshold: float = 0.8 # 去重阈值
quality_filter: str = "fasttext" # 质量分类器
quality_threshold: float = 0.5 # 质量阈值
# 配比
domain_weights: Dict[str, float] = field(default_factory=lambda: {
"web": 0.65, "code": 0.15, "academic": 0.10,
"books": 0.05, "math": 0.05
})
@dataclass
class AblationExperiment:
"""消融实验配置"""
pipeline_config: DataPipelineConfig
model_size: str = "400M" # 代理模型大小
training_tokens: str = "10B" # 训练 token 数
eval_benchmarks: List[str] = field(default_factory=lambda: [
"hellaswag", "arc_challenge", "winogrande",
"piqa", "mmlu_5shot", "ceval"
])
def design_ablation_matrix(base_config: DataPipelineConfig) -> List[AblationExperiment]:
"""
设计消融实验矩阵
核心原则:每次只变一个变量
"""
experiments = []
# 基准实验
experiments.append(AblationExperiment(
pipeline_config=base_config
))
# 变量 1:语言识别阈值
for threshold in [0.5, 0.6, 0.7, 0.8]:
config = DataPipelineConfig(
**{**base_config.__dict__,
"name": f"lang_threshold_{threshold}",
"lang_threshold": threshold}
)
experiments.append(AblationExperiment(pipeline_config=config))
# 变量 2:质量过滤阈值
for threshold in [0.3, 0.5, 0.7, 0.9]:
config = DataPipelineConfig(
**{**base_config.__dict__,
"name": f"quality_threshold_{threshold}",
"quality_threshold": threshold}
)
experiments.append(AblationExperiment(pipeline_config=config))
# 变量 3:去重阈值
for threshold in [0.7, 0.8, 0.9, 1.0]:
config = DataPipelineConfig(
**{**base_config.__dict__,
"name": f"dedup_threshold_{threshold}",
"dedup_threshold": threshold}
)
experiments.append(AblationExperiment(pipeline_config=config))
return experiments
def analyze_results(results: Dict[str, Dict[str, float]]) -> str:
"""
分析消融实验结果
results: {experiment_name: {benchmark: score}}
"""
report = []
report.append("=" * 60)
report.append("消融实验结果分析")
report.append("=" * 60)
baseline = results.get("baseline", {})
if not baseline:
return "Error: no baseline found"
for exp_name, scores in results.items():
if exp_name == "baseline":
continue
deltas = {}
for bench, score in scores.items():
if bench in baseline:
delta = score - baseline[bench]
deltas[bench] = delta
avg_delta = sum(deltas.values()) / len(deltas) if deltas else 0
report.append(f"\n{exp_name}: avg delta = {avg_delta:+.2f}%")
for bench, delta in sorted(deltas.items(), key=lambda x: abs(x[1]), reverse=True):
direction = "↑" if delta > 0 else "↓"
report.append(f" {bench}: {delta:+.2f}% {direction}")
return "\n".join(report)
29.5.1b 不训练也能预判:分布对齐评分 DAS
29.5.1 整节的方法有一个共同前提:必须先训练一个代理模型。FineWeb 团队为了定一个去重阈值训了几十个 1.8B 模型、每个 28B token。候选数据集少时这可行,但当面对「上百个候选数据源,先筛出值得训的十个」这类问题时,代理模型本身就成了瓶颈——你要为每一个候选都付一次训练成本。
DataPrep-Bench(北大 OpenDCAI 联合港中文、浙大、上海算法创新研究院发布)为此给出了一条零训练成本的路径:DAS(Distributional Alignment Score)。
做法只有三步:提取候选集的文本特征 → 与目标领域的代理集比对 → 用 MMD(最大均值差异) 计算分布距离。距离越小,判定训练价值越高。
实测表现(以与真实训练收益的相关系数 r 计,跨 6 个域):
- 6 个域中 4 个取得最强跨模型相关性
- 是唯一在 Math / Science / Medical 三个域上同时做到 r > 0.70 的指标,超过所有参评的质量 / 多样性 / 启发式评估器
- ⚠️ 金融域是它的天花板:r 仅 0.42–0.50,且所有参评指标在该域集体失效
适用边界:DAS 是排序器,不是裁判。它能帮你在零算力成本下把候选池从 100 缩到 10,但不能替代 29.5.1 的代理模型做最终决策——尤其在金融这类 DAS 失效的域上。
📌 与 29.5.1 的关系:串联而非替代。 DAS 做第一轮粗筛(零成本),代理模型做第二轮精选(花算力)。这与篇一 ch00「廉价过滤优先于昂贵过滤」是同一条纪律在评估环节的体现——先便宜后昂贵。29.5.2 即将讨论的「小模型代理的可靠性问题」,恰好也说明了为什么不能只靠代理模型一条腿走路。
⚠️ 待核实:DataPrep-Bench 的 6 个域是否含中文,本书本轮未确认。若不含,在中文语料上引用 DAS 时须注明「未见中文域验证」。
29.5.2 小模型代理的可靠性问题
2025 年底,一篇引发广泛讨论的论文《Can Small Training Runs Reliably Guide Data Curation?》(arXiv: 2512.24503)对小模型代理方法提出了系统性质疑。
⚠️ 本节前一版把这篇论文的机制与补丁都记错了,以下是订正后的版本。 原版写的是「排名在不同随机种子下不稳定」,并据此开出「每个管线跑 3 个种子」的对策。回源 arXiv:2512.24503 全文(v1 HTML,2025-12-30)后,论文的主因不是种子方差,而是“为了公平比较,所有候选配方都用同一套固定超参”这个协议本身;种子/随机性是论文明确讨论的次要项(§2 脚注指向附录 D.2.1,正文图表的阴影区是 3 个种子的标准误),不是主因。机制记错会连带着把补丁也开错——按原版写法,预算会花在”多跑几个种子”上,而真正的修复几乎不花钱。
现象:用标准学习率(GPT2-Small 上常用的 )训练代理模型时,代理规模上的数据配方排名与”每个配方各自调过超参的大模型”上的排名严重不一致,出现大量会导致次优决策的重新排序。
机制:最优训练配置本身是数据依赖的——batch size、学习率、优化器在实际工程中都是按数据调的(论文举的例子是 GPT-3 按梯度噪声尺度定 batch size)。因此数据配方评估的正确目标应该是”该配方在它自己的最优超参下能达到的性能”,而不是”在某个预设的、可能对这份数据并不最优的超参下的性能”。固定超参协议与大模型开发流程也相背离——后者把超参寻优当作标准步骤。实测的敏感性:学习率的微小调整就能翻转配方排名。论文用 DCLM 与其更激进去重变体 DCLM-dedup-GS 做了对照,在”接近最优的两个相似学习率”之间切换,胜出的配方就会反转;在 batch size / weight decay / token-per-parameter 比的扫描中,排名对学习率最敏感。
补丁(成本几乎为零,优先级最高):用极小学习率训练代理模型——比常规小 1–2 个量级,论文给出的量级是 或 。
- 理论上,对随机特征模型可证明这样做保持数据集排序:小学习率下 SGD 的验证损失变化可分解为”反映真实数据质量差距的确定性漂移项”与”随机更新带来的方差项”,学习率足够小时漂移项主导方差项;
- 实测上,用 (而非 )训练 GPT2-125M 作代理,代理到 Pythia-1B 目标(目标模型各自做过数据特化超参寻优)的配方排名 Spearman 相关性提升到 > 0.95,覆盖 个配方对;
- 23 个配方覆盖数据策展的四个维度(去重强度、过滤阈值、混合配比、来源选择一类);
- 附带收益:极小学习率下排名出现得很早且稳定——3k 步(约完整训练的 27%)就能达到 0.95 以上,这直接把代理实验的算力预算砍掉一截。
应对策略(重排优先级):
- 【新增,成本最低、优先做】把代理模型的学习率降到 再训练,排名稳定性问题在源头上就被削掉一大半
- 多种子训练:每个管线至少跑 3 个随机种子,报告均值和方差(处理残余的随机性项,非主因)
- 多 benchmark 交叉验证:不要只看一个 benchmark 的排名
- 关注大幅差异:只有在差距超过 2-3% 时才做决策
- 模块化消融:让消融实验的成本从指数级变成线性级(见 29.5.3)
- 【保留但降档】增大代理模型:如果预算允许,用 1.5B 比 400M 更可靠——但注意它解决的是”代理容量不足”,不是”超参协议错误”,两者是独立的失效路径
⚠️ 待回源:本节前一版给出的”3 个种子方差可达 ±1.5%“与”组间差 0.5-2%“两个数字,在论文 v1 HTML 全文(14.6 万字符)中逐字检索未命中(
1.5零命中)。论文正文确实用了 3 个种子,但只以标准误 / bootstrap CI 的形式出现在图表阴影区,未给出 ±1.5% 这一具体数值。该数字暂按未证实处理,不删除但不再作为决策依据;引用它时须自行回源。
💡 反直觉发现
小模型代理法并不像看起来那么可靠。一个在 400M 模型上”赢了”0.8% 的数据管线,放到 70B 模型上可能反而差了 0.3%。原因在于小模型和大模型对数据的”口味”不完全一致——大模型对数据多样性的需求更高,而小模型可能更偏好低熵、模式明确的数据。
代理模型的下一步:从同构缩小到影响力保持
上述策略都是在”用小模型近似大模型”的框架内打补丁。IProX(ICLR 2026,arXiv:2602.17835)提出了一个更根本的思路:问题不在于代理模型太小,而在于简单缩小的小模型没有保持与大模型一致的数据偏好方向。
IProX 的两阶段框架:
- 通过影响力保持的低秩近似压缩目标模型——不是训练一个新的小模型,而是从大模型本身出发,用低秩分解保留梯度影响力方向
- 通过模型梯度和输出对齐进一步精炼代理
核心区别在于:传统代理是”同架构缩小”(比如从 70B 缩到 400M),参数量变了,数据偏好也跟着变了。IProX 的代理是”影响力保持压缩”——参数量变小,但对数据的梯度响应方向尽量与大模型一致。
需要注意的是,IProX 目前主要在 SFT 数据选择上得到了验证,是否能直接迁移到预训练数据消融场景还有待更多实验。但它指明了一个方向:代理模型的可靠性问题,可能需要在”如何构建代理”而非”如何使用代理”的层面上解决。
29.5.3 消融实验的设计方法
控制变量法
数据消融的核心是控制变量:在其他条件不变的前提下,改变一个数据管线参数,观察其对模型表现的影响。
常见的消融维度:
| 消融维度 | 典型变量 | 关键观察指标 |
|---|---|---|
| 提取器选择 | trafilatura vs resiliparse vs jusText | 文档质量、信息保留率 |
| 语言过滤阈值 | 0.5 → 0.6 → 0.7 → 0.8 | 数据量 vs 语言纯度 |
| 质量分类器 | 无过滤 / fastText / LLM 打分 | benchmark 分数变化 |
| 质量阈值 | 保留 top-10% / 30% / 50% / 80% | 质量-多样性权衡 |
| 去重粒度 | 文档级 / 段落级 / 仅精确去重 | 去重率 vs 多样性损失 |
| 领域配比 | web 50-80% / code 10-30% / math 5-15% | 各能力维度分数 |
| 预训练优化器 | SGD / AdamW / Sharpness-Aware | 后训练效果、量化后性能 |
其中”预训练优化器”一行来自 Sharpness-Aware Pretraining(arXiv: 2605.02105)的发现:预训练阶段的优化器选择(不仅是学习率)会影响模型在 post-training 和量化后的能力保留程度。如果你的 pipeline 包含后训练阶段,建议将优化器类型纳入消融变量列表。
分层消融 vs 全因子消融
在消融实验设计中,一个常见的误区是试图做”全因子设计”——如果你有 5 个维度,每个维度 4 个选项,那就是 4^5 = 1024 个实验。这在大模型数据工程中完全不现实。
推荐的分层消融策略:
第 1 轮:粗筛(每个维度 2-3 个选项,单独消融)
→ 确定每个维度的大致最优范围
第 2 轮:交互实验(选择最可能存在交互效应的 2-3 个维度,组合消融)
→ 发现维度间的交互效应
第 3 轮:精调(在最优范围内细粒度搜索)
→ 确定最终参数
可扩展的模块化消融方法
2024 年 EMNLP 上发表的《Scalable Data Ablation Approximations》提出了一种创新方法:
核心思路:
- 将训练数据分成 N 个独立的”模块”(module),每个模块大约 1-5B token
- 每个模块独立训练一个小模型
- 通过权重合并(weight merging)来近似”在组合数据上训练”的效果
优势:
- 新增一个数据模块,只需要训练一个新的小模型,然后与已有模型合并
- 成本从 O(2^N) 降到 O(N),是线性增长而非指数增长
- 已有模块的训练结果可以复用
限制:
- 权重合并只是近似,对于强交互效应可能不准确
- 在模块数量很多时,合并效果会退化
- 目前主要在 1B 以下模型上验证过
另一个消融维度:数据子集选择策略
前面讨论的消融维度都是”改管线参数”——调阈值、换提取器、改配比。但还有一个正交的维度往往被忽视:在相同管线输出中,用不同策略选择训练子集。
一项针对 19 种数据采样方法的大规模对比研究(ICLR 2026)发现:
- AskLLM(用指令微调 LLM 直接评估每条训练样本的质量)在质量维度表现最优
- Density Sampling(基于数据分布建模选择多样性样本)在覆盖维度表现最优
- 两者组合,可以用 10-30% 的数据达到全量训练的同等性能
这给消融实验设计带来了一个新视角:除了问”哪个管线参数最好”,还可以问”同一管线的输出数据中,用什么策略选子集最高效”。后者的实验成本更低——不需要重新跑数据管线,只需要在已有数据上做采样。
需要注意的是,该研究的验证规模主要在 1-7B 模型上,对于更大规模模型是否同样成立还需要更多实验。但 10-30% 的数据效率数字,至少表明在消融实验的”快速筛选”阶段,数据子集选择是一个值得尝试的低成本方向。
OptiMer:无需训练的配比优化
OptiMer(Cao et al.,京都电信研究院,arXiv:2603.28858)提出了一种完全不同的范式:它不通过消融实验来发现最优配比,而是通过”分布向量合并”来推导最优权重。具体做法:先为每种数据类型单独训练一个小模型,提取每种数据对应的”分布向量”(可理解为模型对该类数据的参数偏移),然后用贝叶斯优化将这些向量合并,得到最优组合权重。这个方法的核心优势是无需在候选配比上反复训练——整个搜索过程的计算成本仅为传统方法的 1/15~1/35。在实践中,如果你的预算只够跑 1-2 个完整训练,OptiMer 提供了在训练前就获得良好初始配比的途径。
29.5.4 工业级消融方法论:SmolLM3 的经验
Jeremy Howard 在 SmolLM3 预训练中完整公开了消融方法论,提供了罕见的工业级实操参考。
消融成本:消融+调试消耗 161,280 GPU-hours,超过主训练运行(276,480 GPU-hours)的 50%。这打破了”消融只是主训练的小开销”的假设——在严谨的预训练中,消融成本通常占总计算预算的 30-50%。
评估策略:CF vs MCF 的互补使用:
| 评估格式 | 全称 | 特点 | 适用阶段 |
|---|---|---|---|
| CF | Cloze Formulation(填空式) | 信噪比高、方差低 | 训练早期(快速筛选) |
| MCF | Multiple-Choice Formulation(多选式) | 信号更准确、但方差较高 | 训练中后期(精细比较) |
关键发现:CF 适合早期快速筛选方向(因为方差低,少量实验就能得到可靠信号),MCF 适合后期精细比较(因为信号更准确,能区分细微差异)。两者应配合使用而非只选其一。
消融设计原则:
- 每个决策都要有消融支撑——不是为了”严谨”,而是为了 debug。如果最终模型出问题,没有消融记录就无从回溯
- 固定架构变数据 vs 固定数据变架构的互补设计——前者隔离数据效应,后者隔离架构效应
- 100+ 消融实验是大规模预训练的常态,不是过度工程
长上下文数据的消融注意事项
长上下文持续预训练(LCCP)有其特殊的消融挑战。工业级模型(>10B 参数,>1T token 训练量)的 LCCP 动态与小规模实验显著不同:
- 小模型上”有效”的长度和课程策略可能在工业规模上失效或需要调整
- 工业级 LCCP 需要更长的训练步数才能观察到短上下文能力的退化
- 建议用比目标规模小 10 倍的模型做 proxy,但训练 token 数应保持与目标规模同量级(而非按比例缩减),以捕捉长上下文训练的特有动态
数据难度与后训练效果的关系
“数据是否越难越好”?张景昭等人(FAIC 2026)的初步结论表明:
- 中训练阶段:中等难度的数据通常优于极难数据——与模型当前能力过于脱节的极难数据容易导致训练不稳定
- 后训练(RL)阶段:数据的难度分布需要重新考虑,太简单的数据可能无法为 RL 提供足够的挑战信号
这与 FineWeb-Edu 的”教育价值分级”形成有趣对比——教育价值高(难度高)的数据在 SFT 阶段有帮助,但在中训练阶段不一定最优。
29.5.5 数据归因 TDA
消融回答”管线参数的影响”,TDA(Training Data Attribution)则回答更细粒度的问题:哪些训练数据对某个测试样本贡献最大——模型在测试样本 x_test 上的正确/错误,是由哪些训练样本导致的?
方法 1:消融实验(最简单)
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 次)。
方法 2:Influence Function(EK-FAC)
用梯度近似计算”如果移除某条训练数据,测试 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 近似有误差,对大模型不友好。
方法 3: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。
两条新路径(研究阶段)
上述三种方法有一个共同的隐含前提:你得先指定一个归因目标——某个测试样本、某个验证集、某个下游任务。这在”MMLU 掉分了,去找是哪批数据的问题”这种场景下没问题,但预训练阶段常常反过来:你想知道某批语料在整个训练过程中起了什么作用,而”代表通用能力的验证集”本身就选不出来。2026 年出现了两条绕开这个前提的路径,都还在研究阶段。
(1)task-agnostic 归因:把”最终参数”当作归因目标
arXiv:2608.13515 换了个定义:一条样本的影响力 = 它的梯度更新让参数向该次预训练最终参数靠近了多少(平方距离的减少量)。目标从”某个任务的 loss”换成”这次训练自己的终点”,于是不需要挑验证集,也不需要重训——直接用训练时已经存下的中间 checkpoint 估计。
在 Pythia 与 PolyPythia 系列的 18 组配置上,它给出了一个跨配置稳定的时序结论:训练早期文学类数据与参数轨迹的对齐度更高,后期换成 STEM 类数据更高。这个”交叉点”对课程设计是直接的证据——数据配比该随训练阶段变,而不是全程一个混合比例。
成熟度:验证规模止于 Pythia 系列(最大 12B 量级的稠密模型),尚未见到在前沿 MoE 规模上的复现。“最终参数”作为归因目标也有个哲学上的循环——它衡量的是”对这次训练的贡献”,而不是”对模型好不好的贡献”,若这次训练本身跑偏了,高影响力数据也可能是坏数据。用它做训练动态分析可以,用它做数据取舍需谨慎。
(2)forward-only 归因:把算力从查询时挪到训练时
Influence Function 的工程痛点是要在推理时做反向传播和 Hessian 近似,每来一个查询都得付一次。arXiv:2511.19803 的做法是把这笔计算搬到训练期:训练时用短程梯度传播模拟每条样本的参数影响并存下来,之后任何查询只用前向评估即可读出归因结果。它保留了与 Influence Function 相同的一阶反事实目标,但去掉了 per-query 的反向传播。
这个取舍匹配一类真实场景:模型可能服务数十亿次查询,但训练数据源是固定有限的——比如一个 LLM 要向特定出版方(论文举的例子是《纽约时报》)核算贡献并付费,归因需要能反复、实时地跑。
⚠️ 引用边界要看清:该论文报告”在 LOO / LDS 指标上匹配或超过 TRAK,且推理成本低数个量级”,但实验是在标准 MLP 基准上做的,不是语言模型。它目前是一个有理论框架支撑的可扩展性主张,而非已在 LLM 规模上验证的替代方案。不要因为”免反向传播”就把它当成 EK-FAC 在大模型上的现成替代品。
当前选用建议:生产归因仍以消融、EK-FAC、TracIn 为主(消融做关键决策、EK-FAC 做大规模、TracIn 做轻量筛查);上面两条适合作为研究性探索或训练动态分析的补充手段,纳入生产前需要自己在目标规模上验证一遍。
TDA 的应用
- 找”毒数据”:测试集掉分时,用 TDA 找出贡献为负的训练样本
- 数据去重:TDA 分数相近的样本冗余,去除其一
- 数据增强:TDA 分数高的样本模式,用类似模式合成更多数据
- 冷启动:新能力快速找到”高价值”训练样本
29.6 污染检测与评估可靠性
评估结果可靠的前提是评估数据没有出现在训练集中。污染检测是评估闭环的守门员。
29.6.1 评估集与训练数据的隔离
Benchmark 数据泄露(data contamination)已经成为 LLM 评估中最头疼的问题之一。2024-2025 年间,大量研究揭示了泄露的普遍性和严重性:
- GPT-4 的论文中承认部分 benchmark 数据可能存在于训练集中
- 有研究发现,在某些模型上,被污染的 benchmark 子集上的分数比未污染的高出 5-15%
- 即使做了”去污染”处理,也不能保证完全干净——因为 benchmark 的变体、改述版本可能通过其他渠道进入训练集
29.6.1b 污染不止一条通道:三通道模型与 fail-closed 编码
上面三条说的全是训练期暴露(benchmark 数据进了训练集)。这是最重要的一条通道,但不是唯一一条——尤其在 Agent 评测里,另外两条通道本章此前完全没有覆盖。
arXiv:2610.05830(2026-10-05)提出的审计框架按”需要什么证据才能支持”把污染主张分成三条独立通道:
| 通道 | 含义 | 本章现有方法是否覆盖 |
|---|---|---|
| 训练期暴露 | 题目/答案在训练阶段就进了模型 | ✅ 覆盖(n-gram / DCR / CDD / Self-Critique / 动态评测全在这一条) |
| 评测期检索 | 评测当时模型能检索到题目或含解法的产物 | ❌ 章内无对应方法 |
| 管线 / scaffold 泄漏 | prompt 模板、工具配置、评测脚手架本身带进答案信息 | ❌ 章内无对应方法 |
每条通道按 open / partial / closed / unknown 四档编码,并采用 fail-closed 规则——证据不足一律记为 unknown,而不是记为”未污染”。这一条对工程习惯是反的:日常报告里”没检出污染”常常被顺手写成”无污染”,fail-closed 要求的是把它写成”未知”。
该研究在 9 个 HAL(Holistic Agent Leaderboard)配置 × 3 条通道共 27 项评估上跑了一遍,没有一项能判为 closed,其中 4 个配置确认了事件。这个结果本身就说明:Agent 榜上的污染主张,绝大多数处在”证据不足”状态,而不是”已排除”。
复评怎么做:行为侧必须用同仓库匹配对照(outcome-blind、same-repository matched-control)+ 对称的 prompt 泄漏筛查,并且报告区间估计而非点估计。论文给的警示案例很典型:SWE-bench Verified 上一个已报告的 file-localization 差距,经对称筛查与配对完整性排除后保留 100 对,GPT-4.1 显示出 +10.0 点的成对加权 Top-3 基准关联差距,但预设的配对 bootstrap 与事后按仓库平衡的分析两者的 95% 区间都包含零,结论只能是”不成立”。只报 +10.0 这个点估计会得出完全相反的结论。
scorer 本身也要过闸:这条与 29.4 的 reward hacking 检测是同一道防线在评测侧的镜像——RL 侧防的是策略骗过 verifier,评测侧防的是 verifier 本身不可信。该研究中,用于复现的 scorer 没有通过它自己的验证闸:相对人类共识标签,两个模型的 scorer 灵敏度都不足以确立,DeepSeek-V4-Flash 在 correct-gold 比较上两次都是假阳。因此任何用于判定”成功/失败”的 scorer(包括 29.4.3 Cross-Verifier 里的 secondary verifier)都必须先用人标签校准灵敏度,通过后才准用于下结论。
论文自陈的三条边界也值得照抄:它不确立训练数据成员关系、不确立污染的普遍程度、不确立分数虚高的幅度。审计框架只回答”这条主张有没有被关闭”。
29.6.2 n-gram 去污染检测
"""
训练数据去污染检测
在数据管线的最后阶段运行,确保评测集不泄露到训练集
"""
from typing import List, Set
import hashlib
class DecontaminationChecker:
"""多层次去污染检测器"""
def __init__(self, benchmark_data: List[str], ngram_size: int = 13):
self.ngram_size = ngram_size
# 构建 benchmark 的 n-gram 索引
self.benchmark_ngrams: Set[str] = set()
self.benchmark_hashes: Set[str] = set()
for item in benchmark_data:
# 精确匹配
self.benchmark_hashes.add(
hashlib.sha256(item.strip().encode()).hexdigest()
)
# N-gram 匹配
tokens = item.lower().split()
for i in range(len(tokens) - ngram_size + 1):
ngram = " ".join(tokens[i:i + ngram_size])
self.benchmark_ngrams.add(ngram)
def check_document(self, doc: str) -> dict:
"""检查单个文档是否包含 benchmark 数据"""
result = {
"exact_match": False,
"ngram_overlap_count": 0,
"ngram_overlap_ratio": 0.0,
"is_contaminated": False,
}
# 层次 1:精确匹配
doc_hash = hashlib.sha256(doc.strip().encode()).hexdigest()
if doc_hash in self.benchmark_hashes:
result["exact_match"] = True
result["is_contaminated"] = True
return result
# 层次 2:N-gram 重叠
tokens = doc.lower().split()
total_ngrams = max(1, len(tokens) - self.ngram_size + 1)
overlap_count = 0
for i in range(total_ngrams):
ngram = " ".join(tokens[i:i + self.ngram_size])
if ngram in self.benchmark_ngrams:
overlap_count += 1
overlap_ratio = overlap_count / total_ngrams
result["ngram_overlap_count"] = overlap_count
result["ngram_overlap_ratio"] = round(overlap_ratio, 4)
# 阈值判定:超过 70% 的 13-gram 重叠视为污染
# 这个阈值参考了 GPT-3 论文和 Llama 的做法
if overlap_ratio > 0.7:
result["is_contaminated"] = True
return result
def check_dataset(self, docs: List[str]) -> dict:
"""检查整个数据集的污染情况"""
total = len(docs)
contaminated_count = 0
contaminated_indices = []
for i, doc in enumerate(docs):
result = self.check_document(doc)
if result["is_contaminated"]:
contaminated_count += 1
contaminated_indices.append(i)
return {
"total_documents": total,
"contaminated_documents": contaminated_count,
"contamination_rate": round(contaminated_count / total * 100, 4),
"contaminated_indices": contaminated_indices[:100],
}
29.6.3 推理模型时代的新挑战:RL 训练会”洗白”污染信号
n-gram 检测是当前最广泛使用的去污染方案,GPT-3 和 Llama 都采用了类似思路。但 2025-2026 年的研究揭示了三个根本性的局限。
1. RL 训练会系统性掩盖污染信号(“洗白”效应)
ICLR 2026 的一项研究(Wang, Li, Ko, Zhang — UIUC ASTRAL Group,arXiv:2510.02386)系统性地揭示了大型推理模型(LRM)的污染检测脆弱性:
Stage I:SFT 污染 → GRPO 洗白
- 初始:SFT 阶段引入的污染可被检测——Min-K%/Max-K%/LOSS 类概率方法 AUROC ~73%,参考模型法 LiRA 达 89%
- 仅 1 轮 GRPO 训练:所有方法的 AUROC 塌回 50%(随机猜测)
- 156 步 GRPO 后:reference-free 方法(Loss、Min-K、Max-K)已接近完全失效
- 实验数据:Qwen2.5-7B-Instruct 上 GRPO 训练后 AUROC 普遍下降 8.99%-19.84%;Llama-3.1-8B-Instruct 上下降 7.22%-32.66%
- 关键:contaminated models 在 GRPO 之后仍保持 +7.14% 的平均性能虚高——RL 保留了污染收益但隐藏了污染证据
机制解释(PPO 式重要性采样 + clipping):
- 对 member 样本已被记牢、概率高,clipping 给梯度加上限阻止继续推高
- 对 non-member 无此约束,概率相对抬升
- 一拉一压让 member/non-member 分布重叠,检测器赖以工作的”见过样本概率更高”信号被磨平
Stage II:CoT SFT 污染高级 LRM
- 直接对强 LRM 做 CoT SFT 污染:pass@1 平均涨 8.82%
- 所有 10 种主流检测方法 AUROC 仍 ~50%——几乎无法检测
这意味着对于使用了 RL 训练的推理模型,传统的去污染检测方法可能完全失效。
对实践清单的修正:
- 单一静态检测器已不够——需要时间维度 + 训练阶段维度的多重检测(训练前/训练中/训练后各做一次)
- Time-based partition(按时间切分数据/任务,看 cutoff 前后的性能悬崖)仍是 2026 年最稳健的检测方法
- reference-based 检测(如 LiRA)虽然准确但不能跨 SFT→RL 边界保持可靠性——仅在 SFT 后或 RL 之前才有意义
- 对 LRM 评估应默认假设存在检测盲区,在解读榜单分数时持保留态度
2. 从二元判定到连续风险量化
传统检测给出的是”污染/不污染”的二元结论。DCR(Data Contamination Risk,EMNLP 2025)提出了一种更细粒度的方案:在语义、信息、数据、标签四个级别检测污染风险,通过模糊推理系统合成统一的 DCR Factor。经调整后的准确率与未污染基准线误差控制在 4% 以内,已在 9 个 LLM(0.5B-72B)上验证通过。
DCR 的价值不在于替代 n-gram 检测,而在于提供了一种”风险调整后分数”的思路——不是简单地去掉疑似污染的样本,而是根据污染风险对评估分数做折扣。
3. 从数据比对到模型内视
另一个互补方向来自 NA-PDD(EMNLP 2025):通过分析模型内部神经元激活模式的差异,判断某条数据是否出现在预训练集中。这种”模型内视”方法不需要访问训练数据或重新训练模型,对于文本被改写、变体等传统 n-gram 方法难以捕捉的间接污染尤其有效。
⚠️ 上面这段需要加三层限定,否则会把一个尚未成熟的手段当成常规选项用。 2026 年的一项专门审计(arXiv:2608.12652)直接指出”the natural way to do this does not work”——朴素地在残差流上做线性探针读”熟悉度”是无效的:
- 基线深度剖面并不平坦。 真实 transformer 上,基线深度剖面在 temporal split 上最大可跨 29.1 个准确率点;且非平坦程度与两组题目的表面差异相关(6 次审计上 r = 0.87)。不做对照,探针读到的就是这条基线,而不是污染。
- 可用协议至少含四项,每一项都是被实测排除掉更简单的替代后才留下的:深度剖面的零和对比 + 以层级匹配的安慰剂基线重中心化 + 标签置换零假设检验 + 参考集规模为可疑集的两倍。其中控制集大小本身就是结果的一部分——实测假阳率随分析师自己的控制集规模在 0.03–0.99 之间变化,必须报告。
- 这类方法会拒答。 在 4 个匹配良好的 Pile arms 上一律返回 null,对 temporal split 拒绝给出结论而不给一个数字。论文自己也提醒:null 结果只是”缺席”的弱证据。
因此定位应当是:在严格对照设计下才可用的补充手段(与 29.5.5「两条新路径(研究阶段)」同级),而不是与 n-gram 并列的常规选项。另有一条对所有”模型内视”类方法都适用的提醒:论文发现为消除偏差而减去的那个量,其方差比它所检验的零假设本身的方差还大(重估后标准差为零假设自身标准差的 1.30–1.56 倍)——即”修正项引入的噪声超过信号”是这一类方法的通病。
29.6.4 污染检测方法的新进展
2025-2026 年间,三种新方法填补了重要的场景空白:
BeyondBench:动态生成抗污染基准(ICLR 2026)
核心思想:与其检测污染,不如从源头避免——通过算法化动态生成数学问题,组合空间超过 10^15 种,使任何固定题库都无法覆盖。44 个算法任务 / 117 个变体 / 3 个难度级别。101 个模型(0.5B-141B 参数)评测显示:
- Hard Suite(10 个任务,68 个变体,NP-complete/约束满足):
- Gemini-2.5-pro:56.21%(最强)
- Qwen2.5-72B:33.37%
- Llama-3.3-70B:29.16%
- 工具依赖性(不用工具时的性能下降):
- GPT-5:-16.81%
- GPT-5-mini:-15.86%
- GPT-5-nano:-43.95%(最受工具影响)
- 关键观察:所有模型在 polynomial → exponential 复杂度提升时性能急剧下降
这代表了一种范式转变:从”事后检测污染”到”事前消除污染可能性”。
CDD:输出分布峰度检测法(arXiv: 2603.03203)
通过分析模型输出分布的峰度来检测小模型的数据污染——受污染样本的输出分布更为尖锐(过度自信)。但有一个重要的实践限制:在 LoRA 微调下检测失效。这意味着 CDD 适用于检测全量预训练的污染,但不适用于仅做了 LoRA 适配的模型。
Self-Critique:策略塌陷检测(PKU,arXiv:2510.09259,2025-10-10 提交;ICLR 2026)
专门针对 RL 后训练阶段的新型污染检测。核心观察是”策略塌陷”现象——当模型在污染问题上训练后,其推理路径会固化。检测方法:要求模型给出同一问题的不同解法,如果多条解法高度相似(而非预期的多样化推理路径),则表明可能存在污染。这是首个专门针对 RL 阶段污染的系统性检测方法——RL 会掩盖传统检测方法的信号,但 Self-Critique 利用的是 RL 训练自身产生的行为异常(策略塌陷)来检测污染。
配套基准 RL-MIA 是专为这一场景构造的污染评测集。实测:Self-Critique 在多个模型与多个污染任务上显著优于基线,AUC 最高提升 30%;而既有方法在 RL 阶段污染上接近随机猜测。
更新后的多层验证建议:
n-gram 检测(显式污染)
→ DCR 风险量化(连续风险等级)
→ Self-Critique(RL 阶段污染)
→ CDD 峰度检测(小模型全量训练污染)
→ BeyondBench/LiveBench(动态评测交叉验证)
→ LRM Contamination Detection Arena(Stage I/II 系统化测试)
29.6.5 动态评测:从源头解决污染
静态 benchmark 都有一个共同的软肋:题目公开之后,就有可能通过各种渠道进入训练数据——直接泄露、网页引用、数据集整合,甚至模型生成的训练数据中包含对这些题目的讨论。
LiveBench(ICLR 2025)代表了一种从根本上解决这个问题的范式:与其在静态 benchmark 上费力做去污染修补,不如用不可能被提前泄露的题目。LiveBench 的核心设计:
- 定期发布全新题目,旧题目退役,确保测试集的”新鲜度”
- 所有题目都有可验证的客观标准答案,不需要 LLM-as-Judge 这类可能引入偏差的评判方式
- 覆盖 6 大类 18 项任务,即使最强模型也很少超过 65% 准确率
对于数据工程团队来说,动态评测的价值在于:当你在静态 benchmark 上的改进很难排除污染因素时,可以用 LiveBench 这类平台做交叉验证。如果改进在动态评测上也成立,那就是真实的数据质量提升。
29.6.6 2026 年污染检测实践清单
截至 2026 年下半年,仍有效且推荐使用的检测方法(按稳健性排序):
| 排名 | 方法 | 类型 | 优势 | 局限 |
|---|---|---|---|---|
| 1 | Time-based partition(按时间切分) | 时间维度 | 最稳健,跨训练阶段保持 | 依赖训练 cutoff 时间记录 |
| 2 | Min-K% probability | 概率(reference-free) | 无需参考模型,部署简单 | SFT+RL 后失效(Stage I) |
| 3 | Time Travel guided-completion attack | 攻击式 | 主动触发记忆,可控 | prompt 构造复杂 |
| 4 | ConStat performance gaps | 行为对比 | 检测改写污染 | 需要原题 + 改写题配对 |
| 5 | BIG-Bench canary GUID memorization | canary 注入 | 精确知道是否被记住 | 仅适用 canary 子集 |
对 LRM 的特殊提示:
- 上述方法在 SFT+RL 训练后均存在脆弱性
- 解读 LRM 榜单时必须配合训练阶段维度的检测
- Wang et al. UIUC 的 LRM Contamination Detection Arena 提供了 10 种检测器的标准化测试平台,应作为 LRM 评估的标准前置步骤
29.6.7 快速评估 vs 完整评估的权衡
在数据迭代的不同阶段,评估的粒度应该不同:
日常开发阶段(快速评估):
- 只用 2-3 个对数据敏感的 benchmark(如 HellaSwag + ARC)
- 模型训练到 10B token 就评估
- 耗时:1-2 天
- 精度:能检测出 >2% 的差异
里程碑阶段(完整评估):
- 用 6-10 个 benchmark 全面评估
- 模型训练到 28-50B token
- 增加多种子验证
- 耗时:5-7 天
- 精度:能检测出 ~1% 的差异
发布前(深度评估):
- 完整 benchmark suite + 人工评估
- 在目标规模模型上验证
- 包含安全性和偏见评估
- 耗时:2-4 周
这与三层评估体系(L1/L2/L3)形成对应:快速评估对应 L1 的节奏,深度评估对应 L3 的严谨。评估粒度应该服务于迭代节奏,而不是一刀切。
29.7 迭代调整:数据闭环的执行
29.7.1 闭环的核心
数据-训练-评估闭环是工业级团队的”肌肉记忆”:
评测发现问题 → 数据归因 → 数据调整 → 训练 → 再评测
数据工程不是一锤子买卖。真正的价值在于建立一个持续迭代的闭环:
Step 1: 发现问题
├── 训练中发现 loss 异常
├── 评估中发现某维度退化
├── 人工抽检发现系统性问题
└── 用户反馈(如果模型已部署)
Step 2: 定位原因
├── 数据抽样分析
├── 回溯管线日志
├── 按维度分桶排查(语言、领域、来源)
└── 与历史版本对比
Step 3: 调整管线
├── 修改过滤规则/阈值
├── 增加/移除某个数据源
├── 调整配比
└── 修复 bug
Step 4: 验证效果
├── 小模型消融实验
├── 人工抽检新数据
├── 数据统计对比
└── 确认无回归
29.7.2 典型闭环场景
场景 1:评测掉分 → 找数据 → 加量改质
现象: MMLU 从 70 掉到 65
归因: TDA 显示新增数据中某些知识类样本质量差
调整: 移除低质量样本 + 补充高质量知识类数据
再评测: MMLU 恢复到 71
场景 2:某评测涨太多 → 检查污染
现象: GSM8K 从 75 暴涨到 95
排查: 用 13-gram 检查训练数据是否含 GSM8K 测试集
发现: 新加入的某开源数据集含 GSM8K 测试集
处理: 移除污染数据,重新训练
经验:任何评测分数”不正常暴涨”都要警惕污染。这正是 29.6 节污染检测体系在闭环中的触发时机。
场景 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
29.7.3 显微镜案例:一次数据迭代的完整记录
背景:某团队在训练一个中英双语的 13B 模型,训练进行到 5T token 时,发现模型在数学推理任务上的表现异常低下——比同期的开源模型低了 8 个百分点。
Step 1: 发现问题
团队的监控 dashboard 在训练到 4T token 时就发出了预警:GSM8K 的 5-shot 准确率停滞在 32%,而预期应该在 45% 左右。
Step 2: 定位原因
# 数据分析过程(简化版)
# 1. 先看数学相关数据的占比
math_data_ratio = count_domain(training_data, "math") / total_tokens
# 结果:2.1%(预期 5%)
# 2. 检查数学数据的质量
math_quality = sample_and_inspect(training_data, domain="math", n=500)
# 发现:大量数学数据来自一个质量较差的来源
# 其中约 30% 的数据包含被截断的 LaTeX 公式
# 3. 回溯管线日志
# 发现:某次管线更新中,数学数据的来源权重被意外修改
# 一个高质量的 arXiv 数学子集被错误地排除了
Step 3: 调整管线
- 恢复 arXiv 数学子集
- 增加数学数据占比从 2.1% 到 5.5%
- 对数学数据增加 LaTeX 完整性检查
- 额外混入合成数学题(经过质量验证的)
Step 4: 验证效果
在 400M 代理模型上训练 10B token,对比调整前后:
| Benchmark | 调整前 | 调整后 | 变化 |
|---|---|---|---|
| GSM8K (5-shot) | 12.3% | 16.7% | +4.4% |
| MATH | 4.1% | 6.2% | +2.1% |
| HellaSwag | 58.2% | 57.9% | -0.3% |
| ARC-Challenge | 35.6% | 36.1% | +0.5% |
数学能力显著提升,通用能力无回归。批准上线。最终在 13B 模型上,GSM8K 分数从 32% 提升到 48%,达到预期目标。
这个案例体现了闭环的完整链路:监控发现问题 → 分桶归因 → 管线修复 → 代理验证 → 上线确认。其中”通用能力无回归”的确认与目标能力的提升同等重要——数据调整最常见的副作用就是按下葫芦浮起瓢。
29.7.4 数据重写作为迭代手段:Transform-and-Retain 范式
传统数据迭代的逻辑是”排除式”的——发现低质量数据,然后过滤掉。但 SwallowCode/SwallowMath(Fujii et al., arXiv: 2505.02881, 2025)提出了一种截然不同的范式:与其排除低质量数据,不如通过重写提升其质量。这被称为”Transform-and-Retain”(转换保留)范式。
核心思想:公共数据中大量内容的”低质量”不是固有的——而是格式混乱、冗余多、上下文缺失。通过 LLM 重写,可以在保留核心知识的同时大幅提升数据质量。
SwallowCode(代码数据重写,~16.1B tokens):
四阶段管线:
1. 语法验证 → 移除无法解析的代码
2. pylint 风格过滤 → 移除风格极差的代码
3. LLM 第一阶段重写 → 修复错误、补充注释、改善结构
4. LLM 第二阶段重写 → 深度优化逻辑、增加 docstring
效果:HumanEval pass@1 提升 +17.0
SwallowMath(数学数据重写,~2.3B tokens):
三阶段管线:
1. 去冗余 → 移除无关推导步骤
2. 恢复上下文 → 补充题目背景和定义
3. 重格式化 → 转为简洁分步解释
效果:GSM8K 提升 +12.4,MATH 提升 +7.6
消融研究的关键洞察:每个处理步骤都带来增量改进,但重写步骤贡献最大。这意味着”转换”本身的价值超过了”过滤”。
与排除式过滤的对比:
| 维度 | 排除式过滤 | Transform-and-Retain |
|---|---|---|
| 数据量 | 减少(丢弃低质量数据) | 保持或增加(重写后保留) |
| 知识损失 | 有(被过滤数据中的知识丢失) | 最小(核心知识被保留) |
| 计算成本 | 低(只做判断) | 高(需要 LLM 推理) |
| 适用场景 | 数据充足时的质量控制 | 数据稀缺时的质量提升 |
实践启示:在数据稀缺的场景下(如特定语言、特定领域),重写比过滤更优——因为每一条数据都弥足珍贵,不应轻易丢弃。即使在数据充足的场景下,对”中等质量”数据做重写也是值得的——它能将大量”灰色地带”数据转化为高质量数据,扩大有效训练集。
29.7.5 数据版本管理
当你的数据管线每周都在迭代时,你需要能够:
- 回溯到任意历史版本的数据
- 对比两个版本的差异
- 在不同版本上复现训练结果
- 追踪每条数据的来龙去脉
推荐的版本管理方案:
方案 1: DVC(Data Version Control)
├── 优点:与 Git 深度集成,学习成本低
├── 缺点:对 PB 级数据的支持较弱
└── 适用:中小规模团队,数据 < 10TB
方案 2: lakeFS
├── 优点:Git-like 分支模型,支持 S3/GCS
├── 缺点:需要额外维护服务
└── 适用:中大规模团队,云原生架构
方案 3: Delta Lake / Iceberg
├── 优点:PB 级数据的高效版本管理
├── 缺点:需要 Spark 生态
└── 适用:大规模团队,已有 data lake 基础设施
最低可行的版本管理实践:
即使你没有上述工具,也应该至少做到用 manifest 文件记录每个版本的数据组成:
"""
最小版本管理方案
用 manifest 文件记录每个版本的数据组成
"""
import json
import hashlib
from datetime import datetime
def create_data_manifest(
version: str,
data_sources: dict,
pipeline_config: dict,
output_stats: dict,
notes: str = ""
) -> dict:
"""
创建数据版本 manifest
每次数据管线运行后生成
"""
manifest = {
"version": version,
"created_at": datetime.now().isoformat(),
"data_sources": data_sources,
# 例如: {"cc_2024_10": {"path": "...", "doc_count": 1000000, "sha256": "..."}, ...}
"pipeline_config": pipeline_config,
# 记录所有管线参数
"output_stats": output_stats,
# 例如: {"total_tokens": "500B", "total_docs": 50000000, "lang_distribution": {...}}
"notes": notes,
}
# 生成 manifest 自身的 hash
manifest_str = json.dumps(manifest, sort_keys=True, ensure_ascii=False)
manifest["manifest_hash"] = hashlib.sha256(manifest_str.encode()).hexdigest()[:16]
return manifest
# 使用示例
manifest = create_data_manifest(
version="v2.3",
data_sources={
"cc_2024_10": {"doc_count": 500_000_000, "tokens": "2T"},
"cc_2024_06": {"doc_count": 480_000_000, "tokens": "1.8T"},
"arxiv_2024": {"doc_count": 2_000_000, "tokens": "50B"},
"github_code": {"doc_count": 50_000_000, "tokens": "300B"},
},
pipeline_config={
"text_extractor": "trafilatura",
"lang_threshold": 0.65,
"dedup_method": "minhash",
"dedup_threshold": 0.8,
"quality_filter": "fasttext_edu_v2",
"quality_threshold": 0.5,
},
output_stats={
"total_tokens": "1.5T",
"total_docs": 200_000_000,
},
notes="修复了数学数据配比问题,arXiv 子集恢复"
)
manifest 是数据卡片(见 29.8.3)的机器可读形态:前者面向管线自动化,后者面向人类审阅与合规。
manifest 里建议再加一类字段:评估侧的版本快照——本次评估用的 judge 模型名与版本号、prompt 模板版本号、采样温度与重复次数。数据有 manifest、模型有 checkpoint,唯独”尺子”常常没有版本记录,而 judge 换代在生产中是高频事件(29.8.4 展开)。三者对齐之后,“某次分数变了”才有唯一解释:数据变了、模型变了,还是尺子变了。
29.7.6 闭环的周期
工业级闭环周期:
- 快闭环:1-2 天(L1 评测 + 数据调整 + 再训练)
- 中闭环:1-2 周(L2 评测 + 配方调整 + 完整训练)
- 慢闭环:1-2 月(L3 评测 + 重大配方调整 + 发版)
每个团队应该同时运行三个闭环,对应不同节奏。这与 29.6.7 的快速/完整/深度评估分层一一对应:评估粒度决定闭环周期,闭环周期反过来约束评估投入。
29.8 闭环组织:工程化与制度化
29.8.1 数据质量监控系统
数据管线不是搭完就完了。互联网数据在变化,爬取策略在更新,上游数据源在演化。你需要一个持续运行的监控系统来捕捉数据质量的漂移。
监控系统的核心组件:
┌──────────────┐
│ 数据管线输入 │
└──────┬───────┘
│
┌──────▼───────┐
│ 阶段性检查点 │
│ (每个阶段末) │
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
┌─────────▼──┐ ┌──────▼─────┐ ┌──▼──────────┐
│ 统计指标采集 │ │ 异常检测引擎 │ │ 人工抽样队列 │
└─────────┬──┘ └──────┬─────┘ └──┬──────────┘
│ │ │
└────────────┼────────────┘
│
┌──────▼───────┐
│ 告警系统 │
│ (阈值触发) │
└──────┬───────┘
│
┌──────▼───────┐
│ Dashboard │
│ (可视化) │
└──────────────┘
关键监控指标:
"""
数据管线监控指标定义
"""
MONITORING_METRICS = {
# 数量指标
"input_doc_count": {
"description": "管线输入文档数",
"alert_rule": "与上一批次偏差 > 20%",
},
"output_doc_count": {
"description": "管线输出文档数",
"alert_rule": "与上一批次偏差 > 20%",
},
"pass_through_rate": {
"description": "通过率 = output / input",
"alert_rule": "< 15% 或 > 80%",
},
# 质量指标
"avg_quality_score": {
"description": "平均质量分",
"alert_rule": "较历史均值偏差 > 0.05",
},
"quality_score_distribution": {
"description": "质量分分布",
"alert_rule": "分布形状发生显著变化(KL 散度 > 0.1)",
},
# 分布指标
"language_distribution": {
"description": "语言分布",
"alert_rule": "任一语言占比变化 > 5%",
},
"length_distribution": {
"description": "长度分布",
"alert_rule": "中位数变化 > 30%",
},
"domain_distribution": {
"description": "领域分布",
"alert_rule": "任一领域占比变化 > 10%",
},
# 管线健康指标
"processing_time": {
"description": "处理耗时",
"alert_rule": "较历史均值慢 > 50%",
},
"error_rate": {
"description": "处理错误率",
"alert_rule": "> 1%",
},
}
29.8.2 数据 A/B 测试的方法论
当你有两个候选的数据管线,并且小模型消融给出的结论不够确定时,你可能需要在真正的大模型训练中做 A/B 测试。
数据 A/B 测试的设计要点:
-
时间切片法:在同一次训练中,前 N% 的 step 用管线 A,后 N% 用管线 B,观察切换前后的评估指标变化
- 优点:只需要一次训练
- 缺点:顺序效应,不够严谨
-
并行训练法:同时启动两次训练,分别用管线 A 和管线 B
- 优点:最严谨
- 缺点:2 倍 GPU 成本
-
混合比例法:用管线 A 作为基线,逐渐混入管线 B 的数据(比如 10%、30%、50%),观察效果变化
- 优点:可以发现最优混合比例
- 缺点:需要多次实验
💡 反直觉发现
数据 A/B 测试的结论在不同训练阶段可能不同。某个数据管线在训练前期(<1T token)表现更好,但在后期(>5T token)可能被另一个管线反超。这是因为模型在不同训练阶段对数据的”需求”不同——前期需要高信息密度的数据来快速建立知识基础,后期需要高多样性的数据来避免过拟合和提升泛化能力。
29.8.3 数据卡片 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: "不可用于医疗/法律等专业领域"
Datasheet 的工业价值:
- 可追溯:训练出问题时能找到数据源头
- 可复现:他人能用同样数据复现训练
- 可审计:合规审计时能证明数据合法
- 可对比:版本间能清楚对比数据变化
29.8.4 评估闭环工业 SOP
1. 三层评估体系搭建:
- L1 自动评测(每 ckpt)
- L2 LLM-as-Judge(每周)
- L3 真人评测(每月)
2. 评测集管理:
- 必扫 20+ 评测集
- 抗污染评测(LiveCodeBench/AIME/LiveBench)
- 多维度覆盖
3. LLM-as-Judge 偏差控制:
- 一致率报 Cohen's κ,不报 raw agreement
- 双向评审去 Position Bias
- 先实测长度偏差幅度,再决定是否长度归一化
- 多 judge 交叉去 Self-preference
- 剥离格式后重评(默认执行,非可选)
- 换代/高风险场景补四轴可信度体检
- **judge 版本要写进评估结果元数据**(换代不是可靠性干预,见下)
- **重复投票的收益要按实测误差相关 ρ 折算,不能按独立同分布假设估算**(见下)
4. Reward Hacking 检测:
- reward 突增告警
- rollout 聚类
- Cross-Verifier
- 人审 sample
5. 消融与数据归因:
- 消融实验(关键决策;消融成本通常占总预算 30-50%)
- Influence Function(大规模)
- TracIn(轻量级)
- task-agnostic / forward-only(研究阶段,需自行验证)
- 模块化消融 / OptiMer(控制成本)
6. 污染检测:
- n-gram 去污染(管线末段必做)
- Time-based partition(最稳健)
- RL 阶段默认假设检测盲区
- 动态评测交叉验证
7. 闭环执行:
- 快闭环 1-2 天
- 中闭环 1-2 周
- 慢闭环 1-2 月
8. 沉淀与治理:
- 数据 manifest(每管线运行必生成)
- 数据卡片(每个版本必填,含动机/构成/采集/质量/局限)
- 持续监控系统 + 告警
为什么 judge 换代要单独记一笔。 Yang et al.(arXiv:2607.08535《When the Judge Changes, So Does the Measurement》)把 evaluator 换代当作测量效度问题审计:候选响应不动、只换 judge,分数就会动。他们在 4 个判定数据集上比较了两条实际可走的升级路径——Qwen3 dense 从 1.7B 扩到 32B,以及 MiniMax M2 到 M2.7 的连续发布版本。结论是换代不可互换:18 次相邻检验经 Holm 校正后,只有 Qwen3 1.7B→4B 这一步在 LLMBar 与 Arena 上仍然显著,MiniMax 的九次相邻版本发布没有一次达到未校正 p<0.05。规模变大也不等于变好:Qwen3 1.7B→32B 在 LLMBar 上从 0.463 升到 0.678,在 PandaLM 上却从 0.779 掉到 0.769,在 Judge’s Verdict 上从 0.595 掉到 0.530——四个数据集里有两个是越大越差。
工程含义只有一句:judge 版本号应当像数据 manifest 一样写进评估结果元数据。否则历史分数与新分数在时序上不可比,你无法判断某次分数变化到底是数据变好了还是尺子换了。这与 29.7.5 的数据版本管理是同一个问题的镜像——数据有 manifest、模型有 checkpoint,评估尺子本身同样需要版本记录。
重复投票的收益有硬上限。 该研究实测了 jury 投票的误差相关:Qwen3 同族 jury 在 LLMBar 上 ρ = 0.944–0.972,MiniMax jury 较低但仍有 ρ = 0.664–0.706。误差高度相关,“多跑几次取多数”就不能按独立同分布假设估算误差下降——Qwen3-1.7B 在 LLMBar 上 K=1 / 3 / 5 的实测准确率是 0.463 / 0.475 / 0.482,从 1 次加到 5 次只换来不到 2 个百分点。混合不同家族的 jury 也达不到独立假设的预测值。可用的口径是先估 ρ 再定 K:ρ 高时加大采样量是纯烧预算,不如把这笔钱换一个偏差更小的 judge。
judge 不是常量,是一组配置——版本字段要记四项。 上面”judge 版本号写进元数据”这条纪律只能防住其中一项。arXiv:2610.05094 在 10 个推理模型 × 多种 prompt / 量表设计上(两个任务:句子情感与毒性的标量打分,以及问答对的二分类准确率判定)量出了各设计选择的位移幅度:
| 设计选择 | 实测位移幅度 | 对 manifest 的要求 |
|---|---|---|
| 换模型 | 宽松度(leniency)下降最多 56.1 个百分点 | 模型身份(含版本号)必记——这是方差的第一来源,也是”换代不可比”的量化依据 |
| 换评分量表 | 测得的偏差移动最多 0.93 点(1–7 量表) | 除 prompt 模板外,manifest 要新增 rating_scale 字段 |
| 换用详细 prompt | 宽松度下降 28.9 个百分点 | prompt 模板版本必记 |
| 降低推理强度 | 既不影响准确率也不影响宽松度 | 可省预算项(见下) |
这三条把 29.7.5 的 manifest 字段建议从”模型名 + 版本号 + prompt 模板版本 + 温度与重复次数”扩成”模型身份 + 量表 + prompt 模板 + 推理强度”四项:量表的位移幅度(0.93 点)虽然小于换模型(56.1pp),但足以翻转一个”有没有偏差”的定性结论,而它此前完全不在记录范围内。
对照着看还有一条反直觉、可以直接省钱的观察:降低推理强度(reasoning effort)既不影响准确率也不影响宽松度,因此在批量判定场景可以在保住质量的前提下降推理档位。
⚠️ 限定:该研究的结论只在上述两个任务(情感/毒性标量打分、QA 二分类)上验证过,且所有数值为论文自报。推理强度那一条尤其要标任务范围——换到开放式长答案评分或推理链路判定上是否仍成立,尚未验证。
基线可信度参考。 同一研究给出的两个”平均水平”数字可以作为判定的参照:评分任务上 judge 与人类金标虽有显著分歧,但平均绝对偏差只有 0.11 点(1–7 量表),多数 judge 仍可认为可靠;二分类准确率任务的平均准确率为 96.5%。也就是说,设计选择造成的位移(56.1pp / 0.93 点)远大于 judge 本身的平均误差(0.11 点)——这是”judge 必须版本化”最直接的论据:误差的主要来源不是 judge 不准,而是配置在变。
29.8.5 认知演变时间线
| 时间 | 认知阶段 | 代表性实践 |
|---|---|---|
| 2020 | ”loss 下降就好了” | GPT-3 只报告了训练 loss 曲线 |
| 2021-2022 | ”用 benchmark 评估” | 但很快发现 benchmark 泄露问题 |
| 2023 | ”系统化消融实验” | DCLM 建立了数据消融方法论 |
| 2024 | ”数据管线的 CI/CD” | FineWeb 展示了完整的迭代闭环;模块化消融方法出现 |
| 2025 | ”多信号融合与动态评测” | FIRE 用 37.5% 数据达到同等性能;LiveBench 动态评测范式兴起;DCR 四级污染风险量化 |
| 2026 | ”推理模型时代的评估危机” | ICLR 2026 揭示 RL 训练系统性掩盖污染信号;IProX 影响力保持代理;AskLLM+Density Sampling 数据高效选择 |
多模态特化:VLM 质量指标与多模态去污染
本章的指标体系(困惑度、启发式、LLM-as-Judge、n-gram 去污染)是围绕纯文本设计的,直接搬到 VLM 会漏掉最关键的一类问题:文本侧指标看不出视觉退化。
质量指标要升级为跨模态一致性。三个可用信号:一是图文对齐分(CLIP 类模型的图文相似度),衡量 caption 与图是否真的相关;二是 OCR 一致性——图内 OCR 出的文字与 caption/答案中提到的文字能否对上,这是检测”图文张冠李戴”最直接的信号;三是可验证性——答案中的实体、数量、颜色、位置能否被检测器或程序复核。
CLIP score 只能软用。它抓的是整体语义相关,对讽刺、隐喻、梗图、反常识合成图会系统性误杀,硬阈值过滤会砍掉恰恰最需要的高阶语义样本。正确做法是软重加权(低分降采样而非删除),与 29.1.3”多信号融合而非并列”一致。
去污染必须双通道。29.6 的 n-gram 去污染只覆盖文本:一条评测样本的题目文本可能被洗得很干净,但它配的那张图早就在预训练图集里躺过了。因此图像侧要再做一遍近重复检测——pHash / dHash 汉明距离 + CLIP 或 DINOv2 嵌入检索的组合(做法见第 7 章),命中的评测样本要么剔除,要么换成未参与训练的图。只看文本通道做去污染,多模态评测分数就是不可信的。
基准选型要防”不看图也能答对”。MMStar 的意义正在于此:它筛掉了纯靠语言先验就能答对的样本,保证每条都真正需要看图。实操建议是常备一组盲测基线(去掉图再跑一遍),两条曲线的差值才是视觉能力的真实量。
VLM-as-Judge 自身的偏差要校准。29.3 的四大陷阱在多模态下全部健在,还多了两个:一是 judge 自己的视觉幻觉(给不存在的细节打高分),二是视觉长度偏差(图更多、描述更长就判优)。校准方法沿用 29.3.5——把它们变成可测量项:构造已知答案的对照集,定期测 judge 的假阳率。
闭环纪律:迭代时必须图文指标一起看。只看文本指标会掩盖视觉退化,这与第 11 章的模态消融、第 15 章的灾难性遗忘监控是同一条原则。
(详见第 26 章多模态数据工程)
本章要点回顾
- 训练 loss 不等于数据质量——低 loss 可能只是因为数据容易预测
- 数据冗余会导致次级缩放——高数据密度让 Scaling Law 变”钝”,去重不只是清理噪声,而是维护 scaling 效率
- 多质量信号应该融合而非并列——FIRE 等框架能用不到 37.5% 的数据达到同等性能
- 三层评估体系(L1/L2/L3)是工业标配——按频率与成本分层,L3 金标校准前两层
- 小模型代理法是当前最实用的评估方案,但有可靠性限制;影响力保持代理(IProX)可能是下一步方向
- 消融实验要分层设计,除了调管线参数,还可以尝试数据子集选择策略;消融成本通常占总预算 30-50%
- LLM-as-Judge 的偏差要在自有配置上实测——一致率报 κ 不报 raw,长度偏差不是常数,四轴体检与偏差注入基准应纳入流程
- Reward hacking 与污染是分数失真的两大来源——reward 突增告警与多层污染检测是必备防线
- Benchmark 去污染是评估的前提,但在推理模型时代面临系统性失效——需要多层次验证,动态评测从源头消除污染
- 数据迭代是持续闭环,不是一锤子买卖;快/中/慢三闭环并行运转
- 数据重写是迭代的新范式——Transform-and-Retain 比排除式过滤更能保留知识价值,尤其适用于数据稀缺场景
- 版本管理(manifest)、数据卡片(Datasheet)与持续监控是数据管线工程化与可审计性的基础