Press "Enter" to skip to content

不平衡多分类问题模型评估指标探讨与sklearn.metrics实践

我们在用机器学习、深度学习建模、训练模型过程中,需要对我们模型进行评估、评价,并依据评估结果决策下一步工作策略,常用的评估指标有准确率、精准率、召回率、F1分数、ROC、AUC、MAE、MSE等等,本文将结合SKlearn的metrics所封装对函数,重点围绕多分类问题实践评估指标。

 

1. 模型评估指标

 

在机器学习、深度学习、数据挖掘领域,工业界往往会根据实际的业务场景拟定相应的业务指标。本文主要是分享分类问题模型的评价指标,对于分类问题,实际应用中往往会遇到多分类情况。

 

其实,分类问题评估指标是适合多分类情况的,只是举例往往是二分类的,本文虽然介绍指标时是以二分类开始,但是公式可以拓展到多分类。

 

分类问题评估指标:

准确率 – Accuracy
精确率(差准率)- Precision
召回率(查全率)- Recall
F1分数
ROC曲线
AUC曲线

回归问题评估指标:

MAE
MSE

优化目标指标:

交叉熵 — cross-entropy
rmse

1.1. 混淆矩阵

 

混淆矩阵(confusion matrix)也称误差矩阵,是表示精度评价的一种标准格式,用 n n n 行 n n n 列的矩阵形式来表示。在人工智能中,特别用于监督学习衡量的是一个分类器分类的准确程度,比较分类结果和实际测得值,可以把分类结果的精度显示在一个混淆矩阵里面。

 

混淆矩阵适用于多分类器的问题,本文为了让读者理解更加容易,以手写数字“5”分类识别二元分类的混淆矩阵为例说明。

真阳性(True Positive,TP):样本的真实类别是正例,并且模型预测的结果也是正例
真阴性(True Negative,TN):样本的真实类别是负例,并且模型将其预测成为负例
假阳性(False Positive,FP):样本的真实类别是负例,但是模型将其预测成为正例
假阴性(False Negative,FN):样本的真实类别是正例,但是模型将其预测成为负例

1.2. 准确率P、召回率R、F1 值

 

如果我们想知道类别之间相互误分的情况,查看是否有特定的类别相互混淆,就可以用混淆矩阵画出分类的详细预测结果。对于包含多个类别的任务,可以很清晰的反映各类别之间的错分概率。

准确率(Accuracy): A c c = T P + T N T P + T N + F P + F N Acc=\frac{TP+TN}{TP+TN+FP+FN} A c c = T P + T N + F P + F N T P + T N ​ 。通俗地讲,就是预测正确的结果占总样本的百分比。

虽然准确率可以判断总的正确率,但是在样本不平衡 的情况下,并不能作为很好的指标来衡量结果。举个简单的例子,比如在一个总样本中,正样本占 90%,负样本占 10%,样本是严重不平衡的。对于这种情况,我们只需要将全部样本预测为正样本即可得到 90% 的高准确率,但实际上我们并没有很用心的分类,只是随便无脑一分而已。 这就说明了:由于样本不平衡的问题,导致了得到的高准确率结果含有很大的水分。即如果样本不平衡,准确率就会失效 。

精准率(Precision): P = T P T P + F P P=\frac{TP}{TP+FP} T P + F P T P ​ 。通俗地讲,就是预测正确的正例数据占预测为正例数据的比例。
召回率(Recall): R = T P T P + F N R=\frac{TP}{TP+FN} T P + F N T P ​ 。通俗地讲,就是预测为正例的数据占实际为正例数据的比例
F1值(F1 score): F 1 = 2 1 P + 1 R F1=\frac{2}{\frac{1}{P} + \frac{1}{R}} F 1 = + 2 ​ 。又称平衡F分数(balanced F Score),它被定义为精确率和召回率的调和平均数。
F1的值同时受到P、R的影响,单纯地追求P、R的提升并没有太大作用。在实际业务工程中,结合正负样本比,的确是一件非常有挑战的事。

1.3. ROC与AUC曲线

 

ROC曲线是Receiver Operating Characteristic Curve的简称,中文名为“受试者工作特征曲线”。

 

ROC曲线的横坐标为假阳性率(False Positive Rate,FPR);纵坐标为真阳性率(True Positive Rate,TPR)。FPR和TPR的计算方法分别为:

真阳性率: T P R = T P T P + F N TPR= \frac{TP}{TP+FN} T P R = T P + F N T P ​

假阳性率: F P R = F P F P + T N FPR=\frac{FP}{FP+TN} F P R = F P + T N F P ​

ROC是由点(TPR,FPR)组成的曲线,AUC就是ROC的面积。

 

一般来说,AUC越大越好;如果TPR越高,同时FPR越低(即ROC曲线越陡),那幺模型的性能就越好;ROC是光滑的,那幺基本可以判断没有太大的overfitting​。

 

为什幺使用 ROC 曲线

 

既然已经这幺多评价标准,为什幺还要使用 ROC 和 AUC 呢?因为 ROC 曲线有个很好的特性:当测试集中的正负样本的分布变化的时候,ROC 曲线能够保持不变。在实际的数据集中经常会出现类不平衡(class imbalance)现象,即负样本比正样本多很多(或者相反),而且测试数据中的正负样本的分布也可能随着时间变化。

 

2. 模型评估工具,sklearn的metrics应用实践

 

sklearn.metrics中包含了许多模型评估指标,包括:分类、回归、聚类等模型评估工具。官方API为: sklearn.metrics: Metrics

 

2.1. 常用分类API列表

 

 

    1. accuracy_score(y_true,y_pre) : 精度

 

    1. auc(x, y, reorder=False) : ROC曲线下的面积

 

    1. average_precision_score(y_true, y_score, average=‘macro’, sample_weight=None):根据预测得分计算平均精度(AP)

 

    1. brier_score_loss(y_true, y_prob, sample_weight=None, pos_label=None):The smaller the Brier score, the better.

 

    1. confusion_matrix(y_true, y_pred, labels=None, sample_weight=None):通过计算混淆矩阵来评估分类的准确性 返回混淆矩阵

 

    1. f1_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’, sample_weight=None): F1值

 

    1. log_loss(y_true, y_pred, eps=1e-15, normalize=True, sample_weight=None, labels=None):对数损耗,又称逻辑损耗或交叉熵损耗

 

    1. precision_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’,) :查准率或者精度;

 

    1. recall_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’, sample_weight=None):查全率 ;recall(查全率)=TP/(TP+FN)

 

    1. roc_auc_score(y_true, y_score, average=‘macro’, sample_weight=None):计算ROC曲线下的面积就是AUC的值,the larger the better

 

    1. roc_curve(y_true, y_score, pos_label=None, sample_weight=None, drop_intermediate=True);计算ROC曲线的横纵坐标值,TPR,FPR

 

 

2.2. 应用实践

 

准确率:accuracy_score

 

# 计算准确率
        accuracy = accuracy_score(self.y_test, predictions)

 

精准率:precision_score

 

precision = precision_score(self.y_test.values, np.array(predictions),average='macro')       
        print('precision Score: %.2f%%' % (precision*100.0))

 

召回率:recall_score

 

recall = recall_score(self.y_test, predictions)

 

报错:

 

ValueError: Target is multiclass but average='binary'. 
Please choose another average setting, one of [None, 'micro', 'macro', 'weighted'].

average参数定义了该指标的计算方法,二分类时average参数默认是binary;多分类时,可选参数有micro、macro、weighted和samples。
None:返回每个班级的分数。否则,这将确定对数据执行的平均类型。
binary:仅报告由指定的类的结果pos_label。仅当targets(y_{true,pred})是二进制时才适用。
micro:通过计算总真阳性,假阴性和误报来全球计算指标。也就是把所有的类放在一起算(具体到precision),然后把所有类的TP加和,再除以所有类的TP和FN的加和。因此micro方法下的precision和recall都等于accuracy。
macro:计算每个标签的指标,找出它们的未加权平均值。这不会考虑标签不平衡。也就是先分别求出每个类的precision再求其算术平均。
weighted:计算每个标签的指标,并找到它们的平均值,按支持加权(每个标签的真实实例数)。这会改变“宏观”以解决标签不平衡问题; 它可能导致F分数不在精确度和召回之间。
samples:计算每个实例的指标,并找出它们的平均值(仅对于不同的多标记分类有意义 accuracy_score)。

recall = recall_score(self.y_test.values, np.array(predictions),average='macro') 
        
        print('Recall Score: %.2f%%' % (recall*100.0))

 

AUC:roc_auc_score

 

 

    1. 对于二分类,直接用预测值与标签值计算。

 

 

Y_pred = clf.predict(X_test)
# 随机森林的AUC值
forest_auc = roc_auc_score(Y_test, Y_pred)

 

 

对于多分类

与二分类Y_pred不同的是,概率分数Y_pred_prob,是一个shape为(测试集条数,分类种数)的矩阵。

 

 

auc = roc_auc_score(self.y_test, predictions)

 

报错:

 

ValueError: multi_class must be in ('ovo', 'ovr')

 

修改代码:

 

recall = recall_score(self.y_test, predictions,average='micro')
        auc = roc_auc_score(self.y_test,  predictions ,multi_class='ovo',average='macro')

 

ROC与AUC曲线

 

下面代码参考自sklearn样例代码 “Compute macro-average ROC curve and ROC area”

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from itertools import cycle
from sklearn.metrics import roc_curve, auc
from scipy import interp
class Multi_class_evaluation(object):
    # 输入原始值,预测值,分类数量
    def __init__(self, y, y_pred, n_class=2, flag=None):
        self.n_class = n_class
        self.flag = flag
        # 构造数据矩阵n*分类数量        
        def datas():
            cols_name = []
            for i in range(self.n_class):
                cols_name.append('v'+str(i))
                
            def f(v):
                vv = []
                for i in range(self.n_class):
                    if v==i:
                        vv.append(1)
                    else:
                        vv.append(0)
                                
                return pd.Series(vv)
        
            self.y = pd.DataFrame()
            self.y[cols_name] = y[self.flag].apply(lambda x:f(x)) 
        datas()
        self.y = self.y.to_numpy()
        self.y_pred = y_pred
    
    # 计算ROC和AUC
    def calculation_ROC_AUC(self):
        # Compute ROC curve and ROC area for each class
        self.fpr = dict()
        self.tpr = dict()
        self.roc_auc = dict()
        for i in range(self.n_class):
            self.fpr[i], self.tpr[i], _ = roc_curve(self.y[:, i], self.y_pred[:, i])
            self.roc_auc[i] = auc(self.fpr[i], self.tpr[i])
        
        # Compute micro-average ROC curve and ROC area
        self.fpr["micro"], self.tpr["micro"], _ = roc_curve(self.y.ravel(), self.y_pred.ravel())
        self.roc_auc["micro"] = auc(self.fpr["micro"], self.tpr["micro"])        
    #默认是多分类    
    def draw_ROC(self,multi_class=None):                 
        # First aggregate all false positive rates
        if multi_class==None:
            all_fpr = np.unique(np.concatenate([self.fpr[i] for i in range(self.n_class)]))
            
            # Then interpolate all ROC curves at this points
            mean_tpr = np.zeros_like(all_fpr)
            for i in range(self.n_class):
                mean_tpr += interp(all_fpr, self.fpr[i], self.tpr[i])
            
            # Finally average it and compute AUC
            mean_tpr /= self.n_class
            
            self.fpr["macro"] = all_fpr
            self.tpr["macro"] = mean_tpr
            self.roc_auc["macro"] = auc(self.fpr["macro"], self.tpr["macro"])
            
            # Plot all ROC curves
            plt.figure()
            lw=2 #折线宽度
            plt.plot(self.fpr["micro"], self.tpr["micro"],
                     label='micro-average ROC curve (area = {0:0.4f})'
                           ''.format(self.roc_auc["micro"]),
                     color='deeppink', linestyle=':', linewidth=4)
            
            plt.plot(self.fpr["macro"], self.tpr["macro"],
                     label='macro-average ROC curve (area = {0:0.4f})'
                           ''.format(self.roc_auc["macro"]),
                     color='navy', linestyle=':', linewidth=4)
            
            colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
            for i, color in zip(range(self.n_class), colors):
                plt.plot(self.fpr[i], self.tpr[i], color=color, lw=lw,
                         label='ROC curve of class {0} (area = {1:0.4f})'
                         ''.format(i, self.roc_auc[i]))
        else:
            plt.figure()
            lw=2 #折线宽度
            plt.plot(self.fpr[multi_class], self.tpr[multi_class],
                     label='ROC curve of class {0} (area = {1:0.4f})'
                           ''.format(multi_class, self.roc_auc[multi_class]),
                     color='deeppink')            
        
        plt.plot([0, 1], [0, 1], 'k--', lw=lw)
        plt.xlim([0.0, 1.0])
        plt.ylim([0.0, 1.05])
        plt.xlabel('False Positive Rate')
        plt.ylabel('True Positive Rate')
        plt.title('Some extension of Receiver operating characteristic to multi-class')
        plt.legend(loc="lower right")
        plt.show()

 

 

ROC曲线的特征是Y轴上的真阳性率和X轴上的假阳性率。这意味着图的左上角是“理想”点——假阳性率为零,真阳性率为一。这不太现实,但它确实意味着曲线下的较大面积(AUC)通常更好。

 

ROC曲线的“陡度”也很重要,因为它是理想的最大化真阳性率,同时最小化假阳性率。

 

ROC曲线通常用于二值分类来研究分类器的输出。为了将ROC曲线和ROC区域扩展到多标签分类,需要对输出进行二值化。每个标签可以绘制一条ROC曲线,但也可以通过将标签指标矩阵的每个元素视为二进制预测(微平均)来绘制ROC曲线。

 

多标签分类的另一种评估方法是宏平均法,它赋予每个标签的分类同等的权重。

 

2.3. 多分类案例

 

客户流失预测分为四个分类:流失、濒临流失、不活跃、活跃,通过XGBoost多分类,代码片段如下:

 

from PredictionModel import Multi_class_evaluation
......
        y_pred=model.predict(xgb.DMatrix(self.x_test))        
        yprob = np.argmax(y_pred, axis=1)  # return the index of the biggest pro
        predictions = [round(value) for value in yprob]
        # 计算准确率
        accuracy = accuracy_score(self.y_test, predictions)
        print("Accuracy: %.2f%%" % (accuracy * 100.0))      
        Mce = Multi_class_evaluation.Multi_class_evaluation(self.y_test,y_pred,n_class=4,flag='flag1')
        Mce.calculation_ROC_AUC()
        Mce.draw_ROC()
        # 画客户流失(分类0,1,2,3中的1)
        Mce.draw_ROC(multi_class=1)
        y,  y_ =Mce.y, Mce.y_pred
        precision = precision_score(self.y_test.values, np.array(predictions),average='macro')       
        print('precision Score: %.2f%%' % (precision*100.0))  
              
        recall = recall_score(self.y_test.values, np.array(predictions),average='macro')       
        print('Recall Score: %.2f%%' % (recall*100.0))
        
        auc = roc_auc_score(y,  y_ ,multi_class='ovo',average='macro')
        print('Roc Auc Score: %.2f%%' % (auc*100.0))

 

画客户流失,经聚类后的分类0,1,2,3中的1为流失分类。

Accuracy: 97.93%
precision Score: 91.15%
Recall Score: 89.39%
Roc Auc Score: 99.76%

 

客户流失预测四个分类,分别绘制流失、濒临流失、不活跃、活跃的ROC图。

3. 总结

 

我们通常倾向于使用准确率(Acc),易于理解和沟通,但是它不是完成任务的最佳工具!在实际工程中,不平衡多分类更为常见,则召回率、精准度等度量指标较更为适合。

 

统计学为我们提供了计算这些指标的形式化定义和方程。数据科学是关于寻找解决问题的正确工具的学科,而且在开发分类模型时,我们更需要超越准确率(accuracy)的单一指标,根据业务变换灵活运用召回率、精准度、F1 score 和 ROC 曲线评估分类模型。现在我们知道如何使用更聪明的衡量指标!

 

对于软件开发测试环节,灵活运用模型评估、评价技术,将助力AI更顺利的落地实施。

 

编者水平有限,欢迎反馈讨论。

 

参考:

 

1.《牢记分类指标:准确率、精确率、召回率、F1 score以及ROC》 简书 ,MiracleJQ ,2018年8月

 

2.《准确率,召回率,F1 值、ROC,AUC、mse,mape评价指标》 CSDN博客 ,雪伦_ ,2016年6月

 

3.《【解决问题】python编译报错 Target is multiclass but average=‘binary’. Please choose another average setting》 CSDN博客 ,君琴 ,2020年5月

 

4.《多分类f1分数_一文看懂分类模型的评估指标:准确率、精准率、召回率、F1等…》 CSDN博客 ,遇见高中生,2020年12月

 

5.《sklearn下对于二分类和多类分类问题的评估方法总结》 CSDN博客 ,Clark_Xu , 2019年8月

 

6.《Receiver Operating Characteristic (ROC)》 scikit-learn 0.24.2 API ,2021年4月

 

7.《机器学习中的评价指标(一)-Accuracy、precision、Recall、F1 Score、ROC Curve、PR Curve、AUC》 CSDN博客 , faithmy509 ,2018年7月

 

8.《深度学习评价指标简要综述》 知乎 ,Error ,2020年8月

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注