大多数不平衡分类问题涉及两个类别:包含多数样本的负例和包含少数样本的正例。
ROC曲线和精确度-召回曲线是两种有助于解释二元(两类)分类预测模型的诊断工具。
可以创建曲线图并用于理解在解释概率预测时,不同阈值下性能的权衡。每个图还可以通过曲线下面积得分进行总结,该得分可用于直接比较分类模型。
在本教程中,您将了解不平衡分类的ROC曲线和精确度-召回曲线。
完成本教程后,您将了解:
- ROC曲线和精确度-召回曲线为二元分类模型提供了诊断工具。
- ROC AUC和精确度-召回AUC提供了总结曲线的得分,可用于比较分类器。
- 在少数类别样本很少的严重不平衡分类问题上,ROC曲线和ROC AUC可能过于乐观。
通过我的新书《Python不平衡分类》来启动您的项目,其中包括分步教程和所有示例的Python源代码文件。
让我们开始吧。

不平衡分类的ROC曲线和精确率-召回率曲线
图片由Nicholas A. Tonelli提供,保留部分权利。
教程概述
本教程分为四个部分;它们是
- 混淆矩阵回顾
- ROC曲线和ROC AUC
- 精确度-召回曲线和AUC
- 严重不平衡下的ROC和精确度-召回曲线
混淆矩阵回顾
在深入了解ROC曲线和PR曲线之前,回顾混淆矩阵非常重要。
对于不平衡分类问题,多数类别通常被称为负面结果(例如“无变化”或“阴性测试结果”),而少数类别通常被称为正面结果(例如“变化”或“阳性测试结果”)。
混淆矩阵不仅提供了对预测模型性能的更多洞察,还提供了哪些类别被正确预测、哪些被错误预测以及发生了哪种类型的错误。
最简单的混淆矩阵适用于二分类问题,包含负类(类别 0)和正类(类别 1)。
在此类混淆矩阵中,表格中的每个单元格都有一个特定且易于理解的名称,总结如下:
1 2 3 |
| 正向预测 | 负向预测 正类别 | 真阳性 (TP) | 假阴性 (FN) 负类别 | 假阳性 (FP) | 真阴性 (TN) |
构成ROC曲线和精确度-召回曲线的指标是根据混淆矩阵中的单元格定义的。
现在我们已经复习了混淆矩阵,让我们更仔细地看看ROC曲线指标。
想要开始学习不平衡分类吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
ROC曲线和ROC AUC
ROC曲线(或接收者操作特征曲线)是一个总结二元分类模型在正类上性能的图表。
x轴表示假阳性率,y轴表示真阳性率。
- ROC曲线:假阳性率(x)与真阳性率(y)的图表。
真阳性率是一个分数,计算方法是真阳性预测总数除以真阳性和假阴性的总和(例如,正类中的所有样本)。真阳性率被称为灵敏度或召回率。
- 真阳性率 = 真阳性 / (真阳性 + 假阴性)
假阳性率的计算方法是假阳性预测总数除以假阳性和真阴性的总和(例如,负类中的所有样本)。
- 假阳性率 = 假阳性 / (假阳性 + 真阴性)
我们可以将该图视为正类正确预测的比例(y轴)与负类错误预测的比例(x轴)。
理想情况下,我们希望正确正类预测的比例为1(图的顶部),错误负类预测的比例为0(图的左侧)。这突出表明,达到完美技能的最佳分类器位于图的左上角(坐标0,1)。
- 完美技能:图左上角的一个点。
阈值应用于正类和负类之间概率的截止点,任何分类器默认设置为0.5,即两个结果(0和1)之间的一半。
真阳性率和假阳性率之间存在权衡,即改变分类阈值将改变预测的平衡,以牺牲假阳性率来提高真阳性率,反之亦然。
通过评估不同阈值下的真阳性和假阳性,可以构建一条从左下角延伸到右上角并向左上角弯曲的曲线。这条曲线被称为ROC曲线。
一个在正类和负类之间没有判别能力的分类器将形成一条对角线,从假阳性率0和真阳性率0(坐标(0,0)或预测所有负类)到假阳性率1和真阳性率1(坐标(1,1)或预测所有正类)。低于这条线的模型表示其技能不如无技能。
该曲线提供了一个方便的诊断工具,用于研究一个分类器在不同阈值下的表现以及对真阳性率和假阳性率的影响。人们可以选择一个阈值来偏置分类模型的预测行为。
它是一种流行的诊断工具,适用于平衡和不平衡二元预测问题,因为它不偏向于多数或少数类别。
ROC分析对在少数类别上表现良好而牺牲多数类别的模型没有任何偏见——当处理不平衡数据时,这是一个非常有吸引力的特性。
— 第27页,《不平衡学习:基础、算法和应用》,2013年。
我们可以使用scikit-learn的roc_curve()函数在Python中绘制模型的ROC曲线。
该函数接收测试集中的真实结果(0,1)和对1类的预测概率。该函数返回每个阈值下的假阳性率、每个阈值下的真阳性率和阈值。
1 2 3 |
... # 计算 roc 曲线 fpr, tpr, thresholds = roc_curve(testy, pos_probs) |
大多数scikit-learn模型可以通过调用predict_proba()函数来预测概率。
这将返回测试集中每个样本的每个类别的概率,例如,在二元分类问题中,每个两个类别有两个数字。正类的概率可以作为此概率数组的第二列获取。
1 2 3 4 5 |
... # 预测概率 yhat = model.predict_proba(testX) # 仅检索正类的概率 pos_probs = yhat[:, 1] |
我们可以在合成数据集上演示这一点,并绘制无技能分类器和逻辑回归模型的ROC曲线。
make_classification()函数可用于创建合成分类问题。在这种情况下,我们将为二元分类问题创建1,000个样本(每个类别约500个样本)。然后,我们将数据集分成大小相等的训练集和测试集,以便拟合和评估模型。
1 2 3 4 5 |
... # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, random_state=1) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) |
逻辑回归模型是一个很好的演示模型,因为其预测概率经过了良好的校准,与并非围绕概率模型开发的其他机器学习模型不同,后者的概率可能需要先进行校准(例如支持向量机)。
1 2 3 4 |
... # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) |
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# 预测模型的ROC曲线示例 from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve from matplotlib import pyplot # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, random_state=1) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 仅检索正类的概率 pos_probs = yhat[:, 1] # 绘制无技能ROC曲线 pyplot.plot([0, 1], [0, 1], linestyle='--', label='No Skill') # 计算模型的ROC曲线 fpr, tpr, _ = roc_curve(testy, pos_probs) # 绘制模型ROC曲线 pyplot.plot(fpr, tpr, marker='.', label='Logistic') # 轴标签 pyplot.xlabel('False Positive Rate') pyplot.ylabel('True Positive Rate') # 显示图例 pyplot.legend() # 显示绘图 pyplot.show() |
运行示例会创建合成数据集,将其分成训练集和测试集,然后在训练数据集上拟合逻辑回归模型,并使用它对测试集进行预测。
逻辑回归模型的ROC曲线(橙色带点)显示。无技能分类器显示为对角线(蓝色带虚线)。

逻辑回归模型和无技能分类器的ROC曲线
现在我们已经看到了ROC曲线,让我们更仔细地看看ROC曲线下面积得分。
ROC曲线下面积 (AUC) 得分
尽管ROC曲线是一种有用的诊断工具,但基于其曲线比较两个或多个分类器可能具有挑战性。
相反,可以计算曲线下面积,为分类器模型在所有阈值上提供一个单一分数。这被称为ROC曲线下面积或ROC AUC,有时也称为ROCAUC。
该分数为0.0到1.0之间的值,1.0表示完美分类器。
AUCROC可以解释为分类器给出的分数将随机选择的正例排名高于随机选择的负例的概率。
— 第54页,《从不平衡数据集学习》,2018年。
这个单一的分数可以直接用于比较二元分类模型。因此,这个分数可能是用于比较不平衡问题分类模型最常用的分数。
最常见的度量标准涉及接收者操作特性 (ROC) 分析和 ROC 曲线下面积 (AUC)。
— 第27页,《不平衡学习:基础、算法和应用》,2013年。
ROC 的 AUC 可以使用 scikit-learn 中的 roc_auc_score() 函数计算。
与 roc_curve() 函数一样,AUC 函数同时接受测试集中的真实结果(0,1)和正类的预测概率。
1 2 3 |
... # 计算 roc auc roc_auc = roc_auc_score(testy, pos_probs) |
我们可以使用相同的合成数据集和逻辑回归模型来演示这一点。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# 预测模型的 roc auc 示例 from sklearn.datasets import make_classification from sklearn.dummy import DummyClassifier from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, random_state=1) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) # 无技能模型,分层随机类别预测 model = DummyClassifier(strategy='stratified') model.fit(trainX, trainy) yhat = model.predict_proba(testX) pos_probs = yhat[:, 1] # 计算 roc auc roc_auc = roc_auc_score(testy, pos_probs) print('无技能 ROC AUC %.3f' % roc_auc) # 有技能模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) yhat = model.predict_proba(testX) pos_probs = yhat[:, 1] # 计算 roc auc roc_auc = roc_auc_score(testy, pos_probs) print('逻辑回归 ROC AUC %.3f' % roc_auc) |
运行示例会创建并分割合成数据集,拟合模型,并使用拟合模型预测测试数据集的概率。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到合成数据集上逻辑回归模型的ROC AUC约为0.903,远优于无技能分类器的0.5左右的得分。
1 2 |
无技能 ROC AUC 0.509 逻辑回归 ROC AUC 0.903 |
尽管ROC AUC被广泛使用,但它并非没有问题。
对于少数类样本很少的严重不平衡分类问题,ROC AUC可能会产生误导。这是因为少量正确或不正确的预测可能导致ROC曲线或ROC AUC分数发生大的变化。
尽管ROC图在存在类别不平衡时被广泛用于评估分类器,但它有一个缺点:在类别稀有性下,即当类别不平衡问题与少数实例的样本量低相关联时,估计可能不可靠。
— 第55页,《从不平衡数据集学习》,2018年。
一个常见的替代方案是精确度-召回曲线和曲线下面积。
精确度-召回曲线和AUC
精确率是量化正确正向预测数量的指标。
其计算方法是真阳性数量除以真阳性和假阳性总数。
- 精确度 = 真阳性 / (真阳性 + 假阳性)
结果是一个介于 0.0(无精确率)到 1.0(完全或完美精确率)之间的值。
召回率是量化所有可能做出的正向预测中正确正向预测数量的指标。
它的计算方法是真阳性数量除以真阳性和假阴性总数(例如,它是真阳性率)。
- 召回率 = 真阳性 / (真阳性 + 假阴性)
结果是一个介于 0.0(无召回)到 1.0(完全或完美召回)之间的值。
精确度和召回率都侧重于正类(少数类),而对真阴性(多数类)不关心。
……精确度和召回率使得评估分类器在少数类别上的性能成为可能。
— 第27页,《不平衡学习:基础、算法和应用》,2013年。
精确度-召回曲线(或PR曲线)是精确度(y轴)和召回率(x轴)在不同概率阈值下的图表。
- PR曲线:召回率(x)与精确度(y)的图表。
具有完美技能的模型表示为坐标为(1,1)的点。熟练模型由向坐标(1,1)弯曲的曲线表示。无技能分类器将是图上的一条水平线,其精确度与数据集中正例的数量成比例。对于平衡数据集,这将是0.5。
PR曲线专注于少数类别,使其成为不平衡二元分类模型的有效诊断工具。
对于高度偏斜的领域,建议使用精确度-召回曲线(PR曲线),因为ROC曲线可能会提供过于乐观的性能视图。
— 不平衡分布下的预测建模调查,2015 年。
可以使用 scikit-learn 的 precision_recall_curve() 函数计算精确度-召回曲线,该函数接收类别标签和少数类别的预测概率,并返回精确度、召回率和阈值。
1 2 3 |
... # 计算精确度-召回曲线 precision, recall, _ = precision_recall_curve(testy, pos_probs) |
我们可以在合成数据集上演示预测模型的这一功能。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 预测模型的精确度-召回曲线示例 from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from matplotlib import pyplot # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, random_state=1) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 仅检索正类的概率 pos_probs = yhat[:, 1] # 计算无技能线,作为正类别的比例 no_skill = len(y[y==1]) / len(y) # 绘制无技能精确度-召回曲线 pyplot.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill') # 计算模型精确度-召回曲线 precision, recall, _ = precision_recall_curve(testy, pos_probs) # 绘制模型精确度-召回曲线 pyplot.plot(recall, precision, marker='.', label='Logistic') # 轴标签 pyplot.xlabel('Recall') pyplot.ylabel('Precision') # 显示图例 pyplot.legend() # 显示绘图 pyplot.show() |
运行示例会创建合成数据集,将其分成训练集和测试集,然后在训练数据集上拟合逻辑回归模型,并使用它对测试集进行预测。
逻辑回归模型的精确度-召回曲线(橙色带点)显示。随机或基线分类器显示为水平线(蓝色带虚线)。

逻辑回归模型和无技能分类器的精确度-召回曲线
现在我们已经看到了精确度-召回曲线,让我们更仔细地看看ROC曲线下面积得分。
精确度-召回曲线下面积 (AUC) 得分
精确度-召回AUC就像ROC AUC一样,它将一系列阈值下的曲线总结为一个单一得分。
该分数可以作为二元分类问题中不同模型之间的比较点,其中1.0分表示具有完美技能的模型。
精确度-召回AUC分数可以使用scikit-learn中的auc()函数计算,该函数接受精确度和召回率值作为参数。
1 2 3 |
... # 计算精确度-召回AUC auc_score = auc(recall, precision) |
同样,我们可以演示在合成数据集上计算逻辑回归的精确度-召回AUC。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 预测模型的精确度-召回AUC示例 from sklearn.datasets import make_classification from sklearn.dummy import DummyClassifier from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from sklearn.metrics import auc # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, random_state=1) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) # 无技能模型,分层随机类别预测 model = DummyClassifier(strategy='stratified') model.fit(trainX, trainy) yhat = model.predict_proba(testX) pos_probs = yhat[:, 1] # 计算精确度-召回AUC precision, recall, _ = precision_recall_curve(testy, pos_probs) auc_score = auc(recall, precision) print('无技能 PR AUC: %.3f' % auc_score) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) yhat = model.predict_proba(testX) pos_probs = yhat[:, 1] # 计算精确度-召回AUC precision, recall, _ = precision_recall_curve(testy, pos_probs) auc_score = auc(recall, precision) print('逻辑回归 PR AUC: %.3f' % auc_score) |
运行示例会创建并分割合成数据集,拟合模型,并使用拟合模型预测测试数据集的概率。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到逻辑回归模型在合成数据集上的精确度-召回AUC约为0.898,远优于无技能分类器在这种情况下将达到的0.632分。
1 2 |
无技能 PR AUC: 0.632 逻辑回归 PR AUC: 0.898 |
严重不平衡下的ROC和精确度-召回曲线
在本节中,我们将探讨在二元分类问题中使用ROC曲线和精确度-召回曲线,该问题存在严重的类别不平衡。
首先,我们可以使用 make_classification() 函数创建 1,000 个分类问题样本,其中少数类别与多数类别的比例约为 1:100。这可以通过设置“weights”参数并指定每个类别生成的实例的权重来实现。
我们将使用 99% 和 1% 的权重,总共 1,000 个样本,这意味着类别 0 大约有 990 个,类别 1 大约有 10 个。
1 2 3 |
... # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], random_state=1) |
然后,我们可以将数据集分成训练集和测试集,并通过在调用 train_test_split() 函数时设置“stratify”参数并将其设置为目标变量数组,确保两者具有相同的总体类别比例。
1 2 3 |
... # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) |
将这些结合起来,下面列出了准备不平衡数据集的完整示例。
1 2 3 4 5 6 7 8 9 10 11 |
# 创建不平衡数据集 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 总结数据集 print('数据集:Class0=%d,Class1=%d' % (len(y[y==0]), len(y[y==1]))) print('训练集:Class0=%d,Class1=%d' % (len(trainy[trainy==0]), len(trainy[trainy==1]))) print('测试集:Class0=%d,Class1=%d' % (len(testy[testy==0]), len(testy[testy==1]))) |
运行示例首先总结了整个数据集的类别比例,然后是训练集和测试集的比例,确认了数据集的分割保持了相同的比例。
1 2 3 |
数据集:Class0=985,Class1=15 训练集:Class0=492,Class1=8 测试集:Class0=493,Class1=7 |
接下来,我们可以在数据集上开发逻辑回归模型,并使用ROC曲线和ROC AUC分数评估模型的性能,并将结果与无技能分类器进行比较,就像我们在前面部分所做的那样。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# 在不平衡数据集上的ROC曲线和ROC AUC from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.dummy import DummyClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve from sklearn.metrics import roc_auc_score from matplotlib import pyplot # 绘制无技能和模型ROC曲线 def plot_roc_curve(test_y, naive_probs, model_probs): # 绘制无技能ROC曲线 fpr, tpr, _ = roc_curve(test_y, naive_probs) pyplot.plot(fpr, tpr, linestyle='--', label='无技能') # 绘制模型ROC曲线 fpr, tpr, _ = roc_curve(test_y, model_probs) pyplot.plot(fpr, tpr, marker='.', label='逻辑回归') # 轴标签 pyplot.xlabel('假阳性率') pyplot.ylabel('真阳性率') # 显示图例 pyplot.legend() # 显示图表 pyplot.show() # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 无技能模型,分层随机类别预测 model = DummyClassifier(strategy='stratified') model.fit(trainX, trainy) yhat = model.predict_proba(testX) naive_probs = yhat[:, 1] # 计算 roc auc roc_auc = roc_auc_score(testy, naive_probs) print('无技能 ROC AUC %.3f' % roc_auc) # 有技能模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) yhat = model.predict_proba(testX) model_probs = yhat[:, 1] # 计算 roc auc roc_auc = roc_auc_score(testy, model_probs) print('逻辑回归 ROC AUC %.3f' % roc_auc) # 绘制ROC曲线 plot_roc_curve(testy, naive_probs, model_probs) |
运行示例会像以前一样创建不平衡二元分类数据集。
然后,在训练数据集上拟合逻辑回归模型,并在测试数据集上进行评估。同时评估一个无技能分类器作为参考。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
报告了两个分类器的 ROC AUC 分数,显示无技能分类器如预期获得最低分数,约为 0.5。逻辑回归模型的结果表明其具有一定技能,分数约为 0.869。
1 2 |
无技能 ROC AUC 0.490 逻辑回归 ROC AUC 0.869 |
还为模型和无技能分类器创建了 ROC 曲线,显示性能不佳,但与对角线无技能分类器相比,确实具有技能表现。

不平衡分类数据集的逻辑回归 ROC 曲线图
接下来,我们可以使用精确度-召回率曲线和 AUC 分数对相同模型在相同数据上进行拟合和评估的分析。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# 不平衡数据集上的 PR 曲线和 PR AUC from sklearn.datasets import make_classification from sklearn.dummy import DummyClassifier from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from sklearn.metrics import auc from matplotlib import pyplot # 绘制无技能和模型的精确度-召回率曲线 def plot_pr_curve(test_y, model_probs): # 计算无技能线作为正类的比例 no_skill = len(test_y[test_y==1]) / len(test_y) # 绘制无技能精确度-召回率曲线 pyplot.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill') # 绘制模型精确度-召回率曲线 precision, recall, _ = precision_recall_curve(testy, model_probs) pyplot.plot(recall, precision, marker='.', label='Logistic') # 轴标签 pyplot.xlabel('Recall') pyplot.ylabel('Precision') # 显示图例 pyplot.legend() # 显示图表 pyplot.show() # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 无技能模型,分层随机类别预测 model = DummyClassifier(strategy='stratified') model.fit(trainX, trainy) yhat = model.predict_proba(testX) naive_probs = yhat[:, 1] # 计算精确度-召回AUC precision, recall, _ = precision_recall_curve(testy, naive_probs) auc_score = auc(recall, precision) print('无技能 PR AUC: %.3f' % auc_score) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) yhat = model.predict_proba(testX) model_probs = yhat[:, 1] # 计算精确度-召回AUC precision, recall, _ = precision_recall_curve(testy, model_probs) auc_score = auc(recall, precision) print('逻辑回归 PR AUC: %.3f' % auc_score) # 绘制精确度-召回率曲线 plot_pr_curve(testy, model_probs) |
如前所述,运行此示例会创建不平衡的二元分类数据集。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到逻辑回归模型实现了大约 0.228 的 PR AUC,而无技能模型实现了大约 0.007 的 PR AUC。
1 2 |
无技能 PR AUC: 0.007 逻辑回归 PR AUC: 0.228 |
还创建了精确度-召回率曲线图。
我们可以看到预期的无技能分类器的水平线,在这种情况下,逻辑回归曲线的锯齿状线接近无技能线。

不平衡分类数据集的逻辑回归精确度-召回率曲线图
为了解释 ROC 和 PR 曲线为何呈现不同的结果,请回想一下 PR 曲线侧重于少数类,而 ROC 曲线则涵盖两个类。
如果我们使用 0.5 的阈值,并使用逻辑回归模型对测试集中的所有样本进行预测,我们会发现它在所有情况下都预测为类别 0 或多数类。这可以通过使用拟合模型预测清晰的类别标签来证实,该标签将使用默认的 0.5 阈值。然后可以总结预测类别标签的分布。
1 2 3 4 5 |
... # 预测类别标签 yhat = model.predict(testX) # 总结类别标签的分布 print(Counter(yhat)) |
然后我们可以创建正类预测概率的直方图,以确认预测概率的质量低于 0.5,因此被映射到类别 0。
1 2 3 4 |
... # 创建预测概率的直方图 pyplot.hist(pos_probs, bins=100) pyplot.show() |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 总结预测概率的分布 from collections import Counter from matplotlib import pyplot from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 仅检索正类的概率 pos_probs = yhat[:, 1] # 预测类别标签 yhat = model.predict(testX) # 总结类别标签的分布 print(Counter(yhat)) # 创建预测概率的直方图 pyplot.hist(pos_probs, bins=100) pyplot.show() |
运行示例首先总结了预测类别标签的分布。正如我们所料,在测试集中,所有样本都预测为多数类(类别 0)。
1 |
计数器({0: 500}) |
还创建了类别 1 预测概率的直方图,显示其重心(大多数预测概率)小于 0.5,实际上通常接近零。

不平衡分类的逻辑回归类别 1 预测概率直方图
这意味着,除非仔细选择概率阈值,否则模型预测中任何熟练的细微差别都将丢失。选择用于将预测概率解释为清晰类别标签的阈值是一个重要话题。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
论文
- 不平衡分布下预测建模的调查, 2015.
书籍
- 不平衡学习:基础、算法与应用 (Imbalanced Learning: Foundations, Algorithms, and Applications), 2013.
- 从不平衡数据集中学习 (Learning from Imbalanced Data Sets), 2018.
API
- sklearn.datasets.make_classification API.
- sklearn.metrics.roc_curve API.
- sklearn.metrics.roc_auc_score API
- precision_recall_curve API.
- sklearn.metrics.auc API.
文章
总结
在本教程中,您学习了不平衡分类的 ROC 曲线和精确度-召回率曲线。
具体来说,你学到了:
- ROC曲线和精确度-召回曲线为二元分类模型提供了诊断工具。
- ROC AUC和精确度-召回AUC提供了总结曲线的得分,可用于比较分类器。
- 在少数类别样本很少的严重不平衡分类问题上,ROC曲线和ROC AUC可能过于乐观。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
感谢分享,这是一篇很好的阅读!
不客气!
谢谢您的教程。
1. 其他用于模型验证的指标是包含估计的模型摘要。我们可以从这些估计中推断出什么?
2. 我之所以这么说,是因为如果这是真实情况,而不是机器生成的随机数。
3. 那么,在发现拟合模型的精确度非常低(即 <0.5)之后,继续将此模型用于未见数据(预测)是否公平?
如果不是,下一步该怎么做?
如果一个模型表现优于一个朴素模型,那么它就具有技能。
您可以决定是否使用它,或者继续测试新模型,看看是否能表现得更好。
当您用完时间/资源或结果对项目干系人足够好时,测试新模型就结束了。
一如既往的精彩文章!感谢您为社区所做的一切。
谢谢 Greg!
你好 Jason,
关于特征提取和特征选择的问题。
它们可以被归类为无监督机器学习吗?
使用 Keras 进行特征提取和特征选择有用吗?
您能用简单的语言解释一下两者之间的区别以及它们的作用吗?
您有简单的例子吗?
非常感谢,
Marco
也许吧。它们并非真正的学习算法。
是的,但这取决于问题的类型。
特征选择控制从数据中哪些输入用于模型。特征提取提供原始特征的视图或投影作为模型的输入。大多数神经网络会自动执行特征提取,例如用于图像分类的 CNN。
谢谢 Jason。
就我所知,似乎存在一种顺序:先特征选择,然后特征提取。对吗?
提取阶段的结果可能与选择阶段(前一阶段)相同吗?
也就是说,提取阶段的数据集可能与选择阶段相同吗?
谢谢
也许吧。这确实取决于数据和模型。
例如,决策树集成执行一种自动类型的特征选择。神经网络执行一种自动类型的特征提取等等。
对于音频/文本数据和标准机器学习算法,特征提取几乎是一个必需的步骤。对于文本数据,则完全不是,除非你称之为特征工程。
诸如此类。这很复杂。
嗨
“清晰类别标签”确切指的是什么?特别是“清晰”这个词?
谢谢
好问题。
我的意思是每个样本的标签,而不是每个样本的类别成员概率。
你好,
请问,如果您能向我们展示如何优化最佳阈值(低于 0.5)以最小化假阴性和假阳性,那将非常棒。
我有一篇关于这个的教程,将出现在我的新书中(即将出版)。
你好,
是哪本书?
2020 年 1 月出版的《Python 不平衡分类》
https://machinelearning.org.cn/imbalanced-classification-with-python/
您的新书有特别优惠/折扣码吗?谢谢
是的,请看这个
https://machinelearning.org.cn/faq/single-faq/can-i-have-a-discount
如果我们得到完美的准确率,但 ROC-AUC、F1-score、精确度和召回率都为零,该如何解释这种情况?
这说不通。您可能在某个地方有 bug。
你好,
非常感谢这篇精彩的文章,我发现它超级有用。
我有一个问题。当您在文章的最后一句提到“这意味着,除非仔细选择概率阈值,否则模型预测中任何熟练的细微差别都将丢失。选择用于将预测概率解释为清晰类别标签的阈值是一个重要话题”,因此在这种情况下,需要选择低于 0.5 的概率,对吗?例如 0.2。
这有道理吗?
您对在这种情况下如何选择最佳阈值有什么建议,还是只需按类别 1 概率排序并选择前 N 个项目?
谢谢,期待您的回复。
Jon
是的,请看这个
https://machinelearning.org.cn/threshold-moving-for-imbalanced-classification/
你好!
我是机器学习新手。我使用神经网络进行语义分割,将图像分为两类。我们将“类别 0”称为多数类,“类别 1”称为少数类,其中类别 1 是正类。
我将类别 0 设为灰色,类别 1 设为绿色。我想根据我的神经网络模型创建精确度-召回率曲线,但我不明白在这种情况下阈值是什么。
您能给我解释一下吗?
谢谢
您可以收集您的概率预测和预期值,并将其直接传递给函数以计算曲线。无需特殊操作。
你好!
谢谢您的回答。我不太明白。事实上,我没有任何概率预测,也不知道如何计算它们。目前,我只创建了一个用于语义分割的神经网络。为了训练,我给它图像和相应的标记图像(带有两种颜色的 .png 图像,用于我的两个类别)。作为我的神经网络的输出,在训练和验证之后,我有一个测试,它给我一个标记图像。我应该使用哪个函数或如何计算概率预测?“预期值”在我的情况下是否对应于“标记图像”?
非常感谢您的帮助
predict() 函数返回概率。
是的,预期值是模型应该预测的标签,0 或 1。
请记住,ROC 曲线需要二元分类任务。
虽然这篇报告很棒,动机也准确,但 PR 曲线存在很多问题,不应该使用,尤其不应该取 AUC。相反,应该使用 PR-增益曲线。这篇论文(NIPS’15)解释了问题和解决方案:https://papers.nips.cc/paper/5867-precision-recall-gain-curves-pr-analysis-done-right.pdf
感谢分享。
嗨,Jason,
感谢您的教程。有几个问题
1. 我们如何知道这一列是类别 1(即正类)的概率?第二列属于正类是约定俗成的吗?
# 仅检索正类的概率
pos_probs = yhat[:, 1]
2. 这个概率到底是什么?它是否意味着给定一组特征,该类别发生的可能性?那么,概率类别中第 0 列和第 1 列的总和是 1 吗?
3. 在上面的直方图图中,我理解它显示了类别 1(即正类)的概率分布。那么,如果阈值为 0.5,这里就没有观察结果会产生类别 1 了?这正确吗?
谢谢你
不客气。
好问题,是的,这是一个约定,我建议阅读二项分布。
https://machinelearning.org.cn/discrete-probability-distributions-for-machine-learning/
模型预测输入样本属于类别 1 的概率。
概率与 1 减去概率之和为 1,再次请参阅二项分布。
https://machinelearning.org.cn/discrete-probability-distributions-for-machine-learning/
我们可以选择一个合适的阈值来解释概率,这被称为阈值移动。
https://machinelearning.org.cn/threshold-moving-for-imbalanced-classification/
ROC 分析对于在多数类上表现良好而牺牲多数类的模型没有任何偏向——这在处理不平衡数据时是一个非常有吸引力的特性。
我对上述说法略感困惑,因为它提到了“多数类以多数类为代价”。我认为其中一个应该是少数类。
ROC分析对在少数类别上表现良好而牺牲多数类别的模型没有任何偏见——当处理不平衡数据时,这是一个非常有吸引力的特性。
请求您对此进行详细阐述。
谢谢,已修正。
嗨 Jason。您有关于使用 GINI 作为评分指标以及为什么它是个好选择的文章吗?
没有,不过是个很棒的建议,谢谢。
你好,
我发现这篇文章非常有趣,但是,由于我不习惯使用 AUPRC,我被一个特定的点弄糊涂了。
我不太明白代码中使用的“虚拟”模型为什么使用“分层”策略。我通常作为我的 ML 模型要击败的基线是一个虚拟模型,它为正类概率输出 trainy.mean(),为负类概率输出 1-trainy.mean()。我刚刚发现我可以使用 DummyClassifier 的“prior”策略构建相同的模型(万岁!)。
这样的模型具有 0.5 的 AUROC 和 AUPRC。这是否意味着“严重不平衡的 ROC 和精确度-召回率曲线”部分中显示的逻辑回归模型实际上毫无用处?
我一直觉得不平衡问题的 AUROC 和 AUPRC 相当令人困惑,我一直在尝试使用适当的评分规则,如对数损失和布里尔分数来获得最佳模型,并且只在构建了尽可能好的模型之后才优化分类阈值。
总之,我是这个网站的粉丝,感谢这篇很棒的文章。
这可能有助于理解这些指标
https://machinelearning.org.cn/tour-of-evaluation-metrics-for-imbalanced-classification/
这可能有助于为指标选择一个朴素模型
https://machinelearning.org.cn/naive-classifiers-imbalanced-classification-metrics/
嗨,Jason,
roc_auc_score() 默认使用 average='macro',它不考虑标签不平衡。类别权重被忽略。这对于不平衡数据上的二元分类器来说,是计算 AUC 的正确方法吗?
取决于您的目标——您想测量什么。
您好。我们如何将上述指标用于多类别问题?有什么相关的阅读材料吗?
通常不能。
有一些尝试的扩展,但很抱歉我并不了解它们。试试 scholar.google.com
嗨 Jason,感谢这篇全面的文章!我有一个关于您“无技能”分类器的 PR 曲线的问题——我看到您在这里绘制的是随机猜测分类器的实际精确度和召回率,我以前见过的大多数图表都有对角线,这纯粹是为了比较 0.5 AUC_PR 的基准吗?谢谢!
是的,ROC 曲线使用对角线,精确度-召回率曲线使用水平线。
你好 Jason,
当我用 CNN-1D 处理我的数据时,我观察到一个相当奇怪的现象。我将数据分为训练、验证和未见类别。我获得了非常高的“验证”准确率(92%)。然而,当我处理完全“未见数据”时,准确率接近 50%(在我看来这似乎是随机水平的准确率)。
请参考以下链接中的“训练和验证图”。
https://drive.google.com/file/d/10pGQG0YsVNMIM7g0YEWdOmzM2lv5l3Ss/view?usp=sharing
我的“未见数据”混淆矩阵如下。准确率为53%。
[[289 191]
[143 97]]
您能否给我一些指导,如何解释这种不匹配?我不明白为什么CNN收敛得这么好?如何识别使其收敛的特征,以及“未见”数据缺少什么?
此致,
斯瓦蒂。
嗨,Swati……一个考虑因素是所用数据集的相对大小。
你好,
首先,感谢您的教程。我正在使用支持向量机算法预测洪水易发区域,共有15个条件因子(包括分类变量和连续变量)。在处理数据时,我观察到一个相当奇怪的现象,我的测试数据准确率是97.45,曲线下面积几乎是100%(实际上是99.7%)。我对此感到怀疑。根据您的看法,这可能是什么原因造成的?
你好Kumudu,
你可能正在处理回归问题并实现零预测误差。
或者,你可能正在处理分类问题并实现 100% 的准确率。
这很不寻常,原因有很多,包括:
你不小心在训练集上评估了模型性能。
你的保留数据集(训练集或验证集)太小或不具代表性。
你的代码中引入了一个错误,它正在做一些与你预期不同的事情。
你的预测问题很容易或微不足道,可能不需要机器学习。
最常见的原因是你的保留数据集太小或不代表更广泛的问题。
可以通过以下方法解决:
使用 k 折交叉验证来估计模型性能,而不是训练/测试拆分。
收集更多数据。
使用不同的数据拆分进行训练和测试,例如 50/50。
嗨!
如果你切换权重,例如在 make_classification 中设置 weights=[0.01, 0.99],使 Class 1 成为主导类别,你最终会得到一个 PR AUC 大于 ROC AUC 的结果……!但你仍然有一个不平衡的数据集,哪个类别是主要的并不重要,对吗?所以我期望得到和以前相同的结果。你如何修改代码来解决这个问题?
谢谢。
嗨 GiaZ……以下资源是理解处理不平衡分类时最佳实践基础知识的绝佳起点。
https://machinelearning.org.cn/framework-for-imbalanced-classification-projects/
你好。
在不平衡数据的情况下,Matthews 分数(也称为 phi 或 mcc)还是 Fowlkes 分数更好?
我知道这些仅针对单个阈值点计算。
你好,谢谢你
在不平衡数据中,哪个度量很重要?
非常欢迎 Ali!以下资源提供了不平衡分类评估指标的概览
https://machinelearning.org.cn/tour-of-evaluation-metrics-for-imbalanced-classification/#:~:text=There%20are%20two%20groups%20of,%2Dspecificity%20and%20precision%2Drecall.
你好。非常感谢你的工作。我从你的频道学到了很多东西。关于使用不平衡数据集时 PR 曲线的 AUC 的快速查询。无技能值(即无技能分类器的精确度)计算为整个集合中正样本的比例。鉴于无技能分类器的 PR 曲线(一条 y 值等于正样本比例的水平线),如果我们要获得平均精确度(曲线下面积的近似值),那不就是长度为 1,高度为正样本比例的矩形面积吗?为什么需要虚拟分类器,当我们可以直接从无技能 PR 曲线计算面积时?
嗨 Suraj……你当然可以使用你的方法。让我们知道你从这两种方法中注意到的差异。
你好 James,非常感谢你的所有帖子,以及分享你知识的努力。
我很难理解:“在少数类样本量严重不平衡的分类问题中,ROC 曲线和 ROC AUC 可能过于乐观。”
样本量较少的类别不总是少数类别吗?
或者上面那句话的意思是:数据集中正常情况的数据不足?我也很难想出一个真实的例子。你能提供一个吗?
我之所以感到困惑,是因为我倾向于将这些类别仅仅视为标签,并总是将样本量较少的类别标记为正例。我这样想是不是错了?
非常欢迎你,Samy!以下资源你可能会感兴趣
https://machinelearning.org.cn/imbalanced-classification-is-hard/
我们创建一个模型并在训练集上进行训练,然后使用该模型预测测试集的概率。并选择一个能根据我们在测试数据上的需求给出最佳 F1 分数或任何其他度量指标的阈值。现在,该性能指标不再对新数据可靠,因为我们根据测试数据选择了参数(阈值),它可能会有偏差。我们是否应该使用单独的交叉验证集来选择阈值,并给出模型在未见测试数据上的性能。还是我这里遗漏了什么?
嗨 Karthikeyan……以下是关于训练、测试和验证以及相关数据集的最佳实践
https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/
我有一个测试数据集,其中异常数据明显多于正常数据。我的异常检测应该能够可靠地识别异常。它不应该给出误报,因为这会导致系统故障(将异常分类为正常)。那么您会使用 PR-AUC 来比较不同的异常检测吗?
或者,我可以从测试数据集中省略许多异常,以便异常数据数量与正常数据数量相同,然后使用 ROC-AUC。
嗨 Mark……选择 PR-AUC(精确度-召回率曲线下面积)和 ROC-AUC(受试者工作特征曲线下面积)取决于您的异常检测任务的具体要求。
1. **PR-AUC:**
– 当数据集高度不平衡时,例如在您的案例中,异常数据明显多于正常数据,PR-AUC 是一个合适的指标。
– PR-AUC 侧重于精确度(检测到的异常中真实异常的比例)和召回率(检测到的真实异常的比例)之间的权衡。
– 当您希望避免误报(将异常分类为正常)时,它特别有用,因为精确度直接衡量正预测(异常)的准确性。
2. **ROC-AUC:**
– 当数据集平衡或误报和漏报的成本大致相等时,ROC-AUC 更适用。
– 它评估真阳性率(TPR 或召回率)和假阳性率(FPR)之间的权衡。
– ROC-AUC 在高度不平衡的数据集中信息量较少,因为它可能会给出模型性能的误导性印象,特别是如果多数类别(正常数据)占主导地位。
鉴于您的首要任务是避免误报并可靠地识别异常,PR-AUC 将是更好的选择。它在精确度和召回率方面提供了对模型性能更具信息量的评估,这与您的任务要求直接相关。
关于从测试数据集中省略许多异常以平衡类别,然后使用 ROC-AUC 的选项
– 这种方法人为地平衡了类别,但可能无法反映异常普遍存在的真实世界场景。
– 它可能无法提供对模型处理不平衡数据和检测异常性能的现实评估。
– 在原始不平衡数据集上使用 PR-AUC 将更准确地表示模型在真实世界条件下检测异常的性能。
因此,考虑到数据集的高度不平衡性质以及避免误报的重要性,我建议使用 PR-AUC 来比较不同的异常检测方法。