第五章 模型评价方法
5.1 模型的评价方法介绍
5.1.1~5 accuracy,precision,recall,F1-score,ROC曲线
分别画图举例,要说出应用场景,例如什么情况用什么评价标准。
- 混淆矩阵
- accuracy(准确率)
- precision(精准度),recall(召回率),F1-score(宏观,微观)
- ROC曲线图
python代码实现,例子。
(有视频参考)
5.2 项目实例运用
模型评价方法介绍
为了对模型的效果进行评估,需要好的评估方法,还需要衡量模型泛化能力的评价标准。评价指标是机器学习任务中非常重要的一环。不同的机器学习任务有着不同的评价指标,同时同一种机器学习任务也有着不同的评价指标,每个指标的着重点不一样。如分类(classification)、回归(regression)、排序(ranking)、聚类(clustering)、热门主题模型(topic modeling)、推荐(recommendation)等。并且很多指标可以对多种不同的机器学习模型进行评价,如精确率-召回率(precision-recall),可以用在分类、推荐、排序等中。像分类、回归、排序都是监督式机器学习。
不同的机器学习任务有着不同的性能评价指标。例如,在垃圾邮件检测系统中,这个系统本质上是一个二分类问题(区分垃圾邮件vs正常邮件),可以使用准确率(Accuracy)、对数损失函数(log-loss)、AUC等评价方法。在股票预测中,这其实是一个实数序列数据预测问题,可以使用平方根误差(root mean square error, RMSE)等指标;又如在搜索引擎中进行与查询相关的项目排序中,可以使用精确率-召回率(precision-recall)等等。
在对比不同模型的能力时,使用不同的评价指标可能会导致不同的结果,这就说明:模型的好坏是相对的,好的模型不仅仅取决于数据和算法,还取决于场景需求。
本章主要介绍分类学习中的常用的评价指标。
5.1.1 准确率
这是分类任务常见的评价标准,定义如下:准确率又称查准率(Precision),为分类任务中分类正确的样本数在总样本数中所占比例,错误率为分类错误的样本数在总样本中所占比例。准确率的计算方法为:
简单来说,在这样的一个应用场景中,某个社团有男的和女的两类,分类器根据自己的判断,将社团中的人分为男女两类。准确率需要得到的是该分类器判断正确的人占总人数的比例。显然,我们可以得到:假设分类器对100人的性别进行判断,而其中60人的性别判定正确,则该分类器的准确率就是60 %(60 / 100)。
其计算代码为:
import numpy as np
from sklearn.metrics import accuracy_score
y_pred = [0, 2, 1, 3]
y_true = [0, 1, 2, 3]
accuracy_score(y_true, y_pred)
输出为:
0.5
根据准确率这一评价方法,我们可以在一些场景中得到一个分类器是否有效,但它并不总是能有效的评价一个分类器的工作。这样的度量错误掩盖了样例如何被分错的事实。例如,在正负样本不平衡的情况下,准确率这个评价指标有很大的缺陷。比如在互联网广告里面,点击的数量是很少的,一般只有千分之几,如果用Accuracy,即使全部预测成负类(不点击)Accuracy可以达到99% 以上,这样可以完爆其它很多分类器辛辛苦苦算的值,但是这个算法显然不是被需求所期待的,那怎么解决呢?这就是precision、recall和f1-score的出现的原因了。
5.1.2 精度
准确率和错误率虽然常见,但是不一定能很好的满足需求。精度(presicion)又可以称为查准率。其中精度是检索出相关文档数与检索出的文档总数的比率,衡量的是检索系统的查准率。直观地来理解,精度是分类器的标签不能被标记为正的样本为负的能力。
对于一个二分类问题,可以将训练集的真实类别与模型预测得到的类别组合,得到以下四种类型:TP(True Positive),TN(True Nagetive),FP(False Positive),FN(False Nagetive)。所有的训练集中的样例都可以被分为这四种类型,组成一个混淆矩阵。
混淆矩阵(Confusion Matrix)是一个普遍适用的工具,它可以帮助我们更好地了解分类中的错误。混淆矩阵也称误差矩阵,是表示精度评价的一种标准格式,用n行n列的矩阵形式来表示,是对有监督学习分类算法准确率进行评估的工具。通过将模型预测的数据与测试数据进行对比,使用准确率,覆盖率和命中率等指标对模型的分类效果进行度量。 混淆矩阵是一个N X N矩阵,N为分类的个数。假如我们面对的是一个二分类问题,也就是N=2,我们就得到一个2 X 2矩阵,如下表所示。
真实类别/预测类别 | 正例 | 负例 |
---|---|---|
正例 | TP | FP |
负例 | FN | FN |
混淆矩阵中,各个内容的含义如下:
- True Positive简称TP,表示测试集中是Positive,模型预测结果是Positive的数据条目,即将正例判断为正例。
- False Positive简称FP,表示测试集中是Negative,模型预测结果是Positive的数据条目。
- False Negative简称FN,表示测试集中是Positive,模型预测结果是Negative的数据条目。
- True Negative简称TN,表示测试集中是Negative,模型预测结果是Negative的数据条目。
对于一个m分的标准分类问题来说,也可以定义如上表所示m×m的m分混淆矩阵和每一个类属的Recall、Precision、F-measure和Accuracy值。
从混淆矩阵中,可以衍生出各种评价的指标。(图片from维基百科)
混淆矩阵的代码实现如下:
from sklearn.metrics import confusion_matrix
y_true = [2, 0, 2, 2, 0, 1]
y_pred = [0, 0, 2, 2, 0, 2]
confusion_matrix(y_true, y_pred)
对于二分类问题( binary problems ),我们可以得到 true negatives(真 negatives), false positives(假 positives), false negatives(假 negatives) 和 true positives(真 positives) 的数量如下:
array([[2, 0, 0],
[0, 0, 1],
[1, 0, 2]])
5.1.3 召回
召回率又称查全率(Recall),召回率是指分类器分类正确的正样本个数占所有的正样本个数的比例。直观的理解,召回率是指分类器查找所有正样本的能力。举例来说,召回率是指检索出的相关文档数和文档库中所有的相关文档数的比率,衡量的是检索系统的查全率。
计算代码为:
from sklearn.metrics import recall_score
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]
recall_score(y_true, y_pred, average='macro')
输出为:
0.33...
精度和召回往往是一对矛盾的变量。一般来说,当精度高时,召回率会偏低;而当召回率高时,精度会偏低。如果按照预测为正例的概率大小进行排序,按照顺序依次进行预测,得到精度和召回,可以绘制一条P-R曲线。P-R图可以直观地展示样本整体的精度和召回的情况。
构造一个高正确率或高召回率的分类器是很容易的,但是很难保证两者同时成立。如果将所有的样例都判为正例,那么召回率达到100%同时正确率很低。构建一个同时使正确率和召回率最大的分类器是具有挑战性的。
5.1.4 F1
为了综合考虑精度和召回,也常常用F1度量。F1-score基于精度和召回的调和平均来定义,它的值更接近于Precision与Recall中较小的值。其计算方法为:
其中,P为准确率,R为召回率。精确率和准确率都高的情况下,F1值也会高。P和R指标有时候会出现的矛盾的情况,这样就需要综合考虑他们。F1-score值达到其最佳值 1 ,其最差分数为 0 。
除了F1之外,还有一个更普遍的计算公式:
F- = (α^2+1) PR/(α^2)P + R)
也可以直接利用sklearn里的函数计算F1-score:
from sklearn.metrics import f1_score
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]
f1_score(y_true, y_pred, average='macro')
输出为:
0.26666666666666666
公式里的α(α>0)是用于衡量查全率对查准率的相对重要性。当α=1时即为标准的F1(基于P、R的调和平均);当α>1时,查全率具有更大的影响;当α<1时查准率具有更大影响。
其中α=2和α=0.5是除了F1之外,两个常用的F-measure:
(1)当α=2,则表示recall的影响要大于precision;
(2)当α=0.5,则表示precision的影响要大于recall.
F1值在实际应用中较常用。相比于P、R的算术平均和几何平均(G-mean),F1值更重视较小值(不平衡数据下的稀有类),这也说明F1对于衡量数据更有利。
5.1.5 ROC
另一个用于分类模型的评价指标的工具是ROC曲线(ROC curve),ROC全称是受试者工作特征(receiver operating characteristic),它最早是在二战期间由电气工程师构建雷达系统时使用过,ROC曲线最早是运用在军事上,后来逐渐运用到医学领域,此后被引入机器学习领域中。ROC(Receiver Operating Characteristic)与P-R曲线类似,按照预测结果对样例进行排序,按照顺序依次进行预测,计算TPR和FPR,并分别以它为横纵坐标作图,这样就得到了ROC曲线。TPR和FPR分别定义为:
召回率TPR(True Positive Rate) =TP/(TP+FN) = a / (a+b)
取伪率FPR(False Positive Rate) = FP/(FP+TN) = c / (c+d)
在图中的ROC曲线中,有几条虚线和一条实线。图中的横轴是取伪率(FPR),Y 轴为召回率(TPR)。ROC曲线给出的是阈值变化时取伪率和召回率的变化情况,左下角的点所对应的是将所有样例判断为反例的情况,而右上角的点对应的则是将所有样例判断为正例的情况。虚线给出的是随机猜测的结果曲线。
取伪率和召回率这两个指标之间相互制约。通俗地来说,即在TPR随着FPR递增的情况下,谁增长得更快,快多少的问题。TPR增长得越快,曲线越往上屈,反映了模型的分类性能就越好。在理想的情况下,最佳的分类起应该尽可能的处于左上角,显而易见,最好的分类器便是FPR=0%,TPR=100%。这就意味着分类器在取伪率很低的同时获得了很高的召回率。例如在垃圾邮件的过滤中,这就相当于过滤掉了所有垃圾邮件,且没有将任何正常邮件误分类为垃圾邮件。但是一般在实践中一个分类器很难会有这么好的效果,即一般TPR不等于1,FPR不等于0的。当正负样本不平衡时,这种模型评价方式比起一般的精确度评价方式的好处尤其显著。
如何描绘ROC曲线?如在二分类中,我们需要设定一个阈值,大于阈值分类正类,否则分为负类。因此,我们可以变化阈值,根据不同的阈值进行分类,根据分类结果计算得到ROC空间中的一些点,连接这些点就形成ROC曲线。ROC曲线会经过(0,0)与(1,1)这两点,实际上这两点的连线形成的ROC代表一个随机分类器,一般情况下分类器的ROC曲线会在这条对角连线上方。
实际上,通过有限实例产生的ROC曲线实际上是一个阶梯函数,该曲线近似于实例数量接近无限时对应的ROC曲线。对模型进行评价时,若某ROC曲线可以将另一条ROC曲线完全包裹,则可以说明效果要好于被包裹的ROC曲线,否则,若两条ROC曲线存在交叉,则很难评价哪一条曲线更优。
ROC曲线有个很好的特性:当测试集中的正负样本的分布变化的时候,ROC曲线能够保持不变。在实际的数据集中经常会出现类不平衡(class imbalance)现象,即负样本比正样本多很多(或者相反),而且测试数据中的正负样本的分布也可能随着时间变化。与F1 score 的指标相比, ROC 不需要优化每个标签的阈值。
ROC曲线是通过与参照线进行比较来判断模型的好坏,虽然很直观好用,但这只是一种直觉上的定性分析,当使用ROC曲线对分类器进行评价时,如果对多个分类器进行比较时,如果直接使用ROC曲线很难去比较,只能通过将ROC分别画出来,然后进行肉眼比较,那么这种方法是非常不便的,因此我们需要一种定量的指标去比较,这个指标便是AUC了,即ROC曲线下的面积,面积越大,分类器的效果越好,AUC的值介于0.5到1.0之间。
AUC(area under curve)即ROC曲线下的面积。其含义是随机给定一个正样本和一个负样本,分类器输出该正样本为正的那个概率值比分类器输出该负样本为正的那个概率值要大的可能性。
假设分类器的输出是样本属于正类的socre(置信度),则AUC的物理意义为,任取一对(正、负)样本,正样本的score大于负样本的score的概率。为了计算AUC,我们通常是对ROC曲线中的多个小矩形的面积进行累加。
计算AUC可以直接调用sklearn:
import numpy as np
from sklearn import metrics
y = np.array([1,1,2,2])
pred = np.array([0.1,0.4,0.35,0.8])
fpr,tpr,thresholds = metrics.roc_curve(y,pred,pos_label=2)
metrics.auc(fpr,tpr)
可得计算结果:
0.75
通常,AUC的值介于0.5到1.0之间,较大的AUC代表了较好的performance。一个完美的分类器的AUC为1.0。
当0.5
ROC曲线和AUC的优势:不受类分布的影响,适合与评估、比较类分布不平衡的数据集。因此ROC曲线与AUC已被广泛用于医疗决策制定、模式识别和数据挖掘等领域。但是ROC和AUC仅适合于两类问题 ,对多类问题 ,无法直接应用。
基尼系数应大于60%,就算好模型。基尼系数经常用于分类问题,其可以直接从AUC中得到。其公式为:Gini = 2*AUC - 1
5.1.6 回归模型评价指标
回归是对连续的实数值进行预测,即输出值是连续的实数值,而分类中是离散值。常用的回归模型的评价指标主要有均方误差根(RMSE)和R-平方(R2)。
RMSE(root mean square error),其又被称为RMSD(root mean square deviation),是预测值与真实值的误差平方根的均值。其定义如下:
其中,yi是真实值,yi^是预测值,n是样本数量,使用了欧式距离。
RMSE的缺点是:对异常点较敏感,如果回归器对某个点的回归值很不理性,那么它的误差则较大,从而会对RMSE的值有较大影响,即平均值是非Robust的。
其python实现代码如下:
import math
def rmse(y_test, y):
return math.sqrt(math.mean((y_test - y) ** 2))
R2_score是多元回归中的回归平方和占总平方和的比例,它是度量多元回归方程中拟合程度的一个统计量,反映了在因变量y的变差中被估计的回归方程所解释的比例。其定义是:
其区间通常在(0,1)之间。0表示不如均值。1表示完美预测。R平方越接近1,表明回归平方和占总平方和的比例越大,回归线与各观测点越接近,用x的变化来解释y值变差的部分就越多,回归的拟合程度就越好。
def R2(y_test, y_true):
return 1 - ((y_test - y_true)**2).sum() / ((y_true - y_true.mean())**2).sum()
5.2 应用
评价方法在很多python包中都有已经写好的函数,以下是Scikit-learn中对应的函数名称以及描述:
指标 | 描述 | Scikit-learn函数 |
---|---|---|
Precision | 精准度 | from sklearn.metrics import precision_score |
Recall | 召回率 | from sklearn.metrics import recall_score |
F1 | F1值 | from sklearn.metrics import f1_score |
Confusion Matrix | 混淆矩阵 | from sklearn.metrics import confusion_matrix |
ROC | ROC曲线 | from sklearn.metrics import roc |
AUC | ROC曲线下的面积 | from sklearn.metrics import auc |
Scikit-learn 还允许在 GridSearchCV, RandomizedSearchCV 和 cross_validate 中评估 multiple metric (多个指数)。
接下来我们将对分类模型评价方法进行一个简单的应用。
5.2.1 Iris多分类
此案例选取的是著名的Iris数据集,建立了一个分类模型,该模型绘制了ROC曲线,运用了AUC评价指标。同时,为了更好地对其他评价指标进行学习,我们也在此模型中对其他评价指标进行了练习。
所使用的数据集是Iris数据集,这是机器学习中常用的分类实验数据集,由Fisher在1936年收集整理。Iris也称鸢尾花卉数据集,包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
先导入iris数据集,可以直接从sklearn里的datasets中加载。
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
print X
print y
若是将X,y打印出来,可以看到X是一组二维的数组,而y是一维数组,且y中包含着三种标签。输出为:
X = array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2],
[5.4, 3.9, 1.7, 0.4],
[4.6, 3.4, 1.4, 0.3],
[5. , 3.4, 1.5, 0.2],
[4.4, 2.9, 1.4, 0.2],
[4.9, 3.1, 1.5, 0.1],
[5.4, 3.7, 1.5, 0.2],
[4.8, 3.4, 1.6, 0.2],
[4.8, 3. , 1.4, 0.1]
……
y = array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
我们在加载数据集之后,加入一些随机数来增加训练集中的噪声以增加分类难度。
import numpy as np
random_state = np.random.RandomState(1)
n_samples, n_features = X.shape
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)]
对训练集与测试集进行分割,并且打乱。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3,random_state=0)
我们构建一个简单的分类器,即svm,对鸢尾花的特征来进行分类。先利用train_test_split切割训练集和测试集并打乱排序,将分类器预测的标签记为y_score。
from sklearn import svm
from sklearn.multiclass import OneVsRestClassifier
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True,random_state=random_state))
y_score = classifier.fit(X_train, y_train).decision_function(X_test)
y_pred = classifier.predict(X_test)
接下来就可以对预测所得到的结果进行评价,我们依次计算这个分类结果的准确率、召回率、F1-score、AUC,并且绘制ROC曲线。先计算模型准确率,accuracy_score这个函数可以帮助我们计算,只需要输入y的真实标签的预测标签两个变量即可:
from sklearn.metrics import accuracy_score
y_pred = svm.predict(X_test)
print ('accuracy is %s'%accuracy_score(y_true, y_pred))
输出为:
accuracy is 0.3111111111111111
计算模型召回率、F1-score和AUC,召回率和F1-score的计算都很简单,与准确率的计算类似,但是AUC需要先计算取伪率和召回率。
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
print('precision is %s'%svm.score(X_train, y_train))
print ('recall is %s'%recall_score(y_true, y_pred, average='macro'))
print ('F1-score is %s'%f1_score(y_true, y_pred, average='macro') )
# 计算ROC曲线,并且为每一个分类计算AUC
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
roc_auc[i] = auc(fpr[i], tpr[i])
print ('第%i种分类的roc:%f'%(i,roc_auc[i]))
输出为:
precision is 1.0
recall is 0.3737373737373737
F1-score is 0.4809941520467836
第0种分类的roc:0.952586
第1种分类的roc:0.598765
第2种分类的roc:0.775401
接下来绘制ROC曲线。figsize参数指绘图对象的宽度和高度,在循环中调用plt.plot是为了各个分类离的ROC曲线,其中,fpr和tpr即取伪率和召回率,分别是ROC曲线的横坐标和纵坐标,label为图例。第二次调用plt.plot是为了绘制随机猜测的结果曲线,便于对比ROC结果。xlabel和ylabel分别是横纵坐标名,title为图片名称。
import matplotlib.pyplot as plt
colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
for i, color in zip(range(n_classes), colors):
plt.plot(fpr[i], tpr[i], color=color, lw=lw,
label='ROC curve of class {0} (area = {1:0.2f})'
''.format(i, roc_auc[i]))
plt.plot([0, 1], [0, 1], 'k--', lw=lw)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Some extension of Receiver operating characteristic to multi-class')
plt.legend(loc="lower right")
plt.show()
结果如图,ROC曲线离虚线(即随机猜测的结果曲线)越远,则AUC越大。
最后附上完整代码
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
from sklearn import svm, datasets
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from scipy import interp
#import sklearn里计算模型评价指标的相关函数
# 导入iris数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 将标签二值化
y = label_binarize(y, classes=[0, 1, 2])
n_classes = y.shape[1]
#向数据集中加入噪声,增加分类的难度
random_state = np.random.RandomState(0)
n_samples, n_features = X.shape
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)]
# 将训练集和测试集打乱
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3,
random_state=0)
# 构造一个简单的svm分类器
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True,
random_state=random_state))
#通过decision_function()计算得到的y_score的值,用在roc_curve()函数中
y_score = classifier.fit(X_train, y_train).decision_function(X_test)
y_pred = classifier.predict(X_test)
# 计算准确率
print ('accuracy is %s'%accuracy_score(y_test, y_pred))
# 计算精度
print('precision is %s'%classifier.score(X_train, y_train))
# 计算召回率
print ('recall is %s'%recall_score(y_test, y_pred, average='macro'))
# 计算f1-score
print ('F1-score is %s'%f1_score(y_test, y_pred, average='macro') )
# 计算ROC曲线,并且为每一个分类计算AUC
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
# 计算取伪率和召回率
fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
# 计算auc的值
roc_auc[i] = auc(fpr[i], tpr[i])
print ('第%i种分类的roc:%f'%(i,roc_auc[i]))
# 绘制所有AUC曲线
colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
for i, color in zip(range(n_classes), colors):
plt.plot(fpr[i], tpr[i], color=color, lw=lw,
label='ROC curve of class {0} (area = {1:0.2f})'
''.format(i, roc_auc[i]))
plt.plot([0, 1], [0, 1], 'k--', lw=lw)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Some extension of Receiver operating characteristic to multi-class')
plt.legend(loc="lower right")
plt.show()
5.2.2 多类AdBoost决策树
此实例是通过随机生成的数据集,目的在于对两种迭代算法SAMME.R算法和SAMME算法进行比较,此处我们对于该模型的评价标准选取的是错误率,即1-准确率。
首先我们载入需要的类库:
from sklearn.externals.six.moves import zip
import matplotlib.pyplot as plt
from sklearn.datasets import make_gaussian_quantiles
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier
这次的数据集是通过随机生成一些随机数据,来做二元分类。make_gaussian_quantiles函数是通过采取多维标准正态分布和定义由嵌套同心多维球分离的类,生成分组多维正态分布的数据。我们共生成13000个样本,其中数据有10个样本特征,数据类别为3类。
X, y = make_gaussian_quantiles(n_samples=13000, n_features=10,
n_classes=3, random_state=1)
将数据分割成训练集和测试集。其中3000个为训练集样本,10000个为测试集样本。
n_split = 3000
X_train, X_test = X[:n_split], X[n_split:]
y_train, y_test = y[:n_split], y[n_split:]
Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器,然后把这些弱分类器集合起来,构成一个更强的最终分类器,即强分类器。AdaBoostClassifier是sklearn中一个AdaBoost的分类框架,除了AdaBoostClassifier,AdaBoostRegressor用于回归。
这里我们分别选择了SAMME.R算法和SAMME算法,最多600个弱分类器,学习率分别是1和1.5。SAMME.R 使用概率估计来更新模型,而SAMME仅仅使用分类。
训练模型并记录模型的错误率(1-准确率)。
bdt_real = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=2),
n_estimators=600,
learning_rate=1)
bdt_discrete = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=2),
n_estimators=600,
learning_rate=1.5,
algorithm="SAMME")
bdt_real.fit(X_train, y_train)
bdt_discrete.fit(X_train, y_train)
real_test_errors = []
discrete_test_errors = []
for real_test_predict, discrete_train_predict in zip(
bdt_real.staged_predict(X_test), bdt_discrete.staged_predict(X_test)):
real_test_errors.append(
1. - accuracy_score(real_test_predict, y_test))
discrete_test_errors.append(
1. - accuracy_score(discrete_train_predict, y_test))
n_trees_discrete = len(bdt_discrete)
n_trees_real = len(bdt_real)
discrete_estimator_errors = bdt_discrete.estimator_errors_[:n_trees_discrete]
real_estimator_errors = bdt_real.estimator_errors_[:n_trees_real]
discrete_estimator_weights = bdt_discrete.estimator_weights_[:n_trees_discrete]
分别绘制SAMME.R算法和SAMME算法关于测试集错误率,训练集错误率,以及权重关于树的个数的变化图。figsize参数为绘制图像的像素大小。
plt.figure(figsize=(15, 5))
plt.subplot(131)
plt.plot(range(1, n_trees_discrete + 1),
discrete_test_errors, c='black', label='SAMME')
plt.plot(range(1, n_trees_real + 1),
real_test_errors, c='black',
linestyle='dashed', label='SAMME.R')
plt.legend()
plt.ylim(0.18, 0.62)
plt.ylabel('Test Error')
plt.xlabel('Number of Trees')
plt.subplot(132)
plt.plot(range(1, n_trees_discrete + 1), discrete_estimator_errors,
"b", label='SAMME', alpha=.5)
plt.plot(range(1, n_trees_real + 1), real_estimator_errors,
"r", label='SAMME.R', alpha=.5)
plt.legend()
plt.ylabel('Error')
plt.xlabel('Number of Trees')
plt.ylim((.2,
max(real_estimator_errors.max(),
discrete_estimator_errors.max()) * 1.2))
plt.xlim((-20, len(bdt_discrete) + 20))
plt.subplot(133)
plt.plot(range(1, n_trees_discrete + 1), discrete_estimator_weights,
"b", label='SAMME')
plt.legend()
plt.ylabel('Weight')
plt.xlabel('Number of Trees')
plt.ylim((0, discrete_estimator_weights.max() * 1.2))
plt.xlim((-20, n_trees_discrete + 20))
# prevent overlapping y-axis labels
plt.subplots_adjust(wspace=0.25)
plt.show()
代码运行结果:
如图所示,SAMME.R算法收敛速度比SAMME更快,在较低的迭代次数下实现了较低的测试误差。每一个boosting迭代后的测试集上的每个算法的误差都显示在左边,每个树的测试集上的分类错误在中间显示,并且在右边显示每个树的提升权重。所有的树在SAMM.R算法中都有一个权重,因此没有示出。
5.3.3 模型概率校准
执行分类时,我们常常希望不仅可以预测类标签,还要获得相应标签的概率。这个概率可以给我们一些预测的信心。一些模型可以提供部分的概率估计,有些模型甚至不支持概率预测。校准模块可以帮助我们更好地校准给定模型的概率,或者添加对概率预测的支持。该模型综合考虑了精度,召回率以及F1-score。
首先导入模型所需的包。
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (brier_score_loss, precision_score, recall_score,
f1_score)
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.model_selection import train_test_split
实验在人工数据集上进行二分类,使用make_classification函数创建一个足够大的且特征较少的数据集,有100000个样本(其中有1000个样本用于模型训练),每个样本有20个样本特征,有10个冗余特征数,只有2个是具有信息的特征数。数据集利用train_test_split函数进行分割。
X, y = datasets.make_classification(n_samples=100000, n_features=20,
n_informative=2, n_redundant=10,
random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.99,
random_state=42)
绘制校准曲线。
def plot_calibration_curve(est, name, fig_index):
# Calibrated with isotonic calibration
isotonic = CalibratedClassifierCV(est, cv=2, method='isotonic')
# Calibrated with sigmoid calibration
sigmoid = CalibratedClassifierCV(est, cv=2, method='sigmoid')
# Logistic regression with no calibration as baseline
lr = LogisticRegression(C=1., solver='lbfgs')
fig = plt.figure(fig_index, figsize=(10, 10))
ax1 = plt.subplot2grid((3, 1), (0, 0), rowspan=2)
ax2 = plt.subplot2grid((3, 1), (2, 0))
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")
for clf, name in [(lr, 'Logistic'),
(est, name),
(isotonic, name + ' + Isotonic'),
(sigmoid, name + ' + Sigmoid')]:
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
if hasattr(clf, "predict_proba"):
prob_pos = clf.predict_proba(X_test)[:, 1]
else: # use decision function
prob_pos = clf.decision_function(X_test)
prob_pos = \
(prob_pos - prob_pos.min()) / (prob_pos.max() - prob_pos.min())
clf_score = brier_score_loss(y_test, prob_pos, pos_label=y.max())
print("%s:" % name)
print("\tBrier: %1.3f" % (clf_score))
print("\tPrecision: %1.3f" % precision_score(y_test, y_pred))
print("\tRecall: %1.3f" % recall_score(y_test, y_pred))
print("\tF1: %1.3f\n" % f1_score(y_test, y_pred))
fraction_of_positives, mean_predicted_value = \
calibration_curve(y_test, prob_pos, n_bins=10)
ax1.plot(mean_predicted_value, fraction_of_positives, "s-",
label="%s (%1.3f)" % (name, clf_score))
ax2.hist(prob_pos, range=(0, 1), bins=10, label=name,
histtype="step", lw=2)
ax1.set_ylabel("Fraction of positives")
ax1.set_ylim([-0.05, 1.05])
ax1.legend(loc="lower right")
ax1.set_title('Calibration plots (reliability curve)')
ax2.set_xlabel("Mean predicted value")
ax2.set_ylabel("Count")
ax2.legend(loc="upper center", ncol=2)
plt.tight_layout()
# Plot calibration curve for Gaussian Naive Bayes
plot_calibration_curve(GaussianNB(), "Naive Bayes", 1)
# Plot calibration curve for Linear SVC
plot_calibration_curve(LinearSVC(), "SVC", 2)
plt.show()
各个模型输出结果为:
Logistic:
Brier: 0.099
Precision: 0.872
Recall: 0.851
F1: 0.862
Naive Bayes:
Brier: 0.118
Precision: 0.857
Recall: 0.876
F1: 0.867
Naive Bayes + Isotonic:
Brier: 0.098
Precision: 0.883
Recall: 0.836
F1: 0.859
Naive Bayes + Sigmoid:
Brier: 0.109
Precision: 0.861
Recall: 0.871
F1: 0.866
Logistic:
Brier: 0.099
Precision: 0.872
Recall: 0.851
F1: 0.862
SVC:
Brier: 0.163
Precision: 0.872
Recall: 0.852
F1: 0.862
SVC + Isotonic:
Brier: 0.100
Precision: 0.853
Recall: 0.878
F1: 0.865
SVC + Sigmoid:
Brier: 0.099
Precision: 0.874
Recall: 0.849
F1: 0.861
第一个图显示了用Logistic回归、高斯朴素贝叶斯和高斯朴素贝叶斯得到的估计概率,同时具有等渗校正和乙状结肠校正。这里可以观察到,逻辑回归被很好地校准,因为其曲线几乎是对角线。可以看出,高斯朴素贝叶斯的表现非常差,但是以线性 SVC 的方式也是如此. 尽管线性 SVC 显示了 sigmoid 校准曲线,但高斯朴素贝叶斯校准曲线具有转置的 sigmoid 结构。在这种情况下,分类器的过度自信是由违反朴素贝叶斯特征独立假设的冗余特征引起的。
第二个图显示了线性支持向量分类器(线性SVC)的校准曲线。线性SVC显示了与高斯朴素贝叶斯相反的行为:线性 SVC 的校准曲线或可靠性图具有 sigmoid 曲线,这是一个典型的不够自信的分类器。在 LinearSVC 的情况下,这是 hinge loss 的边缘属性引起的,这使得模型集中在靠近决策边界(支持向量)的 hard samples(硬样本)上。这两种校准都可以解决这个问题,并产生几乎相同的结果。下图显示了高斯朴素贝叶斯在相同数据上的校准曲线,具有两种校准,也没有校准。