ROC: 接收者操作特征曲线(receiver operating characteristic curve),是反映敏感性和特异性连续变量的综合指标,roc曲线上每个点反映着对同一信号刺激的感受性。
“上边儿那个说tm什么玩意儿啊…”,我也是这样想的,先跳过这个概念,强制告诉自己当前情况下,ROC曲线就是 TP率和FP率连成的曲线
AUC (Area Under Curve) 被定义为ROC曲线下的面积
我先给一个手算的Python代码,中间也有调库的
也不算完全手算的,阈值thresholds
是调库获得的
如果你真想完全搞懂到底怎么算的,建议直接单步debug
roc_curve
用于计算ROC曲线的每个点的坐标,以及对应的阈值
roc_auc_score
用于计算AUC,也就是ROC曲线下的面积
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve
from sklearn.metrics import roc_auc_score
# 固定随机数种子,以使得每次都一样
np.random.seed(1107)
# 编一些数据实例
y_true = np.random.randint(2, size=(100,), dtype="bool")
y_score = np.random.random(size=(100,))
# 按照概率小大排序
order = y_score.argsort()
y_true = y_true[order]
y_score = y_score[order]
fpr, tpr, thresholds = roc_curve(y_true, y_score)
roc_auc = roc_auc_score(y_true, y_score)
print(roc_auc)
# 我自己算一遍
# 随着阈值改变,有多少真的正例被找出来了
tpr_list = []
for th in thresholds:
curr_true = (y_score >= th) & y_true
fenzi = curr_true.sum()
fenmu = y_true.sum()
tpr_list.append( fenzi / fenmu )
tpr_array = np.array(tpr_list)
tpr_bool = (tpr == tpr_array).all()
print("[tpr]正确: ", tpr_bool)
# 随着阈值改变,假阳性率的变化
fpr_list = []
for th in thresholds:
curr_false = (y_score >= th) & ~y_true # 将被当做正例的负例拿出来
fenzi = curr_false.sum()
fenmu = (~y_true).sum()
fpr_list.append( fenzi / fenmu )
fpr_array = np.array(fpr_list)
fpr_bool = (fpr == fpr_array).all()
print("[fpr]正确: ", fpr_bool)
y_delta = np.diff(tpr)
x_delta = (1 - fpr)[:-1]
auc = (y_delta * x_delta).sum()
print("[AUC]计算正确: ", abs(auc-roc_auc) < 10e-6)
# ROC 曲线, 注意横坐标是 fpr
plt.scatter(fpr, tpr, c="red", s=2)
plt.plot(fpr, tpr)
plt.show()
ROC曲线横轴是FP率,纵轴的TP率,写程序绘制ROC曲线时要注意fpr, tpr
的顺序是这样,别反了
plt.plot(fpr, tpr)
当年看ROC曲线时,我死活搞不懂,TP率和FP率怎么会变? 那个玩意儿你的模型算出来之后,不是已经定死了么???
假如问题是二分类,要么 T r u e True True,要么 F a l s e False False,然而实际上大部分模型的输出都是是 T r u e True True的概率 P T r u e P_{True} PTrue, P F a l s e = 1 − P T r u e P_{False}=1-P_{True} PFalse=1−PTrue,除了SVM,SVM只能知道他是正例还是负例,无法知道模型返回的置信度
然而,有一个参数——阈值 v a l val val,来判断一个实例到底是 T r u e True True 还是 F a l s e False False. 所以我们只要让 v a l val val取遍 [ 0 , 1 ] [0, 1] [0,1]的每一个数值,将对应的 ( F P 率 , T P 率 ) (FP率, TP率) (FP率,TP率) 记录下来,并标到图上,即可获得ROC曲线
然而,实际上无需取遍
[
0
,
1
]
[0, 1]
[0,1]中每一个值,我们只需要取上图中直角处的点,就是那个红点,因为相邻红点之间,要么FP率不变,要么TP率不变,也就是两个相邻红点之间,一定是直线
这样的话,可以简化计算了
我们可以调库,查看变量thresholds
打印出来是啥:
>>> thresholds
[1.95765094 0.95765094 0.94528669 0.93034073 0.92842689 0.92657479
0.92291242 0.88343102 0.8810227 0.87744016 0.866908 0.85125653
0.83371683 0.83208944 0.83117887 0.82556135 0.80705162 0.78068904
0.77402651 0.73445252 0.720276 0.69508259 0.67662352 0.66001852
0.63959376 0.62050148 0.61896561 0.59506313 0.57218685 0.54372382
0.52248361 0.47770536 0.47321275 0.45847792 0.39694008 0.39243611
0.38743406 0.38383452 0.37590903 0.35305426 0.34275677 0.33935575
0.31265543 0.28883676 0.25477464 0.24504649 0.2295074 0.22739724
0.22488244 0.21834767 0.20485498 0.16979291 0.16073303 0.11951154
0.10385017 0.07318998 0.06647717 0.06036329 0.04527814 0.01842294
0.01553236]
这些数字是不是有些眼熟呢,来看看这个:
>>> y_score[::-1]
[0.95765094 0.95399522 0.9471417 0.94528669 0.93034073 0.92842689
0.92657479 0.92522284 0.92291242 0.91543346 0.91397315 0.90965487
0.90470366 0.88343102 0.8810227 0.87744016 0.866908 0.85821269
0.85665705 0.85125653 0.83371683 0.83300306 0.83208944 0.83117887
0.82556135 0.82274304 0.81484892 0.80705162 0.78378691 0.78068904
0.77402651 0.76804603 0.73851392 0.73445252 0.720276 0.70781191
0.69508259 0.69000823 0.67662352 0.66001852 0.65615672 0.64420724
0.63959376 0.62050148 0.61896561 0.59506313 0.57218685 0.55803978
0.54372382 0.52248361 0.49487794 0.49375354 0.47770536 0.47321275
0.45847792 0.40366396 0.40161891 0.39694008 0.39243611 0.38743406
0.38383452 0.37590903 0.36949194 0.3685802 0.36168261 0.35305426
0.34275677 0.33935575 0.31265543 0.28883676 0.28117519 0.2734256
0.26876038 0.25477464 0.24504649 0.2295074 0.22739724 0.22542905
0.22488244 0.21834767 0.20485498 0.17463156 0.17237376 0.16979291
0.16073303 0.1575529 0.1450476 0.11951154 0.10385017 0.07318998
0.06647717 0.06605309 0.06036329 0.047198 0.04527814 0.04075525
0.02959499 0.02274728 0.01842294 0.01553236]
thresholds
中有两个要说的点:
thresholds 中的大部分数字来自 y_score ,因为有些 thresholds 即使取了 y_score 中的某个值,也不会使ROC曲线产生直角,也就是说:若 y_score 某个值当做阈值,无法使ROC曲线产生直角,那他也不会被加入到 thresholds 变量中,可以说 :thresholds 变量是经过简化版的 y_score 变量
除了 thresholds[0]
这个数,其余数字都是 y_score 中的数,我也不太清楚 thresholds[0]=1.95765094
这个数字哪里来的,但是他大于1,会使得FP率和TP率都为0
另外,再补充一下,我的AUC是这样计算的:
一横条面积一横条面积不断的加起来的
顺便说下这两句代码:
>>> fpr, tpr, thresholds = roc_curve(y_true, y_score)
>>> roc_auc = roc_auc_score(y_true, y_score)
>>> y_true
[ True False False False False True True False False True False True
False False False True False False False True False True True False
True False True True True True False True False True False False
False False True False True False True True True False True False
False False True False False True False True False True True True
False True True False False True False False False True False False
True True True False True False False True False False False True
False True False False False False False True True False True False
True True True True]
>>> y_score
[0.01553236 0.01842294 0.02274728 0.02959499 0.04075525 0.04527814
0.047198 0.06036329 0.06605309 0.06647717 0.07318998 0.10385017
0.11951154 0.1450476 0.1575529 0.16073303 0.16979291 0.17237376
0.17463156 0.20485498 0.21834767 0.22488244 0.22542905 0.22739724
0.2295074 0.24504649 0.25477464 0.26876038 0.2734256 0.28117519
0.28883676 0.31265543 0.33935575 0.34275677 0.35305426 0.36168261
0.3685802 0.36949194 0.37590903 0.38383452 0.38743406 0.39243611
0.39694008 0.40161891 0.40366396 0.45847792 0.47321275 0.47770536
0.49375354 0.49487794 0.52248361 0.54372382 0.55803978 0.57218685
0.59506313 0.61896561 0.62050148 0.63959376 0.64420724 0.65615672
0.66001852 0.67662352 0.69000823 0.69508259 0.70781191 0.720276
0.73445252 0.73851392 0.76804603 0.77402651 0.78068904 0.78378691
0.80705162 0.81484892 0.82274304 0.82556135 0.83117887 0.83208944
0.83300306 0.83371683 0.85125653 0.85665705 0.85821269 0.866908
0.87744016 0.8810227 0.88343102 0.90470366 0.90965487 0.91397315
0.91543346 0.92291242 0.92522284 0.92657479 0.92842689 0.93034073
0.94528669 0.9471417 0.95399522 0.95765094]
y_true
和 y_score
的shape都是 (n,)
,(二分类是这样的,多类的暂时不知道…)
y_true 是每个类别
y_score 是类别为 True 的概率
还不懂的话,可以参考这俩篇,我的氵文大多是方便我自己记录和理解,请海涵:
https://blog.csdn.net/u014264373/article/details/80487766
https://www.deeplearn.me/1522.html