假设我们从以下情况开始:
import numpy as np
from sklearn import metrics
现在我们设置真实的y
和预测的scores
:
y = np.array([0, 0, 1, 1])
scores = np.array([0.1, 0.4, 0.35, 0.8])
(注意:与您的问题相比,y
已向下移动1。 这无关紧要:在预测1,2或0,1时获得完全相同的结果(fpr、tpr、阈值等),但是如果不使用0,1,则部分sklearn.metrics
函数会变得麻烦。)
让我们看看这里的AUC:
>>> metrics.roc_auc_score(y, scores)
0.75
就像你的例子一样:
fpr, tpr, thresholds = metrics.roc_curve(y, scores)
>>> fpr, tpr
(array([ 0. , 0.5, 0.5, 1. ]), array([ 0.5, 0.5, 1. , 1. ]))
这将产生以下图表:
plot([0, 0.5], [0.5, 0.5], [0.5, 0.5], [0.5, 1], [0.5, 1], [1, 1]);
![enter image description here](https://istack.dev59.com/A142Q.webp)
对于有限长度的y,ROC曲线将由矩形组成:
因此,对于有限的y,ROC曲线将始终由连接(0, 0)和(1, 1)的水平和垂直线序列来描述。
AUC是这些矩形的总和。如上所示,AUC为0.75,因为矩形的面积为0.5 * 0.5 + 0.5 * 1 = 0.75。
在某些情况下,人们选择通过线性插值计算AUC。假设y的长度远大于FPR和TPR计算出的实际点数。那么,在这种情况下,线性插值是中间点可能存在的近似值。在某些情况下,人们还遵循猜想,即如果y足够大,则两点之间的点将被线性插值。 sklearn.metrics
不使用此猜想,为了获得与sklearn.metrics
一致的结果,需要使用矩形而不是梯形求和。
让我们编写自己的函数直接从fpr
和tpr
计算AUC:
import itertools
import operator
def auc_from_fpr_tpr(fpr, tpr, trapezoid=False):
inds = [i for (i, (s, e)) in enumerate(zip(fpr[: -1], fpr[1: ])) if s != e] + [len(fpr) - 1]
fpr, tpr = fpr[inds], tpr[inds]
area = 0
ft = zip(fpr, tpr)
for p0, p1 in zip(ft[: -1], ft[1: ]):
area += (p1[0] - p0[0]) * ((p1[1] + p0[1]) / 2 if trapezoid else p0[1])
return area
该函数获取FPR和TPR,并包含一个可选参数,用于指定是否使用梯形求和。运行该函数,我们得到:
>>> auc_from_fpr_tpr(fpr, tpr), auc_from_fpr_tpr(fpr, tpr, True)
(0.75, 0.875)
对于矩形求和,我们得到与 sklearn.metrics
相同的结果,对于梯形求和,我们得到不同但更高的结果。
所以,现在我们只需要看一下如果在 FPR 为 0.1 时终止会发生什么情况。我们可以使用bisect
模块来实现这一点。
import bisect
def get_fpr_tpr_for_thresh(fpr, tpr, thresh):
p = bisect.bisect_left(fpr, thresh)
fpr = fpr.copy()
fpr[p] = thresh
return fpr[: p + 1], tpr[: p + 1]
这是如何工作的?它仅检查thresh
在fpr
中的插入点。鉴于FPR的属性(必须从0开始),插入点必须位于水平线上。因此,此之前的所有矩形都不应受影响,此之后的所有矩形都应被删除,而此矩形可能会被缩短。
让我们应用它:
fpr_thresh, tpr_thresh = get_fpr_tpr_for_thresh(fpr, tpr, 0.1)
>>> fpr_thresh, tpr_thresh
(array([ 0. , 0.1]), array([ 0.5, 0.5]))
最后,我们只需要从更新的版本计算AUC:
>>> auc_from_fpr_tpr(fpr, tpr), auc_from_fpr_tpr(fpr, tpr, True)
0.050000000000000003, 0.050000000000000003)
在这种情况下,矩形和梯形求和法都得出相同的结果。请注意,通常情况下它们不会得到相同的结果。为了与
sklearn.metrics
保持一致,应该使用第一个方法。