金标定向补难例 —— 用信息增益指导数据集扩充
金标定向补难例 —— 用信息增益指导数据集扩充
日期: 2026-06-26 阶段: Phase 1 - 产品定义×评测×可观测底座 标签: #hard-cases #active-learning #golden-dataset #decision-boundary
核心问题
W1 我们造出了 66 案金标(getGoldenDataset),规则基线在三类典型学上 recall 1.0、normal FPR 5.6%。但这个数字有个致命的诱惑:它看起来已经很好,于是想"再多造 200 个案件让数据集更壮"。
这就是本篇要反驳的直觉。盲目扩量(uniform augmentation)几乎不改变 eval 的判别力——你只是在已经判对的区域上反复确认。真正能让 eval "区分出好评测对象与坏评测对象"的,是决策边界附近的难例(hard cases near the decision boundary)。
核心问题落到三个:
- 难例从哪里来?如何系统性地挖,而不是靠运气撞上?
- 为什么"定向补难例"在理论上优于"盲目扩量"?信息增益(information gain)视角怎么解释?
- 合成难例如何保证金标标注正确 + 可复现(seeded)?
这不是抽象方法论。AML 场景里,真正会害死你的不是那笔 6 笔 $9,000 的教科书式 structuring——规则一定抓得到;而是那个 3 笔 $9,500 的合法卖车款(normal 却长得像 structuring)和那个跨度 11 天的 7 笔存款(structuring 却落在规则 10 天窗口之外)。这些边界样本,才是 eval 的金矿。
关键内容
A. 难例挖掘的三条系统化路径
"难例"不是模糊的"难一点的案例",而是有精确定义的三类。逐条说清"怎么挖 + 为什么这样挖"。
A.1 taxonomy 盲区(coverage gap mining)
错误分析(Day 9-11 的 open coding → axial coding)产出了一棵失败 taxonomy。每个失败类是一条"系统在某情形下会犯的错"的断言。盲区挖掘的做法是:对 taxonomy 的每个叶子节点,反问"我的金标里有几个样本能触发它?" 若某个失败类(例如"跨窗口边界的 structuring 被漏报")在金标中样本数为 0,则这个失败类对 eval 完全不可见——评测对象在这里犯错也不会扣分。
盲区挖掘算法:
for leaf in taxonomy.leaves():
coverage[leaf] = count(case in golden where triggers(case, leaf))
if coverage[leaf] < MIN_PER_LEAF: # 经验值:每叶 ≥3,太少则方差大
gap_list.append((leaf, MIN_PER_LEAF - coverage[leaf]))
return sort(gap_list, by=severity * (1/current_coverage))
为什么这样设计:Hamel & Shreya 的错误分析强调"小而互斥、可二元判定的失败类"(2025-09),但 taxonomy 是从已观测的 trace 归纳出来的——它只覆盖你已经见过的失败。盲区挖掘是把 taxonomy 倒过来用:从"已知的失败模式"反推"金标缺哪些样本",把归纳偏差(只见过的才进 taxonomy)补成演绎覆盖。
A.2 决策边界挖掘(decision-boundary mining)
规则引擎的判定阈值是 ASSESS_THRESHOLD = 0.5(typology.ts)。一个 structuring 案件聚合得分 0.6(主规则 STRUCT-01 命中)就过线;0.4(只命中辅规则 STRUCT-02)就掉到 normal。得分落在 0.4–0.6 这条带上的案件,就是决策边界样本——它们的标签对参数的微小扰动最敏感,因此判别价值最高。
主动学习(active learning)的不确定性采样理论说得很直白:模型最不确定的样本就在决策边界附近,对这些样本观测标签能获得关于边界位置最多的信息(Encord 2025;PLOS One 2025-07)。把这套思想搬到 eval 数据集构造上:我们要刻意制造一批"得分贴着 0.5 上下浮动"的案件,让任何调参/换模型/换 prompt 的改动,都会在这批样本上立刻显形。
A.3 对抗扰动(adversarial perturbation)
取一个金标里"判对"的案件,施加一个最小语义保持扰动,看判定是否翻转:
- structuring 案件:把存款窗口从 9 天拉到 11 天(仍是同一笔洗钱意图,但越过规则的 10 天窗口)→ 规则漏报,标签仍是 structuring。这是一个"假阴"难例。
- normal 案件:给合法卖车款加第 3 笔现金存款 → 越过 STRUCT-01 的 ≥3 笔门槛 → 规则误报。这是一个"假阳"难例。
对抗扰动的价值在于它沿着规则的具体阈值方向施力,精准戳中规则的脆弱处,而不是随机噪声。
反直觉洞察①:增加易例不提升判别力,反而稀释它。 假设当前 eval 用 acc 衡量,规则基线在 60 个易例上全对、在 6 个难例上对 3 个 → acc = 63/66 = 95.5%。现在盲目再加 100 个易例(全对)→ acc = 163/166 = 98.2%。数字更漂亮了,但当你换一个更强的评测对象,它在 6 个难例上对 5 个,acc 从 98.2% 升到 98.8%——0.6 个百分点的提升,被 160 个易例的"背景信号"淹没成噪声级别。 易例不携带"区分两个候选系统"的信息,它们只在抬高 baseline、压缩动态范围。判别力 ≈ Δacc/Δ能力 在易例占比升高时单调下降。
B. 为什么"定向补难例"优于"盲目扩量"——信息增益视角
把 eval 看成一次"测量":我们想用金标 D 去估计"系统能力 θ"。一个样本 x 对这次测量的价值,等于观测它的标签能减少多少关于 θ 的不确定性,即互信息 I(θ; y_x | D)。
对一个易例(所有合理系统都判对),y_x 的条件分布几乎是确定的(恒为"对")→ H(y_x | θ) ≈ 0 对所有 θ → I(θ; y_x) ≈ 0。它不携带关于 θ 的信息。
对一个边界难例(弱系统判错、强系统判对),y_x 强烈依赖 θ → H(y_x | θ) 随 θ 变化大 → I(θ; y_x) 大。一个边界样本的信息量可以抵几十个易例。
这就是为什么"定向补难例"是信息论上的最优采样,而盲目扩量是在低信息区做重复抽样。用 expected error reduction 的语言(active learning 经典框架):边界样本的"到决策边界距离"最小、"预测熵"最大,期望误差下降最大(Springer ML 2021;Encord 2025)。
| 扩充策略 | +100 案件成本 | 边界样本占比 | eval 判别力 Δ | 适用场景 |
|---|---|---|---|---|
| 盲目扩量(uniform) | 高(100×标注) | ~5%(自然分布) | ≈0(动态范围被压缩) | ❌ 几乎从不推荐 |
| 分层采样(stratified) | 高 | ~20% | 中 | 想保持类别平衡时 |
| 定向补难例 | 中(20–30 难例即可) | ~80% | 高 | ✅ 默认策略 |
| 纯对抗(adversarial only) | 低 | 100% | 高但偏 | 已知具体脆弱点时 |
成本-收益的关键数字:用 20–30 个定向难例达到的判别力,盲目扩量需要 300+ 易例都达不到——因为后者的判别力上限被易例占比锁死。
C. 难例的金标标注与可复现(seeded)
合成难例最大的陷阱不是"造不出来",而是"造出来后标错 + 造出来后复现不了"。
C.1 标注必须与扰动解耦。 对抗扰动改的是特征(存款窗口天数、存款笔数),不改意图标签。把卖车款从 2 笔加到 3 笔,它仍然是 normal——加的是"合法的第三笔车款尾款",洗钱意图为零。金标标签 = 生成时的真实意图,而非规则的判定结果。 这条纪律在 generator.ts 里已经体现:genNormalCase 里那个 cashBiz 现金密集型商户案件,label 写死 'normal',哪怕规则会把它误报为 structuring——标签是 ground truth,规则命中是被测对象的输出,二者必须严格分离。
反直觉洞察②:用规则的判定结果当金标标签,会制造"自证循环"。 如果你"造一批规则判为 structuring 的案件,然后标成 structuring",那 eval 永远 100% recall——你测的不是系统对不对,而是系统和它自己一不一致。难例尤其危险:边界样本的规则判定本来就摇摆,拿它当标签等于把噪声固化成真理。金标标注必须回到人类专家定义的"真实意图",规则碰不到这条线。
C.2 可复现靠 seed,不靠运气。 项目所有合成都走 createPrng(seed)(确定性 PRNG,与 dsdb-lab ledger 同一套)。难例集必须是新 seed 的独立数据集,而不是在 aipa-golden-v1 上随机追加——否则一改追加逻辑,前面 66 案的 PRNG 流全部错位,整个 v1 金标都变了。正确做法是把难例做成 getGoldenDatasetV11:基于 v1 + 一个 aipa-hardcases-v1 seed 的难例批,v1 案件的 id/PRNG 流原封不动,难例 caseSeq 从 v1 末尾续号。
C.3 每个难例带"难在哪"的元数据。 不只是 label,还要标 hardClass(属于哪个失败类)和 expectFail(预期规则基线在这里会犯哪种错)。这样 eval 报告能输出"难例上的失败分布",而不只是一个聚合 recall。
难例生成(seeded、标签解耦)伪代码:
hardCases = []
prng = createPrng('aipa-hardcases-v1')
for (failClass, targetCount) in gapList: # 来自 A.1 盲区挖掘
for i in range(targetCount):
base = pickGoldenCaseFor(failClass.typology) # 取一个判对的种子案
perturbed = applyPerturbation(base, failClass, prng) # A.3 对抗扰动
perturbed.label = base.intentLabel # ← C.1 标签来自意图,非规则
perturbed.hardClass = failClass.id
perturbed.expectFail = failClass.expectedError # 'false_negative'|'false_positive'
hardCases.append(perturbed)
assert allDeterministic(hardCases, seed='aipa-hardcases-v1') # C.2 可复现断言
return hardCases
| 难例类型 | 扰动方向 | 目标失败类 | 金标 label | 预期规则行为 |
|---|---|---|---|---|
| 跨窗口 structuring | 存款窗口 9→11 天 | 边界漏报 | structuring | 假阴(STRUCT-01 窗口外) |
| 三笔合法车款 | normal +1 笔现金 | 现金密集误报 | normal | 假阳(凑够 ≥3 笔) |
| 短链 layering | 链长 3→2 账户 | 链长不足漏报 | layering | 假阴(< 3 账户门槛) |
| 慢速 mule | fan-in 跨度 13→16 天 | 窗口外 fan-in | mule_network | 假阴(超 14 天窗口) |
| 整百非整千过账 | 金额 $25k→$24.5k | 整数特征失效 | layering | LAYER-02 漏命中 |
| 边界得分 normal | 单笔 $9,500 现金 | 0.4 分掉线 | normal | 真阴但贴边界 |
设计要点
| 决策 | 选择 | 理由 |
|---|---|---|
| 扩充策略 | 定向补难例(非盲目扩量) | 信息增益视角:易例 I(θ;y)≈0 |
| 难例来源 | taxonomy 盲区 + 边界 + 对抗 三路 | 覆盖归纳盲区 + 最大判别价值 + 精准戳脆弱点 |
| 数据集组织 | 新 seed 独立批 v1.1,v1 不动 | 保护 PRNG 流确定性,避免回归基线漂移 |
| 标签来源 | 生成时的真实意图,非规则判定 | 切断自证循环 |
| 元数据 | 每难例带 hardClass + expectFail | 支持"难例上的失败分布"报告 |
| 难例数量 | 每盲区叶 ≥3,总量 20–30 即可 | 判别力来自质,不来自量 |
对本项目的落地
-
新增
getGoldenDatasetV11(src/aml/generator.ts):在现有getGoldenDataset()(66 案,aipa-golden-v1)基础上,追加一个aipa-hardcases-v1seed 的难例批。关键约束:v1 的GOLDEN_SEED/GOLDEN_COUNTS/caseSeq流必须一字不改——generateDataset对 v1 的输出保持 byte-identical,难例只能续号在后。这样 W1 进 CI 的基线(recall 1.0×3、normal FPR 5.6%)不会因扩充而无声漂移。 -
新增难例生成器函数:仿照
genStructuringCase/genNormalCase的模式,写genCrossWindowStructuring(窗口拉到 11 天)、genThreeLegitCarPayments(normal +1 笔现金,复用现有cashBiz思路)、genShortChainLayering(nAccounts=2)。每个函数的label字段写意图标签,而非规则会判出的结果——这正是genNormalCase里cashBiz案件已经在做的(label 恒为'normal',哪怕规则误报)。 -
扩展
AmlCase类型(src/aml/types.ts):可选字段hardClass?: string与expectFail?: 'false_negative' | 'false_positive'。v1 的 66 案不带这两个字段(向后兼容),只有难例批携带。 -
扩展
evalRuleBaseline(src/aml/evalBaseline.ts):在BaselineEval里加一段hardCaseBreakdown——按hardClass分组统计规则基线在难例上的 recall/FP,输出"系统在哪类难例上失分"。这比单一聚合 recall 对 PM 更有用:它直接对应一条可执行的"下一步该补哪条规则"。 -
CI 双门:现有 size-budget gate 之外,加一条"难例回归门"——规则基线允许在难例上失分(难例本来就为暴露脆弱点而生),但失分模式必须稳定(同一 seed 同样的失败集)。一旦某次改动让难例失败集变化,CI 标记"判别行为变了",强制人工确认这是改进还是回归。
参考资料
| 资源 | 类型 | 发布日期 | 链接 |
|---|---|---|---|
| Hamel Husain LLM Evals FAQ(error analysis / open & axial coding / theoretical saturation) | 工程博客原文 | 2025(持续更新) | https://hamel.dev/blog/posts/evals-faq/ |
| Aakash G. AI Evals Masterclass with Hamel & Shreya(轴向编码→可二元判定失败类) | 课程纪要 | 2025-09 | https://www.aakashg.com/ai-evals-masterclass-with-hamel-shreya/ |
| Encord Active Learning in ML: Guide & Strategies 2025(不确定性采样、决策边界) | 工程博客 | 2025 | https://encord.com/blog/active-learning-machine-learning-guide/ |
| PLOS One Enhanced uncertainty sampling with category information | 论文 | 2025-07 | https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0327694 |
| Springer ML How to measure uncertainty in uncertainty sampling for active learning | 论文 | 2021 | https://link.springer.com/article/10.1007/s10994-021-06003-9 |
项目 src/aml/generator.ts(seeded 合成 + label=意图纪律) | 代码 | 本仓库 | — |
SOTA 检查 (2026-06-11)
- 本主题当前 SOTA:错误分析驱动的 eval 数据集构造(Hamel & Shreya 路线)是 2025-2026 AI eval 的主线方法论,OpenAI CPO 2025-08 "PM 最重要技能是写 evals" 进一步把它推成产品岗位的硬技能。难例挖掘的理论底座(active learning / 不确定性采样)则是十余年的成熟框架,2025 仍在迭代(category-aware uncertainty sampling, PLOS One 2025-07)。
- 是否仍是 SOTA:✅ 是。"先错误分析、再定向补数据"是当下主流共识;"盲目扩量"已被明确视为反模式。
- 2026 进展 / 替代:
- LLM 辅助难例生成(用 LLM 沿 taxonomy 盲区批量造对抗样本)开始普及,但与本项目纪律一致的告诫是——"LLM 模拟用户是不可靠代理"(2026-01),难例的金标标注关键评分仍须人工抽检,不能让 LLM 既造样本又当裁判。
- τ²-bench 的 pass^k(2025-06,2026-04 更新 38 模型)把"难"从单样本难度上升到"多次重试的稳定性难度",提示难例不只挑空间维度,也要挑时间/重试维度。
- 本项目对齐:定向难例批 + 标签解耦 + seeded 复现,正是上述 SOTA 在一个确定性、可进 CI 的合成 AML 数据集上的落地;规则基线允许在难例失分但失分模式须稳定,对应 pass^k 的"稳定性即正确性"思想。