2639 字
约 8 分钟
15
S23-X10 · 评测指标:P/R/F1、Macro-F1 与 AUC 的口径

报出"准确率 96%"而没有标签分布,这句话等于没说。指标的争吵通常不在数学,在口径:同一个 F1,micro 与 macro 能差出二十个点;同一个 AUC,换个人算就是另一个数。下面把口径逐个拆开。


1. 混淆矩阵:所有指标的母体

              预测正        预测负
实际正   TP(命中)      FN(漏报)
实际负   FP(误报)      TN(正确拒绝)
指标 公式 成立前提
Precision TP/(TP+FP) "预测为正"稀缺才有意义,否则被稀释
Recall TP/(TP+FN) 必须有完整标注才能确定正样本全集
Specificity TN/(TN+FP) 负样本远多于正样本时最敏感
F_beta (1+β²)PR/(β²P+R) β 必须固定,否则跨版本不可比
Accuracy (TP+TN)/全体 只有类别平衡才有解释力

2. P 与 R 为什么必须成对

两者方向相反:阈值调低 → FP 增、R 升、P 降。所以单指标存在退化最优解——全判为正则 Recall = 1,只判一个样本则 Precision = 1。单看任一项都能被平凡拉满,这就是必须成对的数学原因;第二层原因是代价不对称。

场景 更贵的错 取向 阈值
垃圾邮件拦截 FP 误杀正常邮件 偏 P 偏高
疾病初筛、风险预警 FN 漏掉高代价样本 偏 R 偏低
交易风控 两者都贵 F-beta 按赔付比定 按金额分层
内容审核 FP 伤创作者、FN 伤合规 双阈值分流 高置信自动、低置信人审

互换的代价不是线性的:Recall 从 0.90 拉到 0.95,常要拿 10 个点以上的 Precision 去换。

3. F1 为什么是调和平均

算术平均对小数不敏感,1.0 与 0.0 的平均是 0.5,看起来"还行";调和平均把小数拖到底,正确表达"一头废了整体就废"。

方案 A:P=0.90, R=0.20
  算术平均 = (0.90+0.20)/2 = 0.55
  F1       = 2·0.90·0.20/(0.90+0.20) ≈ 0.33
方案 B:P=0.60, R=0.54
  算术平均 = 0.57
  F1       = 2·0.60·0.54/(0.60+0.54) ≈ 0.57

算术平均认为 A 与 B 打平,F1 判 B 明显胜出。F1 的偏好是两头都要及格。数字仅为公式演算,不是实测。

平均 性质 何时误导
算术 (P+R)/2 线性 一头近 0 仍给高分
几何 √(PR) 乘法,更严格 报告习惯少见
调和 F1 只对较低项敏感 P、R 都 < 0.5 时仍中等
F-beta β>1 偏 R β 一变等于换尺子

4. micro / macro / weighted

三类样本量:A 950、B 40、C 10。

from sklearn.metrics import precision_recall_fscore_support as prf
macro    = prf(y_true, y_pred, average="macro")     # 各类等权
micro    = prf(y_true, y_pred, average="micro")     # 全局累计 TP/FP/FN
weighted = prf(y_true, y_pred, average="weighted")  # 按支持度加权
平均 算法 在 A950/B40/C10 下的表现
micro 先累计再算 被 950 条大类支配,接近 accuracy
macro 每类算完等权平均 10 条的 C 与 950 条的 A 同权
weighted 按类样本数加权 小类权重被压得极低

macro 惩罚小类可直接演算:F1 分别为 0.96 / 0.50 / 0.30。

macro-F1    = (0.96+0.50+0.30)/3 = 0.587
weighted-F1 = (0.96·950+0.50·40+0.30·10)/1000 = 0.935

同一份预测,报 0.94 还是 0.59,只取决于 average 参数。整体吞吐看 micro;长尾业务看 macro;对齐线上分布看 weighted。

5. 多分类与多标签

维度 多分类 多标签
标签数 恰好 1 0 到 N
混淆矩阵 一个 K×K 装下 逐标签 2×2
micro 含义 与 accuracy 高度重合 标签级整体正确性
关键指标 macro-F1、top-k subset accuracy、Hamming loss、逐标签 F1

多标签的 subset accuracy 要求整条样本全部标签都判对,比逐标签平均严格得多。打标系统常出现逐标签 F1 好看、subset accuracy 只有三成的局面。

6. AUC:定义与乐观偏差

AUC 的含义是随机取一个正样本和一个负样本,模型给正样本打分更高的概率,等价于 Mann-Whitney 统计量,只看排序、与阈值无关。

AUC = Σ_{正}Σ_{负} ( 1[s_pos > s_neg] + 0.5·1[s_pos = s_neg] ) / (n_pos · n_neg)
正:负 AUC 可读性 原因
1:1 与直觉一致 两轴样本量对等
1:10 尚可 轻微稀释
1:10000 明显乐观 FPR 轴前段被单个 FP 撑住

演算:10,000 个负样本里只 1 个 FP 时 FPR = 0.0001,曲线横轴几乎不动,而 TPR 已可到 0.8。AUC 0.95 的模型在 1% 告警预算下可能一文不值,原因就在这里。

from sklearn.metrics import roc_auc_score, average_precision_score
auc = roc_auc_score(y_true, y_score)
ap  = average_precision_score(y_true, y_score)   # AUC-PR 的阶梯积分近似
top = y_score.argsort()[::-1][:200]
precision_at_k = y_true[top].mean()              # 业务只处理前 K 条

7. PR 曲线为什么更适合不平衡

PR 的分母不含 TN。TN 在不平衡场景里是巨大且无信息量的数字,把它删掉,指标才不会被海量负样本稀释。

对比 ROC PR
轴 TPR vs FPR Precision vs Recall
受 TN 影响 强 无
合理基线 0.5 正样本比例
回答的问题 排序能力 告警质量

注意误读:正样本占 2% 时,AP = 0.10 可能已可用,拿 0.5 当及格线会误判。

8. 阈值选择的两个依据

阈值不是模型属性,是部署决策。

threshold_policy:
  basis: cost_ratio        # 或 target_metric
  cost: {fp: 2.0, fn: 60.0}
  expected_cost: "fp*FP(th) + fn*FN(th)"
  target_metric: {metric: recall, floor: 0.92, tie_break: precision}
  procedure:
    - 在验证集上扫阈值,不在训练集
    - bootstrap 给指标加置信区间
    - 记录阈值随漂移的复核周期
依据 输入 失效情形
业务成本比 FP/FN 单次成本 成本靠拍脑袋,阈值不可信
目标指标 硬约束如 Recall ≥ 0.92 约束过高只能靠人审兜底
双阈值 高置信自动 + 低置信人审 没算人工队列容量会爆

9. 生成式任务的指标

任务 指标 硬伤
分类/抽取 P、R、F1 需标注,粒度固定
翻译 BLEU 认可改写;与人工相关性视任务
摘要 ROUGE-N/L 只看词面,不看事实
语义相似 BERTScore 对"数字错了""说法反了"不敏感
可用性 人工评分 贵、方差大,需评分卡

结论只有一句:生成任务必须至少保留一列人工或强判定结果。BLEU 涨而人工跌,常见原因是模型学会抄原文片段拉 n-gram 重叠。

10. 指标涨了但效果变差:四种成因

成因 机制 识别
数据泄漏 特征混入标签派生量、时间穿越 查管道;做时间切分
测试集污染 训练语料含测试原文 n-gram/嵌入近邻去重
指标与目标错位 优化 F1,业务要低漏报 单独埋业务指标
指标被优化 反复对着测试集调参 看实验次数 vs 集大小

第四种最隐蔽:500 条测试集上试 50 组配置取最优,等于在测试集上训练,提升是过拟合测试集的必然结果。

11. 评测集建设

要点 做法 不做的后果
分层抽样 按类别/来源/时段分层,小类保底配额 少数类个位数,方差巨大
标注一致性 双标 + Cohen's κ 抽样核算 标注漂移被当成模型波动
留出集 只跑最终候选 全量集被优化污染
线上抽样 定期回流真实分布样本 集与线上逐渐脱节
def stratified(df, groups, n_total, min_per_group=30, seed=90):
    parts = []
    for key, g in df.groupby(groups):
        n = max(min_per_group, int(round(n_total * len(g) / len(df))))
        parts.append(g.sample(min(n, len(g)), random_state=seed))
    return pd.concat(parts).sample(frac=1, random_state=seed).reset_index(drop=True)

留出集防的是人(研发过程污染),线上抽样防的是世界变了(分布漂移),两者不能互相替代。

12. 排错:六种指标异常

现象 原因 处理
accuracy 很高但业务无效 正样本极少 补报 P/R 与基准率
两版报告差二十个点 macro 与 micro 混用 报告头固定口径
离线涨线上无感 阈值在训练集上调 换验证集或时间后段
换 seed 就波动 单次随机种子 多种子取均值与方差
逐标签 F1 好、整条样本差 多标签只看标签级 补 subset accuracy
PR 面积 0.5 被当及格 基线误用 0.5 基线改为正样本比例

13. 边界:这些场景指标本就该换

场景 分类指标不够用的原因 该补什么
排序/推荐 不看位置 NDCG@k、Recall@k、MRR
长尾多类 macro 仍被多数类噪声影响 每类曲线 + 混淆对分析
自由文本生成 词面指标不看事实 判定模型 + 人工抽样
强成本约束 指标不体现钱 期望成本曲线
在线系统 离线集覆盖不到分布 影子流量、A/B

指标是尺子,口径是刻度。尺子可以换,刻度必须写死在报告抬头。


14. 自测

  1. 正样本占 2% 时全判为负,accuracy 是多少?为什么没有解释力?
  2. P=0.8/R=0.2 与 P=0.5/R=0.5,算两种平均,说明 F1 的偏好。
  3. 三类 950/40/10,F1 为 0.96/0.50/0.30,macro 与 weighted 各是多少?
  4. "随机正负样本对排序正确概率"这一定义,为什么在不平衡下会乐观?
  5. AUC-PR 的及格基线为什么是正样本比例?
  6. 500 条集上试 50 组配置取最优,指标提升说明了什么?
S23-X10 · 评测指标:P/R/F1、Macro-F1 与 AUC 的口径
https://www.clxhxhhr.top/posts/6002/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。