🏠 总目录📚 本教程 12 · 标注预算
📑 本页目录(点开跳转)

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 · 评估与过拟合 留出集和交叉验证的基本功;本章第四节是它在非随机抽样下的失效场景

✅ 检查点

  1. 总预算该切成哪三份?评测集占多少、为什么是第一笔花的钱?
  2. 实跑里,切 200 条给评测集会损失多少模型准确率、换来多大的置信区间?一条都不切会怎样?
  3. 四种选样策略各是什么?为什么 EGL 基本用不起?
  4. 1000 条预算下,不确定性采样和随机采样各是多少?随机采样要标到多少条才追得上?
  5. 为什么 entropy 反而输给了随机采样?该用什么替代?
  6. 什么时候该用 QBC 而不是 margin?
  7. 冷启动的实测数字是什么(从第几条开始主动学习才赢)?判据和三条做法是什么?
  8. 为什么主动学习的标注集不能当评测集?给出那个偏差的具体数字和类别占比的变化。
  9. 预算分配六条规则里,「每轮留 10% 随机名额」有哪两个作用?
  10. 标注的边际收益怎么估?从 8000 到 16000 每千条换多少个百分点?三条停止准则是什么,哪一条最常被忽略?
  11. 那个工单分类的事故里,根因是什么?为什么线上宏 F1 比评测集高 12 个点反而是坏消息?优化方向该按什么排序?
👀 答案
  1. 评测集(15%–25%)+ 种子集(5%–10%)+ 主动池(其余)。评测集必须是第一笔钱,因为等主动学习跑完再从池子里"补一块"当评测集,拿到的一定是被主动学习挑偏过的样本 —— 那就是第六节那个事故。
  2. 切 200 条:模型从 0.7989 掉到 0.7857(只损失 1.3pp),换来 ±5.69pp 的置信区间。一条都不切:模型 0.7989,但完全无法评测 —— 你唯一能用的就是主动学习自己挑出来的那批样本,而那会低估 19 个百分点。
  3. 不确定性采样(least confidence / margin / entropy,默认选择)、多样性采样(聚类 / core-set)、委员会投票 QBC(选分歧最大的)、期望模型改变 EGL(选梯度变化最大的)。EGL 用不起是因为要对每一条候选样本算一次梯度,池子大一点就完全跑不动。
  4. 1000 条时:uncertainty 0.7989 / hybrid 0.7954 / random 0.7501。随机采样标到 2000 条也才 0.7870,还没追上 —— 也就是主动学习省了一半以上的预算
  5. 因为多分类 + 类别不平衡时,熵最大的样本是"三个类都有可能"的边缘垃圾,往往正是第 11 章说的"本来就不该有确定标签"的样本。实测 1000 条时 entropy 只有 0.7299,比随机的 0.7501 还低。⭐ 默认用 margin(最优类与次优类之差)
  6. 模型的概率输出不可信时(没做校准、树模型的 predict_proba 很粗糙)。QBC 用多个模型的分歧代替单模型的概率,稳健得多。概率可信就用 margin —— 实测这份数据上 QBC(0.7607)是输给 margin(0.7989)的。
  7. 24 条时主动学习输 0.0244、36 条输 0.0206、48 条输 0.0104,到 84 条才第一次反超(+0.0111)。判据:先用随机/分层把模型跑到学习曲线的拐点(每类至少 30–50 条,或总预算的 5%–10%);拐点的认法是连续两轮随机采样的提升 < 1pp;⭐ 而且种子集必须覆盖每一个类 —— 缺了某一类,模型不知道它存在,就永远不会去挑它。
  8. 因为主动学习挑的就是难样本,挑完之后这个集合已经系统性地比总体难了。实测:uncertainty 的真实准确率 0.7998,拿标注集自己估是 0.6060,低估 19.4 个百分点;类别占比从总体的 [0.595, 0.301, 0.104] 变成 [0.413, 0.399, 0.188](稀有类从 10.4% 变 18.8%)。这是结构性偏差,方向永远相同,不是随机误差。
  9. 喂模型:让它看到自己不会主动去挑的那些区域;② ⭐ 监控偏差:随机名额那部分样本是无偏的,可以用来估计"主动池到底偏了多少",也是做逆概率加权的前提。
  10. 幂律拟合 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 倍。三条停止准则:①达到事先写好的目标区间 ②边际收益 < 边际成本 ③⭐⭐预测的下一轮提升 < 评测集置信区间的半宽(最常被忽略)—— 这时候就算真提升了也测不出来,该做的不是继续标训练集,是去把评测集加大或修干净
  11. 根因是评测集和训练集来自同一个主动学习管道 —— 评测集里长尾占 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

打卡记录保存在你的浏览器里,首页能看到总进度