如果人生的途程上没有障碍,人还有什么可做的呢。
——俾斯麦
全文字数:4101字
阅读时间:12分钟
前言
本系列是《玩转机器学习教程》一个整理的视频笔记。本小节主要介绍描述TPR和FPR两个指标的ROC曲线,并通过编程绘制ROC曲线。通常在实际使用中使用ROC曲线下面的面积来评估不同模型之间的优劣,最后使用sklearn中的roc_auc_score函数返回ROC曲线下面的面积。
a
TPR和FPR与ROC曲线
上一小节介绍了Precision-Recall曲线,这一小节介绍另外一个非常重要的曲线:ROC曲线。
▲ROC曲线描述TPR和FPR之间的关系
ROC曲线是"Receiver Operation Characteristic Curve"的简称。ROC是一个在统计学中经常使用的术语,它描述的是TPR和FPR之间的关系。TPR和FPR是两个全新的名词,接下来看一看这两个指标的具体含义。
- 什么是TPR?
▲TPR指标
TPR的英文全称为"True Positive Rate"。TPR指标非常简单,TPR和前面介绍的Recall召回率是一个意思,也就是算法预测为"类别1"并且预测正确的样本数(TP)占所有真实为"类别1"的样本数(TP FN)的比重。
- 什么是FPR?
▲FPR指标
FPR的英文全称为"False Positive Rate"。顾名思义,FPR和TPR是相对应的两个指标,FPR就是算法预测为"类别1"但是预测错误的样本数(FP)占所有真实为"类别0"的样本数(TN FP)的比重。
类似Precision和Recall两个指标,TPR和FPR两个指标之间同样存在联系。依然以前几个小节使用的score分布图为例,score轴上依次分布着12个样本,此时我们关注五角星,因此五角星为"类别1"圆圈为"类别0"。接下来看当我们取不同的threshold阈值进行分类时,相应的TPR和FPR是怎样变化的?
▲12个样本score值的分布
- threshold阈值设置非常大时的FPR和TPR
▲threshold设置非常大时的FPR和TPR
设置对应threshold阈值时。
- FPR = 0 / 6 = 0.00。算法预测两个样本为"类别1"但是预测错误的样本数为0(FP = 0),而真实为"类别0"的样本总数为6(TN FP),所以FPR = FP / (TN FP) = 0 / 6;
- TPR = 2 / 6 = 0.33。算法预测两个样本为"类别1"并且都预测正确(TP = 2),而真实为"类别1"的样本总数为6(TP FN = 6),所以Recall = TPR = TP / (TP FN) = 2 / 6;
- threshold阈值设置小一些时的FPR和TPR
▲threshold设置小一些时的FPR和TPR
设置对应threshold阈值时:
- FPR = 1 / 6 = 0.16。算法预测5个样本为"类别1"但是预测错误的样本数只有1个(FP = 1),而真实为"类别0"的样本总数为6(TN FP),所以FPR = FP / (TN FP) = 1 / 6;
- TPR = 4 / 6 = 0.67。算法预测5个样本为"类别1"不过只预测对了4个样本(TP = 4),而真实为"类别1"的样本总数为6(TP FN = 6),所以Recall = TPR = TP / (TP FN) = 4 / 6;
- threshold阈值非常小时的TPR和FPR
▲threshold设置非常小时的FPR和TPR
设置对应threshold阈值时:
- FPR = 2 / 6 = 0.33。算法预测8个样本为"类别1"但是预测错误的样本数有2个(FP = 2),也就是threshold阈值右边的两个圆圈,因为阈值太低了,所以算法将本来为"类别0"的样本错误分类成"类别1",而真实为"类别0"的样本总数为6(TN FP),所以FPR = FP / (TN FP) = 2 / 6;
- TPR = 6 / 6 = 1.00。算法预测8个样本为"类别1"将真实分类为"类别1"的6个样本都预测正确了(TP = 6),而真实为"类别1"的样本总数为6(TP FN = 6),所以Recall = TPR = TP / (TP FN) = 6 / 6;
通过控制threshold阈值观察FPR和TPR两个指标的变化,可以非常容易的看出:随着threshold阈值的逐渐降低,FPR的值逐渐的升高,与此同时TPR的值也在逐渐的升高。
▲threshold值越小,FPR和TPR值越大
换句话说,FPR和TPR两个指标呈现相一致的关系,随着FPR值的升高,TPR的值也会随着升高,FPR的值降低相对应的TPR的值也会随着降低。这和之前介绍的Precision和Recall两个指标恰好相反。我们可以直观的来理解,为了提高TPR,让算法将更多的样本分类为"类别1"(提高TP值),相应的就需要降低threshold阈值,不过在降低threshold阈值的同时,算法分类错误的样本数也会随着变多(FP的值升高),因此TPR升高的同时FPR也会相应的升高。
ROC曲线就是描述TPR和FPR两个指标之间的关系。在具体介绍ROC曲线之前,首先来实现TPR和FPR这两个指标。具体代码放到我们自己实现的playML包下的metrics模块中,这里将前面介绍的指标也一起放到metrics模块中:
代码语言:javascript复制def TN(y_true, y_predict):
assert len(y_true) == len(y_predict)
return np.sum((y_true == 0) & (y_predict == 0))
def FP(y_true, y_predict):
assert len(y_true) == len(y_predict)
return np.sum((y_true == 0) & (y_predict == 1))
def FN(y_true, y_predict):
assert len(y_true) == len(y_predict)
return np.sum((y_true == 1) & (y_predict == 0))
def TP(y_true, y_predict):
assert len(y_true) == len(y_predict)
return np.sum((y_true == 1) & (y_predict == 1))
def confusion_matrix(y_true, y_predict):
return np.array([
[TN(y_true, y_predict), FP(y_true, y_predict)],
[FN(y_true, y_predict), TP(y_true, y_predict)]
])
def precision_score(y_true, y_predict):
assert len(y_true) == len(y_predict)
tp = TP(y_true, y_predict)
fp = FP(y_true, y_predict)
try:
return tp / (tp fp)
except:
return 0.0
def recall_score(y_true, y_predict):
assert len(y_true) == len(y_predict)
tp = TP(y_true, y_predict)
fn = FN(y_true, y_predict)
try:
return tp / (tp fn)
except:
return 0.0
def f1_score(y_true, y_predict):
precision = precision_score(y_true, y_predict)
recall = recall_score(y_true, y_predict)
try:
return 2. * precision * recall / (precision recall)
except:
return 0.
def TPR(y_true, y_predict):
tp = TP(y_true, y_predict)
fn = FN(y_true, y_predict)
try:
return tp / (tp fn)
except:
return 0.
def FPR(y_true, y_predict):
fp = FP(y_true, y_predict)
tn = TN(y_true, y_predict)
try:
return fp / (fp tn)
except:
return 0.
下面就来通过具体实例来绘制ROC曲线。使用手写数字识别制作有偏的二分类数据集的过程和前几个小节一样,选择样本标签是否为9作为二分类是否为1的标准,接下来使用train_test_split将数据集划分为训练集和测试集。
之后在训练集上训练逻辑回归算法,最后通过将X_test传入decision_function函数获得所有测试样本的分数值score(对于逻辑回归算法score = θT · xb) 。
接下来通过遍历不同的阈值来获得对应阈值下的FPR和TPR。
- 定义两个命名为fprs和tprs的list空列表,其中fprs列表中用于存放在不同阈值上计算的FPR值,tprs列表中用于存放在不同阈值上计算的TPR;
- decision_function(X_test)函数计算测试集中每个样本的score值并存放到命名为decision_scores的数组中,找到decision_scores数组中的最大值和最小值,通过np.arange函数产生一个起点为np.min(decision_scores)终点为np.max(decision_scores)且固定步长为0.1的等差数列,命名为thresholds,thresholds数组中存放着所有选取的阈值;
- 接下来就可以调用for循环,计算每个阈值下的精准率和召回率,然后将计算出来的FPR和TPR对应的添加到fprs和tprs列表中。
有了这些准备,接下来就可以绘制ROC曲线了。ROC曲线其实就是x轴取FPR的值,而y轴取TPR的值。
通过上面ROC曲线很明显的可以看到:随着FPR值的逐渐升高(x轴表示FPR),相应的TPR的值也逐渐的升高(y轴表示TPR)。
b
使用Sklearn绘制ROC曲线
接下来看看如何通过sklearn绘制ROC曲线,绘制ROC曲线的roc_curve函数在sklearn的metrics模块中。
首先从sklearn.metrics中import导入名为roc_curve函数,具体的调用方式和前面绘制Precision-Recall曲线的precision_recall_curve函数类似。roc_curve函数需要传入两个参数:
- 第一个参数就是分类的真值y_test;
- 第二个参数就是decision_function(X_test)函数计算出的所有测试样本的score值,即前面的decision_scores数组;
函数返回的三个数组分别是fprs、tprs以及thresholds,返回的这三个数组就是前面我们自己实现代码中的fprs、tprs以及thresholds。先来回顾一下我们前面自己实现的代码。
有了fprs、tprs和thresholds这三个数组,类似上面我们自己实现的代码,接下来就可以直接绘制ROC曲线。
对于ROC曲线来说,我们通常关注的是ROC曲线下面的面积大小。如果这条ROC曲线下面的面积越大,表示这个分类算法或者说我们训练出的模型的分类效果越好。这是因为ROC曲线FPR越小的时候(x轴),换句话说FP这样的错误越少的时候(FP表示算法预测样本为"类别1"但是预测错误的样本数),相对应的y值就越大(y值表示的是TPR,也就是TPR越大),ROC曲线整体会被抬高,相对应的ROC曲线下面的面积也会越大。在这种情况下,我们的分类算法就会更好,所以ROC曲线下面的面积可以作为衡量分类算法优劣的一个指标。
ROC曲线下面的面积可以作为衡量分类算法的指标,那么该如何求ROC曲线下面的面积呢?sklearn为我们提供了一个接口可以非常方便的求出ROC曲线下面的面积。
sklearn将ROC曲线下面的面积作为衡量分类算法好坏的指标,所以如果想要使用接口同样需要在sklearn的metrics包下导入,sklearn提供的计算ROC曲线下面积的接口为roc_auc_score,其中auc英文全称为"Area Under The Curve",也就是曲线下的面积。ROC曲线下面的面积范围在[0, 1]之间,最大值为1,这是因为无论是FPR还是TPR指标的取值范围都在0,1之间,所以ROC曲线的定义域和值域都是[0, 1],因此在极端情况下,ROC曲线下面的面积为1。
调用roc_auc_score函数同样需要传入两个参数:
- 第一个参数就是分类的真值y_test;
- 第二个参数就是decision_function(X_test)函数计算出的所有测试样本的score值,即前面的decision_scores数组;
其实和调用roc_curve函数传入的参数是一样的,可以简单理解成roc_auc_score函数:
- 首先使用roc_curve函数绘制出ROC曲线;
- 然后求出对应ROC曲线下面的面积;
对于前面的逻辑回归算法,ROC曲线下面的面积为0.98(roc_auc_score指标),这是一个非常高的值。前面使用的是通过digits手写数字识别构造的极度偏斜的数据集,不过通过roc_auc_score为0.98的结果来看,roc_auc_score对极度偏斜的数据分类问题不像精准率Precision和召回率Recall这两个指标那样敏感,所以针对有偏数据的分类问题,还是非常有必要看一看精准率Precision和召回率Recall的。
roc_auc_score指标主要应用场景在于比较两个模型的孰优孰劣。下面表示两个ROC曲线。
此时有两条ROC曲线,这两个不同的曲线分别代表了两个模型、两个不同算法或者同一个算法对应的两组不同的超参数计算得到的两条ROC曲线。在这种情况下,我们应该选择roc_auc_score指标(ROC曲线下面的面积)最大对应的那个模型,我们认为这样的模型才是最好的模型。
本章针对极度有偏的数据进行二分类引出了很多新的评价指标。我们对于什么指标在什么情况下使用有了更加深刻的认识。不过至此我们讨论的所有指标都是基于二分类问题,在下一小节将会介绍如何利用我们前几个小节所学的知识来评估多分类任务。