交叉验证

在数据抽样部分我们已经讨论过如何抽样来进行训练和验证.但这往往不够完全的评估一个模型的性能.

当评价估计器需要设置超参时,由于在训练集上,通过调整参数设置使估计器的性能达到了最佳状态;但在测试集上可能会出现过拟合的情况.此时,测试集上的信息反馈足以颠覆训练好的模型,评估的指标不再有效反映出模型的泛化性能.为了解决此类问题,因此还应该准备另一部分验证集,模型训练完成以后在验证集上对模型进行评估.当验证集上的评估实验比较成功时,在测试集上进行最后的评估.

然而,通过将原始数据分为3个数据集合,我们就大大减少了可用于模型学习的样本数量,并且得到的结果依赖于集合对(训练,验证)的随机选择.这个问题可以通过 交叉验证来解决.交叉验证仍需要测试集做最后的模型评估,但不再需要验证集.

基本算法

k-折交叉验证(K-Fold)

最基本的方法被称之为k-折交叉验证.k-折交叉验证将训练集划分为k个较小的集合,其验证流程是:

  1. 将k-1份训练集子集作为训练集训练模型

  2. 将剩余的1份训练集子集作为验证集用于模型验证获得一份验证集上的评分(也就是利用该数据集计算模型的性能指标,例如准确率)

k-折交叉验证得出的性能指标是循环计算中每个值的平均值.该方法虽然计算代价很高,但是它不会浪费太多的数据(如固定任意测试集的情况一样),在处理样本数据集较少的问题时比较有优势.一般来书说k会选择10

重复K-折交叉验证(RepeatedKFold)

重复K-Fold n次.在每次重复中产生不同的分割.

留一交叉验证 (LOO)

LeaveOneOut是一个简单的交叉验证.每个学习集都是通过除了一个样本以外的所有样本创建的,测试集是被留下的样本.因此,对于 n 个样本,我们有 n 个不同的训练集和 n 个不同的测试集.这种交叉验证程序不会浪费太多数据,因为只有一个样本是从训练集中删除掉的.

LOO潜在的用户选择模型应该权衡一些已知的警告.当与 k 折交叉验证进行比较时,可以从 n 样本中构建 n 模型,而不是 k 模型,其中$n>k$.此外,每个在$n-1$个样本而不是在$(k-1)n/k$ 上进行训练.在两种方式中,假设 k 不是太大,并且$k<n$,LOO 比 k 折交叉验证计算开销更加昂贵.

就精度而言,LOO经常导致较高的方差作为测试误差的估计器.直观地说,因为n个样本中的n - 1被用来构建每个模型,折叠构建的模型实际上是相同的,并且是从整个训练集建立的模型.

但是,如果学习曲线对于所讨论的训练大小是陡峭的,那么 5- 或 10- 折交叉验证可以泛化误差增高.

作为一般规则,大多数作者和经验证据表明, 5- 或者 10- 交叉验证应该优于 LOO.

留P交叉验证 (LPO)

LeavePOut与LeaveOneOut非常相似,因为它通过从整个集合中删除p个样本来创建所有可能的训练/测试集.对于n个样本,这产生了$ {n \choose p} $个训练-测试 对.与LeaveOneOut和KFold不同,当$p>1$时,测试集会重叠.

随机排列交叉验证

ShuffleSplit迭代器将会生成一个用户给定数量的独立的训练/测试数据划分.样例首先被打散然后划分为一对训练测试集合.

可以通过设定明确的 random_state,使得伪随机生成器的结果可以重复.

ShuffleSplit可以替代KFold交叉验证,因为其提供了细致的训练/测试划分的数量和样例所占的比例等的控制.

时间序列分割

时间序列分割是 k-fold 的一个变体,它首先返回 k 折作为训练数据集,并且(k+1)折作为测试数据集.请注意,与标准的交叉验证方法不同,连续的训练集是超越前者的超集.另外,它将所有的剩余数据添加到第一个训练分区,它总是用来训练模型.

这个类可以用来交叉验证以固定时间间隔观察到的时间序列数据样本.

数据分层

一些分类问题在目标类别的分布上可能表现出很大的不平衡性:例如,可能会出现比正样本多数倍的负样本.在这种情况下,建议采用分层抽样方法,确保相对的类别频率在每个训练和验证折叠中大致保留。

分层 k 折

分层 k 折是 k-fold 的变种,会返回分层的折叠--每个小集合中,各个类别的样例比例大致和完整数据集中相同.

层随机排列交叉验证

分层随机排列交叉验证是随机排列交叉验证的一个变种,返回直接的划分,类似分层k折和k折的关系

用于分组数据

如果潜在的生成过程产生依赖样本的分组,那么我们就应该以这个分组来作为拆分数据的依据.

这样的数据分组是特定领域的.比如从多个患者收集医学数据,从每个患者身上采集多个样本.而这样的数据很可能取决于个人群体.在例子中,每个样本的患者ID将是其组标识符.

在这种情况下,我们想知道在一组特定的组上训练的模型是否能很好地适用于看不见的组.为了衡量这一点,我们需要确保验证对象中的所有样本来自配对训练折叠中完全没有表示的组.

sklearn中的交叉验证接口

与交叉验证相关的接口包括:

数据集交叉验证拆分接口

  • model_selection.GroupKFold([n_splits])|分组K-叠交叉验证
  • model_selection.GroupShuffleSplit([…])|分组随机排列交叉验证迭代器
  • model_selection.KFold([n_splits, shuffle, …])|K叠交叉验证
  • model_selection.LeaveOneGroupOut()|分组Leave One交叉验证
  • model_selection.LeavePGroupsOut(n_groups)|分组Leave P交叉验证
  • model_selection.LeaveOneOut()|Leave-One-Out交叉验证
  • model_selection.LeavePOut(p)|Leave-P-Out交叉验证
  • model_selection.PredefinedSplit(test_fold)|预定义交叉验证
  • model_selection.RepeatedKFold([n_splits, …])|重复K叠交叉验证
  • model_selection.RepeatedStratifiedKFold([…])|重复分层K折叠交叉验证
  • model_selection.ShuffleSplit([n_splits, …])|随机排列交叉验证
  • model_selection.StratifiedKFold([n_splits, …])|分层K折交叉验证
  • model_selection.StratifiedShuffleSplit([…])|分层随机排列交叉验证
  • model_selection.TimeSeriesSplit([n_splits, …])|时间序列交叉验证

模型验证器接口

  • model_selection.cross_validate(estimator, X)|通过交叉验证来评估度量,并记录适合度/评分时间
  • model_selection.cross_val_predict(estimator, X)|为每个输入数据点生成交叉验证估计
  • model_selection.cross_val_score(estimator, X)|通过交叉验证评估分数
  • model_selection.learning_curve(estimator, X, y)|学习曲线
  • model_selection.permutation_test_score(…)|用排列评估交叉验证得分的重要性
  • model_selection.validation_curve(estimator, …)|验证曲线

例:对iris数据交叉验证svm模型

最基础的交叉验证,只定义分成5块


In [15]:
import numpy as np
from sklearn import datasets
from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_val_predict
from sklearn import metrics

In [10]:
iris = datasets.load_iris()

In [11]:
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)
scores


Out[11]:
array([ 0.96666667,  1.        ,  0.96666667,  0.96666667,  1.        ])

In [12]:
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))


Accuracy: 0.98 (+/- 0.03)

通过交叉验证获取预测


In [14]:
predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)

In [16]:
metrics.accuracy_score(iris.target, predicted)


Out[16]:
0.97333333333333338

指定特定的交叉验证方法


In [17]:
from sklearn.model_selection import KFold

In [21]:
kf = KFold(n_splits=20)

In [22]:
scores = cross_val_score(clf, iris.data, iris.target, cv=kf)

In [23]:
scores


Out[23]:
array([ 1.        ,  1.        ,  1.        ,  1.        ,  1.        ,
        1.        ,  1.        ,  1.        ,  0.875     ,  0.875     ,
        0.85714286,  1.        ,  1.        ,  1.        ,  1.        ,
        1.        ,  1.        ,  0.85714286,  1.        ,  1.        ])