最近看了一篇2011年在The 5th ACM conference上的论文《Setting Goals and Choosing Metrics for Recommender》,论文对推荐系统中常用的预测评分准确度、分类准确度和排序准确度三大类算法评估指标进行了概述,其中:
- 预测评分准确度,衡量算法预测的评分和用户实际评分的贴近程度,可以对连续型数据进行误差计算;
- 分类准确度,只衡量分类正确和不正确的数量,忽略准确的等级和排名;
- 排序准确度,衡量推荐物品的准确度,只关注排名不关注准确度;排序准确度更适合于与已知用户偏好的排名进行比较。
上图为总结的一些常用模型评估指标,为加强对评估指标的适用性理解,本文基于UCI数据实例,以分类算法为例,对分类准确度中常用的几个指标进行分析,以下是具体分析过程。
数据来源
UCI数据库UCI Machine Learning Repository中的Iris数据集
01 可视化——数据整体情况描述
在进行分类之前,首先了解一下获得的Iris数据集的整体情况,可以看到,Iris数据集包含150条数据记录,包括SepalLengthCm
、SepalWidthCm
、PetalLengthCm
、PetalWidthCm
、Species
五个字段,其中最后一行的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
类。
为了格式更清晰,对Species
的值进行数据清洗,去掉该特征中的Iris-
字符:
# 2.特征工程
df_Iris['Species']= df_Iris.Species.str.replace('Iris-','')
df_Iris.Species.unique()
接下来用可视化的方式对数据进行展示,可以更直观的看出不同特征变量与分类结果之间的相关性,首先看一下SepalLengthCm
和SepalWidthCm
这两个变量的散点图。
# 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()
除了散点图,也可以用折线图的方式看一下,直接在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()
此外,四分位图或者称作箱线图也可以用作显示一组数据分散情况资料的统计图,主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较。
# 展示四分位图
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()
下图展示了四个属性取值的分布情况,可以对四个属性的中位数、最大值、最小值进行比较。
但是对于我们来说,更重要的是属性取值的分布与最终分类结果之间的关系,所以将Species
也加入到箱线图中。
# 将种类加入进行对比
sns.boxplot(x='Attribute', y='Data',hue='Species', data=Iris)
plt.show()
最后,可以对总体分布情况进行一个整体的查看,对图的具体分析这里不再展开。
#删除Id特征, 绘制整体情况的分布图
sns.pairplot(df_Iris.drop('Id', axis=1), hue='Species')
# plt.show()
02 构建二分类模型
由于本文想使用ROC曲线评估分类模型效果,而ROC曲线使用的时候有两个前提:
- 分类的类型:必须为数值型。
- 只针对二分类问题。
因此这里首先进行二分类,对Iris数据集的分类结果改为0
和1
两类,前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
以及即将要讨论的ROC
和AUC
。接下来分别对这五个指标进行分析,可以采用下图的计算方式利用已得到的混淆矩阵进行计算,也可以直接调用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曲线是在Precision
和Recall
的基础上绘制的曲线,分类算法对样本进行分类时,都会得出一个置信度,即该样本是正样本的概率,通过置信度可以对所有的样本进行排序,再逐个选择阈值,在阈值之前的都属于正例,在阈值之后的都属于负例。每个样本作为划分阈值时都会计算相应的精确率和召回率,由此就可以绘出分类模型的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曲线越向右上角凸也就意味着该模型越好。
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()
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)——分类模型评估与调参总结(下)