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

最近看了一篇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 二分类模型评估

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

  • True Positive(真正, TP):将正类预测为正类数.
  • True Negative(真负 , TN):将负类预测为负类数.
  • False Positive(假正, FP):将负类预测为正类数,误报 (Type I error).
  • False Negative(假负, FN):将正类预测为负类数,漏报 (Type II error).

这里以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)——分类模型评估与调参总结(下)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350