ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PR曲线与ROC曲线:不平衡分类模型评估的终极指南

PR曲线与ROC曲线:不平衡分类模型评估的终极指南 我做机器学习这几年发现一个特别有意思的现象很多人选模型的时候特别较真XGBoost还是LightGBM、核函数用RBF还是多项式研究得头头是道但一到了模型评估环节就只盯着准确率Accuracy看准确率高了就觉得自己模型天下无敌准确率低了就开始盲目调参。说实话这是典型的还没入门的做法。准确率这个指标在绝大多数真实场景下是会骗人的。尤其是分类问题里正负样本不平衡的时候比如垃圾邮件识别、欺诈交易检测、罕见病筛查你就算写个永远预测为负样本的智障模型准确率也能轻松超过95%。这时候真正能反映模型好坏、帮你做判断的是PR曲线和ROC曲线这两个评估利器。这篇文章我就用最直白的方式把这俩曲线彻底聊透。不仅讲清楚它们的定义、怎么画、怎么看还会把期末考试、面试里最刁钻的坑点一一点出来最后带大家用Python在真实数据集上走一遍完整流程。不管是正在学机器学习的学生、准备复试的研究生还是刚入行做算法应用开发的工程师这篇文章都值得你花二十分钟读完读完之后你对模型评估的认知会直接上一个台阶。1. 为什么模型评估比调参更重要从分类任务的两个基本问题说起1.1 分类模型输出的不是“0/1”而是“分数”很多人对分类模型有个误解以为逻辑回归或者SVM输出的结果就是样本属于正类还是负类的硬标签。实际上绝大多数分类模型输出的都是一个分数或者叫置信度——代表这个样本属于正类的概率或者置信程度。以逻辑回归为例模型计算出来的是一个在0到1之间的概率值比如0.82、0.45、0.13这样的小数。我们之所以看到预测为正类和预测为负类这样的结果是因为我们人为地设定了一个阈值threshold超过这个阈值就归为正类低于就归为负类。默认情况下sklearn会把这个阈值设为0.5但0.5并不是什么天经地义的标准在很多场景下你完全可以把它调到0.3、0.7没有对错只看你想要什么样的评估效果。理解了这一点就理解了两条曲线存在的根本意义PR曲线和ROC曲线都是在模型分数的基础上通过扫描不同阈值来全面评估模型能力的工具。它们不依赖某一个固定阈值而是把所有可能阈值下的表现都画出来给你看让你对模型有一个全貌式的认识。这是一个很关键的思维转变——模型好不好不是某一个阈值下准不准而是分数排序是否合理。1.2 评估指标的起点混淆矩阵和四个基础量在聊PR和ROC之前必须先建立一个公共语言混淆矩阵Confusion Matrix。这玩意儿结构非常简单就是把预测结果和真实情况做了一个二维交叉统计一共四个格子TP真正例True Positive真实为正类模型也预测为正类。FP假正例False Positive真实为负类但模型预测成了正类。这类错误也被称为误报。FN假负例False Negative真实为正类但模型预测成了负类。这类错误也被称为漏报。TN真负例True Negative真实为负类模型也预测为负类。这四个量是一切分类评估指标的根源。准确率就是TPTN除以总数它把两个格子都对了的情况合计在一起看起来很全面但也正是因为太全面反而容易掩盖问题。比如样本里99个负类、1个正类模型把1个正类也判成负类也就是TP0、TN99、FP0、FN1准确率是99%但正类一个都没找出来这在很多业务场景下等于完全失败。所以后来人们才设计出了更精细的指标——查准率Precision、查全率Recall、真正例率TPR、假正例率FPR分别从预测为正类的样本里有多少是对的、真实正类里有多少被找出来了、真实负类里有多少被误伤了等不同角度去度量一个模型。咱们接下来要讲的PR曲线和ROC曲线就是围绕这些基础量构建的。2. PR曲线查准率与查全率的博弈2.1 查准率、查全率先用一个通俗例子看懂把这两个指标放到一个具体场景里会好理解得多。假设你在做一个电商平台的风险订单识别模型任务是从每天几十万订单里找出那些可能是诈骗或者恶意下单的订单交给风控团队去人工审核。查准率Precision模型说这是风险订单的里面有多少单是真有问题的。如果模型标记了100个订单但人工复查发现只有50个确实有问题那查准率就是50%。它衡量的是抓出来的质量也叫精确率。查全率Recall当天全平台实际发生了20笔诈骗订单模型成功找出了其中15笔那查全率就是75%。它衡量的是抓得全不全也叫召回率。这两个指标天然是矛盾的。你把阈值调低一点模型会标记出更多疑似风险订单这样挖出来的真实诈骗单子会变多查全率上去了但同时也必然混进来很多正常订单查准率就下降了。反过来阈值调高模型只敢标记那些概率极高的订单查准率上去了但很多狡猾的、分数不算高的诈骗单会被漏掉查全率就下来了。2.2 PR曲线的绘制逻辑与面积AP值到底在衡量什么PR曲线全称是Precision-Recall Curve横轴是查全率Recall纵轴是查准率Precision把模型输出概率从高到低排好然后从概率最高的样本开始逐个把样本点纳入预测为正类的集合中每纳入一个样本就重新计算一组查准率查全率把所有的点连起来就得到一条PR曲线。这里有个细节很多人容易忽略PR曲线不一定是光滑的递减曲线。由于每次只增加一个样本查准率会在这个样本是真正例时上升、是假正例时下降所以曲线会带有锯齿状波动。当模型完全没有区分能力时曲线会退化成一条水平线高度约等于正样本占比。有了PR曲线之后咱们常说的AP值Average Precision其实就是PR曲线下的面积的一种计算方式只不过更常用的做法是对每个样本点的查准率取平均或者用插值法。AP值越高说明这个模型在不牺牲太多查全率的同时保持高查准率的能力越强。AP1.0是完美模型的理想值随机瞎猜的模型AP约等于正样本占比。这里顺便提一个考试高频坑PR曲线和ROC曲线相比前者对样本不平衡更敏感也更适合当正负样本极不均衡时的评估指标。这一点咱们后面专门展开讲先记住结论就行。2.3 PR曲线的适用场景正负样本极度不平衡时的救命稻草什么时候优先看PR曲线一句话当你更关心正类样本少数类能不能被准确找出来的时候。拿垃圾邮件识别举例真实场景中垃圾邮件可能只占全部邮件的1%不到。如果你用准确率来评判全判为正常邮件都有99%的准确率毫无意义。但如果你看PR曲线因为正样本极少模型但凡多标几个垃圾邮件查准率就会明显下滑曲线会非常敏感地反映模型在区分少数正类上的真实水平。医疗领域的罕见病筛查、故障检测中的异常样本识别、金融领域的欺诈交易检测全部是类似的场景。在这些场景下我个人的习惯是主要看PR曲线和AP值ROC曲线作为辅助参考。因为ROC曲线有一个特性我们马上就会提到——它在样本不平衡时表现得过于乐观容易让人觉得模型还不错但实际上对少数类的捕捉能力可能很弱。3. ROC曲线真正例率与假正例率的对决3.1 真正例率、假正例率绕开查准率卷的另一组指标ROC曲线全称是Receiver Operating Characteristic Curve中文常翻译为受试者工作特征曲线。这个名字听起来很神秘其实它最初来源于二战时期的雷达信号检测后来被引入心理学、医学再到机器学习领域成了最经典的二分类模型评估工具之一。ROC曲线的横轴是假正例率FPRFalse Positive Rate纵轴是真正例率TPRTrue Positive Rate。这两个指标的定义如下真正例率TPR TP ÷TP FN也就是真实正类中被正确找出来的比例。等等这看起来不是和查全率Recall一模一样吗确实TPR在数值上就等于Recall。假正例率FPR FP ÷FP TN也就是真实负类中被误判为正类的比例。你可以这么理解ROC曲线的思想我们不再关心预测为正类的那些里有多少是对的而是换了一个角度——在把所有真实正类捞出来的过程中到底误伤了多少真实负类。这个视角对类别不平衡没那么敏感因为它本身是在用比率做比较。3.2 从阈值移动到整条曲线ROC的绘制与AUCROC曲线的绘制方式和PR曲线完全一致也是把所有测试样本按模型输出分数从高到低排序然后从阈值1开始即一个都不预测为正类逐渐把阈值降低每次把分数高于阈值的新样本判为正类计算这一时刻的FPR, TPR把所有点连起来就得到了ROC曲线。这个过程中有几个关键点位值得记一下当阈值1时没有任何样本被判为正类TP0、FP0所以TPR0、FPR0曲线从(0,0)出发当阈值0时所有样本都被判为正类FP所有负类样本数、TP所有正类样本数所以TPR1、FPR1曲线结束于(1,1)对角线yx代表随机瞎猜的模型真正例率和假正例率永远相等不管阈值怎么扫捞到正类的比例和误伤负类的比例始终一样多。ROC曲线下方的面积就是AUCArea Under the ROC Curve它衡量的是随机挑一个正样本和一个负样本模型给正样本打的分比给负样本打的分高的概率。这句话值得反复读几遍因为它是AUC最本质的概率解释。AUC0.5意味着模型和抛硬币没有区别AUC0.8意味着在正负样本随机配对的情况下模型有80%的概率把正样本排在负样本前面。这是面试和期末考试的绝对高频考点。3.3 ROC曲线的特点对类别分布不敏感、横纵轴含义ROC曲线有一个非常出色的性质对测试集中的正负样本比例不敏感。这个性质不是玄学而是一项统计学特性导致的——TPR和FPR在计算时分别只用到了正样本内部的统计TP和FN和负样本内部的统计FP和TN两类样本之间不会互相干扰。所以你把测试集里正样本数量翻一倍TPR和FPR的期望值基本不会变ROC曲线基本稳定。这个特性在实际工作里非常香。当训练集和测试集的正负比例发生变化时ROC曲线和AUC依然能给你一个相对稳定的模型能力评估PR曲线则不同它的两个轴Precision和Recall都跟正样本占比直接相关正负比例一变曲线和AP值立刻大变样。但这并不意味着PR曲线不好恰恰相反正是因为它对分布敏感才能暴露出那些在样本极端不平衡下原形毕露的模型。两条曲线是互补的关系不是替代关系。这里额外说一个做题时的常见误区ROC曲线的面积AUC和所有样本里预测对的比率没有任何直接关系AUC反映的是排序能力。比如一个模型把所有正样本打分都排在负样本前面AUC就是1.0但如果你强行用0.5的阈值去切可能得到的准确率并不高。这两个评价维度完全不同不要混为一谈。4. PR曲线和ROC曲线到底该选哪个4.1 核心差异一张表看懂很多初学者最大的困惑就是这两条曲线看起来长得也差不多都是衡量分类模型的能力到底什么时候用哪个我先用一张表给大家理清楚主要差异再展开说背后的逻辑。对比维度PR曲线ROC曲线横轴查全率Recall / TPR假正例率FPR纵轴查准率Precision真正例率TPR / Recall对类别不平衡的敏感度非常敏感不敏感AUC的面积含义AP值约等于PR曲线下面积随机正样本排在随机负样本前面的概率随机基线约为正样本占比对角线0.5适用场景正负样本不平衡关注少数类类别相对均衡需要全局排序能力评估这张表的核心信息是ROC曲线更像一个端水大师它综合评估模型在各类别上的排序能力且不随样本分布剧烈波动PR曲线则是一个显微镜专门盯着少数类看只要你在少数类上表现不好它立刻给你脸色。4.2 为什么样本不平衡时ROC可能“骗人”而PR更真实这是一个非常关键、也非常容易在面试中翻车的话题。假设你做一个欺诈检测任务10000个样本里只有50个是真正欺诈其余9950个都是正常样本。你训练了一个尽量把所有样本都判为正常的保守模型。这个模型在ROC曲线上表现如何ROC的纵轴是TPR因为50个欺诈样本一个都没找出来所以TPR0横轴是FPR因为正常样本全被正确判为正常误伤的很少FPR也接近0。于是曲线几乎贴着左上角走AUC可能依然很高甚至能达到0.9以上。听起来很唬人是吧但如果这时候你去看PR曲线发现查全率同样是0查准率也是0AP值直接归零模型的真实水平一目了然——它根本一个欺诈样本都没抓出来纯粹是个摆设。为什么会有这个反差因为ROC计算FPR时分母是全部负样本9950个少量误报在这么大的分母下被稀释了曲线看起来依旧优美而PR曲线的两个轴都直接作用和预测为正的那一撮样本以及真实正类的50个样本上任何一点失误都会被放大。所以在类别极度不平衡的业务场景里PR曲线和AP值才是更能反映模型业务价值的评估方式。这也是我求求各位一句别只盯着AUC好看就觉得自己模型牛看看PR曲线再说。4.3 两者在模型调参中的配合先看ROC粗筛、再看PR细选在实际项目里我的习惯是把两条曲线配合起来用而不是非此即彼。具体操作分三步走。第一步模型快速筛选阶段数据集类别相对均衡或者没有明显的少数类倾向时直接看ROC和AUC因为AUC是标量、好对比适合在几个候选模型之间快速拉开差距。第二步进入优化阶段之后把业务具体场景考虑进来如果正负样本比例已经不均衡了或者少数类才是业务核心马上切换视角看PR曲线和AP值同时结合业务上能接受的最大误报率来调阈值。第三步确定阈值时不要用默认的0.5而是根据PR曲线上的Precision, Recall组合或者ROC曲线上离左上角最近的点去选出实际业务最满意的操作点。这个方法说白了就是宏观上用AUC判断模型底子怎么样微观上用PR判断模型对目标少数类的真正战斗力阈值用需求反推。这几步走完一个二分类模型的评估闭环才算真正闭合了。5. 实操用Python从零绘制PR曲线和ROC曲线5.1 准备数据与模型随便造一个二分类但要让代码能直接用理论说得再多不跑一遍代码全是空中楼阁。下面我用Python带大家完整走一遍绘制PR曲线和ROC曲线的流程。先说明一下使用的环境Python 3.9以上scikit-learn版本1.2以上matplotlib画图numpy处理数据。没有装的同学用pip安装一下就行非常快。这里我就不用某个具体业务数据了直接调用sklearn的make_classification函数人工生成一份样本因为这样任何人都能复制代码直接跑出来而且我们可以精确控制正负样本比例方便演示两条曲线的差异。生成数据的代码如下from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import (precision_recall_curve, roc_curve, auc, average_precision_score) import numpy as np import matplotlib.pyplot as plt # 生成2000个样本其中正类比例约10%用来模拟不平衡场景 X, y make_classification( n_samples2000, n_features20, n_informative10, n_redundant5, n_clusters_per_class1, weights[0.9, 0.1], # 90%负类10%正类 random_state42 ) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) model LogisticRegression(max_iter1000) model.fit(X_train, y_train) y_score model.predict_proba(X_test)[:, 1] # 取正类概率作为分数这里我用weights[0.9, 0.1]硬生生制造了一个不平衡数据目的就是为了后面演示PR曲线和ROC曲线在不平衡样本下的表现差异。这组数据的正样本占比只有10%如果你用一个永远预测负类的模型准确率能到90%但屁用没有——后面画出的曲线会把这个残酷现实暴露无遗。5.2 计算混淆矩阵与各指标把四个基础量搞到手在实际绘制曲线之前先选一个固定阈值算出混淆矩阵和各基础指标满足一下直觉。默认阈值0.5的情况下代码如下from sklearn.metrics import confusion_matrix, classification_report y_pred (y_score 0.5).astype(int) tn, fp, fn, tp confusion_matrix(y_test, y_pred).ravel() print(fTP{tp}, FP{fp}, FN{fn}, TN{tn}) print(fPrecision {tp / (tp fp):.3f}) print(fRecall {tp / (tp fn):.3f}) print(fTPR {tp / (tp fn):.3f}) print(fFPR {fp / (fp tn):.3f}) print(classification_report(y_test, y_pred))从这份输出里我们就能直观看到各指标的数值关系TPR和Recall本来就完全相等Precision反映的是预测为正类里有多少是对的FPR反映的是负类被误伤的比例。这几个具体数值会为接下来理解曲线上的每个点做好铺垫。5.3 从零绘制PR曲线并计算AP值实际操作中我们不需要自己写阈值扫描逻辑sklearn的precision_recall_curve函数已经帮你做完了全部事情。你只需要把真实标签和模型预测分数传进去就行。precision, recall, thresholds precision_recall_curve(y_test, y_score) ap_score average_precision_score(y_test, y_score) print(fAP {ap_score:.3f}) plt.figure(figsize(7, 6)) plt.plot(recall, precision, colordarkorange, lw2, labelfPR curve (AP {ap_score:.3f})) plt.xlabel(Recall) plt.ylabel(Precision) plt.title(Precision-Recall Curve) plt.legend(locupper right) plt.grid(True) plt.show()这里我要展开解释一下precision_recall_curve返回的三个值的含义。第一个precision和第二个recall是浮点数组一一对应组成PR曲线上的坐标点第三个thresholds则记录着每个坐标点对应的阈值。注意这个函数返回的precision和recall长度会比thresholds多1因为最后一个点对应的阈值是全部判为正类的情况此时没有明确的单一阈值曲线坐标里多出一个终结点此时precision等于正样本占比、recall1.0。如果你面试时候能把这一句话讲清楚面试官基本就会认定你真的调过包而不是背过八股文。至于AP值average_precision_score的实现方式是对每个recall阈值点取该点及之后的最大precision作为插值后的precision然后对所有阈值变化区间的precision取加权平均权重是recall的增量。这也是为什么它可以直接无量纲地表示PR曲线的整体水平。5.4 从零绘制ROC曲线并计算AUCROC曲线的绘制思路和PR曲线几乎一模一样只是换了一个评估函数把纵轴换成TPR、横轴换成FPR。fpr, tpr, thresholds roc_curve(y_test, y_score) roc_auc auc(fpr, tpr) print(fAUC {roc_auc:.3f}) plt.figure(figsize(7, 6)) plt.plot(fpr, tpr, colorblue, lw2, labelfROC curve (AUC {roc_auc:.3f})) plt.plot([0, 1], [0, 1], colorgray, linestyle--, labelRandom Classifier) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.title(Receiver Operating Characteristic Curve) plt.legend(loclower right) plt.grid(True) plt.show()打印出来的AUC通常在0.9左右甚至更高看起来非常好看。但配合上刚才那份10%正样本占比的数据你会发现ROC曲线给出的乐观判断和PR曲线用AP0.7左右数值给出的没你想的那么好之间存在一个明显的落差。这恰好呼应了前面第4节的核心观点——类别不平衡时ROC容易给出偏乐观的评价PR曲线才更接地气。有个细节顺带提一下roc_curve返回的thresholds长度和fpr、tpr是相等的而PR曲线的precision、recall长度比thresholds多1这俩函数的返回值长度习惯不一样很多新手在这里被坑过写代码取索引的时候一不小心就数组越界。建议大家在拿到返回值之后先print(len(...))看一眼心里有个底。6. 期末复习与面试高频那些要命的坑和底层逻辑6.1 高频概念辨析题别再混淆的三个陷阱本部分内容来自我平时帮学弟学妹辅导、以及在技术社区回答问题时发现的高频误区如果你正在准备期末考试或者面试下面这些点建议逐条核对一下。第一TPR、Recall到底是不是一回事是但在ROC曲线的语境下我们习惯叫TPR在PR曲线的语境下习惯叫Recall数值定义完全一致。考试时如果在一道题里同时看到这两个名字要立刻反应过来它们是一个东西。第二AUC0.5到底意味着什么有两种等价理解一是模型是随机猜测的二是随机抽一个正样本和一个负样本模型给正样本打高分、负样本打低分的概率是50%。但注意这并不意味着模型一定全错更不意味着准确率是50%不少初学者在这里栽跟头。第三PR曲线的随机基线为什么不是0.5很多教材不会特意提这事因为PR曲线的随机基线是正样本占比p比如正样本占10%随机线的高度就是0.1而ROC曲线的随机基线永远是0.5这条对角线。这个区别理解透了你在解释为什么同一模型在不平衡数据上PR曲线看起来比ROC曲线低那么多的时候就能抓住本质了。6.2 高频计算题套路混淆矩阵倒推一切指标期末和面试里最常见的一类计算题就是给你一个混淆矩阵让你求准确率、查准率、查全率、F1、TPR、FPR甚至让你手绘ROC曲线上的一个点。这类题没有任何技术含量唯一的难点就是别把公式记混。我的建议是记混淆矩阵的时候别死记字母死记行是预测、列是真实这种表格结构然后每次现场推导一遍。举一个标准例子假设测试集100个样本混淆矩阵如下TP40、FP10、FN20、TN30。那么查准率40/(4010)0.8查全率40/(4020)≈0.667F12×0.8×0.667/(0.80.667)≈0.727FPR10/(1030)0.25TPR0.667。如果把(0.25, 0.667)画到ROC平面里就是这个模型在特定阈值下的操作点。一次题做下来你就把PR曲线和ROC曲线的基础坐标全部打通了。6.3 实际工作中的踩坑记录比书本更痛的教训最后聊几个我在实际项目里掉过坑的经历希望你能绕开。第一个坑只看AUC选模型上线后效果稀烂。我在一个金融客户项目里两个候选模型的AUC分别是0.93和0.92前者略微胜出我就选了前者。结果A/B测试阶段前者在真实业务上的转化率反而明显低于后者。后来复盘发现真实业务的正负样本比例跟测试集差了一个数量级而前者的高AUC主要靠能把一堆负样本排到最后撑起来跟业务真正关心的少数类没关系。从那以后我再也不单凭一个AUC做决策了。第二个坑只画了PR曲线忘记看具体操作点对应的阈值。PR曲线和ROC曲线本质上都是阈值扫描全景图曲线好看不代表当前默认阈值下的业务指标好看。后来我养成了一个习惯画完曲线之后直接打印出几个关键阈值对应的Precision和Recall结合业务上对误报和漏报的容忍度手动选出真正能用的阈值。默认0.5在绝大多数实际场景里都是一个糟糕的选择需要自己去抠。第三个坑在数据集上做分类时忘了先检查正负样本分布的稳定性。我见过一个团队用ROC做日常模型监控某天AUC突然暴跌排查了几天最后发现是新接入的数据流里正样本比例发生了漂移。虽然ROC对测试集正负比例不敏感但业务上样本分布变化本身就是一个值得关注的大事件别被评估指标的稳定性蒙蔽了。样本分布变化本身就是信号曲线稳定只是说明模型排序能力没崩不代表业务没问题。做评估从来不是算几个数那么简单你要把指标当成一副透视镜透过它看清模型在真实场景里到底行不行。PR曲线和ROC曲线这两条曲线一个让我们紧盯少数类的死活一个让我们站高一点看全局排序配合使用就是一套完整的模型体检套餐。最后再分享一个小技巧如果你画完这两条曲线之后还想快速判断一个模型有没有改进空间先看看ROC曲线左上角那一带的曲线是否够鼓再看看PR曲线在Recall从0.8到1.0这一段是不是断崖式下跌。如果PR曲线在高Recall段突然垂直坠落说明模型虽然能找到绝大多数正样本但代价是误报率爆炸——这时候你就要考虑是否该换模型、引入更多特征或者在业务侧加人工兜底了。这两个小细节是我在无数次模型调优中用血泪换来的经验今天一并交给你们了。
返回列表