性能测量
评估分类器比评估回归器要困难得多,因此本章将用很多篇幅来讨论这个主题,同时会涉及许多性能考核的方法。
使用交叉验证测量准确率
交叉验证是一个评估模型的好办法。
相比于Scikit-Learn
提供cross_val_score()
这一类交叉验证的函数,有时你可能希望自己能控制得多一些。在这种情况下,你可以自行实现交叉验证,操作也简单明了。下面这段代码与前面的cross_val_score()
大致相同,并打印出相同的结果:
代码实现交叉验证
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone
skfolds = stratifiedKFold(n_splits = 3,shuffle = True ,random_state =42)
for train_index ,test_index in skfolds.split(X_train , y_train_5):
clone_clf = clone(sgd_clf) #这里的clone()函数可以理解为克隆的意思吧,复制
x_train_folds = X_train.iloc[train_index]
y_train_folds = y_train_5[train_index]
x_test_fold = x_test.iloc[test_index]
y_test_fold = y_test[test_index]
clone_clf.fit(x_train_folds,y_train_folds)
y_pred = clone_clf.predict(x_test_fold)
n_correct = sum(y_pred == y_test_fold)
print(n_correct / len(y_pred))
stratifiedKFold
可以看作一个将原数集分为测试集,和训练集的函数。
n_splits
:分成多少折。
这里我们可以看看train_index
处理后的训练集的索引
print(train_index)
结果如下:
[ 2 3 5 ... 59992 59996 59997]
[ 0 1 3 ... 59997 59998 59999]
[ 0 1 2 ... 59995 59998 59999]
得到一个二维数组。这便是StratifiedKFold
让我们来看看交叉验证的结果:
0.9669
0.91625
0.96785
每个折叠由StratifiedKFold
执行分层抽样产生。其所包含的各个类的比例符合整体比例。每个迭代会创建一个分类器的副本。用训练集对这个副本进行训练,然后用测试集进行预测。最后计算正确预测的次数,输出正确预测与预测总数的比例。
现在用cross_val_score()
函数来评估SGDClassfier
模型。采用K-折交叉验证法(三个折叠)。
K-折交叉验证的意思是将训练集分解成K个折叠,然后每次留其中1个折叠进行预测,剩余的折叠用来训练。
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf,X_train,y_train_5 , cv = 3,scoring ="accuracy")
结果如下:
array([0.9456 , 0.95195, 0.9509 ])
混淆矩阵
评估分类器性能的更好方法是混淆矩阵,其总体思路就是统计A类别实例被分成为B类别的次数。例如,要想知道分类器将数字3和数字5混淆多少次,只需要通过混淆矩阵的第5行第3列来查看。
要计算混淆矩阵,需要先有一组预测才能将其与实际目标进行比较。当然,可以通过测试集来进行预测,但是现在先不要动它(测试集最好留到项目的最后,准备启动分类器时再使用)。作为替代,可以使用cross_val_predict()
函数:
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf , x_train , t_train , cv = 3)
与cross_val_score()
函数一样,cross_val_predict()
函数同样执行K-折交叉验证,但返回的不是评估分数,而是每个折叠的预测。这意味着对于每个实例都可以得到一个干净的预测(“干净”的意思是模型预测时使用的数据在其训练期间从未见过)。
现在可以使用confusion_matrix()
函数来获取混淆矩阵了。只需要给出目标类别(y_train_5)
和预测类别(y_train_pred)
即可:
3折的结果是将每一折结果都组合到一起,并按照它们在原始训练集中的顺序重新排列,形成 y_train_pred
数组。最终得到的y_train_pred
与x_train
长度相同。
由上文的StratifiedKFold
可知,每一次迭代运算只获得一折预测值,三次则可得到三折预测值。
现在可以使用confusion_matrix()
函数来获取混淆矩阵了。只需要给出目标类别(y_train_5)
和预测类别(y_train_pred)
即可:
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5 , y_train_pred) #实际值在前 预测值在后。
结果如下:
array([[53892, 687], #实际负 即实际非5
[ 1891, 3530]]) #实际正,即实际5
#猜测负 #猜测正
左上表示两个负,右下表示两个正。687张被错误地分类成了“5”(假正类:错误的猜成正类);第二行表示所有“5”(正类)的图片中:1891张被错误地分为“非5”类别(假负类:错误的猜成负嘞),3530张被正确地分在了“5”这一类别(真正类)
一个完美的分类器只有真正类和真负类,所以它的混淆矩阵只会在其对角线(左上
到右下)上有非零值:
y_train_perfect_predictions = y_train_5
confusion_matrix(y_train_5 , y_train_perfect_predictions)
结果如下:
array([[54579, 0],
[ 0, 5421]])
混淆矩阵能提供大量信息,但有时你可能希望指标更简洁一些。正类预测的准确率是一个有意思的指标,它也称为分类器的精度,即猜的正类里猜中的有多少占比。
精度
=
T
P
T
P
+
F
P
精度 = \frac{TP}{TP+FP}
精度=TP+FPTP
TP是真正类的数量,FP是假正类的数量。
分类器会忽略这个正类实例之外的所有内容。因此,精度通常与另一个指标一起使用,这个指标就是召回率,即猜出的正类占总体正类的多少。
召回率
=
T
P
T
P
+
F
N
召回率 = \frac{TP}{TP+FN}
召回率=TP+FNTP
FN是假负类的数量。
精确率是分类正确的正样本 / 判定为正样本的总数;召回率是分类正确的正样本 / 真正正样本的总数。
混淆矩阵显示了真负(左上)、假正(右上)、假负(左下)和真正(右下)的示例
精度和召回率
Scikit-Learn
提供了计算多种分类器指标的函数,包括精度和召回率:
from sklearn.metrics import precision_score ,recall_score
precision_score(y_train_5 , y_train_pred) #3530 / (3530 + 687)
recall_score(y_train_5 , y_train_pred) #3530 / (3530+1891)
现在再看,这个5-检测器看起来并不像它的准确率那么光鲜亮眼了。当它说一张图片是5时,只有72.9%的概率是准确的,并且也只有75.6%的数字5被它检测出来了。
因此我们可以很方便地将精度和召回率组合成一个单一的指标,称为F1分数。当你需要一个简单的方法来比较两种分类器时,这是个非常不错的指标。F1分数是精度和召回率的谐波平均值。
F 1 = 2 1 精度 + 1 召回率 = 2 × 精度 × 召回率 精度 + 召回率 = T P T P + F P + F N 2 F_1 = \frac{2}{\frac{1}{精度}+\frac{1}{召回率}} = 2 \times \frac{精度\times 召回率}{精度+召回率} = \frac{TP}{TP+\frac{FP+FN}{2}} F1=精度1+召回率12=2×精度+召回率精度×召回率=TP+2FP+FNTP
要计算F1分数,只需要调用f1_score()
即可
from sklearn.metrics import f1_score
f1_score(y_train_5,y_train_pred)
# 0.7325171197343847
F1分数对那些具有相近的精度和召回率的分类器更为有利。这不一定能一直符合你的期望:在某些情况下,你更关心的是精度,而另一些情况下,你可能真正关心的是召回率。
遗憾的是,鱼和熊掌不可兼得,你不能同时增加精度又减少召回率,反之亦然。这称为精度/召回率权衡。