1.0 模型评价思想
与一般的产品质量检验类似,模型的评价就是对模型的功能——预测的好坏及稳健性进行评判,本文介绍的指标也是围绕这一目标进行的。
1.1 测试集与验证集
通常应准备两套数据集,一套用于对模型进行训练(拟合),即测试集,用于训练模型的预测效果;另一套数据用于检验,即验证集,用于验证模型的预测效果。
1.2 数据分割
通常所获得的数据是一套整体,因此需要对其进行分割。sklearn中有相应的方法可以直接使用:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=666)
其中,test_size是划分比例,即从数据集中抽取多少比例作为测试集,非必需参数,默认就是0.2,可根据需要调整。random_state是随机种子,非必需参数,给定random_state参数的好处在于便于调试,否则每运行一次train_test_split()数据集分割的结果都会改变。
1.3 数据分割思路
通常为提高模型效果的准确性和稳定性,仅采用一组测试集是不够的。为了充分使用数据集的价值,通常会将数据集分割为独立不同的5组(如前20%,20%~40%,40%~60%,60%~80%,后20%),每一组分别作为测试集(剩余数据作为验证集)完成一次测试训练和验证,共通过5次训练后所得的模型结果能够相对稳定。
1.4 随机数据集与有序数据集
有时候我们得到的数据集的标签向量的数据组织方式是不同的。一种是随机的,如sklearn中的digits数据集,其标签为array([0, 1, 2, ..., 8, 9, 8]),数值之间是随机排列的。这种数据直接使用train_test_split()进行数据分割即可。如需要划分为独立的若干组的话可以直接用切片器分割
另一种是序数化的,如sklearn中的iris数据集,其标签为array([0, 0, ... 0, 0, 1, 1... 1, 1, 2, 2 ... 2, 2]),数值是按照从小到大顺序排列的,此时若以此取20%的数据作为训练集或出现较大的偏差,因此需要先对数据做乱序处理,在进行数据分割。
#数据乱序方法一:打乱索引,然后按照乱序后的索引分割数据;便于重复操作,分割灵活性更高;
from sklearn import datasets
import numpy as np
iris = datasets.load_iris()
X = iris.data
y = iris.target
shuffle_index = np.random.permutation(len(X)) #permutation()生成从0到len(X)-1的乱序索引
test_size = int(len(shuffle_index) * 0.2)
train_index = shuffle_index[:test_size] #取前20%作为测试集的索引
test_index = shuffle_index[test_size:] #剩余作为验证集的索引
X_train = X[train_index]
X_test = X[test_index]
y_train = y[train_index]
y_test = y[test_index]
#数据乱序方法二:直接打乱数据
(包导入略,数据导入略)
temp = np.concatenate((X, y.reshape(-1,1)), axis=1) #concatenate()对X和y进行拼接,axis=1按列拼接
np.random.shuffle(tempConcat) #打乱数据集
shuffle_X,shuffle_y = np.split(tempConcat, [4], axis=1) #从打乱后的数据集中把特征向量和标签向量分开
X_train, X_test, y_train, y_test = train_test_split(shuffle_X, shuffle_y, test_ratio=0.2, random_state=666)
2.0 模型评价的指标
2.1 分类模型
2.1.1 分类准确度accuracy
准确度accuracy是最直接、最简单的评价指标,该评价指标直接对验证数据的预测标签与实际标签进行比较,统计结果相同的数量,即为准确量,而准确量的占验证数据总量的百分比即为准确率。
sklearn中的accuracy计算:
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=666)
kNN_clf = KNeighborsClassifier(n_neighbors=3)
kNN_clf.fit(X_train, y_train)
y_predict = kNN_clf.predict(X_test)
accuracy_score(y_test, y_predict) # 需要把预测结果y_predict输入
kNN.score(X_test, y_test) #无需y_predict
2.1.2 超参数及网格搜索
在得到分类模型的准确性评分之后,该如何调整模型以使分数变得更高呢?这就需要超参数实现。
所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。常说的调参就是调的超参数,如kNN算法中的k和w。
与超参数相对的概念是模型参数,即算法学习训练过程中得到的参数,如回归模型中的回归系数,kNN中没有模型参数。
以kNN为例,kNN中包含两个超参数,一个是k,即指定的近邻数量;一个是w,即判断近邻重要性的权重指标,默认值为"uniform",即无论距离原因,所有近邻点的权重均一样,也可指定为"distance",即距离越近权重越高,具体的权重值为距离的倒数。当w设置为distance时,还有另一个参数p,就是距离的计算方式,距离的计算为明可夫斯基距离,默认情况下p=2,明可夫斯基距离即欧式距离,当p=1时明可夫斯基距离即为曼哈顿距离,也可设置为更高次。
网格搜索就是通过对不同的参数组合进行试验,最终得到使得模型准确性最高的那一组参数。
sklearn中的kNN的超参数和网格搜索GridSearch:
(数据导入、包导入略)
from sklearn.model_selection import GridSearchCV # 导入网格搜索包
param_search = [ # 超参数组合
{"weights" : ["uniform"], "n_neighbors" : [i for i in range(1,11)]},
{"weights" : ["distance"], "n_neighbors" : [i for i in range(1,11)], "p" : [i for i in range(1,6)]}
]
kNN_clf = KNeighborsClassifier()
grid_search = GridSearchCV(kNN_clf, param_search)
grid_search.fit(X_train, y_train)
grid_search.best_estimator_ # 查看测试集下的最佳参数组合
grid_search.best_score_ # 查看测试集下的最佳参数组合的准确性得分
kNN_clf = grid.search.best_estimator_ #用最佳参数构建分类器
kNN_clf.score(X_test, y_test) # 最佳参数组合的验证集得分
2.2 混淆矩阵与精准率,召回率
分类的准确度accuracy在评价分类算法时,会有很大的问题的,尤其是在遇到极度偏斜(Skewed Data)的数据,只使用分类准确度是不能衡量。这就需要混淆矩阵来判别。
2.2.1 混淆矩阵 confusion matrix
TN:实际为0,预测为0 (Negative),预测正确 (True)
FP:实际为0,预测为1 (Positive),预测错误 (False)
FN:实际为1,预测为0 (Negative),预测错误 (False)
TP:实际为1,预测为1 (Positive),预测正确 (True)
2.2.2 精准率precision 和 召回率recall
现在假设有1万人对某项疾病进行预测,对结果建立混淆矩阵如下:
依据上表的例子,计算精准率和召回率:
精准率presicion = TP/(TP+FP) = 8/(8+12) = 40%
召回率recall = TP/(TP+FN) = 8/(8+2) = 80%
精准率presicion,描述的是在所有预测值为1的里面,实际值也是1的比例。犹如射击的准度一样,开了20枪,有8枪命中目标。精准率的关注点在于“准”,也成为查准率。
召回率recall,描述的是在所有真实值为1的里面,预测值也是1的比例。犹如召回事件一样,所有的有问题的产品中,有多少被召回了。召回率的关注点在于“全”,也称为查全率。
从上表的例子来看,模型预测的准确度达到了99.86%,但从精准率和召回率的结果来看,高准确度却没有什么意义。
sklearn中的混淆矩阵、精准率和召回率:
from sklearn.metrics import confusion_matrix # 导入confusion_matrix包
confusion_matrix(y_test, y_predict)
from sklearn.metrics import precision_score # 导入presicion_score包
precision_score(y_test, y_predict)
from sklearn.metrics import recall_score #导入recall_score包
recall_score(y_test, y_log_predict)
2.3 F1 score
那么精准率和召回率这两个指标,对于同一个模型来说,到底应该关注哪一个?或者是如何综合考量这两个指标?下面回答这些问题。
首先,二者之间的选取,需要根据实际的需要来看。如前面所给的指标解释,精准率关注的就是“准”,召回率关注的是“全”,那么当“准”的重要性高于“全”时,则主要看精准率,如股票涨跌的预测、项目可行性的预测等,因为当预测能够有收益时,我们希望是抓住每一次的机会,从而得到真实的收益,此时全不全并不重要,因为不可能把所有上涨的股票或可行的项目都覆盖到。而在召回有问题的商品时,希望达到的是尽可能全面,因为一旦有问题商品未被召回,可能会引发更大的问题。
其次,当需要综合考虑这两个指标的时候,此时使用新的指标F1 score。均值一直是综合考虑若干个指标特征是的常规选择,这里F1 score就是精准率和召回率的调和平均数:
1/F1 score = 1/precision + 1/recall
sklearn中没有计算F1 score的直接方法,可以按照以上公式进行计算。
2.4 ROC和AUC
2.4.1 分类阈值
分类阈值,即设置判断样本为标签为1的阈值thr。功能上可以理解未及格线,及格线以上的为1,及格线以下的为0。
sklearn中在建立好机器学习模型后,会有一个decision_function()方法,返回各个样本的标签值或类别值(当阈值为及格线时,标签值就是考试分数):
decision_scores = reg.decision_function(X_test)
y_predict = np.array(decision_score >= 5, dtype='int') # 返回阈值为5时的分类结果
精准率和召回率这两个指标有内在的联系,并且相互冲突。precision随着阈值的增加而增加,recall随着阈值的增大而减小。如果某些场景需要precision,recall都保持在80%,可以通过调整阈值方找出合适的阈值。
2.4.2 TPR和FRP
TPR是指预测为1占实际就为1的比例,其实就是召回率,TPR = TP/(TP+FN)
FPR与TPR完全相反,是指预测为1占实际为0的比例(可以理解为判错率),FPR = FP/(FP+TN)
2.4.3 ROC曲线
那么,当阈值变化时,TPR和FPR会有不同的取值组合,将TPR(y轴)和FPR(x轴)取值组合会旨在二维坐标系中,所得到的曲线就是ROC(receiver operation characteristic curve)曲线。
从上面可知,TPR就是判断对的比例,FPR是判断错比例。从机器学习的角度来说,目标当然是判对的比例越高越好,判错的比例越低越好。但从ROC曲线的形式可以知道,TPR和FPR是正向关联的,当TPR提高的时候,FPR也不免被提高。
sklearn中的ROC:
(数据和包导入略)
from sklearn.metric import roc_curve
fprs, tprs, thrs = roc_curve(y_test, decision_score)
plt.plot(fprs, tprs)
plt.show()
对于ROC的解读其实很简单,模型的目标是TPR越高、FPR越低,因此ROC曲线距离左上角越近,证明模型效果越好。当两个模型比较时,若模型1的ROC曲线从左上方完全包含模型2,则可以断定模型1优于模型2。
很多时候两个模型的ROC曲线存在相交,无法直接判断哪个模型效果更好,此时可以计算曲线下的面积作为性能度量,即AUC。
2.4.4 AUC
AUC(Area Under Curve)就是ROC曲线向下与X轴之间的面积。
由于ROC曲线的X和Y轴的区间均是(0,1),因此AUC的面积同样是属于(0,1)。
当AUC = 1,是完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。AUC = 0.5,跟随机猜测一样,模型没有预测价值。AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
sklearn中的AUC:
from sklearn.metrics import roc_auc_score
roc_auc_score(y_test, decision_scores)