衡量一个分类器性能的更好的办法是混淆矩阵。它基于的思想是:计算类别A被分类为类别B的次数。例如在查看分类器将图片5分类成图片3时,们会看混淆矩阵的第5行以及第3列。
为了计算一个混淆矩阵,们首先需要有一组预测值,之后再可以将它们与标注值(label)进行对比。们也可以在测试集上做预测,但是最好是先不要动测试集(测试集仅需要在最后的阶段使用,在们有了一个准备上线的分类器后,最后再用测试集测试性能)。接下来,们可以使用cross_val_predict() 方法:
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
y_train_pred.shape
>(60000,)
与cross_val_score() 方法一样,cross_val_predict() 会执行K-折交叉验证,但是不会返回评估分数,而是返回在每个测试折上的预测值,加起来就是整个训练数据集的预测值。现在们可以使用confusion_matricx() 方法获取混淆矩阵。直接传入label数据(y_train_5)以及预测数据(y_train_pred)即可:
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
>array([[53892, 687],
[ 1891, 3530]])
在这个混淆矩阵中,每一行代表一个真实类别,每一列代表一个预测类别。第一行代表的是"非5”(亦称为negative class)图片:53892张图片被分类为"非5"类别(它们亦称为true negatives)。剩下的687 张图片被错误的分类为"非5”(亦称为false positives)。第二行代表的是"真5”(亦称为 positive class):1891张图片被错误地分类为"非5"类别(亦称为false negatives),剩下的3530 张图片被正确地分类为"真5”(亦称为true positives)。一个完美的分类器应该仅包含true positives 以及true negatives,所以它的混淆矩阵应该仅有主对角线上有非0数值,其他值应都为0。例如,假设们有了以下一个完美的预测:
y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5, y_train_perfect_predictions)
>array([[54579, 0],
[ 0, 5421]])
混淆矩阵可以给们提供很多信息,但是有时候们可能需要一个更精准的指标。一个比较好的方式是:查看positive predictions的精准度。它也称为分类器的精度(precision),它的公式为:
Precision
Precision=TP / (TP + FP)
这里TP 是true positives 的数量,FP 是false positive 的数量。
对于精度,们仍有办法去构造一个完整精度。比如假设测试集里全部是数字5,然后模型的逻辑是仅输出True。这样就可以构造一个 100% 精度的模型。所以精度(precision)一般与另一个指标一起用,这另一个指标称为回调(recall),也称为sensitivity或true positive rate(TPR):它是分类器正确分类positive 条目的比率,公式为:
Recall
TP / (TP+FN)
这里FN是false negatives的数目。
如果对混淆矩阵的这些概念比较模糊的话,可以看看下图:
Precision 与 Recall
Sk-learn提供了一些方法用于计算分类器的各个指标,包括精准率(precision)与回调率(recall):
from sklearn.metrics import precision_score, recall_score
print(precision_score(y_train_5, y_train_pred)) 3530/(3530+687)
print(recall_score(y_train_5, y_train_pred)) 3530/(3530+1891)
>0.8370879772350012
0.6511713705958311
从precision与recall来看,这个分类器的表现并不像之前准确度(accuracy)那样亮眼了。当这个分类器认为某张图片是数字5时,它仅有83.7% 的概率是正确的。并且它仅识别出了65.1%的数字5图片。
一般们还会将precision和recall结合成一个指标:F~1~ 分数。特别是在需要使用一个简单的办法对比两个分类器时。F~1~分数是precision与recall的调和平均数(harmonic mean):
如果一个分类器的recall与precision分数都比较高的话,则最终才会得到一个较高的F~1~ 分数。其中任意一个recall或是precision比较低的话,F~1~分数都不会太高。
在sk-learn中,直接调用f1_score() 方法即可计算F~1~分数:
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)
>0.7325171197343846
F~1~分数会比较倾向于那些precision值与recall值接近的分类器。不过这个需求并不是在任何场景下都是必须的,在一些场景下,们可能更关心精准率,而在另一些场景下更关注回调率。例如,如果们训练一个分类器,用于为孩子们检测一些健康的视频。在这个场景下,们可能会更倾向于使用:一个可以更精准的判断视频是否为健康的视频(高精准),但是可能会误杀掉一些健康的视频(低回调)的分类器。而不是一个有着高回调,但是会让小部分不健康的视频通过的分类器。另一方面,假设们训练一个判断监控里小偷的分类器,即使这个分类器只有30%的精准率(precision)也是可以的,只要是它有99%左右的回调率(recall),依然可以达到们的需求(即使警报可能会响很多次,但是基本都会抓到小偷)。
不过可惜的是,precision与recall无法二者兼得:增加precision会降低recall,反之亦然。这个被称为精准/回调折中(precision/recall tradeoff)。
精准 / 回调折中( Precision/Recall Tradeoff )
为了理解这种折中,们看一下SGDClassifier是如何做分类决策的。对每条数据,它首先根据决策方法,计算出一个分数,如果此分数大于某个阈值,则将这条数据分类为正类(positive class),反之则分类为负类(negative class)。
下图是一个例子,低分在左边,被分为负类,高分在右边,被分为正类。假设决策阈值的位置在正中间(下图中间的两个5之间):们可以看到阈值右边有4个true positives(真正为数字5),以及1个false positive (真正为数字6)。所以,在这个阈值下,精准率precision是80%(4/5)。这个集合中一共有6个数字5,但是在这个阈值下,只检测出了4个,所以召回率recall是67%(4/6)。
现在假设们升高这个阈值(将箭头向右移),则之前的那个数字6从false positive 变成了true negative,所以此时false positive 现在是0,precision是100%(3/3)。而之前的数字5 由true positive 变成了false negative,所以召回率recall现在是50%(3/6)。同样,减少阈值后,召回率会上升,但是精准率precision会下降。
Sk-learn并不允许用户直接设置阈值,但是可以指定一个决策分数,用于做预测。之前们是调用predict() 方法做预测,现在们可以先使用 decision_function() 方法,它会返回每条数据的分数。然后们可以根据这些分数,提供的阈值进行预测:
y_scores = sgd_clf.decision_function([X_test[0], X_test[1], X_test[2], X_train[0]])
y_scores
>array([-8542.1753957 , -4410.49112461, -3416.59592945, 2164.22030239])
threshold = 0
y_demo_digit_pred = (y_scores > threshold)
y_demo_digit_pred
>array([False, False, False, True])
SGDClassifier 使用的是0作为阈值,所以上面的方法返回的结果与直接调用 predict() 的结果是一致的。下面们可以试着改一下这个阈值:
threshold = 8000
y_demo_digit_pred = (y_scores > threshold)
y_demo_digit_pred
>array([False, False, False, False])
这个也证明了:提高阈值后,recall会下降。最后这张图片本来是数字5,并且在阈值为0的情况下,分类器可以将它正确识别。但是在阈值升高到8000后,此图片便被识别为"非数字5”。
现在,们如何决定使用哪个阈值呢?首先们需要获取训练数据中所有数据的得分。再次使用cross_val_predict() 方法即可,但是这次们要指定它返回决策分数,而不是做预测:
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method='decision_function')
y_scores
>array([ 1200.93051237, -26883.79202424, -33072.03475406, ...,
13272.12718981, -7258.47203373, -16877.50840447])
现在有了这些分数,们可以计算在各种可能的阈值下,precision与recall的值,使用precision_recall_curve()方法:
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
最后们可以画出precision与recall的函数图,以threshold为因变量,使用matplotlib:
def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):
plt.plot(thresholds, precisions[:-1], 'b--', label="Precision")
plt.plot(thresholds, recalls[:-1], 'g--', label="Recall")
plot_precision_recall_vs_threshold(precisions, recalls, thresholds)
plt.show()
大家可能会好奇,为什么precision的图相较于recall的图抖动的更剧烈。这是因为:在阈值上升后,precision可能偶尔会下降(虽然一般它会上升)。为了便于理解,大家可以回看一下之前"精准/回调折中"里的那张图。假设们的阈值设在最中间,然后下一个阈值只向右移动了一个单位,则precision从80%(4/5)下降到了75%(3/4)。而另一边,recall仅会在阈值增加的时候才会下降,所以它的曲线看起来更平滑。
另一个比较好的选择"精准/回调折中"的办法是直接画出precision对应于recall的图,如:
可以看到在大约recall在80% 左右的样子,precision开始急速下降。所以们一般会选择一个它下降前的一个"精准/回调折中”(precision/recall tradeoff),例如在60%的回调左右。不过这个最终的决定取决于们的项目需求。
下面假设们定的目标是90%的precision。从第一副图们可以得知,阈值大约为8000 左右。为了更精确地获取这个阈值,们可以搜索满足90%精准率的最小阈值(np.argmax() 可以返回第一个最大值,在这个例子中就是第一个True值):
threshold_90_precision = thresholds[np.argmax(precisions >=0.90)]
threshold_90_precision
下一步,做决策。这次们不再用predict() 方法,而是使用:
y_train_pred_90 = (y_scores >= threshold_90_precision)
y_train_pred_90
>array([False, False, False, ..., True, False, False])
然后们检查一下这些预测的precision与recall:
precision_score(y_train_5, y_train_pred_90)
>0.9000345901072293
recall_score(y_train_5, y_train_pred_90)
>0.4799852425751706
现在们就有了一个90%精准率的分类器!正如你所见,创建一个高精准率的分类器其实很简单,只需要设置更高的阈值即可。但是,如果一个分类器即使precision很高,而recall很低的话,这个分类器基本没太大用处。所以如果有人说,他的分类器达到了99%的精准率,那们可以继续问问他"recall是多少?”
接下来们还会继续介绍另一种性能衡量的办法: ROC曲线。它是另一种与Precision/Recall 曲线类似的曲线。
原文创作:ZacksTang
原文链接:https://www.cnblogs.com/zackstang/p/12325718.html
集成学习与随机森林四Boosting与Stacking
集成学习与随机森林二Bagging与Pasting
集成学习与随机森林三随机森林与随机子空间
集成学习与随机森林一投票分类器
降维二PCA
降维三LLE与其他降维技术
降维一维度灾难与降维主要方法
机器学习项目流程四选择并训练模型
机器学习项目流程五模型调优
机器学习项目流程二探索并可视化数据
机器学习项目流程三为机器学习准备数据
机器学习项目流程一初探数据集
天池题目:工业蒸汽预测一 数据探索
分类问题四ROC曲线
分类问题六误差分析
分类问题五多元分类
分类问题二分类器的性能衡量
分类问题三混淆矩阵,Precision与Recall
分类问题七多标签分类与多输出分类
分类问题一MINST数据集与二元分类器
决策树二决策树回归
决策树一决策树分类
使用AWS SageMaker进行机器学习项目
使用AWS Glue进行 ETL 工作
airflow二集成EMR使用
XGBoost介绍
Spark Structured Streaming二实战
Spark Structured Streaming一基础
SVM支持向量机二非线性SVM分类
SVM支持向量机三SVM回归与原理
SVM支持向量机一线性SVM分类
Netty二线程模型
Netty三Netty模型
Netty一IO模型
NLP与深度学习四Transformer模型
NLP与深度学习六BERT模型的使用
NLP与深度学习五BERT预训练模型
NLP与深度学习二循环神经网络
NLP与深度学习三Seq2Seq模型与Attention机制
NLP与深度学习一NLP任务流程
Kubernetes四Pod详解
Kubernetes八安全认证
Kubernetes五 Pod控制器详解
Kubernetes二资源管理
Kubernetes三实战入门
Kubernetes七数据存储
Kubernetes一Overview
Kaggle泰坦尼克数据科学解决方案
Kaggle 题目 nucs6220assignment1
Elasticsearch 入门
Docker二Image 与网络
Docker一概念与基础
DebeziumFlinkHudi:实时流式CDC
ClickHouse介绍四ClickHouse使用操作
ClickHouse介绍二MergeTree引擎
ClickHouse介绍三MergeTree系列表引擎
ClickHouse介绍一初次使用
Bike Sharing Analysis二 假设检验方法
Bike Sharing Analysis一 探索数据
Apache Kylin二在EMR上搭建Kylin
Apache Kylin三Kylin上手
Apache Kylin一Kylin介绍