📑 本页目录(点开跳转)
12 · 标注预算与主动学习
⏱ 64 分钟 | ⭐ 钱有限的时候,「标哪些」比「标多少」值钱
🎯 一句话
标注预算的第一个决定不是"标多少条",是"这笔钱怎么切"。 选样策略能让同样的预算买到接近两倍的模型效果 —— 但它有两个陷阱,踩中任何一个,你会亏得比随便乱标还多。
💰 一、先把预算切成三份,而不是一份
绝大多数团队的做法是:拿到 10 万块标注费,全砸进训练集, 等模型训完了才想起来「我们拿什么评测?」——然后从训练集里随机切一块出来。 这个顺序是反的。
总预算 1000 条的正确切法:
├─ 评测集 200 条 ⭐ 随机/分层抽样、独立标注、只标一次、锁起来
│ 这是【第一笔】花的钱,不是最后一笔
├─ 种子集 80 条 随机或分层,把第一个模型跑起来
└─ 主动池 720 条 分 8–15 轮迭代花掉
为什么评测集要先花、要占这么多?实跑一遍就清楚了 (总预算固定 1000 条,剩下的全用主动学习):
| 留给评测集 | 训练条数 | 模型准确率 | 评测集 95% CI 半宽 |
|---|---|---|---|
| 0 | 1000 | 0.7989 | 💀 无法评测 |
| 100 | 900 | 0.7946 | ±7.92pp |
| 200 | 800 | 0.7857 | ±5.69pp ⭐ |
| 300 | 700 | 0.7798 | ±4.69pp |
| 400 | 600 | 0.7643 | ±4.16pp |
⭐ 读这张表的方式: 切 200 条给评测集,模型只损失 1.3 个百分点(0.7989 → 0.7857), 换来的是一把 ±5.7 个百分点的尺子。 一条都不切,你的模型高那 1.3 个点 —— 但你永远不知道它高在哪、高多少。 💀 而且这时候你唯一能拿来评测的东西,就是主动学习自己挑出来的那批样本, 那是本章第四节要讲的最贵的一个坑。
💡 实用规则:评测集占总预算的 15%–25%。 预算越小,这个比例反而要越高 —— 因为 CI 的宽度按 √n 收敛, 100 条时是 ±7.9pp,翻到 400 条也只收到 ±4.2pp。 小预算下,你更需要知道自己在哪,而不是多走两步。
🎯 二、四种选样策略(附实跑曲线)
主动学习的假设很朴素:不是每条样本的信息量都一样。 一条模型早就分对的样本,标了也白标。
| 策略 | 怎么选 | 什么时候用 |
|---|---|---|
| 不确定性采样 | 选模型最没把握的:least confidence / margin / entropy | ⭐ 默认选择,最便宜 |
| 多样性采样 | 对未标注池聚类,每簇取代表;或 core-set 覆盖 | 冷启动、数据高度冗余时 |
| 委员会投票(QBC) | 训 N 个不同的模型,选它们分歧最大的 | 单模型不确定性不可靠时 |
| 期望模型改变(EGL) | 选"标了之后会让梯度变化最大"的 | 理论最优,但要对每条样本算一次梯度,贵到基本用不起 |
import numpy as np
from sklearn.cluster import KMeans
def select_batch(model, X_unlab, batch, strategy="hybrid", seed=0):
"""返回 X_unlab 中要送去标注的下标"""
rng = np.random.default_rng(seed)
if strategy == "random":
return rng.choice(len(X_unlab), batch, replace=False)
p = model.predict_proba(X_unlab)
s = np.sort(p, axis=1)
margin = s[:, -1] - s[:, -2] # ⭐ 最优类和次优类的差,越小越纠结
if strategy == "uncertainty":
return np.argsort(margin)[:batch]
if strategy == "entropy":
pp = np.clip(p, 1e-9, 1)
return np.argsort(-(-(pp * np.log(pp)).sum(1)))[:batch]
# hybrid:先按不确定性取 5 倍候选,再聚类去重,保证批内不都是同一类样本
cand = np.argsort(margin)[:batch * 5] # ⭐ 5 倍是经验值
km = KMeans(n_clusters=batch, n_init=3, random_state=seed).fit(X_unlab[cand])
out = []
for k in range(batch):
m = np.where(km.labels_ == k)[0]
if len(m) == 0:
continue
d = ((X_unlab[cand[m]] - km.cluster_centers_[k]) ** 2).sum(1)
out.append(cand[m[d.argmin()]]) # 每簇取离簇心最近的
return np.array(out)
# ⚠️ 一个批次里全是"同一个难点"的样本,等于只标了一条 —— 这就是要加聚类的原因
实跑(16000 条池、3 分类、类别 60/30/10 不平衡、测试集 6000 条, 种子 40 条、每轮 40 条、3 个随机种子平均):
| 预算 | random | uncertainty | entropy | QBC | diversity | hybrid |
|---|---|---|---|---|---|---|
| 120 | 0.6351 | 0.6476 | 0.6271 | 0.6433 | 0.6288 | 0.6578 |
| 320 | 0.6850 | 0.7210 | 0.6763 | 0.6902 | 0.6873 | 0.7246 |
| 600 | 0.7213 | 0.7643 | 0.7038 | 0.7190 | 0.7138 | 0.7652 |
| 1000 | 0.7501 | 0.7989 | 0.7299 | 0.7607 | 0.7358 | 0.7954 |
对照组(随机采样要标多少才能追上):
随机 1000 条 → 0.7501
随机 2000 条 → 0.7870 ← 还没追上主动学习的 1000 条
随机 4000 条 → 0.8162
全量 16000 条 → 0.8598(天花板)
⭐ 核心结论:不确定性采样标 1000 条,效果超过随机采样标 2000 条。 在 ¥1.2/条 的价位上,这一条差别就是 1200 块 vs 2400 块。 而实现它只需要上面那 20 行代码。
⚠️ 但注意 entropy 那一列:它比随机采样还差(1000 条时 0.7299 vs 0.7501)。 原因很具体:多分类 + 类别不平衡时,熵最大的样本是"三个类都有可能"的边缘垃圾 —— 它们往往就是第 11 章说的那种"本来就不该有确定标签"的样本。 ⭐ 默认用 margin(最优类和次优类的差),不要用 entropy。
💡 QBC 在这份数据上只有 0.7607,输给单模型的不确定性。 它的价值不在准确率,在稳健性:当你的模型概率输出不可信(没校准、树模型的
predict_proba很粗糙)时,多个模型的分歧比单个模型的概率靠谱得多。 概率可信 → 用 margin;概率不可信 → 用 QBC。
⚠️ 三、陷阱一:冷启动
主动学习有一个循环依赖:它靠模型来挑样本,而模型靠样本变强。 一开始模型很差的时候,它挑出来的东西没有价值 —— 甚至有负价值。
实跑(种子集只有 12 条、每轮 12 条、5 个随机种子平均):
| 已标注 | random | uncertainty | 差 |
|---|---|---|---|
| 24 | 0.5332 | 0.5088 | −0.0244 ⚠️ |
| 36 | 0.5716 | 0.5509 | −0.0206 ⚠️ |
| 48 | 0.5936 | 0.5832 | −0.0104 ⚠️ |
| 60 | 0.5955 | 0.5953 | −0.0003 |
| 72 | 0.6035 | 0.6029 | −0.0006 |
| 84 | 0.6106 | 0.6218 | +0.0111 ← 从这里才开始赢 |
| 120 | 0.6294 | 0.6472 | +0.0178 |
| 180 | 0.6503 | 0.6740 | +0.0237 |
⭐ 一句话记住冷启动: 模型还不行的时候,它说的"我不确定"只是"我自己乱"。 前 60 条里,主动学习不但没帮忙,还平均每轮亏 1–2 个百分点。
判据和做法:
① 先用【随机或分层】把模型跑到学习曲线的拐点
经验值:每个类至少 30–50 条,或总预算的 5%–10% ⭐
② 拐点怎么认:连续两轮随机采样带来的提升 < 1 个百分点
—— 说明"随便标一条"的边际收益已经降下来了,该换策略了
③ 种子集必须【覆盖到每一个类】
💀 种子集里缺了某一类 → 模型不知道它存在 → 永远不会去挑它
⭐ 稀有类必须【分层】进种子集,不能靠随机撞
💡 好消息:不确定性采样对稀有类是天然友好的。 实测:96 条预算用完时,随机采样的标注集里稀有类占 10.4%(和总体一样), 不确定性采样是 14.8% —— 它自动多标了 40% 的稀有类样本。 但这只在种子集里已经有这一类的前提下成立。
💀 四、陷阱二:采样偏差 —— 标注集不能再当评测集用
这是本章最贵的一条,也是最反直觉的一条。
主动学习挑的就是难样本。挑完之后,这个集合已经不代表总体了。
实跑(1000 条预算跑完之后,拿标注集自己做 5 折交叉验证来估准确率):
| 策略 | 真实测试准确率 | 拿标注集自己估 | 偏差 | 标注集类别占比 |
|---|---|---|---|---|
| random | 0.7587 | 0.7390 | −0.0197 | [0.575, 0.304, 0.121] |
| uncertainty | 0.7998 | 0.6060 | −0.1938 💀 | [0.413, 0.399, 0.188] |
| QBC | 0.7667 | 0.6180 | −0.1487 💀 | [0.352, 0.360, 0.288] |
(总体的真实类别占比是 [0.595, 0.301, 0.104])
💀 看 uncertainty 那一行:模型的真实准确率是 0.7998, 但拿它自己的标注集去估,得到的是 0.6060 —— 低估了 19.4 个百分点。 而且类别占比也全变了:稀有类从总体的 10.4% 变成标注集里的 18.8%。 ⭐⭐ 这不是随机误差,是结构性的:主动学习就是在系统性地挑难样本, 所以它的标注集必然比总体难。偏差的方向永远是同一个。
三条必须遵守的推论:
① 评测集必须在主动学习【开始之前】就随机/分层抽好、独立标注、锁死
⚠️ 事后从主动池里"补一块"当评测集,是最常见也最贵的错误
② 主动学习的标注集不能用来算任何【总体统计量】:
· 类别先验 / 正样本率 · 阈值标定(0.5 该定在哪)
· 校准曲线 / ECE · 「线上会遇到多少这种样本」
③ 非要用它做统计,就必须记录每一条样本【被选中的概率】,
然后做重要性加权(逆概率加权)—— ⭐ 所以每轮留一部分随机名额,
不只是为了保险,也是为了让这件事在数学上还有救
⭐ 这和第 15 章是同一件事的两个面: 那一章讲"数据是怎么被自然筛选偏的",这一章讲"我们主动把它筛偏了"。 区别只在于:主动学习的偏差是你自己造成的,所以它是可以被记录下来的 —— 只要你在每一轮记下选中概率,这份偏差就是可修的;不记,就永远修不了。
🛑 读到这里可以停 —— 前半章讲完了(约 24 分钟)。 后半章还有:一份可以直接抄的预算分配规则 · 什么时候该停:标注的边际收益 · 事故复盘:省下来的钱,赔在了评测集上 回来的时候不用重读,直接从下一节接着看就行。
📐 五、一份可以直接抄的预算分配规则
① 评测集优先,占总预算 15%–25%
随机或分层抽样、独立标注、多人交叉、锁死不动 ⭐⭐ 第一笔钱
② 种子集占 5%–10%,用分层随机,保证【每类至少 30 条】
③ 剩下的分 8–15 轮
· 轮次太少(2–3 轮)→ 退化成随机采样,白搭一套系统
· 轮次太多(每轮 10 条)→ 重训成本吃掉全部收益
· 经验:每轮不超过剩余预算的 15%
④ 默认策略:margin 不确定性 + 聚类去重(hybrid)
⑤ 每轮留 10% 的名额给【随机抽样】 ⭐ 这是保险丝
它同时干两件事:喂模型看没见过的区域 + 监控偏差有多大
⑥ 预算 < 500 条时,别上主动学习,直接分层随机
—— 冷启动会吃掉全部收益,而且你没有余量试错
两条容易被忽略的成本侧规则:
| 情况 | 该怎么做 |
|---|---|
| 标注成本不均匀(有的样本 20 秒,有的 5 分钟) | ⭐ 按每元信息量排序,不是每条信息量:得分 / 预估耗时 |
| 一条样本该标几遍 | 黄金集多人标 + 仲裁;普通样本一人标。多标一遍的边际收益,几乎总是低于多标一条新样本 |
| 类目极多(几十上百) | 先标粗粒度,模型稳定后再对混淆最严重的几对做细粒度标注 |
| 有历史遗留标注 | ⭐ 先用第 11 章的置信学习审一遍旧标注,常常比标新的更划算 |
📉 六、什么时候该停:标注的边际收益
「再标一万条能涨多少?」——这个问题几乎每个项目都会被问一次, 而且它是可以估出来的,不需要真的去标。
学习曲线在很宽的范围内都服从幂律:err(n) ≈ a·n^(−b) + c。 拿已有的三四个点拟合一下,就能外推。本章那份数据的实测:
| 标注量 | 准确率 | 相比上一档 | 每多标 1000 条换来 |
|---|---|---|---|
| 1000(主动学习) | 0.7989 | — | — |
| 2000(随机) | 0.7870 | — | — |
| 4000(随机) | 0.8162 | +0.0292 | +1.46pp |
| 8000(随机) | 0.8387 | +0.0225 | +0.56pp |
| 16000(全量) | 0.8598 | +0.0211 | +0.26pp |
⭐ 注意最右边那一列的形状:从 2000 到 4000,每千条换 1.46 个百分点; 从 8000 到 16000,每千条只换 0.26 个百分点 —— 边际收益掉了 5.6 倍。 💀 而标注费是线性的。所以「标注量翻倍」这件事, 在项目早期是最划算的投入,在后期几乎总是最贵的一个选项。
import numpy as np
from scipy.optimize import curve_fit
def extrapolate_budget(ns, accs, targets=(0.86, 0.88, 0.90)):
"""用已有的 (标注量, 准确率) 点拟合幂律,外推到目标准确率需要多少条。
ns/accs 至少要 4 个点,且跨度要大(比如 500 / 1000 / 2000 / 4000)"""
ns, accs = np.asarray(ns, float), np.asarray(accs, float)
f = lambda n, a, b, c: c - a * n ** (-b) # ⭐ c 就是这套数据的天花板
(a, b, c), _ = curve_fit(f, ns, accs, p0=[1.0, 0.3, 0.95], maxfev=20000)
out = {"拟合天花板": round(float(c), 4)}
for t in targets:
out[f"达到 {t}"] = ("永远达不到" if t >= c
else int((a / (c - t)) ** (1 / b))) # ⭐ 反解 n
return out
# ⚠️ 两个使用前提:① 至少 4 个点、跨度覆盖一个数量级
# ② 外推超过现有最大值 3 倍以上就不要信了
拿本章那条随机采样曲线(1000/2000/4000/8000/16000 条)实跑一次:
拟合出的天花板 ....... 0.9437
达到 0.86 需要 ....... 16,494 条
达到 0.88 需要 ....... 41,140 条 ← 比 0.86 多花 2.5 倍的钱
达到 0.90 需要 ...... 145,199 条 ← 比 0.88 再多花 3.5 倍 💀
💀 这三个数字放在一起,就是标注预算谈判时最有说服力的一张图: 从 0.86 到 0.90,准确率只涨 4 个百分点,标注费涨了近 9 倍。 ⭐ 有了它,「我们要做到 95%」这种需求,可以在项目第一周就被定价, 而不是在第三个月发现钱不够。
三条停止准则(满足任意一条就该停):
① 达到目标:模型指标已经进了业务能接受的区间
⭐ 而且这个区间要在项目开始时就写下来,不能事后定
② 边际收益低于边际成本:
再标 1000 条带来的提升 × 单位提升的业务价值 < 1000 条的标注费
③ 提升被评测集的噪声淹没: ⭐⭐ 最常被忽略的一条
预测的下一轮提升 < 评测集置信区间的半宽
—— 这时候你就算真的提升了,也【测不出来】
→ 该做的不是继续标训练集,是【去把评测集加大或修干净】(第 11、13 章)
⭐ 第 ③ 条值得单独强调:本章第一节那张表里,200 条的评测集 CI 半宽是 ±5.69pp。 也就是说:当你预计下一轮只能涨 1 个点时,继续标训练集是没有意义的 —— 那个提升会完整地落在噪声里。 此时同样一笔钱花在扩大评测集上,才是唯一能推进项目的动作。
💀 七、事故复盘:省下来的钱,赔在了评测集上
系统:企业工单自动分类(38 个类目,路由到不同处理组)
预算:一期 12 万元标注费,¥1.2/条,约 10 万条
团队:2 个算法,3 个月
发生了什么
团队用不确定性采样跑了 8 轮,标了 9.4 万条, 剩下 6 千条当评测集 —— 而这 6 千条是从"最后一轮主动学习已选出、但预算用完没标" 的池子里拿的。
| 时间 | 事件 |
|---|---|
| 第 4 周 | 主动学习跑完,评测集上宏 F1 0.71。团队觉得偏低,但"38 类本来就难" |
| 第 5–16 周 | 按评测集的错误分布安排优化:三个月全花在长尾类目上 |
| 第 17 周 | 上线。线上实测宏 F1 0.83(比评测集高 12 个点,被解释为"线上样本简单") |
| 第 22 周 | 客服组投诉路由错得越来越多。查下来:微 F1 只有 0.79,而且还在跌 |
为什么没被发现
① 评测集和训练集来自【同一个主动学习管道】 ⭐⭐ 根因
→ 评测集里长尾类目占 41%,而线上只占 6%
→ 这套评测集测的是"模型在最难的样本上怎么样"
而不是"模型在真实流量上怎么样"
② 只看宏 F1(每个类目权重相同),没有按【流量占比】加权
→ 占线上 63% 流量的三个头部类目,在评测集里只占 9%
→ 它们的错误率从上线时的 4.2% 一路涨到 7.8%,评测集完全看不见
③ 从来没有人拿一份【随机抽样】的样本核对过评测集的类目分布
—— 这件事只需要半天
代价
方向错配 2 人 × 3 个月,全用在占 6% 流量的长尾上
人工返工 头部类目路由错误约 1.1 万单 × 多花 6 分钟 ≈ 1,100 工时
重建评测集 6,000 条随机分层重标 + 仲裁,¥7.2 万,3 周
⭐ 讽刺的一笔账:
主动学习确实省了钱 —— 同样效果少标约 4 万条 ≈ 省 ¥4.8 万
但评测集选错造成的返工和方向错配,代价是这笔钱的两倍以上
该补什么
| 缺失 | 补上之后 |
|---|---|
| 评测集从主动学习管道里出 | ⭐⭐ 评测集在项目第一天就按线上流量分层随机抽好、独立标注、锁死 |
| 评测集类目分布没人核对过 | 每次评测集变更,打印类目分布 vs 线上流量分布,JS 散度 > 0.15 拦住 ⭐ |
| 只看宏 F1 | 宏 F1 和流量加权的微 F1 一起报;两者差 > 5pp 就必须解释 |
| 主动学习没留随机名额 | 每轮留 10%,这批随机样本同时是偏差监控的探针 |
| 优化方向按评测集错误分布定 | 按 错误率 × 流量占比 × 单次错误代价 排序,不是按错误率排 ⭐⭐ |
💀 一句话总结这个事故: 主动学习让训练集偏向难样本,这是特性; 让评测集也偏向难样本,这是事故。 ⭐ 两者的分界线,就是"评测集有没有在主动学习开始之前被锁死"这一件事。
🔗 这一章连到哪里
| 去哪 | 为什么 |
|---|---|
| 第 11 章 · 标签噪声 | 上一章的结论「先修评测集再修训练集」,这一章给出具体的切法 |
| 第 13 章 · 评测集也是数据 | ⭐ 那 15%–25% 的评测预算具体怎么花:抽样、大小、污染、置信区间 |
| 第 15 章 · 采样偏差与幸存者偏差 | ⭐⭐ 第四节那个 −19.4 个百分点的偏差,机制和那一章讲的完全一样 |
| 第 9 章 · 标注体系设计 | 预算的另一半:标注指南、类目体系、单条成本从哪来 |
| 推荐算法 13 · 冷启动与探索利用 | ⭐ 「探索 vs 利用」和「随机名额 vs 主动选样」是同一个权衡的两种语境 |
| 模型上线之后 16 · 重训练 | 上线之后标注预算怎么持续投:增量标注和重训练的节奏 |
| ML 基础 05 · 评估与过拟合 | 留出集和交叉验证的基本功;本章第四节是它在非随机抽样下的失效场景 |
✅ 检查点
- 总预算该切成哪三份?评测集占多少、为什么是第一笔花的钱?
- 实跑里,切 200 条给评测集会损失多少模型准确率、换来多大的置信区间?一条都不切会怎样?
- 四种选样策略各是什么?为什么 EGL 基本用不起?
- 1000 条预算下,不确定性采样和随机采样各是多少?随机采样要标到多少条才追得上?
- 为什么 entropy 反而输给了随机采样?该用什么替代?
- 什么时候该用 QBC 而不是 margin?
- 冷启动的实测数字是什么(从第几条开始主动学习才赢)?判据和三条做法是什么?
- 为什么主动学习的标注集不能当评测集?给出那个偏差的具体数字和类别占比的变化。
- 预算分配六条规则里,「每轮留 10% 随机名额」有哪两个作用?
- 标注的边际收益怎么估?从 8000 到 16000 每千条换多少个百分点?三条停止准则是什么,哪一条最常被忽略?
- 那个工单分类的事故里,根因是什么?为什么线上宏 F1 比评测集高 12 个点反而是坏消息?优化方向该按什么排序?
👀 答案
- 评测集(15%–25%)+ 种子集(5%–10%)+ 主动池(其余)。评测集必须是第一笔钱,因为等主动学习跑完再从池子里"补一块"当评测集,拿到的一定是被主动学习挑偏过的样本 —— 那就是第六节那个事故。
- 切 200 条:模型从 0.7989 掉到 0.7857(只损失 1.3pp),换来 ±5.69pp 的置信区间。一条都不切:模型 0.7989,但完全无法评测 —— 你唯一能用的就是主动学习自己挑出来的那批样本,而那会低估 19 个百分点。
- 不确定性采样(least confidence / margin / entropy,默认选择)、多样性采样(聚类 / core-set)、委员会投票 QBC(选分歧最大的)、期望模型改变 EGL(选梯度变化最大的)。EGL 用不起是因为要对每一条候选样本算一次梯度,池子大一点就完全跑不动。
- 1000 条时:uncertainty 0.7989 / hybrid 0.7954 / random 0.7501。随机采样标到 2000 条也才 0.7870,还没追上 —— 也就是主动学习省了一半以上的预算。
- 因为多分类 + 类别不平衡时,熵最大的样本是"三个类都有可能"的边缘垃圾,往往正是第 11 章说的"本来就不该有确定标签"的样本。实测 1000 条时 entropy 只有 0.7299,比随机的 0.7501 还低。⭐ 默认用 margin(最优类与次优类之差)。
- 模型的概率输出不可信时(没做校准、树模型的
predict_proba很粗糙)。QBC 用多个模型的分歧代替单模型的概率,稳健得多。概率可信就用 margin —— 实测这份数据上 QBC(0.7607)是输给 margin(0.7989)的。 - 24 条时主动学习输 0.0244、36 条输 0.0206、48 条输 0.0104,到 84 条才第一次反超(+0.0111)。判据:先用随机/分层把模型跑到学习曲线的拐点(每类至少 30–50 条,或总预算的 5%–10%);拐点的认法是连续两轮随机采样的提升 < 1pp;⭐ 而且种子集必须覆盖每一个类 —— 缺了某一类,模型不知道它存在,就永远不会去挑它。
- 因为主动学习挑的就是难样本,挑完之后这个集合已经系统性地比总体难了。实测:uncertainty 的真实准确率 0.7998,拿标注集自己估是 0.6060,低估 19.4 个百分点;类别占比从总体的 [0.595, 0.301, 0.104] 变成 [0.413, 0.399, 0.188](稀有类从 10.4% 变 18.8%)。这是结构性偏差,方向永远相同,不是随机误差。
- ① 喂模型:让它看到自己不会主动去挑的那些区域;② ⭐ 监控偏差:随机名额那部分样本是无偏的,可以用来估计"主动池到底偏了多少",也是做逆概率加权的前提。
- 用幂律拟合
err(n) ≈ a·n^(−b) + c,拿已有的四个以上、跨度覆盖一个数量级的点外推。实测:2000→4000 时每千条换 +1.46pp,8000→16000 时只换 +0.26pp,边际收益掉了 5.6 倍,而标注费是线性的。拟合出的天花板 0.9437:达到 0.86 要 16,494 条、0.88 要 41,140 条、0.90 要 145,199 条 —— 涨 4 个百分点,钱涨近 9 倍。三条停止准则:①达到事先写好的目标区间 ②边际收益 < 边际成本 ③⭐⭐预测的下一轮提升 < 评测集置信区间的半宽(最常被忽略)—— 这时候就算真提升了也测不出来,该做的不是继续标训练集,是去把评测集加大或修干净。 - 根因是评测集和训练集来自同一个主动学习管道 —— 评测集里长尾占 41% 而线上只占 6%,它测的是"模型在最难的样本上怎么样"而不是"在真实流量上怎么样"。线上比评测集高 12 个点不是好消息,是评测集偏难的证据 —— 它说明这把尺子根本没在量线上那批样本,于是占 63% 流量的头部类目错误率从 4.2% 涨到 7.8% 全程无人发现。优化方向该按
错误率 × 流量占比 × 单次错误代价排序,而不是按错误率排。
🛑 可以停在这里
⚡ 走神救援
⭐预算的第一个决定不是"标多少"是"怎么切":评测集 15–25%(第一笔钱,随机/分层、独立标注、锁死)+ 种子集 5–10% + 主动池其余。实跑:总预算 1000 条时切 200 条给评测集,模型只从 0.7989 掉到 0.7857(−1.3pp),换来一把 ±5.69pp 的尺子;💀一条都不切,模型高 1.3 个点但你永远不知道它在哪 —— 而且那时你唯一能用的评测数据就是主动学习自己挑的那批,会低估 19 个点。⭐预算越小,评测集比例反而要越高(CI 按 √n 收敛,100 条 ±7.9pp、400 条也才 ±4.2pp)。四种选样:不确定性(least confidence/margin/entropy)、多样性(聚类/core-set)、QBC 委员会投票、EGL(理论最优但要对每条样本算梯度,⚠️基本用不起)。实跑(16000 池、3 分类、1000 条预算):⭐uncertainty 0.7989 / hybrid 0.7954 / QBC 0.7607 / random 0.7501 / diversity 0.7358 / entropy 0.7299;对照:随机标 2000 条也才 0.7870,还没追上主动学习的 1000 条 —— 主动学习省了一半以上预算,实现只要 20 行代码。⚠️但 entropy 比随机还差,因为多分类不平衡时熵最大的样本是"三类都有可能"的边缘垃圾(正是第 11 章说的"不该有确定标签"的样本)——⭐默认用 margin,不要用 entropy;概率不可信(没校准、树模型 predict_proba 很粗)时才换 QBC。批内还要加聚类去重,否则一批全是同一个难点的样本,等于只标了一条。⚠️陷阱一:冷启动 —— 实测种子 12 条、每轮 12 条时,24 条时主动学习输 0.0244、36 条输 0.0206、48 条输 0.0104,到 84 条才第一次反超。⭐一句话:模型还不行的时候,它说的"我不确定"只是"我自己乱"。做法:先用随机/分层把模型跑到学习曲线拐点(每类至少 30–50 条,或总预算 5–10%),拐点的认法是连续两轮随机采样提升 < 1pp;💀种子集必须覆盖每一个类,缺了某一类模型就永远不会去挑它。好消息:不确定性采样对稀有类天然友好(96 条时稀有类占比 14.8% vs 总体 10.4%)。💀💀陷阱二:采样偏差 —— 标注集不能再当评测集。实跑:uncertainty 的真实准确率 0.7998,拿标注集自己 5 折估只有 0.6060,低估 19.4 个百分点;类别占比从总体 [0.595, 0.301, 0.104] 变成 [0.413, 0.399, 0.188]。⭐⭐这是结构性偏差不是随机误差:主动学习就是在系统性挑难样本,偏差方向永远相同。三条推论:①评测集必须在主动学习开始之前就抽好锁死(事后补一块是最贵的错)②标注集不能算任何总体统计量(类别先验、阈值标定、校准曲线、ECE)③非要用就得记录每条的选中概率做逆概率加权。六条预算规则:评测集优先 15–25%、种子集分层每类 ≥30 条、分 8–15 轮(太少退化成随机、太多重训成本吃掉收益,每轮 ≤ 剩余预算 15%)、默认 margin+聚类、⭐每轮留 10% 随机名额(既喂模型也当偏差探针)、预算 < 500 条别上主动学习。成本侧:标注耗时不均时按
得分 ÷ 预估耗时排;黄金集多人标、普通样本一人标;⭐有历史遗留标注时,先用第 11 章的置信学习审旧标注常常比标新的划算。📉什么时候该停:学习曲线服从幂律err(n) ≈ a·n^(−b) + c,四个点就能外推。实测2000→4000 每千条换 +1.46pp,8000→16000 只换 +0.26pp(掉 5.6 倍),而标注费是线性的;拟合天花板 0.9437 时,达到 0.86 要 16,494 条、0.88 要 41,140 条、0.90 要 145,199 条 —— 涨 4 个点,钱涨近 9 倍,这张表能在项目第一周就给"我们要做到 95%"定价。三条停止准则:达到事先写好的目标 / 边际收益 < 边际成本 / ⭐⭐预测的提升 < 评测集 CI 半宽(最常被忽略 —— 200 条评测集半宽 ±5.69pp,此时再标训练集毫无意义,该花的钱是扩大或修干净评测集)。💀事故:工单分类 38 类,9.4 万条主动学习 + 剩下 6 千条从"最后一轮主动学习已选但没标"的池子里拿来当评测集。结果评测集里长尾占 41% 而线上只占 6%,团队按评测集的错误分布把三个月全花在长尾上;线上宏 F1 比评测集高 12 个点,⭐这不是好消息,是评测集偏难的证据;同期占线上 63% 流量的三个头部类目错误率从 4.2% 涨到 7.8% 全程无人发现。代价:2 人 3 个月方向错配、1100 工时返工、重建评测集 ¥7.2 万 + 3 周 —— 主动学习省下的 ¥4.8 万,赔进去两倍以上。改法:评测集第一天按线上流量分层抽好锁死、每次变更打印类目分布 vs 线上分布(JS 散度 > 0.15 拦住)、宏 F1 和流量加权微 F1 一起报、⭐⭐优化方向按错误率 × 流量占比 × 单次错误代价排序。💀总结:主动学习让训练集偏向难样本是特性,让评测集也偏向难样本是事故 —— 分界线就是评测集有没有在主动学习开始前被锁死。
下一节 👉 13-评测集也是数据.md