学习
实践
活动
专区
工具
TVP
写文章

理解Scikit-Learn中分类性能度量指标

暗中观察

默默关注

Understanding Data Science Classification Metrics in Scikit-Learn in Python

在本教程中,我们将介绍Python的scikit-learn中的一些分类度量指标 - 从头开始学习和编写我们自己的函数,以理解其中一些函数背后的数学知识。

数据科学中预测建模的一个主要领域是分类。分类就是试图预测一个群体中某一特定样本来自哪个类别。例如,如果我们试图预测某个病人是否会再次住院,可能的两类是住院(正)和非住院(负)。然后,分类模型试图预测每个患者是否将住院或未住院。换句话说,分类只是试图简单地预测人群中某一特定样本应该被放在哪个桶中(预测是正的还是预测是负的),如下所示。

在训练分类预测模型时,你需要评估它的好坏程度。有趣的是,有很多不同的方法来评估性能。大多数使用Python进行预测建模的数据科学家都使用名为scikit-learn的Python库。 Scikit-learn包含许多用于分析模型性能的内置函数。在本教程中,我们将介绍其中的一些指标,并从头开始编写我们自己的函数,以理解其中一些指标背后的数学原理。如果您只想阅读关于性能度量的文章,请参阅我之前的一篇文章:

https://towardsdatascience.com/data-science-performance-metrics-for-everyone-4d68f4859eef。

专知公众号以前也写过

本教程将介绍sklearn.metrics中的以下指标:

confusion_matrix

accuracy_score

recall_score

precision_score

f1_score

roc_curve

roc_auc_score

让我们开始吧

有关示例数据集和jupyter notebook,请参阅我的github:

https://github.com/andrewwlong/classification_metrics_sklearn。

我们将从头开始编写自己的函数,现在假设有两个类别。请注意,您需要标记为#your code here的地方填写您自己的部分。

让我们加载一个样本数据集,其中包含两个模型(model_RF 和 model_LR)的真实标签(actual_label)和预测概率。这里的概率是第1类的概率。

importpandasaspd

df = pd.read_csv('data.csv')

df.head()

在大多数数据科学项目中,您需要定义一个阈值,以定义哪些预测概率被标记为预测正值,哪些预测概率被标记为预测负值。现在让我们假设阈值是0.5。让我们再添加另外两列,将概率转换为预测标签。

thresh =0.5

df['predicted_RF'] = (df.model_RF >=0.5).astype('int')

df['predicted_LR'] = (df.model_LR >=0.5).astype('int')

df.head()

confusion_matrix

给定一个实际的标签和一个预测的标签,我们能做的第一件事就是把样本分成4个部分:

True positive(真阳性) — 真实值(actual) = 1, 预测值(predicted) = 1

False positive(假阳性) — 真实值(actual) = 0, 预测值(predicted) = 1

False negative(假阴性) — 真实值(actual) = 1, 预测值(predicted) = 0

True negative(真阴性) — 真实值(actual) = 0, 预测值(predicted) = 0

这四个部分可以用以下图像表示,我们将在下面的许多计算中用到此图像。引用自:

https://en.wikipedia.org/wiki/Precision_and_recall#/media/File:Precisionrecall.svg。

这四个部分还可以使用混淆矩阵表示,如下所示:

我们可以从scikit-learn获得混淆矩阵(作为一个2x2数组),它将实际标签和预测标签作为输入:

fromsklearn.metrics importconfusion_matrix

confusion_matrix(df.actual_label.values,df.predicted_RF.values)

其中有5047个 true positive,2360个false positive,2832个false negative和5519个true negative。让我们定义自己的函数来验证confusion_matrix。请注意,我的代码只写了第一个,你需要自己来写其他3个。

deffind_TP(y_true,y_pred):

#counts the number of true positives (y_true = 1, y_pred = 1)

returnsum((y_true ==1) & (y_pred ==1))

deffind_FN(y_true,y_pred):

#counts the number of false negatives (y_true = 1, y_pred = 0)

return# your code here

deffind_FP(y_true,y_pred):

# countsthe number of false positives (y_true = 0, y_pred = 1)

return# your code here

deffind_TN(y_true,y_pred):

#counts the number of true negatives (y_true = 0, y_pred = 0)

return# your code here

您可以检查您的结果是否匹配:

print('TP:',find_TP(df.actual_label.values,df.predicted_RF.values))

print('FN:',find_FN(df.actual_label.values,df.predicted_RF.values))

print('FP:',find_FP(df.actual_label.values,df.predicted_RF.values))

print('TN:',find_TN(df.actual_label.values,df.predicted_RF.values))

让我们写一个函数,它将为我们计算所有这四个,以及另一个函数来复制confusion_matrix:

importnumpyasnp

deffind_conf_matrix_values(y_true,y_pred):

#calculate TP, FN, FP, TN

TP=find_TP(y_true,y_pred)

FN=find_FN(y_true,y_pred)

FP=find_FP(y_true,y_pred)

TN=find_TN(y_true,y_pred)

returnTP,FN,FP,TN

defmy_confusion_matrix(y_true,y_pred):

TP,FN,FP,TN = find_conf_matrix_values(y_true,y_pred)

returnnp.array([[TN,FP],[FN,TP]])

检查您的结果是否匹配:

my_confusion_matrix(df.actual_label.values,df.predicted_RF.values)

与其手工比较,不如使用Python内置的assert和numpy的array_equal函数来验证我们的函数是否有效:

assertnp.array_equal(my_confusion_matrix(df.actual_label.values,

df.predicted_RF.values),confusion_matrix(df.actual_label.values,

df.predicted_RF.values) ),'my_confusion_matrix() is not correct for RF'

assertnp.array_equal(my_confusion_matrix(df.actual_label.values,

df.predicted_LR.values),confusion_matrix(df.actual_label.values,

df.predicted_LR.values) ),'my_confusion_matrix() is not correct for LR'

给定了这四个部分(TP、FP、FN、TN),我们可以计算许多其他性能指标。

accuracy_score

最常用的分类度量指标就是准确性,即正确预测的样本的分数,如下图所示:

我们可以从scikit-learn获得accuracy score,它以实际标签和预测标签作为输入:

fromsklearn.metrics importaccuracy_score

accuracy_score(df.actual_label.values,df.predicted_RF.values)

复制accuracy_score来定义您自己的函数,使用上面的公式。

defmy_accuracy_score(y_true,y_pred):

#calculates the fraction of samples predicted correctly

TP,FN,FP,TN= find_conf_matrix_values(y_true,y_pred)

return# your code here

assertmy_accuracy_score(df.actual_label.values,df.predicted_RF.values)

==accuracy_score(df.actual_label.values,df.predicted_RF.values),

'my_accuracy_score failed on RF'

assertmy_accuracy_score(df.actual_label.values,df.predicted_LR.values)

==accuracy_score(df.actual_label.values,df.predicted_LR.values),

'my_accuracy_score failed on LR'

print('Accuracy RF:%.3f'%(my_accuracy_score(df.actual_label.values,

df.predicted_RF.values)))

print('Accuracy LR:%.3f'%(my_accuracy_score(df.actual_label.values,

df.predicted_LR.values)))

使用accuracy作为性能指标,RF模型(0.67)比LR模型(0.62)准确率更高。那么我们应该说RF模型是最好的模型吗?不!准确性(Accuracy)并不总是用于评估分类模型的最佳指标。例如,假设我们要预测的是100次中只有1次发生的事情。我们可以建立一个模型,说这个事件从来没有发生过,从而获得99%的准确率。然而,我们真正关心的事件却获得了0%的正确率。其实,这里的0%是另一个性能度量,称为recall(召回率)。

recall_score

Recall召回率是您正确预测的positive事件的分数,如下所示:

我们可以从scikit-learn获得recall score,它以实际标签和预测标签作为输入:

fromsklearn.metrics importrecall_score

recall_score(df.actual_label.values,df.predicted_RF.values)

复制recall_score来定义您自己的函数,使用上面的公式。

defmy_recall_score(y_true,y_pred):

#calculates the fraction of positive samples predicted correctly

TP,FN,FP,TN= find_conf_matrix_values(y_true,y_pred)

return# your code here

assertmy_recall_score(df.actual_label.values,df.predicted_RF.values)

== recall_score(df.actual_label.values,df.predicted_RF.values),

'my_accuracy_score failed on RF'

assertmy_recall_score(df.actual_label.values,df.predicted_LR.values)

== recall_score(df.actual_label.values,df.predicted_LR.values),

'my_accuracy_score failed on LR'

print('Recall RF:%.3f'%(my_recall_score(df.actual_label.values,

df.predicted_RF.values)))

print('Recall LR:%.3f'%(my_recall_score(df.actual_label.values,

df.predicted_LR.values)))

提高召回率的一种方法是通过降低predicted positive的阈值来增加predictedpositive的样本数。但是,这也会增加false positive的数量。另一个称为精确度(precision)的性能指标考虑到了这一点。

precision_score

Precision(精确度)是实际为正的事件所占总的预测阳性事件的比,如下所示:

我们可以从scikit-learn获得precision score,它以实际标签和预测标签作为输入:

fromsklearn.metrics importprecision_score

precision_score(df.actual_label.values,df.predicted_RF.values)

复制precision_score来定义您自己的函数,使用上面的公式。

defmy_precision_score(y_true,y_pred):

#calculates the fraction of predicted positives samples that

are actuallypositive

TP,FN,FP,TN= find_conf_matrix_values(y_true,y_pred)

return# your code here

assertmy_precision_score(df.actual_label.values,

df.predicted_RF.values) == precision_score(df.actual_label.values,

df.predicted_RF.values),'my_accuracy_score failed on RF'

assertmy_precision_score(df.actual_label.values,

df.predicted_LR.values) ==precision_score(df.actual_label.values,

df.predicted_LR.values),'my_accuracy_score failed on LR'

print('Precision RF:%.3f'%(my_precision_score(df.actual_label.values,

df.predicted_RF.values)))

print('Precision LR:%.3f'%(my_precision_score(df.actual_label.values,

df.predicted_LR.values)))

在这种情况下,看起来RF模型在召回率和精确度方面都更好。但如果一个模型在召回率上更好,另一个在精度上更好,你会怎么做?一些数据科学家使用的方法称为F1 score。

f1_score

f1 score是召回率和精确度的调和平均值,得分越高越好。f1 score的计算公式如下:

我们可以从scikit-learn获得f1 score,它以实际标签和预测标签作为输入:

fromsklearn.metrics importf1_score

f1_score(df.actual_label.values,df.predicted_RF.values)

复制f1_score来定义您自己的函数,使用上面的公式。

defmy_f1_score(y_true,y_pred):

#calculates the F1 score

recall= my_recall_score(y_true,y_pred)

precision= my_precision_score(y_true,y_pred)

return# your code here

assertmy_f1_score(df.actual_label.values,df.predicted_RF.values) ==

f1_score(df.actual_label.values,df.predicted_RF.values),

'my_accuracy_score failed on RF'

assertmy_f1_score(df.actual_label.values,df.predicted_LR.values) ==

f1_score(df.actual_label.values,df.predicted_LR.values),

'my_accuracy_score failed on LR'

print('F1 RF:%.3f'%(my_f1_score(df.actual_label.values,

df.predicted_RF.values)))

print('F1 LR:%.3f'%(my_f1_score(df.actual_label.values,

df.predicted_LR.values)))

到目前为止,我们假设我们定义了0.5的阈值,用于选择哪些样本被预测为正样本。如果我们更改此阈值,性能指标将会发生变化。如下所示:

print('scores with threshold= 0.5')

print('Accuracy RF:%.3f'%(my_accuracy_score(df.actual_label.values,

df.predicted_RF.values)))

print('Recall RF:%.3f'%(my_recall_score(df.actual_label.values,

df.predicted_RF.values)))

print('Precision RF:%.3f'%(my_precision_score(df.actual_label.values,

df.predicted_RF.values)))

print('F1 RF:%.3f'%(my_f1_score(df.actual_label.values,

df.predicted_RF.values)))

print(' ')

print('scores with threshold = 0.25')

print('Accuracy RF:%.3f'%(my_accuracy_score(df.actual_label.values,

(df.model_RF >=0.25).astype('int').values)))

print('Recall RF:%.3f'%(my_recall_score(df.actual_label.values,

(df.model_RF >=0.25).astype('int').values)))

print('Precision RF: %.3f'%(my_precision_score(df.actual_label.values,

(df.model_RF >=0.25).astype('int').values)))

print('F1 RF:%.3f'%(my_f1_score(df.actual_label.values,

(df.model_RF >=0.25).astype('int').values)))

如果我们最初没有选择一个阈值,我们如何评估模型?一种非常常见的方法是使用ROC曲线。

roc_curve 和 roc_auc_score

ROC曲线非常有助于理解真阳性率(true-positive rate)和假阳性率(false positive rates)之间的平衡。 Scikit learn为实现和分析ROC曲线构建了函数。这些函数的输入(roc_curve和roc_auc_score)是实际标签和预测概率(不是预测标签)。

roc_curve和roc_auc_score都是复杂的函数,所以我们不会让你从头开始编写这些函数。相反,我们将向您展示如何使用scikit learn中的函数来实现并解释关键点。让我们先用roc_curve来做ROC图。

fromsklearn.metrics importroc_curve

fpr_RF,tpr_RF,thresholds_RF =roc_curve(df.actual_label.values,

df.model_RF.values)

fpr_LR,tpr_LR,thresholds_LR =roc_curve(df.actual_label.values,

df.model_LR.values)

roc_curve函数返回三个列表:

thresholds = 按降序排列的所有唯一预测概率

fpr = 每个阈值的假阳性率(FP/ (FP + TN))

tpr = 每个阈值的真阳性率(TP/ (TP + FN))

我们可以绘制每个模型的ROC曲线,如下所示。

importmatplotlib.pyplot asplt

plt.plot(fpr_RF,tpr_RF,'r-',label='RF')

plt.plot(fpr_LR,tpr_LR,'b-',label='LR')

plt.plot([,1],[,1],'k-',label='random')

plt.plot([,,1,1],[,1,1,1],'g-',label='perfect')

plt.legend()

plt.xlabel('False Positive Rate')

plt.ylabel('True Positive Rate')

plt.show()

我们可以从这个图中得到一些结论:

一个随机猜测标签的模型应该如黑色的线所表示的那样,你想要一个在黑线之上有一条曲线的模型

距离黑线较远的ROC更好,因此RF(红色)看起来比LR(蓝色)好

虽然没有直接看到,但是高阈值会产生左下角的点,低阈值产生了右上角的点。这意味着当您降低阈值时,您将获得更高的TPR,但代价是更高的FPR

为了分析性能,我们将使用area-under-curve(曲线下面积)这一度量指标.

fromsklearn.metrics importroc_auc_score

auc_RF = roc_auc_score(df.actual_label.values,df.model_RF.values)

auc_LR = roc_auc_score(df.actual_label.values,df.model_LR.values)

print('AUC RF:%.3f'% auc_RF)

print('AUC LR:%.3f'% auc_LR)

如您所见,RF模型的曲线下面积(AUC = 0.738)优于LR(AUC = 0.666)。当我绘制ROC曲线时,我喜欢在图例中添加AUC,如下所示。

importmatplotlib.pyplot asplt

plt.plot(fpr_RF,tpr_RF,'r-',label='RF AUC:%.3f'%auc_RF)

plt.plot(fpr_LR,tpr_LR,'b-',label='LR AUC:%.3f'%auc_LR)

plt.plot([,1],[,1],'k-',label='random')

plt.plot([,,1,1],[,1,1,1],'g-',label='perfect')

plt.legend()

plt.xlabel('False Positive Rate')

plt.ylabel('True Positive Rate')

plt.show()

总的来说,在这个示例中,模型RF在每个性能指标上都是获胜的。

总结

在预测分析中,当在两个模型之间做决定时,选择单个性能指标非常重要。正如您在此处所看到的,您可以选择许多(准确度,召回率,精确度,f1-score,AUC等等)。最终,您应该使用最适合当前业务问题的性能度量。许多数据科学家更喜欢使用AUC来分析每个模型的性能,因为它不需要选择阈值并有助于平衡真阳性率(true positive rate)和假阳性率(false positiverate)。

参考链接:

https://towardsdatascience.com/understanding-data-science-classification-metrics-in-scikit-learn-in-python-3bc336865019

加入社区

更多内容

关注AICUG

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180817B00EPQ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

关注

腾讯云开发者公众号
10元无门槛代金券
洞察腾讯核心技术
剖析业界实践案例
腾讯云开发者公众号二维码

扫码关注腾讯云开发者

领取腾讯云代金券