资讯动态

不平衡分类评估:精准率、召回率、PR曲线与ROC曲线

发布时间:2026/10/2 5:16:33 来源:尧图企业网站定制
但凡做过分类任务只要样本里正负比例不是五五开就一定被精准率、召回率、PR曲线、ROC曲线这四个东西教育过。我早年带过几个刚入门的朋友做反欺诈模型他们第一版报告写着准确率98.7%我看到这个数字就笑了——因为那批数据里欺诈样本只占1.3%模型把所有样本都判成正常准确率照样有98.7%。这就是为什么评估一个分类器光看准确率等于没看。真正能告诉你模型在少数类上到底行不行的是精准率、召回率、PR曲线、ROC曲线这一整套组合拳。这篇东西写给三类人刚学完混淆矩阵还没搞懂指标怎么选的新手、模型上线前要做阈值评审的工程同学、以及被两条ROC曲线完全重合折磨过的同行。我会从业务场景一路讲到代码实现把每个指标背后的取舍逻辑、每一行代码为什么这么写、踩过的坑长什么样全部摊开来讲。1. 从混淆矩阵开始先把四个格子认对1.1 一个信贷审批场景把准确率打回原形设想一个信贷审批场景你需要从一万个申请人里挑出那批真正会逾期的人。这批人只有一百个占比1%。如果你写一个return 0的函数对所有申请人都放款那么这一万个样本里你判对了9900个准确率99%。但你会赔得裤子都没了因为真正会逾期的100个人你一个都没拦住。这就是分类评估的第一个反直觉点准确率把判对正类和判对负类当成同等重要的事情可现实里这两件事的成本往往差着几十倍。放款给一个坏客户损失的是本金错拒一个好客户损失的是一个利息收入加口碑。两者根本不等价。所以评估指标必须能够拆开来看看清楚模型在正类上到底表现如何而拆开的第一刀就是混淆矩阵。混淆矩阵听名字唬人其实就是把你的预测结果和真实标签做一次交叉盘点。真实有是/否两种预测也有是/否两种交叉一下就得到四个格子。四个格子分别装四类样本这个盘点表就是后面所有指标的原始素材任何一个指标都是从这四个数里算出来的没有例外。1.2 四个格子到底怎么记才不会混第一次学的时候TP、FP、FN、TN 这四个缩写特别容易记串。我的记法是先记住后半段的 T/F 表示预测对不对前半段的 P/N 表示模型报的是什么。所以TPTrue Positive模型报正类而且报对了。真正例。FPFalse Positive模型报正类但报错了。假正例也叫误报。FNFalse Negative模型报负类但报错了。假负例也叫漏报。TNTrue Negative模型报负类而且报对了。真负例。再配一个中文记忆口诀报正的里面有多少是真的报负的里面有多少是真的。FP 和 FN 这两个是错误一个叫宁可错杀造成的一个叫放虎归山造成的。搞风控的人最怕 FN把坏人放进去搞内容审核的人最怕 FP把正常内容误封。你看同样是错误业务上怕的往往只有一种这就是评估指标必须拆开的经济学根源。在代码里sklearn.metrics.confusion_matrix返回的是一个二维数组ravel()之后的顺序是 TN, FP, FN, TP。这个顺序在写论文、写报告的时候一定要标清楚因为不同框架的排布顺序并不一致我见过有人直接把 ravel 出来的四个数按 TP, FP, FN, TN 写进 PPT结果整张表全错位被评审当场问住。1.3 为什么样本一不平衡准确率就必然虚高用一点简单的代数就能说清楚。假设正类占比为 π读作 pi准确率可以写成Accuracy π × TPR (1 - π) × TNR其中 TPR 是正类被正确识别的比例TNR 是负类被正确识别的比例。当 π 很小的时候准确率这个数字被 (1-π) × TNR 这一项牢牢主导。哪怕 TPR 掉到 0.1只要 TNR 维持在 0.99准确率依然是 0.99 × 0.99 0.01 × 0.1 ≈ 0.980。也就是说你在正类上做得再烂准确率几乎看不出来。这就是不平衡数据下准确率失真的数学本质它是对两类错误的加权平均而权重恰好等于两类的样本占比。样本占比越偏权重越偏指标就越失真。所以只要正类占比低于二八开我就基本不看准确率了直接上精准率和召回率。这不是经验主义是上面这个公式推出来的必然结果。2. 精准率与召回率一对天生互相拉扯的指标2.1 精准率回答的是抓出来的有多少是真的精准率的定义是Precision TP / (TP FP)看分母就知道它只在被模型报为正类的那批样本里做统计。换句话说它衡量的是报出来的结果有多干净。你如果把模型的判定阈值调得非常高只对得分极其接近1的样本判正那么报出来的样本几乎个个都是真的精准率可以轻松干到95%以上。但代价是什么呢大量真实正类因为分数不够高被放掉了。所以高精准率本身不是功劳它可能只是模型太保守而已。在垃圾邮件拦截、内容风控、司法判决辅助这类场景里精准率往往是第一优先。因为这些场景的共同点是误伤的代价极高把一封正常邮件扔进垃圾箱用户可能就错过了一个工作机会把一条合规内容判定为违规可能引发投诉。这种时候宁可漏一点也不能错杀。2.2 召回率回答的是真的里面抓到了多少召回率的定义是Recall TP / (TP FN)分母是真实正类的总数它衡量的是真实存在的正类被找出多少。在疾病筛查、欺诈识别、设备故障预警这些场景里召回率才是第一优先。因为漏掉一个真实病人、漏掉一笔真实欺诈、漏掉一次即将发生的设备故障后果都可能是灾难性的。这时候逻辑反过来宁可多报一些待人工复核也不能放过。有个非常好用的类比召回率像渔网的密度网眼越小漏网的鱼越少但你捞上来的除了鱼还有一堆水草和垃圾后续分拣成本暴涨。精准率就是捞上来的东西里有多少是真鱼。这两个指标描述的其实是同一个动作的两端你对阈值做任何调整都是在这两端之间滑动不可能同时变好。2.3 为什么这两个指标必然互相拉扯这不是模型能力不足导致的而是阈值单调性造成的数学必然。当你的判定阈值从低往高扫的时候阈值低更多样本被判正TP 增加FP 也增加召回率上升精准率下降。阈值高更少样本被判正TP 减少FP 也减少召回率下降精准率上升。也就是说精准率和召回率作为阈值的函数一个单调不增一个单调不减它们的变化方向天然对立。你在同一个模型上永远可以得到高精准低召回或低精准高召回两套截然不同的报告数字取决于你阈值取在哪。这也是为什么只报一个精准率或者只报一个召回率是没有意义的必须成对报告并且说清楚阈值是怎么定的。我见过一个典型事故某团队离线调参时把阈值定在 0.9 拿到漂亮的精准率上线后发现实际召回只有 0.2业务方炸了。原因就是离线报告只写了精准率没人看召回。教训很直接——任何一次指标评审两个数必须一起出现还得分场景说清哪个是主导。2.4 F1、Fβ 与业务权重的取舍方法当你想用一个数字概括精准率和召回率时最常用的是调和平均 F1F1 2 × P × R / (P R)为什么用调和平均而不是算术平均因为调和平均对短板极其敏感。当 P 和 R 中任何一个接近0时F1 也接近0。这正好符合业务直觉一个极端偏科的模型不该得高分。如果 P1.0R0.1算术平均是0.55看起来还行调和平均算出来只有0.18立刻暴露问题。如果需要调节两个指标的权重用 FβFβ (1 β²) × P × R / (β² × P R)β 的作用是放大召回率的权重。β2 时召回率权重是精准率的4倍适合医疗筛查这类漏报代价高的场景β0.5 时精准率权重是召回率的4倍适合风控拦截这类误报代价高的场景。选 β 的方法不是拍脑袋而是成本换算。假设误报一次的代价是 C_fp漏报一次的代价是 C_fn那么理论上最优的 β 满足β² C_fn / C_fp。举例一条垃圾短信误拦成本约0.1元用户自己能从垃圾箱找回一条诈骗短信漏放成本约50元那么 β² 500β ≈ 22。这个数值看起来夸张但它诚实地反映了业务上两类错误的量级差异比嘴上说召回更重要要有说服力得多。3. PR曲线不平衡数据下的第一选择3.1 PR曲线的本质是把所有阈值扫一遍单个阈值下的精准率和召回率只是一个点把阈值从头到尾扫一遍每个阈值产生一个 (Recall, Precision) 坐标对把这些点连起来就是 PR 曲线。横轴是召回率纵轴是精准率曲线整体位于右上方说明模型强。理解 PR 曲线有一个很好的切入点它是一个模型在所有可能的保守程度下的完整画像。你看这条曲线的时候其实是在问如果我要把召回率做到80%这个模型的精准率能撑到多少这个问题比单点指标有用得多因为它直接对应业务目标。业务方通常会说我们要覆盖90%的欺诈案件那么你就在 PR 曲线上找 Recall0.9 的位置看对应的 Precision 是多少这个数才是可以承诺的。有一条容易踩的坑PR 曲线的形状极度依赖正类比例。同一份数据你把负类样本随机下采样到原来的十分之一曲线会整体上移看起来模型变强了。这不是模型变好了只是分母变了。所以报 PR 曲线的时候必须说明正类占比跨数据集比较毫无意义。3.2 手写一版PR曲线把每行代码讲透自己写一遍比调库更能吃透细节。下面这个实现完全用 numpy不依赖 sklearnimport numpy as np def pr_curve_manual(y_true, y_score): # 把所有出现过的预测分数从高到低排好作为候选阈值 thresholds np.unique(y_score)[::-1] precisions, recalls, ths [], [], [] P np.sum(y_true 1) # 真实正类总数只算一次 for t in thresholds: y_pred (y_score t).astype(int) tp np.sum((y_pred 1) (y_true 1)) fp np.sum((y_pred 1) (y_true 0)) # 一个样本都没报的时候精准率没有定义按惯例记 1.0 precision tp / (tp fp) if (tp fp) 0 else 1.0 recall tp / P precisions.append(precision) recalls.append(recall) ths.append(t) return np.array(recalls), np.array(precisions), np.array(ths)这里有几个细节值得说清楚。第一阈值取np.unique(y_score)而不是等距花点是因为预测分数本身就是离散的等距取点会产生大量重复坐标让曲线出现假的水平段。第二P写在循环外面避免重复计算。第三当tp fp 0时我把精准率记成 1.0这是 sklearn 的约定为的是让曲线从左上角开始画出来更好看但如果你要把这个数值写进报告必须知道它是未定义的不是真实的性能表现。拿到结果以后画图import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [DejaVu Sans] rc, pr, _ pr_curve_manual(y_true, y_score) plt.figure(figsize(6, 5)) plt.step(rc, pr, wherepost, labelModel A) plt.xlabel(Recall) plt.ylabel(Precision) plt.xlim(0, 1.05) plt.ylim(0, 1.05) plt.legend() plt.tight_layout() plt.show()注意这里用的是plt.step而不是plt.plot。因为 PR 曲线在相邻两个阈值之间精准率是保持不变的用折线连接会画出一堆不存在的斜线段视觉上会低估曲线下的面积。这个细节在面试里被问到的概率不低。3.3 sklearn 一行搞定的写法与它的坑实际项目里当然用现成的from sklearn.metrics import precision_recall_curve, average_precision_score precision, recall, thresholds precision_recall_curve(y_true, y_score) ap average_precision_score(y_true, y_score)这里最大的坑是返回数组的长度不一致。precision和recall的长度比thresholds多 1。原因在于 sklearn 在最后补了一个把所有样本都判负的边界点此时 recall0precision1。如果你想把阈值和指标一一对应起来做业务分析必须把最后一个点丢掉代码长这样precision precision[:-1] recall recall[:-1] assert len(precision) len(thresholds)我用脚本扫阈值找业务最优点的时候就因为这个长度问题写过越界报错排查了半小时才发现是官方这个约定俗成的补点行为。写进笔记里省你一次调试。3.4 AP、AUC-PR 和平均精准率到底差在哪average_precision_score返回的 AP本质上是 PR 曲线下的面积但它的计算方式不是简单的梯形积分而是按召回率增量加权的精准率求和AP Σ (R_n - R_{n-1}) × P_n这种做法比直接梯形积分更合理因为它在召回率不变的那一段不会重复计入面积避免了 PR 曲线高召回区剧烈抖动带来的高估。对比一下常见的几个名字sklearn 里的auc(recall, precision)是直接用梯形法算 PR 曲线面积数值上会比 AP 略高一点点而平均精准率这个中文名有时候指 AP有时候指 macro 平均的 precision看文档要小心。我在做实验对比的时候统一用average_precision_score一是跟学术界的 AP 定义一致二是它对曲线的抖动更稳健不会因为某一段锯齿而虚高。还有一个使用要点PR 曲线的 AP 值没有像 ROC 的 AUC 那样0.5 是随机这个漂亮的基准。AP 的随机基线大约等于正类占比。如果你的正类占比是 5%那么 AP 跑到 0.10 都已经比随机好一倍了不要看到 0.10 就觉得模型废了。这一点在向业务方汇报的时候一定要提前说明否则很容易被误解成模型只有10分。4. ROC曲线阈值无关的排序能力刻画4.1 TPR 和 FPR 是ROC的两个坐标轴ROC 曲线的横轴是假正率 FPR纵轴是真正率 TPRTPR TP / (TP FN) # 就是召回率 FPR FP / (FP TN) # 负类里被误报的比例注意 TPR 其实就是召回率只不过在 ROC 的语境里换了名字。所以 ROC 和 PR 曲线是同一组预测分数在不同坐标系下的两种投影PR 关注报正的那部分有多准ROC 关注正类和负类的分布有多大分离。ROC 有一个 PR 不具备的性质它对类别比例不敏感。因为 TPR 只涉及正类内部FPR 只涉及负类内部两边都是各自独立归一化的。你随便改变正负样本比例只要模型给出的分数排序不变ROC 曲线就纹丝不动。这个性质是好是坏取决于场景做跨数据集对比实验时它很方便但在极度不平衡的真实业务里它又容易给出过于乐观的印象让人忽视模型在少数类上的糟糕表现。4.2 AUC 的概率解释与 Mann-Whitney 等价关系AUC 就是 ROC 曲线下的面积取值 0.5 到 1。它有一个非常直观的概率解释是我最喜欢讲的一点随机抽取一个真实正样本和一个真实负样本模型给正样本的分数高于负样本的分数这个事件的概率就是 AUC。这个解释来自 AUC 与 Mann-Whitney U 统计量的等价性。你甚至可以用暴力法验证它import numpy as np def auc_by_pairing(y_true, y_score): pos y_score[y_true 1] neg y_score[y_true 0] # 广播比较得到一个 P×N 的布尔矩阵 cmp (pos[:, None] neg[None, :]).astype(float) # 处理分数完全相等的平局情况各算一半 ties (pos[:, None] neg[None, :]).astype(float) return (cmp 0.5 * ties).mean()我用这个方法在几万条数据上验证过roc_auc_score两者差值在 1e-9 量级说明实现是对的。这个等价关系的实用价值在于AUC 衡量的是模型的排序能力而不是它在某个阈值上的判定能力。所以 AUC 高不代表你的业务指标好只代表你的分数排序方向是对的。最终落地还是得回到阈值选择这一步 AUC 帮不了你得靠 PR 曲线和成本计算。顺带说下平局的处理。如果两个样本分数完全相同sklearn 按 0.5 计入。这个细节在手写实现里必须体现否则当分数是整数离散值时结果会偏差很大。这也是为什么我建议生产模型输出保留足够的小数位尽量避免分数扎堆。4.3 手写ROC并与sklearn对拍手写一版方便理解阈值扫描的过程import numpy as np def roc_curve_manual(y_true, y_score): thresholds np.unique(y_score)[::-1] P np.sum(y_true 1) N np.sum(y_true 0) tprs, fprs [], [] for t in thresholds: y_pred (y_score t).astype(int) tp np.sum((y_pred 1) (y_true 1)) fp np.sum((y_pred 1) (y_true 0)) tprs.append(tp / P) fprs.append(fp / N) # 补上 (0,0) 起点保证曲线闭合到左下角 fprs np.concatenate([[0.0], fprs]) tprs np.concatenate([[0.0], tprs]) return fprs, tprs def auc_trapezoid(fprs, tprs): return np.trapz(tprs, fprs)对拍校验from sklearn.metrics import roc_curve, roc_auc_score fpr_m, tpr_m roc_curve_manual(y_true, y_score) fpr_s, tpr_s, _ roc_curve(y_true, y_score) print(np.allclose(fpr_m, fpr_s), np.allclose(tpr_m, tpr_s)) print(auc_trapezoid(fpr_m, tpr_m), roc_auc_score(y_true, y_score))对不上的话八成是三个原因阈值扫描方向反了、没补 (0,0) 起点、或者 sklearn 在drop_intermediate参数下省略了一些共线的中间点。第三个原因最常见只要把drop_intermediateFalse传进去点数就能完全对齐。我刚开始做模型报告时画出来的曲线比同事少了十几个点纠结了半天才发现是这个参数在起作用。4.4 用信息量视角看ROC从KL散度到KS统计量前面提到网络上有人讨论用信息量法计算ROC曲线这个思路其实很有意思值得单独拆一段。它的出发点是既然 AUC 刻画的是正负两个分数分布的分离程度那么完全可以绕过逐阈值扫描直接用分布之间的距离来衡量可分性。具体做法是把预测分数分箱得到正类样本的分数分布 p 和负类样本的分数分布 q然后计算两个分布的 KL 散度import numpy as np def kl_separation(y_true, y_score, bins20): pos y_score[y_true 1] neg y_score[y_true 0] edges np.linspace(y_score.min(), y_score.max(), bins 1) p, _ np.histogram(pos, binsedges, densityTrue) q, _ np.histogram(neg, binsedges, densityTrue) eps 1e-12 p p / p.sum() eps q q / q.sum() eps return np.sum(p * np.log(p / q))KL 散度越大说明两个分布离得越开模型把正负样本分开的能力越强反映到 ROC 上就是 AUC 越大。KL 散度的信息量含义是用负类分布去编码正类样本时平均每个样本多付出的比特数。这个解释非常直观——如果我拿一个根本不了解正类的分布去压缩正类样本需要的编码长度就更长多出来的部分就是模型提供的信息量。不过 KL 散度有三个必须知道的局限。第一它不对称KL(p||q)不等于KL(q||p)所以结果取决于你把哪个放前面做实验时要固定写法。第二它对分箱数量和边界极度敏感20个箱和50个箱可能得出完全不同的排序。第三当两个分布完全不重叠时KL 散度会趋向无穷大数值上非常不稳定所以我一般在代码里用 JS 散度替代def js_separation(y_true, y_score, bins20): # JS 散度是对称且归一化到 [0, ln2] 的版本 pos y_score[y_true 1] neg y_score[y_true 0] edges np.linspace(y_score.min(), y_score.max(), bins 1) p, _ np.histogram(pos, binsedges, densityTrue) q, _ np.histogram(neg, binsedges, densityTrue) eps 1e-12 p p / p.sum() eps q q / q.sum() eps m 0.5 * (p q) return 0.5 * np.sum(p * np.log(p / m)) 0.5 * np.sum(q * np.log(q / m))还有一个更常用的量是 KS 统计量定义是KS max|TPR - FPR|正好就是 ROC 曲线到对角线的最远距离。它在风控行业几乎是标配指标因为它的业务含义很直白在最优阈值下模型能把正负样本的累计分布拉开多少。KS 大于 0.3 通常认为是可用模型大于 0.45 就相当不错了。注意KL 散度、JS 散度、KS 统计量都是对 AUC 的侧面刻画它们的排序结果在绝大多数情况下与 AUC 一致但严格来说并不等价。把它们当辅助诊断手段可以别拿它们替代标准的 AUC 报告。我用这套信息量方法做过一个对比实验在同一批数据上AUC 排序和 KS 排序的前三名完全一致第四第五名出现了交换。查看细节发现是分箱边界把两个分数接近的模型切到了不同的箱里。结论就是这类方法对工程细节非常敏感做结果对比时一定要固定分箱策略并写进实验记录。4.5 PR 与 ROC 该用哪个一张对照表说清这两个曲线吵了很多年其实判断标准很简单看你关心的是排序能力还是少数类的识别能力。维度PR 曲线ROC 曲线横轴召回率 Recall假正率 FPR纵轴精准率 Precision真正率 TPR随机基线等于正类占比恒为 0.5 对角线类别比例敏感度高度敏感不敏感极端不平衡数据更能暴露问题容易过于乐观典型使用场景欺诈检测、信息检索、目标检测医学诊断、模型排序能力对比、特征筛选曲线下面积名AP / AUC-PRAUC具体到实操我的习惯是正类占比低于 10% 时主看 PR 和 APROC 只作为辅助正类占比在 20% 以上时两个都看因为它们给的结论通常一致不一致的地方反而值得深挖。如果两个模型的 ROC 几乎相同而 PR 差异明显那说明它们在大体排序上差不多但你在意的那部分高分区域表现不同这时候 PR 才是决策依据。5. 常见问题与排查技巧实录5.1 plt 画 ROC 曲线两条线完全重合是怎么回事这是被问得最多的一个问题网络热词里也提到了。原因通常有五类我按出现频率排一下可能原因判断方法解决办法两个模型的排序能力确实相同单独算 AUC看是否只差 1e-4 量级换个真正不同的模型或接受结论y_score 误传成了 0/1 预测值打印np.unique(y_score)只有一个阈值就说明错了传连续概率或决策函数值阈值点太少折线几乎是一条直线打印len(thresholds)设drop_intermediateFalse两行的颜色线型完全一样视觉重叠看 legend 和代码里的 plot 参数分别设linestyle和marker画在同一个 axes 但用了相同 label检查label参数给每个模型独立命名第一种情况最容易被忽略。有一种真实场景是模型 B 是模型 A 的分数经过单调递增变换得到的比如 A 输出概率B 输出 logit这种情况下两条 ROC 曲线在数学上完全一致AUC 也完全相同。这不是 bug是 ROC 只关心排序这个性质的直接体现。如果你希望两者有区别那说明你关心的是概率校准该去看 Brier score 或者校准曲线而不是 ROC。第二种情况我亲手踩过。当时我把model.predict(X)的结果传给了roc_curve得到的结果是一堆重叠的直角折线AUC 只有 0.75而用predict_proba(X)[:, 1]时 AUC 是 0.91。原因就是predict已经把概率二值化了排序信息全丢了。这个坑太常见以至于我现在的代码模板里第一行就是断言assert len(np.unique(y_score)) 2, y_score 疑似是二值预测请传概率值5.2 PR 曲线末端垂直下跌、锯齿严重PR 曲线在高召回区经常出现垂直下跌一根线直插到底部看起来非常难看。这不是模型坏了而是样本极度不平衡时的数学必然。当阈值降到最低所有样本都被判正此时 FP 数量等于负类总数precision 会被压到正类占比附近于是曲线末端直接掉下来。处理方式有两种。一是画图时只展示 Recall 到 0.9 或 0.95 的区间把末端那段截掉因为那部分在实际业务中根本不可用。二是使用平滑版本比如对相邻几个阈值点的精准率做滑动平均def smooth(arr, window5): kernel np.ones(window) / window return np.convolve(arr, kernel, modesame)至于锯齿严重多半是因为测试集里正类样本太少某个阈值一跨过去就多一个 TP精准率直接跳一大格。这种情况下最需要的不是平滑代码而是扩大测试集。我一般要求 PR 曲线的有效区域内至少包含 200 个正类样本否则曲线形状完全不可信做模型选型也是白选。5.3 多分类下的 macro、micro、weighted 到底怎么选二分类搞懂之后一上多分类就会遇到三个 averaging 参数很多人的做法是随手填个weighted就交差了。这三个的区别其实很清楚macro对每个类单独算指标再取算术平均所有类别权重相同。它关心的是每个类都做得好不好任何一个类崩了都会明显拉低分数。micro把所有类的 TP、FP、FN 全部加起来再算指标。在单标签多分类里micro-F1 恒等于准确率所以它其实没什么额外信息量。weighted按每个类的样本数加权平均。样本多的类说话更响适合关心整体业务收益的场景。选择逻辑是当你的目标是每个类都不能太差时用 macro目标偏整体效率时用 weighted。比如做多品类商品分类如果有一个小众品类完全识别不出来macro 会立刻报警weighted 可能因为该品类样本少而毫无反应。反过来如果你只关心整体的分类吞吐效率weighted 更贴近真实感受。我通常会同时打印 macro 和 weighted两个数差得超过 0.05 就去看混淆矩阵找出拖后腿的那个类。5.4 常见问题速查表把上面这些坑整理成一张表方便随时对照现象可能原因快速验证处理建议准确率高但业务效果差正类占比过低统计正类比例改用 PR / AP 做主指标精准率极高召回极低阈值过高画 PR 曲线看整体按成本重新选阈值PR 曲线末端垂直掉低阈值下 FP 暴增看最低阈值处的 precision截取有效召回区间ROC 曲线两条重合排序相同或分数已二值化检查unique(y_score)传概率值 / 换模型AUC 0.9 但线上指标差训练集与线上分布不一致做 PSI 分布对比重新采样并校准阈值AP 值看着很低随机基线等于正类占比算正类占比与基线对比而非与 1 比ROC 点数异常少drop_intermediateTrue省略共线点打印阈值数量设为 FalseF1 高但两大类表现都不均类别不平衡掩盖了小类分开算每类 F1用 macro 平均复核最后再补一个实战技巧阈值不要靠 AUC 选要靠成本函数选。做法是在 PR 曲线上扫一遍对每个阈值计算期望损失L FP × C_fp FN × C_fn取 L 最小的那个点。我用这个方法帮一个内容审核团队把误封率降了三分之一同时召回只掉了两个点原因就是原来看起来很科学的 0.5 阈值在两边成本差 20 倍的情况下根本不合理。这个思路比任何指标排名都管用指标只是描述世界的语言成本才是做决定的秤。关于信息量法这套东西我自己在项目里的定位是离线诊断工具而不是上线判据。它帮我快速看出两个模型在分数分布上的差异来源尤其是当 AUC 差不多但业务表现差很多的时候KL 散度和 KS 能指出问题出在分布重叠区还是尾部这个信息对特征迭代方向的指导价值比单纯看一个 AUC 数字大得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑