分类模型评价指标说明
分类涉及到的指标特别容易搞混,不是这个率就是那个率,最后都分不清谁是谁,这份文档就是为此给大家梳理一下。
文章目录
混淆矩阵
混淆矩阵很重要,很多指标都是源于混淆矩阵,这个务必要弄懂。
例子
为了解释混淆矩阵,先来看看下面这个二分类的例子。
例:有20个病人来医院检查,是否患病的预测值和真实值如下表所示。
病号 | 预测值 | 真实值 | 病号 | 预测值 | 真实值 |
---|---|---|---|---|---|
1 | 1 | 1 | 11 | 0 | 0 |
2 | 0 | 0 | 12 | 0 | 0 |
3 | 1 | 1 | 13 | 0 | 0 |
4 | 0 | 0 | 14 | 1 | 1 |
5 | 0 | 0 | 15 | 0 | 0 |
6 | 1 | 1 | 16 | 1 | 0 |
7 | 0 | 0 | 17 | 1 | 1 |
8 | 0 | 0 | 18 | 0 | 0 |
9 | 0 | 1 | 19 | 0 | 0 |
10 | 0 | 0 | 20 | 0 | 1 |
其中,1表示患病,0表示不患病。
本文档默认用0和1来作为二分类符号。
你也可以用其他符号来表示,如1表示患病,-1表示不患病。只要能区分就行。
这样就出现4种结果:
- 预测为1,实际也为1,包括病号1,3,6,14,17,一共5个样本;
- 预测为1,实际为0,包括病号16,只有1个样本;
- 预测为0,实际为1,包括病号9,20,只有2个样本;
- 预测为0,实际也为0,包括病号2,4,5,7,8,10,11,12,13,15,18,19,一共12个样本。
我们把各个结果的数量填到下面这个表格中
这就是病患例子的混淆矩阵。
混淆矩阵定义
二分类混淆矩阵的一般定义只是将1和0叫做正例和负例,把4种结果的样本数量用符号来表示,用什么符号呢?
如果我们用P(Positive)代表1,用N(Negative)代表0,那这四种结果分别是PP,PN,NP,NN,但这样表示有点问题,譬如,PN的意思是预测为1实际为0还是预测为0实际为1?需要规定好了,还得记住,好麻烦。
干脆再引入符号T(True)代表预测正确,F(False)表示预测错误,那么之前的P和N代表预测是1还是0,T和F表示预测是否正确。
四种情况可以分别表示为
- TP:预测为1,预测正确,即实际也为1;
- FP:预测为1,预测错误,即实际为0;
- FN:预测为0,预测错误,即实际为1;
- TN:预测为0,预测正确,即实际也为0。
混淆矩阵的定义如下:
混淆矩阵代码
采用sklearn.metrics中的confusion_matrix函数计算混淆矩阵,数据用的还是之前那个病患检查的样本。
from sklearn.metrics import confusion_matrix
# 真实值
y_true = [1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1]
# 预测值
y_pred = [1,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,1,0,0,0]
c_matrix = confusion_matrix(y_true, y_pred)
print(c_matrix)
代码输出
[[12 1]
[ 2 5]]
有了混淆矩阵,就可以定义一些指标了。
正确率
准确率(Accuracy)的定义很简单,就是猜对的样本占总样本的比例,公式如下:
Accuracy = 猜 对 的 样 本 量 样 本 总 量 = T P + T N T P + F P + F N + T N \text{Accuracy} = \frac{猜对的样本量}{样本总量} = \frac{TP+TN}{TP+FP+FN+TN} Accuracy=样本总量猜对的样本量=TP+FP+FN+TNTP+TN
正样本是实际为正例的样本,负样本是实际为负例的样本。
计算正确率可以调用sklearn.metrics的accuracy_score函数,代码如下:
from sklearn.metrics import accuracy_score
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 预测值
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0]
mc = accuracy_score(y_true, y_pred)
print('Accuracy: %.2f'%mc)
结果为
Accuracy: 0.85
正确率作为评价指标有一个很致命的缺点,就是样本不平衡时正确率无法反映模型结果的好坏。
举个例子,预估某个网站上某一天广告的点击率,假如一天有1000个人浏览,实际有50个人点击广告,假如分类器预测没有人会点击,那么这个模型结果的正确率是多少呢?
我们算一下:分类器预测正确的有950个样本,一共有1000个样本,根据定义 Accuracy = 950 1000 = 0.95 \text{Accuracy} = \frac{950}{1000} = 0.95 Accuracy=1000950=0.95,正确率为95%!!!
一个点击的人都没有预测对,正确率都能有95%,那这个指标对模型的评价不合理。
那样本不平衡的时候怎么办呢?
细心想想,样本不平衡的问题是正负样本在数量上有很大差距,数量少的那方被重视程度低,比较吃亏,要解决这个问题,把正负样本分开评价不就好啦,大家河水不犯井水。
按照这个思路,引入下面两个概念:真阳率和假阳率。
真阳率和假阳率
真阳率
真阳率(True Positive Rate, TPR)的定义是:正样本中猜对的比例。公式如下
T P R = T P T P + F N TPR = \frac{TP}{TP+FN} TPR=TP+FNTP
假阳率
假阳率(False Positive Rate, FPR)的定义是:负样本中猜错的比例。公式如下
F P R = F P T N + F P FPR = \frac{FP}{TN+FP} FPR=TN+FPFP
真阳率和假阳率的公式比较
T P R = T P T P + F N F P R = F P T N + F P \begin{aligned} TPR &= \frac{TP}{TP+FN} \\ FPR &= \frac{FP}{TN+FP} \end{aligned} TPRFPR=TP+FNTP=TN+FPFP
TPR公式的分母是正样本数量,FPR公式的分母是负样本数量,这就遵循了正负样本分开评价的思路。
TPR公式的分子是TP,说明这个指标关注正确率;FPR公式的分子是FP,说明这个指标关注错误率。
通常,这两个指标不单独使用,那要怎么用呢?
那就不得不介绍ROC/AUC的概念了。
ROC/AUC
例子
还是那个病患事例,不同在于预测值不是0和1的离散值,而是一个0到1的连续值,叫做置信度(confidence score),可以理解为”概率“,越接近1,结果越可能为1;越接近0,结果越可能为0。
病号 | 置信度 | 真实值 | 病号 | 置信度 | 真实值 |
---|---|---|---|---|---|
1 | 0.8 | 1 | 11 | 0.8 | 0 |
2 | 0.2 | 0 | 12 | 0.1 | 0 |
3 | 0.4 | 1 | 13 | 0.2 | 0 |
4 | 0.1 | 0 | 14 | 0.9 | 1 |
5 | 0.4 | 0 | 15 | 0.3 | 0 |
6 | 0.8 | 1 | 16 | 0.6 | 0 |
7 | 0.3 | 0 | 17 | 0.8 | 1 |
8 | 0.2 | 0 | 18 | 0.2 | 0 |
9 | 0.6 | 1 | 19 | 0.2 | 0 |
10 | 0.5 | 0 | 20 | 0.4 | 1 |
预测值是置信度的话,要怎么算TPR和FPR呢?
很简单,给个阈值就行,不小于这个阈值就设为1,小于设为0。
注意,在实际的做法中,一般不用卡阈值的方法,而是按照置信度排序,然后取前N条样本,其实效果等同取阈值。
但阈值设多大好呢?
这就很关键了,因为阈值的大小会影响TPR和FPR。
阈值对TPR和FPR的影响
假如病患例子的阈值设为0.9,阈值判决后的预测结果如下表。
病号 | 预测值 | 真实值 | 病号 | 预测值 | 真实值 |
---|---|---|---|---|---|
1 | 0 | 1 | 11 | 0 | 0 |
2 | 0 | 0 | 12 | 0 | 0 |
3 | 0 | 1 | 13 | 0 | 0 |
4 | 0 | 0 | 14 | 1 | 1 |
5 | 0 | 0 | 15 | 0 | 0 |
6 | 0 | 1 | 16 | 0 | 0 |
7 | 0 | 0 | 17 | 0 | 1 |
8 | 0 | 0 | 18 | 0 | 0 |
9 | 0 | 1 | 19 | 0 | 0 |
10 | 0 | 0 | 20 | 0 | 1 |
可以算出TP=1,TN=13,FP=0,FN=6,那么
T P R = T P T P + F N = 1 1 + 6 ≈ 0.14 F P R = F P T N + F P = 0 \begin{aligned} TPR &= \frac{TP}{TP+FN} = \frac{1}{1+6} \approx 0.14 \\ FPR &= \frac{FP}{TN+FP} = 0 \end{aligned} TPRFPR=TP+FNTP=1+61≈0.14=TN+FPFP=0
这结果TPR和FPR都很低,FPR低是好事,说明负样本的预测错误率低,但TPR也低就不好了,因为正样本的预测正确率不高。
那换个阈值再试试,阈值设为0.1,就是全部猜作正例,不列详细计算过程了,直接给出结果
T P R = 1 F P R = 1 \begin{aligned} TPR &= 1 \\ FPR &= 1 \end{aligned} TPRFPR=1=1
这结果刚好相反,TPR和FPR都很高,正样本的预测正确率上来了,负样本的预测错误率也变大了。
通过上面的比较,能看出来:阈值设得越高,TPR和FPR越低;阈值设得越低,TPR和FPR越高。
ROC曲线
上一节我们知道了TPR和FPR会随阈值变化而变化,你要是把所有阈值对应的TPR和FPR求出来,画个直角坐标系,以FPR为横轴,TPR为纵轴,把不同阈值下的(FPR,TPR)坐标点标上并连起来,你就能看到TPR和FPR的整个变化曲线,而这条曲线就称为ROC(Receiver Operating Characteristic)曲线。
Receiver Operating Characteristic这名字挺奇怪的,可能是因为最早出现在雷达信号检测领域,用于评价接收器(Receiver)侦测敌机的能力。
尝试画出病患事例的ROC曲线,先求不同阈值下的FPR和TPR,置信度从大到小(重复的不算)排列为[0.9, 0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1],一共有8个阈值,如果手算TPR和FPR那太费劲了,幸好sklearn.metrics模块有现成的roc_curve函数来算,代码如下:
import pandas as pd
from sklearn.metrics import roc_curve
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 计算TPR和FPR
fpr, tpr, thresholds = roc_curve(y_true, y_score)
# 把fpr,tpr,thresholds用DataFrame表格保存,方便显示
result = pd.DataFrame([thresholds,tpr,fpr], index=
['threshold','TPR','FPR'])
print(result)
结果如下
0 1 2 3 4 5 6 \
threshold 1.9 0.900000 0.800000 0.600000 0.500000 0.400000 0.300000
TPR 0.0 0.142857 0.571429 0.714286 0.714286 1.000000 1.000000
FPR 0.0 0.000000 0.076923 0.153846 0.230769 0.307692 0.461538
7 8
threshold 0.200000 0.1
TPR 1.000000 1.0
FPR 0.846154 1.0
上面结果有两点需要注意:
- roc_curve函数结果的第一列没有什么实际意义,只是画ROC曲线图一般都会有原点(0,0),它直接帮用户给加上了。
- 关于第一列的threshold为什么是1.9?根据官方API的解释,它是用
max(y_score) + 1
算的,为什么要这么算?官方API没有说明,所以我也不知道这脑洞是怎么来的。
接下来,就是根据FPR和TPR结果画ROC曲线,画出来如下图。
画图代码如下:
import matplotlib.pyplot as plt
plt.figure()
# 画散点图,标出点的位置
plt.scatter(fpr, tpr)
# 画ROC曲线图
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve')
plt.xlim([-0.05, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
如果样本多了之后,画出来的ROC曲线会平滑得多。
ROC曲线的用处
当你需要评价多个分类模型结果时,ROC曲线能帮你看出这些模型的优劣。
下面给出了A和B两个分类模型的ROC曲线图,哪一个模型的结果比较好呢?
很显然是模型A,为什么呢?
因为模型A的ROC曲线要比模型B的往左上凸,这样的话,如果固定FPR,模型A的TPR大于模型B;如果固定TPR,模型A的FPR要小于模型B。怎么样都是模型A比模型B强。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHPaDfif-1603466581745)(pict/2019-07-13 19-13-08屏幕截图.png)]
模型C是有特殊意义的,如果抛硬币来做二分类预测(取任一类的概率是0.5),最后画出来的ROC曲线图就跟C很接近。
可以做个实验:用概率为0.5取0或1来预测真实值,看看算出来的TPR和FPR的结果。
先构造一个1000样本的真实值列表。
from sklearn.metrics import confusion_matrix import random # 构造真实值,正例有100个,负例有900个,用shuffle随机打乱顺序 y_true = [1]*100+[0]*900 random.shuffle(y_true)
用概率为0.5取0或1做预测,并计算TPR和FPR。
import numpy as np # 随机生成1000个0和1的预测值 y_pred = np.random.randint(0,2,size=1000) # 计算TPR和FPR tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel() print('FPR: %.2f'%(fp/(tn+fp))) print('TPR: %.2f'%(tp/(tp+fn)))
结果如下
FPR: 0.50 TPR: 0.53
由于预测值是随机的,每次出来结果会有不同,但基本都围绕在点(FPR,TRP)=(0.5,0.5)附近,也就是说,按概率为0.5取0或1的方式做预测,势必经过(0.5,0.5),其ROC曲线就会表现为一条往右上的对角线。
某个模型全面碾压的情况不太多,大多数情况会如下图所示,两个模型的ROC曲线是相交的。
那哪个模型的结果比较好呢?
需要分情况。比如,如果限定FPR要小于相交点,无疑模型A好于模型B。
AUC
如果没有特定的限制,那怎么选模型呢?有一招,直接算ROC曲线下的面积,称为AUC(Area Under Curve)。
AUC越大,模型结果越好,下面算算医患事例的AUC,用sklearn.metrics的roc_auc_score函数。
from sklearn.metrics import roc_auc_score
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 计算AUC
auc = roc_auc_score(y_true, y_score)
print('AUC: %.2f'%auc)
结果是
AUC: 0.89
AUC能评价二分类模型结果,其实是有概率解释的,AUC的概率含义是:随机从样本集中取一对正负样本,正样本得分(置信度)大于负样本的概率。实际上可以理解为,模型把正样本(按照置信度)排在负样本前面的概率。
具体的解释参考下面链接:
https://tracholar.github.io/machine-learning/2018/01/26/auc.html#auc%E5%AF%B9%E6%AD%A3%E8%B4%9F%E6%A0%B7%E6%9C%AC%E6%AF%94%E4%BE%8B%E4%B8%8D%E6%95%8F%E6%84%9F
精准率和召回率
在信息检索、Web搜索领域,时常会关心“检索的信息有多少是用户感兴趣的”、“用户感兴趣的信息有多少被检索出来”,为满足这样的评价需求,有了精准率和召回率这两个指标。
精准率
精准率(Precision)的定义是:预测为正的样本中猜对的比例。公式如下
Precision = T P T P + F P \text{Precision} = \frac{TP}{TP+FP} Precision=TP+FPTP
这个指标反映的是你预测正样本预测有多准,关键在准,因此Precision也被称为查准率。
召回率
召回率(Recall)的定义是:实际为正的样本中被猜对的比例。公式如下
Recall = T P T P + F N \text{Recall} = \frac{TP}{TP+FN} Recall=TP+FNTP
看定义,召回率是有点不好理解,举个例子吧。
假如患病的为正样本,不患病的为负样本,100个人里面有10个病患,医生检查出了病患中的8个,那这个结果的召回率是多少?
按照定义,先看实际为正的样本,也就是患病的人,共有10个,这里面医生猜对的有8个,那么 Recall = 8 10 = 0.8 \text{Recall} = \frac{8}{10} = 0.8 Recall=108=0.8。由此可知,召回率关注的是病患(正样本)是不是都找全了,关键在全,因此Recall也被称为查全率。