机器学习

机器学习 | 基于Iris数据集的分类模型评估指标分析

2019-09-28  本文已影响0人  cathy1997

最近看了一篇2011年在The 5th ACM conference上的论文《Setting Goals and Choosing Metrics for Recommender》,论文对推荐系统中常用的预测评分准确度、分类准确度和排序准确度三大类算法评估指标进行了概述,其中:

上图为总结的一些常用模型评估指标,为加强对评估指标的适用性理解,本文基于UCI数据实例,以分类算法为例,对分类准确度中常用的几个指标进行分析,以下是具体分析过程。

数据来源

UCI数据库UCI Machine Learning Repository中的Iris数据集

01 可视化——数据整体情况描述

在进行分类之前,首先了解一下获得的Iris数据集的整体情况,可以看到,Iris数据集包含150条数据记录,包括SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpecies五个字段,其中最后一行的Species是分类结果。

import pandas as pd
# 1.查看基本信息
df_Iris = pd.read_excel('D:/Pythonworkspace/Iris.xlsx')
df_Iris.head() # 前5行
df_Iris.tail() # 后5行
df_Iris.info() # 查看数据整体信息
df_Iris.describe()
# 查看分类的类别数量
df_Iris.describe(include =['O']).T
# 对每一类(Species)进行计数统计
df_Iris.Species.value_counts()

该数据集一共有三个类别,分别有50个Iris-setosa类、50个Iris-versicolor类和50个Iris-virginica类。

数据整体情况.png

为了格式更清晰,对Species的值进行数据清洗,去掉该特征中的Iris-字符:

# 2.特征工程
df_Iris['Species']= df_Iris.Species.str.replace('Iris-','')
df_Iris.Species.unique()

接下来用可视化的方式对数据进行展示,可以更直观的看出不同特征变量与分类结果之间的相关性,首先看一下SepalLengthCmSepalWidthCm这两个变量的散点图。

# 3.数据可视化展示
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

sns.set()#sns初始化 
# hue表示按照Species对数据进行分类, 而style表示每个类别的标签系列格式不一致
sns.relplot(x='SepalLengthCm', y='SepalWidthCm', hue='Species', style='Species', data=df_Iris )
plt.title('SepalLengthCm and SepalWidthCm data by Species')
plt.show()
散点图-花萼的长度和宽度与种类之间的关系.png

除了散点图,也可以用折线图的方式看一下,直接在relplot()函数里面添加参数kind='line'

sns.relplot(x='SepalLengthCm', y='SepalWidthCm', hue='Species', style='Species', kind='line', data=df_Iris )# hue表示按照Species对数据进行分类, 而style表示每个类别的标签系列格式不一致
plt.title('SepalLengthCm and SepalWidthCm data by Species')
plt.show()
折线图-花萼的长度和宽度与种类之间的关系.png

此外,四分位图或者称作箱线图也可以用作显示一组数据分散情况资料的统计图,主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较。

# 展示四分位图
sns.boxplot(x='SepalLengthCm', data=df_Iris)

#对于每个属性的data创建一个新的DataFrame
Iris1 = pd.DataFrame({"Id": np.arange(1,151), 'Attribute': 'SepalLengthCm', 'Data':df_Iris.SepalLengthCm, 'Species':df_Iris.Species})
Iris2 = pd.DataFrame({"Id": np.arange(151,301), 'Attribute': 'SepalWidthCm', 'Data':df_Iris.SepalWidthCm, 'Species':df_Iris.Species})
Iris3 = pd.DataFrame({"Id": np.arange(301,451), 'Attribute': 'PetalLengthCm', 'Data':df_Iris.PetalLengthCm, 'Species':df_Iris.Species})
Iris4 = pd.DataFrame({"Id": np.arange(451,601), 'Attribute': 'PetalWidthCm', 'Data':df_Iris.PetalWidthCm, 'Species':df_Iris.Species})
Iris = pd.concat([Iris1, Iris2, Iris3, Iris4])#将四个DataFrame合并为一个
sns.boxplot(x='Attribute', y='Data', data=Iris)
plt.show()

下图展示了四个属性取值的分布情况,可以对四个属性的中位数、最大值、最小值进行比较。

四个属性的四分位图.png

但是对于我们来说,更重要的是属性取值的分布与最终分类结果之间的关系,所以将Species也加入到箱线图中。

# 将种类加入进行对比
sns.boxplot(x='Attribute', y='Data',hue='Species', data=Iris)
plt.show()
将种类加入进行对比.png

最后,可以对总体分布情况进行一个整体的查看,对图的具体分析这里不再展开。

#删除Id特征, 绘制整体情况的分布图
sns.pairplot(df_Iris.drop('Id', axis=1), hue='Species')
# plt.show()
整体情况分布.png

02 构建二分类模型

由于本文想使用ROC曲线评估分类模型效果,而ROC曲线使用的时候有两个前提:

  1. 分类的类型:必须为数值型
  2. 只针对二分类问题。

因此这里首先进行二分类,对Iris数据集的分类结果改为01两类,前75个为1,后75个为0。在操作过程中,将数据按照8:2的比例随机分为训练集和测试集,此外还可以采用留出法、交叉验证法、自助法等方法划分训练集、测试集和验证集。

本文将分别构建SVM朴素贝叶斯决策树三种分类模型,并进行指标对比分析。

# 4.构建模型
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn.naive_bayes import MultinomialNB
from sklearn import tree

X = df_Iris[['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm']]
y = df_Iris['Species']
#将数据按照8:2的比例随机分为训练集, 测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

#初始化模型
svm = svm.SVC(kernel='linear', probability=True)
mnb = MultinomialNB()
model_tree = tree.DecisionTreeClassifier()
#训练模型
svm.fit(X_train, y_train)
mnb.fit(X_train,y_train)
model_tree.fit(X_train,y_train)
#用测试集评估模型的好坏
print('SVM Test score:{:.8f}'.format(svm.score(X_test,y_test)))
print('Bayes Test score:{:.8f}'.format(mnb.score(X_test,y_test)))
print('Decision Tree Test score:{:.8f}'.format(model_tree.score(X_test,y_test)))

利用svm.score()mnb.score()函数可以直接得到两个模型预测的准确率(Accuracy),但是我们希望对查准率、召回率等更多指标进行分析,因此对该模型进行进一步评估分析。

03 二分类模型评估

为便于之后进行指标计算,首先生成混淆矩阵,对于二分类问题,真实的样本标签有两类,学习器预测的类别有两类,因此在混淆矩阵中有四类情形:

这里以SVM模型为例:

# 5,检验分类结果,生成混淆矩阵
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator

column1 = list(y_test)
column2 = list(svm.predict(X_test))
def confusion(column1, column2):
    TP,FP,FN,TN = 0,0,0,0
    for i in range(0, len(column1)):
        if round(column1[i]) == 1 and round(column2[i]) == 1:
            TP = TP + 1
        if round(column1[i]) == 1 and round(column2[i]) == 0:
            FN = FN + 1
        if round(column1[i]) == 0 and round(column2[i]) == 1:
            FP = FP + 1
        if round(column1[i]) == 0 and round(column2[i]) == 0:
            TN = TN + 1
        column1[i] = round(column1[i])
        column2[i] = round(column2[i])
    return TP, TN, FP, FN
 
def plotCM(classes1,classes2,matrix):
    # Normalize by row
    matrix = matrix.astype(np.float)
    linesum = matrix.sum(1)
    linesum = np.dot(linesum.reshape(-1, 1), np.ones((1, matrix.shape[1])))
    matrix /= linesum
    matrix = np.round(matrix,2)

    fig = plt.figure(figsize=(4,4))
    ax = fig.add_subplot(111)
    cax = ax.matshow(matrix, cmap=plt.cm.get_cmap('Blues'))
    fig.colorbar(cax)
    ax.xaxis.set_major_locator(MultipleLocator(1))
    ax.yaxis.set_major_locator(MultipleLocator(1))
    for i in range(matrix.shape[0]):
        for j in range(matrix.shape[1]):
            ax.text(i, j, str(matrix[i][j]), va='center', ha='center')

    ax.set_xticklabels([''] + classes1, rotation=45)
    ax.tick_params(axis='x', bottom=True, top=False, labelbottom=True, labeltop=False)
    ax.set_yticklabels([''] + classes2)
    
    ax.set_xlabel('Predicted label')
    ax.set_ylabel('True label')
    plt.title('Confusion Matrix')
    plt.show()

# 生成二维矩阵
matrix = [[0]*2 for i in range(2)]
matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1] = confusion(column1, column2)
matrix = np.array(matrix)

classes1 = ['P', 'N']
classes2 = ['P', 'N']
plotCM(classes1=classes1, classes2=classes2, matrix=matrix)

首先分别对TP、TN、FP、FN四种情况进行计算,得到混淆矩阵的值,然后以可视化的方式绘制混淆矩阵的图。

混淆矩阵

对于分类算法,评价指标主要有精确率(precision)、召回率(recall)、F1-score以及即将要讨论的ROCAUC。接下来分别对这五个指标进行分析,可以采用下图的计算方式利用已得到的混淆矩阵进行计算,也可以直接调用sklearn.metrics中的相关函数得到。

指标的计算方式
# 6.计算评价指标
from sklearn import metrics
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import average_precision_score
from sklearn.metrics import accuracy_score

print('准确率Accuracy:', metrics.accuracy_score(y_test, svm.predict(X_test)))
print('查准率Precision:', metrics.precision_score(y_test, svm.predict(X_test)))
print('查全率Recall:', metrics.recall_score(y_test, svm.predict(X_test)))
print('调和平均指标F1:', metrics.f1_score(y_test, svm.predict(X_test)))

这里得到SVM的准确率Accuracy: 0.666666666667;查准率Precision: 0.818181818182;查全率Recall: 0.529411764706;调和平均指标F1: 0.642857142857。
朴素贝叶斯的准确率Accuracy: 0.733333333333;查准率Precision: 1.0
查全率Recall: 0.529411764706;调和平均指标F1: 0.692307692308。
决策树的准确率Accuracy: 0.7;查准率Precision: 0.9;查全率Recall: 0.529411764706;调和平均指标F1: 0.666666666667。

P-R曲线是在PrecisionRecall的基础上绘制的曲线,分类算法对样本进行分类时,都会得出一个置信度,即该样本是正样本的概率,通过置信度可以对所有的样本进行排序,再逐个选择阈值,在阈值之前的都属于正例,在阈值之后的都属于负例。每个样本作为划分阈值时都会计算相应的精确率和召回率,由此就可以绘出分类模型的P-R曲线。
在这里调用sklearn.metrics中的precision_recall_curve()函数可以直接绘制。

# 7.绘制P-R曲线
# 获取每项记录分类为1的概率y_scores
y_predict = svm.predict_proba(X_test)
y_scores = []
for i in range(0,len(y_predict)):
    y_scores.append(y_predict[i][1])

y_predict1 = mnb.predict_proba(X_test)
y_scores1 = []
for i in range(0,len(y_predict1)):
    y_scores1.append(y_predict1[i][1])

y_predict2 = model_tree.predict_proba(X_test)
y_scores2 = []
for i in range(0,len(y_predict2)):
    y_scores2.append(y_predict2[i][1])
    
# 第二种计算y_scores的方法
# y_scores = svm.decision_function(X_test)

precision, recall, thresholds = precision_recall_curve(y_test, y_scores)
precision1, recall1, thresholds1 = precision_recall_curve(y_test, y_scores1)
precision2, recall2, thresholds2 = precision_recall_curve(y_test, y_scores2)
plt.plot(recall,precision,label='SVM',color='r',marker='o')
plt.plot(recall1,precision1,label='Naive Bayes',color='green',marker='o')
plt.plot(recall2,precision2,label='Decision Tree',color='blue',marker='o')
plt.legend()
plt.show()

当一个模型的P-R曲线将另一个模型的P-R曲线包住时,就说明前者的性能优于后者,P-R曲线越向右上角凸也就意味着该模型越好。


P-R曲线

ROC曲线也是一种常用的二分类模型评估指标,ROC曲线的横轴为“假正例率”(False Position Rate,FPR),即实际为负但是预测为正样本的可能性;纵轴为“真正例率”(True Position Rate,TPR),表示实际为正且预测为正样本的可能性。

计算公式

与P-R曲线相比,ROC曲线还有个很好的特性,即当测试集中正负样本的比例发生较大变化时,ROC曲线也不会发生很大的变化。调用sklearn.metrics中的roc_curve()函数可以直接绘制ROC曲线。

# 8.绘制ROC曲线并计算AUC
from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt

fpr, tpr, thresholds  =  roc_curve(y_test, y_scores) 
roc_auc = auc(fpr,tpr)

fpr1, tpr1, thresholds1  =  roc_curve(y_test, y_scores1) 
roc_auc1 = auc(fpr1,tpr1)

fpr2, tpr2, thresholds2  =  roc_curve(y_test, y_scores2) 
roc_auc2 = auc(fpr2,tpr2)

plt.figure()
lw = 2
plt.figure(figsize=(4,4))
plt.plot(fpr, tpr, color='darkorange',lw=lw, label='SVM ROC curve (area = %0.2f)' % roc_auc) #假正率为横坐标,真正率为纵坐标做曲线
plt.plot(fpr1, tpr1, color='green',lw=lw, label='Bayes ROC curve (area = %0.2f)' % roc_auc1)
plt.plot(fpr2, tpr2, color='blue',lw=lw, label='Tree ROC curve (area = %0.2f)' % roc_auc2)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
ROC曲线

AUC定义为ROC曲线下方的面积,其取值在[0.5,1.0]之间。调用sklearn.metrics中的auc()函数计算AUC,可以得到SVM的AUC=0.86,Bayes的AUC=0.90,决策树的AUC=0.73,当有多个模型进行比较时,一般AUC的值越大则表示该模型预测效果越好。

本文主要实现了二分类模型,后续将继续进行多分类模型及评估指标的分析,并采用决策树、朴素贝叶斯等其他分类算法构建分类器,再对不同的算法进行评估。

参考:
【出图】python生成混淆矩阵 - ty_1996的博客
ROC原理介绍及利用python实现二分类和多分类的ROC曲线
python SVM 案例,sklearn.svm.SVC 参数说明_慕课手记
Python机器学习(sklearn)——分类模型评估与调参总结(下)

上一篇下一篇

猜你喜欢

热点阅读