Advertisement

数据挖掘算法和实践(二十五):分类模型的评估方法

阅读量:

分类模型的评估方法
核心内容
本文详细介绍了分类模型的评估方法,涵盖二分类与多分类场景的关键指标与技术。
主要概念
混淆矩阵:用于总结模型预测结果与真实标签之间的差异。
准确率(accuracy):预测正确样本数量的比例。
精确率(precision):真阳/(真阳+假阳)。
召回率(recall):真阳/(真实值+假阴)。
F1值(F1-score):精确率与召回率的调和平均数。
ROC曲线:通过不同阈值下的TPR与FPR绘制曲线。
AUC(Area Under the ROC Curve):衡量ROC曲线下面积大小。
对数损失(logloss):衡量预测概率准确性。
多分类处理
对于多分类问题:
采用One-Vs-One (OvO)策略:将每一对类别视为二元问题进行建模。
采用One-Vs-Regular (OvR)策略:将剩余类别视为一类进行建模。
实际应用
案例分析展示了如何利用Logistic回归对鸢尾花三分类问题进行建模,并通过混淆矩阵、ROC曲线等方法进行性能评估。

分类模型的评估方法

该文档涵盖主流二分类模型的评估手段,并系统性地阐述了绝对值、相对值以及通用评估标准。随后扩展至多分类场景下的处理方式及其验证方法。最后通过鸢尾花三分类案例展示了具体实现,并提供了基于One-vs-One和One-vs-Rest策略的具体代码段。

一、什么是分类模型?

作为监督学习的一种代表,在构建机器学习模型时需依赖于样本数据的支持。该模型能够处理不同类型的数据输入:既可以接受连续型变量的数据输入;输出结果通常是预先确定好的离散类别标签。在实际应用中,通常依据具体问题中出现的分类标签数量来判断属于二元分类还是多元分类问题。

大多数回归算法的目标函数基于均值-方差最小化原则;相比之下,分类算法的设计方案更为丰富,在很多实际应用中,默认假设问题可转化为回归任务进行建模更为便捷;而通过应用映射函数(如逻辑回归中的sigmod函数),则可将回归模型的结果转换为分类任务。在很多实际应用中,默认假设问题可转化为回归任务进行建模更为便捷;而通过应用映射函数(如逻辑回归中的sigmod函数),则可将回归模型的结果转换为分类任务。在很多实际应用中,默认假设问题可转化为回归任务进行建模更为便捷;而通过应用映射函数(如逻辑回归中的sigmod函数),则可将回归模型的结果转换为分类任务。

常用的分类分析方法涵盖以下几种:回归分析方法(如逻辑回归)、决策树模型(包括随机森林(RF)、CART)、支持向量机模型(SVM)、贝叶斯分类器(Naïve Bayes)、K近邻分类器(KNN)、深度学习模型中的神经网络技术以及Light Gradient Boosting(LGB)、梯度提升决策树(GBDT)和扩展的梯度提升框架(XGboost)。这些技术在集成学习中得到了进一步的发展和应用。

二、分类模型的评估

绝对指标:混淆矩阵(Confusion Matrix)

在二分类模型中进行核心任务时需要明确区分每个样本的具体归属,在实际应用中我们需要准确划分正负两类。具体而言,在收集好高质量的标注数据后能够在真实场景中明确识别出哪些样本属于正类或负类。与此同时,在经过训练后的分类型模型也能推断出其对各个测试样本的分类结果。

因此,我们就能得到这样四个基础指标:

真阳 :真实值是positive,模型认为是positive的数量(True Positive=TP

真阴 :真实值是negative,模型认为是negative的数量(True Negative=TN

假阳 :真实值是negative,模型认为是positive的数量(False Positive=FP

假阴 :真实值是positive,模型认为是negative的数量(False Negative=FN

将这四个指标一起呈现在表格中,就能得到如下这样一个混淆矩阵:

复制代码
    # 建模
    x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=2020)
    logistic_a= LogisticRegression()
    logistic_a.fit(x_train,y_train)
    y_pred = logistic_a.predict(x_test) 
    logistic_a.score(x_test,y_test) 
    # 混淆矩阵
    from sklearn.metrics import confusion_matrix
    confusion_matrix(y_test, y_pred,labels=[0, 1, 2])

相对指标

混淆矩阵是一种用于分类模型性能评估的重要分析工具;通过分析混淆矩阵中各个行列的数据分布情况,能够计算出包括精确率、召回率、准确率和错误率在内的多个关键统计指标。

复制代码
    # 分类报告
    from sklearn.metrics import classification_report
    print(classification_report(y_test, y_pred))
精确率(precision)

用于评估分类属性中某一个分类标签的精确度水平, 其计算公式采用真阳性率除以(真阳性率加假阳性率)的方法

复制代码
    #精确率
    from sklearn.metrics import roc_auc_score,f1_score,recall_score,accuracy_score,precision_score
    precision_score(y_test,y_pred,average='macro')
召回率/灵敏度(recall)

用于评估该分类标签的误判率其计算公式为真阳性除以真阳性与假阴性的总和即 TP/(TP+FN)

img
复制代码
    #召回率
    from sklearn.metrics import roc_auc_score,f1_score,recall_score,accuracy_score,precision_score
    recall_score(y_test,y_pred,average='macro')

在理想情况下,尽可能提高精确度与召回度是最佳目标,但在实际应用中,二者常存在冲突:当提升某一指标的同时往往会降低另一指标,这种现象可通过分析PR曲线来直观理解。例如,在信息检索过程中,若仅返回单个最相关的网页结果,此时的精确度可达100%,但其召回度极为有限;相反地,若所有网页都被检索结果包含,则可实现全面的 Recall 但相应的 Precision 却大幅下降。因此,在不同的应用场景中应根据具体需求权衡这两个指标的重要性。

准确率(accuracy)
img

这两个指标常常被误认为相同,在二分类场景中,精确率通常用于评估模型性能;相比之下,在多分类问题中,准确率则能够广泛应用于各种场景。其计算公式为:

img
复制代码
    #准确率
    from sklearn.metrics import roc_auc_score,f1_score,recall_score,accuracy_score,precision_score
    accuracy_score(y_test,y_pred)
错误率(errorrate)
img
复制代码
    from sklearn.metrics import det_curve
    fpr, fnr, thresholds = det_curve(y_test['setosa'].T, y_pred[:,0])
    #  DET
    plt.plot(fpr, fnr, c='r')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('False Negtive Rate')
    plt.show()
宏平均、微平均、加权平均

这三个指标分别代表macro average、micro average和weighted average,并即为精准度(precision)、召回效率(recall)以及f1分数的一种不同表述方式。

宏平均 macro avg: 每个类别的精准率、召回率和F1 加和求平均;

微平均 micro avg: 该算法对各类别样本一视同仁,在综合评价中通过加权求和的方式得到精确度、召回率以及F1值的综合指标。

加权平均 weighted avg: 加权平均是对宏平均的一种优化方法,在计算过程中更加注重各类别在整体数据中的比例;

复制代码
     # 分类报告
    from sklearn.metrics import classification_report
    print(classification_report(y_test, y_pred))
在这里插入图片描述

通用指标

综合指标F值(F1-scores)
rac{2}{F_1} = rac{1}{P} + rac{1}{R}
img

F值可泛化为对精确率和召回率赋不同权值进行加权调和:

F_{lpha} = rac{ PR}{lpha^2 P+R}

通过对α设定不同的权重进行调整后,在评估模型性能时会得到不同的F指标;其中当α取1时被称为F₁指标,在信息检索中常被用来衡量精确度与召回度之间的平衡关系;该指标是精确度与召回度两个关键参数的加权调和平均数

复制代码
    #f1-score:精确率和召回率的调和平均数
    f1_score(y_test,y_pred,average='macro')#0.8666666666666668
ROC曲线

在众多机器学习模型中,许多模型会输出预测的概率值。然而,在采用精确率和召回率等指标评估模型性能时,则需要对这些预测概率进行分类阈值设置——例如将超过阈值的样本归类为正例。这种做法会引入一个超参数,并影响到模型的整体泛化能力。值得注意的是,ROC曲线则无需设定这样的阈值。

两者的某些特性具有相似性

在数据样本较少的情况下,在绘制ROC曲线时会出现不规则形状;而当数据样本充足的情况下,在绘制ROC曲线时则会呈现出较为平稳的趋势。

通常情况下,在ROC曲线较为平滑的情况下(例如,在图中显示的数值范围(如0.2至0.4)可能暗示存在过拟合风险),但需要注意的是样本量较少的情况下(比如图中所述的区域),我们可以大致判断模型未出现严重的过拟合情况)。此时调整模型时应重点关注AUC指标的变化情况,并认为较高的AUC值通常预示着更好的分类性能。

复制代码
    # 二分类的ROC图
    fpr1, tpr1, _ = roc_curve(y_test,y_pred)
    #  ROC
    plt.plot(fpr1, tpr1, c='r',label=' (area = %0.2f)' % auc1)
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC Graph')
    plt.show()
AUC(Area Under the ROC Curve)

可以将AUC视为ROC曲线下方区域的度量值。该度量值能够有效评估模型的学习效果并作为其性能的重要指标。在实际应用中,可以通过累加不同区域的面积来计算该度量值。目前用于计算AUC的方法主要包括以下几种:基于积分原理的方法、基于梯形法则近似估算的方法以及基于统计抽样的蒙特卡洛方法等。从几何学的角度来看, AUC直接反映了分类器的整体性能。

img

从概率论的角度来看,在机器学习中,AUC主要衡量的是模型对样本排列顺序的能力,这与其产生的排序错误直接相关,并且能够推导出计算公式。

img

ROC 曲线在多分类场景中被广泛认为不适用。
当且仅当二分类任务中的 Positive 类别与 Negative 类别具有同等重要性时,
适合使用 ROC 曲线进行评估。
如果有必要将 ROC 分析扩展至多分类场景,
则可将其转化为多个"一分为二"的问题进行处理。
具体而言,
在每个子问题中选择一个类别作为正例(Positive),
其余类别则被视为负例(Negative),
并相应地生成相应的 ROC 曲线。

PR曲线

以召回率R为横轴、以精确率P为纵轴,能够画出P-R曲线,如下图:

img

通过查看图形数据可以看出, precision与Recall之间存在权衡关系,即所谓的折中(trade off)。在坐标系中,当曲线越接近左上角时性能越优,而曲线下方的面积则被称为AP分数,能够一定程度上反映模型在精确率和召回率上的平衡能力。然而,计算起来较为繁琐。综合而言,在计算效率方面,AUC指标更为便捷。

首先比较平滑与不平滑的状态,在考察上下文的变化情况(基于同一测试集的数据),通常而言上面的情境往往优于下面的情况;例如,在图中用红线表示的区域表现更好。

F₁值在P和R越接近时也会越大。
通常会绘制从(0,0)到(1,1)的直线,
并在PR曲线上与该直线相交的位置计算对应的F₁值(在光滑情况下),
此时该F₁值类似于AUC对ROC曲线的意义。
单个数值相比整条曲线更容易调整模型参数。

复制代码
    #PR 曲线
    from sklearn.metrics import precision_recall_curve
    precision, recall, thresholds = precision_recall_curve(y_test['setosa'].T, y_pred[:,0])
    plt.plot(recall, precision, c='r')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('recall')
    plt.ylabel('precision')
    plt.title('PR Graph')
    plt.show()

注:由于Precision和Recall均为二分类指标,则PR曲线自然也是二分类指标。然而,在这种情况下实际上是将多分类问题转化为二分法分析(即将关注的目标类别视为一类,并将剩余的所有类别归为另一类)。

对数损失 (logistic loss,logloss)

在对数损失最小化的框架下,在已知样本分布的基础上寻求能够生成该分布的最优参数,并使这种分布对应的概率达到最大值。对于二分类问题而言,其对应的计算公式为:-\frac{1}{N}\sum_{i=1}^{N}\left[ y_i \ln\hat{y}_i + (1 - y_i)\ln(1 - \hat{y}_i) \right]

logloss = -rac{1}{N}um_{i=1}^{N} dot log

对数损失在多分类问题中也使用广泛,其计算公式为:

img
复制代码
    # 对数损失
    from sklearn.metrics import log_loss
    log_loss(y_true, y_pred)
KS曲线

KS(Kolmogorov-Smirnov) 检验法基于将真阳率(TPR)和假阳性率(FPR)分别设为纵轴变量,并将阈值设定为横轴变量的基础上绘制两条曲线。这些差异反映了不同阈值下两个检验指标之间的差距。

复制代码
    # KS 曲线
    from sklearn.metrics import roc_curve
    
    fpr, tpr, thresholds= roc_curve(y_test['setosa'].T, y_pred[:,0])
    ks_value = max(abs(fpr-tpr))
    
    # 画图,画出曲线
    plt.plot(fpr, label='bad')
    plt.plot(tpr, label='good')
    plt.plot(abs(fpr-tpr), label='diff')
    # 标记ks
    x = np.argwhere(abs(fpr-tpr) == ks_value)[0, 0]
    plt.plot((x, x), (0, ks_value), label='ks - {:.2f}'.format(ks_value), color='r', marker='o', markerfacecolor='r', markersize=5)
    plt.scatter((x, x), (0, ks_value), color='r')
    plt.legend()
    plt.show()

三、多分类问题评估

多分类模型同样可以采用二分类模型的理论基础及验证手段。尽管如此,在大多数情况下,我们依赖一些策略来利用二分类算法解决多分类问题。例如:OvO、OvR。

OvO

采用二分类算法作为手段应对多分类问题。\n\n由此可见其基本理念即一对一模式。\n\n其中"一"代表各个类别而"对"则指将数据划分为两个互斥的类别组以构建多个分类器。

划分标准不复杂即可。假设当前训练数据集按照以下图示分布(其中A、B、C分别代表不同的类别):

在这里插入图片描述

ovo就是要构造得到3个分类器。

在这里插入图片描述

在预测阶段中,则只需让测试样本依次输入到由训练阶段所得出的三个分类器中完成预测任务。随后对三个分类器所得结果进行汇总统计。票数最高的结果即为最终的预测结果。如下图所示:

在这里插入图片描述

OvR

主要采用二分类算法来处理多标签数据的策略。其基本概念源于"一胜其余"的哲学。当面对n个类别时,则选择其中一个类别与其他所有类别形成对比。将其余所有类别归入同一组即可形成一个二分类问题。与之相似,在模型训练阶段也需要相应的数据划分过程。

在这里插入图片描述

在预测阶段中,在调用测试样本时,请确保将其传递给训练完成的三个分类器。最终确定结果时,请依据各分类器输出的概率值选择具有最高概率的那一类。如下图所示:

在这里插入图片描述

用于对多分类问题进行模型评估的过程中,在采用OVO和OVR方法建立模型后完成后续步骤时,分别采用二分类方式进行验证。

复制代码
    #多分类的ROC曲线,需要先把分类转成one-hot标签,然后分别进行ROC作图
    fpr1, tpr1, _ = roc_curve(y_test['setosa'].T, y_pred[:,0])
    fpr2, tpr2, _ = roc_curve(y_test['versicolor'].T, y_pred[:,1])
    fpr3, tpr3, _ = roc_curve(y_test['virginica'].T, y_pred[:,2])
    auc1 = metrics.auc(fpr1, tpr1)
    auc2 = metrics.auc(fpr2, tpr2)
    auc3 = metrics.auc(fpr3, tpr3)
    #  ROC
    plt.plot(fpr1, tpr1, c='r',label=' (area = %0.2f)' % auc1)
    plt.plot(fpr2, tpr2, c='b',label=' (area = %0.2f)' % auc2)
    plt.plot(fpr3, tpr3, c='y',label=' (area = %0.2f)' % auc3)
    
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Muti-Class ROC Graph')
    plt.legend(loc="lower right")
    plt.show()

四、案例

复制代码
    # 鸢尾花的三分类模型
    from sklearn.metrics import roc_auc_score,f1_score,recall_score,accuracy_score,precision_score
    import sklearn.datasets as dt
    import pandas as pd
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LogisticRegression
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import numpy as np
    %matplotlib inline
    # 汉字字体,优先使用楷体,如果找不到楷体,则使用黑体
    plt.rcParams['font.sans-serif'] = ['KaiTi', 'SimHei', 'FangSong']  
    plt.rcParams['font.size'] = 12  # 字体大小
    plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
    
    iris = dt.load_iris()
    feature = iris.data
    target = iris.target
    feature_columns=['Sepal.Length','Sepal.Width','Petal.Length','Petal.Width']
    feature_pandas= pd.DataFrame(feature,columns=feature_columns)
    target_pandas= pd.DataFrame(target,columns=['label'])
    data=feature_pandas.join(target_pandas)
    data.head()
    
    x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=2020)
    logistic_a= LogisticRegression()
    logistic_a.fit(x_train,y_train)
    y_pred = logistic_a.predict(x_test) 
    logistic_a.score(x_test,y_test) 
    
    # 混淆矩阵
    from sklearn.metrics import confusion_matrix
    confusion_matrix(y_test, y_pred,labels=[0, 1, 2])
    
    #精确率
    precision_score(y_test,y_pred,average='macro') 
    #准确率
    accuracy_score(y_test,y_pred) 
    #召回率
    recall_score(y_test,y_pred,average='macro') 
    #f1-score:精确率和召回率的调和平均数
    f1_score(y_test,y_pred,average='macro')
    # 分类报告
    from sklearn.metrics import classification_report
    print(classification_report(y_test, y_pred))
    
    #-----------------多分类
    # 多分类的ROC曲线,需要先把分类转成one-hot标签,然后分别进行ROC作图
    from sklearn.metrics import roc_curve
    dummies_label  = pd.get_dummies(data['label'])
    dummies_label.columns=['setosa','versicolor','virginica']
    data_2 = feature_pandas.join(dummies_label)
    # 数据分割
    x_train, x_test, y_train, y_test = train_test_split(data_2.iloc[:,:-3], data_2.iloc[:,-3:], test_size=0.9, random_state=2020)
    # 模型预测, prodict_proba输出概率
    y_pred = logistic_a.predict_proba(x_test)  
    
    fpr1, tpr1, _ = roc_curve(y_test['setosa'].T, y_pred[:,0])
    fpr2, tpr2, _ = roc_curve(y_test['versicolor'].T, y_pred[:,1])
    fpr3, tpr3, _ = roc_curve(y_test['virginica'].T, y_pred[:,2])
    auc1 = metrics.auc(fpr1, tpr1)
    auc2 = metrics.auc(fpr2, tpr2)
    auc3 = metrics.auc(fpr3, tpr3)
    #  ROC
    plt.plot(fpr1, tpr1, c='r',label=' (area = %0.2f)' % auc1)
    plt.plot(fpr2, tpr2, c='b',label=' (area = %0.2f)' % auc2)
    plt.plot(fpr3, tpr3, c='y',label=' (area = %0.2f)' % auc3)
    
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Muti-Class ROC Graph')
    plt.legend(loc="lower right")
    plt.show()
    
    # logloss 对数损失
    from sklearn.metrics import log_loss
    log_loss(y_test['setosa'].T, y_pred[:,0])
    
    #PR 曲线
    from sklearn.metrics import precision_recall_curve
    precision, recall, thresholds = precision_recall_curve(y_test['setosa'].T, y_pred[:,0])
    plt.plot(recall, precision, c='r')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('recall')
    plt.ylabel('precision')
    plt.title('PR Graph')
    plt.show()
    
    # KS 曲线
    from sklearn.metrics import roc_curve
    
    fpr, tpr, thresholds= roc_curve(y_test['setosa'].T, y_pred[:,0])
    ks_value = max(abs(fpr-tpr))
    
    # 画图,画出曲线
    plt.plot(fpr, label='bad')
    plt.plot(tpr, label='good')
    plt.plot(abs(fpr-tpr), label='diff')
    # 标记ks
    x = np.argwhere(abs(fpr-tpr) == ks_value)[0, 0]
    plt.plot((x, x), (0, ks_value), label='ks - {:.2f}'.format(ks_value), color='r', marker='o', markerfacecolor='r', markersize=5)
    plt.scatter((x, x), (0, ks_value), color='r')
    plt.legend()
    plt.show()

全部评论 (0)

还没有任何评论哟~