🧠 第九章 模型评估和优化
📄 一句话概括

阈值从 0.1 抬到 3.0,精确率从 0.179 升到 1.000,召回从 0.850 掉到 0.020, 误报从 390 个降到 0 个。一个阈值旋钮,四个数一起动,没有免费的方向。

📝 本节和前面几节的关系

上一节说准确率不够用。 这一节讲第一个替代品:精确率(也叫精度),它专门回答「我抓出来的东西里有多少是对的」。

一、精确率是什么

公式:真正例 ÷(真正例 + 假正例)。分母是「模型判为正类的全部样本」。 换个说法:精确率等于 1 减去误报率(这里的误报率指「判为正的里面错的比例」,不是 FPR)。 它衡量的是「可信度」——模型说「这个有问题」的时候,你有几分能信。 和它配套的是召回率:真正例 ÷(真正例 + 假反例)。分母是「全部真正的正类」。 衡量的是「覆盖率」——该抓的抓到了多少。 两个分母不一样,这是它们永远打架的根源。一个看「判出来的」,一个看「本来该判的」。

二、阈值一动,四个数一起动

模型输出的是分数,要变成「是/否」得定个阈值。Demo 扫了 7 个阈值。 阈值 0.1:命中 85、误报 390、精确率 0.179、召回 0.850、误报率 0.433。 阈值 3.0:命中 2、误报 0、精确率 1.000、召回 0.020、误报率 0.000。 中间是连续的过渡。这就是精确率-召回率权衡曲线:往精确那头走,召回必掉;往召回那头走,精确必掉。 没有一个阈值是「理论上最优」的。要定这个数,必须知道误报和漏报各值多少钱。

三、两个场景,两种选法

垃圾邮件过滤:误报的代价是把一封正常邮件扔进垃圾箱,用户可能漏掉面试通知。 漏报的代价只是多看到一封广告。所以这里要精确率优先,阈值往高定。 癌症筛查:误报的代价是做一次复查,抽个血。漏报的代价是错过治疗窗口。 所以这里要召回率优先,阈值往低定。 同样是二分类,同样是「找一个少见的正类」,阈值方向完全相反。所以定阈值不能看指标,得看业务。 做训练师最常犯的错,是拿一个通用阈值(比如 0.5)套到所有任务上。 0.5 只对「训练时用的损失函数、且两类等代价」的情形有意义,换个场景就该重新定。

四、精确率有个假高陷阱

阈值 3.0 那行精确率是 1.000,看着完美。但它只命中了 2 个。 分母太小的时候,精确率极不稳定。抓到 2 个全对是 1.000,抓到 20 个错 1 个是 0.950——后者其实更可信。 所以看精确率一定要同时看分母(或者看命中数)。只看 1.000 就下结论,等于被小样本骗了。 规范做法是给精确率标一个置信区间,或者干脆要求「命中数不少于 N」才报精确率。 这一点在 AB 测试和小流量场景里特别重要。

五、动手 Demo:阈值扫描:精确率从 0.179 升到 1.000,召回从 0.850 掉到 0.020

造 1000 个样本、正类 100 个(10%),正类打分均值 1.1、负类 0,标准差都是 1; 阈值从 0.1 扫到 3.0 共七档,每档算命中数、误报数、精确率、召回率、误报率。

for th in (0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0):
    pred = (score > th).astype(int)
    tp = int(((pred == 1) & (y == 1)).sum())
    fp = int(((pred == 1) & (y == 0)).sum())
    pre = tp / float(tp + fp) if tp + fp else 0.0
    rec = tp / float(tp + fn) if tp + fn else 0.0

实跑输出:

--------------------------------------------------------------------
精确率与召回率:阈值一动,两者互换
--------------------------------------------------------------------
  样本数                   1000,正类 100 个(10%)
  打分分布                 正类 ~ N(1.1, 1),负类 ~ N(0, 1)

  阈值   命中   误报   精确率   召回率   误报率
  --------------------------------------------
   0.1     85    390    0.179    0.850    0.433
   0.5     74    270    0.215    0.740    0.300
   1.0     51    136    0.273    0.510    0.151
   1.5     34     63    0.351    0.340    0.070
   2.0     18     22    0.450    0.180    0.024
   2.5     10      5    0.667    0.100    0.006
   3.0      2      0    1.000    0.020    0.000

  阈值是最直接的旋钮。往下调,命中变多、误报也变多;往上调,两头都变少。
  表里看得很清楚:阈值 0.1 时召回 0.850,但误报了 **390** 个,精确率只有 0.179;阈值 3.0 时误报降到 0 个、精确率 1.000,但召回只剩 0.020。
  没有一个阈值是「最对的」。选哪个取决于误报和漏报哪个更贵——垃圾邮件过滤可以容忍漏一条,但绝对不能把正常邮件扔进垃圾箱(精确率优先);癌症筛查可以容忍误报,但绝对不能漏(召回率优先)。
  这一步是评估里最需要「业务判断」的地方,不是纯技术问题。
  注意精确率在阈值 3.0 那行是 1.000,但这个 1.000 建立在只有 2 个命中的基础上——样本太少时精确率会虚高,别被它骗了。

四个数(命中、误报、精确率、召回率)在同一条阈值线上是联动的。 表里每一行都能看出这个联动,没有例外。 阈值 0.1 那行误报 390 个,占总负类的 43.3%——等于「每两个正常样本就误判一个」。 这种模型上线会被用户骂死,但它召回 0.850,指标上看最「全」。 阈值 3.0 那行精确率 1.000,但只命中 2 个。分母太小,这个 1.000 不可信。 中间几行才是实用区间。 真实项目里通常先定一个业务可接受的误报上限(比如误报率不超过 5%), 然后在满足这个上限的阈值里取召回最高的那个。

30 秒实验

  1. 把正类的打分均值从 1.1 提到 2.0,看还有没有这么陡的权衡曲线。
  2. 加一条规则:只在误报率不超过 0.05 的阈值里选召回最高的,看会选到哪个阈值。
  3. 想一想:为什么精确率的分母是「判为正类的数量」,而不是「真正的正类数量」?

常见误区

  1. 「精确率越高模型越好」——阈值抬到顶,精确率 1.000,但一个正类都抓不到。
  2. 「阈值 0.5 是标准值」——它只对特定损失函数和等代价场景有意义。
  3. 「精确率 1.000 就完美了」——命中只有 2 个时,这个数没有统计意义。
自测三问(点开对答案) 1. 精确率怎么算? —— 真正例除以(真正例加假正例),分母是模型判为正类的全部样本。 2. 为什么精确率和召回率会打架? —— 两者分母不同:精确率看判出来的,召回率看本该判出来的,调阈值必然一升一降。 3. 垃圾邮件过滤和癌症筛查该偏向哪个指标? —— 前者偏精确率(怕误杀正常邮件),后者偏召回率(怕漏诊)。

精确率和召回率总是打架,那就用一个数把它们合起来。下一节讲 F1。

相关笔记