本篇主要内容:混淆矩阵(Confusion Matrix) 、精准率(Precision)、召回率(Recall )
准确率的陷阱
在之前我们都是用分类准确度来评价分类算法的好坏,但其实用准确率来评价分类算法的好坏是存在很大问题的。
准确率的问题
现在有一个癌症预测系统,输入你的体检信息,来判断是否有癌症,这个系统能达到99.9%的预测准确率,那这是一个好系统吗?不少人会觉得99.9%的准确率当然是一个好系统,实际真的是这样吗?想象一种情况,如果这种癌症本身的发病率只有0.01%,那这就意味着,根本不需要任何机器学习算法,无论是什么样的体检信息,都预测他是健康的不会患癌症,这样肯定有99.99%的准确率,而现在的系统只有99.9%的准确率还不如没有机器学习的准确率高,说明这个系统是失败的。
这就是用分类准确度来衡量一个分类系统性能好坏的问题所在。这样的问题发生在数据是极度偏斜(Skewed Data)的情况下,即不同类别的数量差异巨大,这样的情况下,只使用分类准确度是远远不够的。于是有了混淆矩阵,在混淆矩阵上我们可以得到比分类准确度还要好的分类性能评价指标。
混淆矩阵Confusion Matrix
什么是混淆矩阵呢?以二分类问题为例,对于二分类问题,混淆矩阵是一个2×2的矩阵:
真实\预测 | 0 | 1 |
---|---|---|
0 | 预测negative正确 TN |
预测positive错误 FP |
1 | 预测negative错误 FN |
预测positive正确 TP |
混淆矩阵中行代表真实值,列代表预测值,0-Negative,1-Positive,1往往是我们比较关注的类别。
比如现在有10000个人,预测患癌症的情况,1代表患癌症,0代表未患癌症,相应的混淆矩阵:
真实\预测 | 0 | 1 |
---|---|---|
0 | 9978 | 12 |
1 | 2 | 8 |
该矩阵表示有9978个人实际没有患癌症预测结果也是没有患癌症,有12个人实际没有患癌症但预测结果是患癌症;有2个人患了癌症预测结果是没患,有8个人患了癌症预测结果也是患癌症。
精准率与召回率
基于混淆矩阵,我们有两个新的衡量分类算法性能的指标——精准率和召回率。精准率precision的计算公式为:
精准率是我们预测我们关注的事件,它相应的有多准确。对于上面癌症预测的例子,相应的精准率计算为:它表示预测结果为1中预测正确(实际也为1)的概率,在有偏数据中,1通常是我们比较关注的对象,因此叫精准率,比如癌症预测中,癌症是我们真正关注的对象。本例精准率的含义是我们每做出100次患病的预测,有40次是准确的。
和精准率相对应的召回率recall的计算公式为:
召回率是我们关注的事件真实的发生了,在真实发生的我们关注的事件中,我们成功预测了多少。
对于癌症预测的例子,相应的召回率计算为:含义是每有100个癌症患者,通过现在的机器学习算法,我们能成功的找出其中的80个患者。
为什么说精准率和召回率是比准确率要优秀的评价指标呢,仍然考虑最初的例子,有10000个人,癌症病发率为0.01%,现在有一个算法,不管怎样都预测他是健康的,这样的准确率是99.99%,但是尽管准确率高,实际是一点帮助都没有的。相应的精准率精准率和召回率呢?首先给出此时的混淆矩阵:
真实\预测 | 0 | 1 |
---|---|---|
0 | 9999 | 0 |
1 | 1 | 0 |
计算得到精准率为是没有意义的,对于没有意义的精准率直接定义为它可能的最小值也就是0。召回率,可以看到对于我们都知道的没有意义的算法,准确率能达到99%以上,而精准率和召回率却都是最低值,这就是精准率和召回率的意义。
实现精准率与召回率
在手写数字数据集上进行测试,不过由于手写数字本身是10个类别且每个类别数量是差不多的,这里我们人工将其变为两个类别(标签等于9为类1,标签不等于9的为类0)以达到skewed data的效果:
import numpy as np
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target.copy()
'''人工加入偏斜'''
y[digits.target==9]=1
y[digits.target!=9]=0
'''10分类变为2分类,用Logistic分类'''
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
log_reg.score(X_test,y_test)
Logistic回归准确率
在Logistic回归下准确率为97.5%,不过这并不能说明什么,因为不等于9的本身就在全部数据中占有90%的比例,就算不用机器学习算法全部预测为类1,准确率也有90%,于是考虑精准率和召回率,定义混淆矩阵计算函数:
'''计算混淆矩阵各项'''
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)]
])
'''Logistic分类的混淆矩阵'''
confusion_matrix(y_test,y_log_predict)
相应的混淆矩阵:
混淆矩阵有了混淆矩阵,再定义精准率和召回率计算函数:
'''精准率'''
def precision_score(y_true,y_predict):
tp = TP(y_true,y_predict)
fp = FP(y_true,y_predict)
'''分母为0'''
try:
return tp/(tp+fp)
except:
return 0.0
'''召回率'''
def recall_score(y_true,y_predict):
tp = TP(y_true,y_predict)
fn = FN(y_true,y_predict)
'''分母为0'''
try:
return tp/(tp+fn)
except:
return 0.0
调用计算函数,得到精准率和召回率为:
precision_and_recall精准率和召回率看起来还是不错的,在实际中它们有时精准率小有时召回率小,具体如何平衡它们,以及它们如何评价模型将在下篇介绍。
同样,对于这些操作,我们写的是模拟sklearn的逻辑的,sklearn中也封装好了相应的模块,接下来使用sklearn中的混淆矩阵和准确率召回率计算:
得到了同样的结果。
网友评论