3.1. 交叉验证:评估估计器性能#
学习预测函数的参数并在相同的数据上测试它是一个方法上的错误:一个模型只会重复它刚刚看到的样本的标签,它会有一个完美的分数,但无法预测任何有用的数据。这种情况被称为 overfitting .为了避免这种情况,在执行(监督)机器学习实验时,常见的做法是将部分可用数据保存为 test set X_test, y_test
.请注意,“实验”一词并不旨在仅表示学术用途,因为即使在商业环境中,机器学习通常也是从实验开始的。以下是模型训练中典型交叉验证工作流程图。最佳参数可以通过以下方式确定 grid search 技术.

在scikit-learn中,可以使用 train_test_split
助手功能。让我们加载虹膜数据集,以适应线性支持向量机::
>>> import numpy as np
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import datasets
>>> from sklearn import svm
>>> X, y = datasets.load_iris(return_X_y=True)
>>> X.shape, y.shape
((150, 4), (150,))
我们现在可以快速采样训练集,同时保留40%的数据用于测试(评估)我们的分类器::
>>> X_train, X_test, y_train, y_test = train_test_split(
... X, y, test_size=0.4, random_state=0)
>>> X_train.shape, y_train.shape
((90, 4), (90,))
>>> X_test.shape, y_test.shape
((60, 4), (60,))
>>> clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.96
当评估估计器的不同设置(“超参数”)时,例如 C
必须手动为支持工具设置的设置,仍然存在过度匹配的风险 on the test set 因为可以调整参数,直到估计器表现最佳。这样,有关测试集的知识就可以“泄露”到模型中,并且评估指标不再报告概括性能。为了解决这个问题,数据集的另一部分可以被称为所谓的“验证集”:在训练集上进行训练,之后对验证集进行评估,当实验似乎成功时,可以对测试集进行最终评估。
然而,通过将可用数据分为三组,我们大幅减少了可用于学习模型的样本数量,并且结果可能取决于对(训练、验证)集对的特定随机选择。
这个问题的解决方案是一个称为 cross-validation (CV简称)。测试集仍然应该被保留用于最终评估,但是在进行CV时不再需要验证集。在基本方法中,称为 k -fold CV,将训练集分为 k 较小的集合(下面描述了其他方法,但通常遵循相同的原则)。每个都遵循以下步骤 k “折叠”:
模型的训练使用 \(k-1\) 作为训练数据的褶皱;
the resulting model is validated on the remaining part of the data (i.e., it is used as a test set to compute a performance measure such as accuracy).
报告的绩效衡量标准 k -fold交叉验证是循环中计算的值的平均值。这种方法在计算上可能很昂贵,但不会浪费太多数据(就像修复任意验证集时的情况一样),这在样本数量非常小的逆推理等问题中是一个主要优势。

3.1.1. 计算交叉验证的指标#
使用交叉验证的最简单方法是调用 cross_val_score
估计器和数据集上的助手函数。
下面的示例演示了如何通过拆分数据、匹配模型并连续5次计算分数(每次具有不同的拆分)来估计线性核支持载体机在虹膜数据集上的准确性::
>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1, random_state=42)
>>> scores = cross_val_score(clf, X, y, cv=5)
>>> scores
array([0.96, 1. , 0.96, 0.96, 1. ])
因此,平均分和标准差由下式给出::
>>> print("%0.2f accuracy with a standard deviation of %0.2f" % (scores.mean(), scores.std()))
0.98 accuracy with a standard deviation of 0.02
默认情况下,每次CV迭代计算的分数是 score
估计者的方法。可以通过使用评分参数来更改这一点::
>>> from sklearn import metrics
>>> scores = cross_val_score(
... clf, X, y, cv=5, scoring='f1_macro')
>>> scores
array([0.96, 1., 0.96, 0.96, 1.])
看到 的 scoring 参数:定义模型评估规则 有关详细信息就Iris数据集而言,样本在目标类别之间是平衡的,因此准确性和F1得分几乎相等。
当 cv
参数是一个整数, cross_val_score
使用 KFold
或 StratifiedKFold
默认策略,如果估计器来自 ClassifierMixin
.
还可以通过传递交叉验证迭代器来使用其他交叉验证策略,例如::
>>> from sklearn.model_selection import ShuffleSplit
>>> n_samples = X.shape[0]
>>> cv = ShuffleSplit(n_splits=5, test_size=0.3, random_state=0)
>>> cross_val_score(clf, X, y, cv=cv)
array([0.977, 0.977, 1., 0.955, 1.])
另一种选择是使用可迭代的收益(训练、测试)拆分作为索引数组,例如::
>>> def custom_cv_2folds(X):
... n = X.shape[0]
... i = 1
... while i <= 2:
... idx = np.arange(n * (i - 1) / 2, n * i / 2, dtype=int)
... yield idx, idx
... i += 1
...
>>> custom_cv = custom_cv_2folds(X)
>>> cross_val_score(clf, X, y, cv=custom_cv)
array([1. , 0.973])
使用持有的数据进行数据转换#
正如在训练、预处理(如标准化、特征选择等)中保留的数据上测试预测器一样,和类似 data transformations 类似地,应该从训练集学习并应用于保存的数据以进行预测::
>>> from sklearn import preprocessing
>>> X_train, X_test, y_train, y_test = train_test_split(
... X, y, test_size=0.4, random_state=0)
>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> X_train_transformed = scaler.transform(X_train)
>>> clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
>>> X_test_transformed = scaler.transform(X_test)
>>> clf.score(X_test_transformed, y_test)
0.9333
A Pipeline
使构建估计器变得更容易,在交叉验证下提供此行为::
>>> from sklearn.pipeline import make_pipeline
>>> clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
>>> cross_val_score(clf, X, y, cv=cv)
array([0.977, 0.933, 0.955, 0.933, 0.977])
看到 管道和复合估计量 .
3.1.1.1. cross_validate函数和多指标评估#
的 cross_validate
功能与 cross_val_score
通过两种方式:
它允许指定多个指标进行评估。
除了测试分数之外,它还返回一个包含适合时间、得分时间(以及可选的训练分数、适合的估计量、训练测试分裂指数)的dict。
对于单一指标评估,其中评分参数是字符串、可调用或无,关键字将是- ['test_score', 'fit_time', 'score_time']
对于多指标计算,返回值是具有以下键的dict- ['test_<scorer1_name>', 'test_<scorer2_name>', 'test_<scorer...>', 'fit_time', 'score_time']
return_train_score
设置为 False
默认情况下可以节省计算时间。要评估训练集的分数,您需要将其设置为 True
.您还可以通过设置保留适合每个训练集的估计器 return_estimator=True
.同样,您可以设置 return_indices=True
保留用于将数据集拆分为每个简历拆分的训练和测试集的训练和测试指数。
多个指标可以指定为列表、数组或预定义评分者名称集::
>>> from sklearn.model_selection import cross_validate
>>> from sklearn.metrics import recall_score
>>> scoring = ['precision_macro', 'recall_macro']
>>> clf = svm.SVC(kernel='linear', C=1, random_state=0)
>>> scores = cross_validate(clf, X, y, scoring=scoring)
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']
>>> scores['test_recall_macro']
array([0.96, 1., 0.96, 0.96, 1.])
或者作为将评分者名称映射到预定义或自定义评分函数的dict::
>>> from sklearn.metrics import make_scorer
>>> scoring = {'prec_macro': 'precision_macro',
... 'rec_macro': make_scorer(recall_score, average='macro')}
>>> scores = cross_validate(clf, X, y, scoring=scoring,
... cv=5, return_train_score=True)
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_prec_macro', 'test_rec_macro',
'train_prec_macro', 'train_rec_macro']
>>> scores['train_rec_macro']
array([0.97, 0.97, 0.99, 0.98, 0.98])
Here is an example of cross_validate
using a single metric:
>>> scores = cross_validate(clf, X, y,
... scoring='precision_macro', cv=5,
... return_estimator=True)
>>> sorted(scores.keys())
['estimator', 'fit_time', 'score_time', 'test_score']
3.1.1.2. 通过交叉验证获得预测#
功能 cross_val_predict
具有类似的界面 cross_val_score
,但对于输入中的每个元素,返回该元素在测试集中时获得的预测。只能使用将所有元素恰好分配给测试集一次的交叉验证策略(否则,会引发异常)。
警告
关于cross_val_predict使用不当的注释
的结果 cross_val_predict
可能与使用 cross_val_score
因为元素以不同的方式分组。功能 cross_val_score
取交叉验证折叠的平均值,而 cross_val_predict
简单地返回来自几个不同模型的标签(或概率),没有区别。因此, cross_val_predict
不是概括误差的适当衡量标准。
- 功能
cross_val_predict
适合: 从不同模型获得的预测可视化。
模型混合:当使用一个监督估计器的预测来训练集成方法中的另一个估计器时。
可用的交叉验证迭代器将在下一节中介绍。
示例
sphx_glr_auto_examples_feature_selection_plot_rfe_with_cross_validation.py ,
绘制交叉验证预测 ,
3.1.2. 交叉验证迭代器#
以下部分列出了用于生成索引的实用程序,这些索引可用于根据不同的交叉验证策略生成数据集拆分。
3.1.2.1. i. d的交叉验证迭代器数据#
假设某些数据是独立且相同分布的(i.i.d.)假设所有样本都来自同一生成过程,并且生成过程被假设没有过去生成的样本的记忆。
在此类情况下可以使用以下交叉验证器。
备注
虽然i. i. d.数据是机器学习理论中的一个常见假设,但在实践中很少成立。如果知道样本是使用时间相关过程生成的,则使用 time-series aware cross-validation scheme .同样,如果我们知道生成过程具有群体结构(从不同的对象、实验、测量设备收集的样本),那么使用起来就更安全 group-wise cross-validation .
3.1.2.1.1. k折#
KFold
将所有样本分成 \(k\) 样本组,称为折叠(如果 \(k = n\) ,这相当于 Leave One Out 策略),大小相等(如果可能)。预测函数是使用 \(k - 1\) 折叠,遗漏的折叠用于测试。
对包含4个样本的数据集进行2重交叉验证的示例::
>>> import numpy as np
>>> from sklearn.model_selection import KFold
>>> X = ["a", "b", "c", "d"]
>>> kf = KFold(n_splits=2)
>>> for train, test in kf.split(X):
... print("%s %s" % (train, test))
[2 3] [0 1]
[0 1] [2 3]
下面是交叉验证行为的可视化。注意 KFold
不受班级或团体的影响。

每个折叠由两个数组组成:第一个与 training set ,第二个到 test set .因此,可以使用numpy索引创建训练/测试集::
>>> X = np.array([[0., 0.], [1., 1.], [-1., -1.], [2., 2.]])
>>> y = np.array([0, 1, 0, 1])
>>> X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]
3.1.2.1.2. 重复K折叠#
RepeatedKFold
重复 KFold
\(n\) 时间,在每次重复中产生不同的分裂。
2倍K折叠重复2次的示例::
>>> import numpy as np
>>> from sklearn.model_selection import RepeatedKFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> random_state = 12883823
>>> rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=random_state)
>>> for train, test in rkf.split(X):
... print("%s %s" % (train, test))
...
[2 3] [0 1]
[0 1] [2 3]
[0 2] [1 3]
[1 3] [0 2]
同样, RepeatedStratifiedKFold
重复 StratifiedKFold
\(n\) 每次重复中随机化不同的次数。
3.1.2.1.3. Leave One Out(LOO)#
LeaveOneOut
(or LOO)是一个简单的交叉验证。每个学习集都是通过取除一个样本之外的所有样本来创建的,测试集是遗漏的样本。因此 \(n\) 样品,我们有 \(n\) 不同的训练集和 \(n\) 不同的测试集。此交叉验证过程不会浪费太多数据,因为仅从训练集中删除一个样本::
>>> from sklearn.model_selection import LeaveOneOut
>>> X = [1, 2, 3, 4]
>>> loo = LeaveOneOut()
>>> for train, test in loo.split(X):
... print("%s %s" % (train, test))
[1 2 3] [0]
[0 2 3] [1]
[0 1 3] [2]
[0 1 2] [3]
LOO用于型号选择的潜在用户应该权衡一些已知的警告。较 \(k\) - 折叠交叉验证,一个构建 \(n\) 模型从 \(n\) 样品而不是 \(k\) 模型,其中 \(n > k\) .此外,每个人都接受过培训 \(n - 1\) 样品,而不是 \((k-1) n / k\) .在这两种情况下,假设 \(k\) 不是太大, \(k < n\) ,LOO的计算成本比 \(k\) - 折叠交叉验证。
就准确性而言,LOO作为测试误差的估计器通常会导致高方差。直觉,因为 \(n - 1\) 的 \(n\) 样本用于构建每个模型,从褶皱构建的模型几乎彼此相同,并且与从整个训练集构建的模型相同。
然而,如果所讨论的训练规模的学习曲线很陡,那么5或10倍交叉验证可能会高估概括误差。
一般来说,大多数作者和经验证据表明,5或10倍交叉验证应该优于LOO。
引用#
<http://www.faqs.org/faqs/ai-faq/neural-nets/part3/section-12.html>
_;T.哈斯蒂河蒂布希拉尼、J·弗里德曼、 The Elements of Statistical Learning ,Springer 2009
L.布莱曼,P·斯佩克特 Submodel selection and evaluation in regression: The X-random case 《1992年国际统计评论》;
R.科哈维, A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection ,国际。Jnt。会议AI
R.巴拉特·拉奥,G.冯河罗萨莱斯, On the Dangers of Cross-Validation. An Experimental Evaluation ,SIAM 2008;
G.詹姆斯,D.威滕,T.哈斯蒂河蒂布希拉尼, An Introduction to Statistical Learning ,Springer 2013。
3.1.2.1.4. Leave P Out(LPO)#
LeavePOut
非常类似于 LeaveOneOut
因为它通过删除所有可能的训练/测试集, \(p\) 整套的样本。为 \(n\) 样本,这产生 \({n \choose p}\) 训练测试对。不像 LeaveOneOut
和 KFold
,测试集将重叠 \(p > 1\) .
包含4个样本的数据集上的Leave-2-Out示例:
>>> from sklearn.model_selection import LeavePOut
>>> X = np.ones(4)
>>> lpo = LeavePOut(p=2)
>>> for train, test in lpo.split(X):
... print("%s %s" % (train, test))
[2 3] [0 1]
[1 3] [0 2]
[1 2] [0 3]
[0 3] [1 2]
[0 2] [1 3]
[0 1] [2 3]
3.1.2.1.5. 随机排列交叉验证,又名洗牌和拆分#
的 ShuffleSplit
迭代器将生成用户定义数量的独立训练/测试数据集拆分。样本首先被洗牌,然后分成一对训练集和测试集。
通过显式播种,可以控制结果重现性的随机性 random_state
伪随机数生成器。
以下是一个使用示例::
>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.arange(10)
>>> ss = ShuffleSplit(n_splits=5, test_size=0.25, random_state=0)
>>> for train_index, test_index in ss.split(X):
... print("%s %s" % (train_index, test_index))
[9 1 6 7 3 0 5] [2 8 4]
[2 9 8 0 6 7 4] [3 5 1]
[4 5 1 0 6 9 7] [2 3 8]
[2 7 5 8 0 3 4] [6 1 9]
[4 1 0 6 8 9 3] [5 2 7]
下面是交叉验证行为的可视化。注意 ShuffleSplit
不受班级或团体的影响。

ShuffleSplit
因此是一个很好的替代方案 KFold
交叉验证,允许更好地控制迭代次数和训练/测试分裂每一侧的样本比例。
3.1.2.2. 具有基于类标签分层的交叉验证迭代器#
一些分类任务自然会表现出罕见的类别:例如,负面观察结果可能比正面观察结果高出几个数量级(例如医学筛查、欺诈检测等)。因此,交叉验证拆分可以生成训练或验证折叠,而不会出现任何特定类。这通常会导致未定义的分类指标(例如ROC AUC),尝试调用时会出现异常 fit 或输出中缺少列 predict_proba
或 decision_function
在不同折叠上训练的多类分类器的方法。
为了缓解此类问题,诸如此类的拆分器 StratifiedKFold
和 StratifiedShuffleSplit
实施分层抽样,以确保每个折叠中大致保留相对类别频率。
备注
scikit-learn引入分层抽样是为了解决上述工程问题,而不是解决统计问题。
分层使交叉验证折叠更加均匀,因此隐藏了用有限数量的观察数据来匹配模型所固有的一些变异性。
因此,分层可以人为地缩小跨交叉验证迭代测量的指标的传播:折叠间变异性不再反映存在稀有类时分类器性能的不确定性。
3.1.2.2.1. 分层K折叠#
StratifiedKFold
的变型 K-fold 它返回 stratified 折叠:每个集合包含的每个目标类别的样本百分比与完整集合大致相同。
以下是对来自两个不平衡类别的50个样本的数据集进行分层3重交叉验证的示例。 我们显示每个类别中的样本数量并与 KFold
.
>>> from sklearn.model_selection import StratifiedKFold, KFold
>>> import numpy as np
>>> X, y = np.ones((50, 1)), np.hstack(([0] * 45, [1] * 5))
>>> skf = StratifiedKFold(n_splits=3)
>>> for train, test in skf.split(X, y):
... print('train - {} | test - {}'.format(
... np.bincount(y[train]), np.bincount(y[test])))
train - [30 3] | test - [15 2]
train - [30 3] | test - [15 2]
train - [30 4] | test - [15 1]
>>> kf = KFold(n_splits=3)
>>> for train, test in kf.split(X, y):
... print('train - {} | test - {}'.format(
... np.bincount(y[train]), np.bincount(y[test])))
train - [28 5] | test - [17]
train - [28 5] | test - [17]
train - [34] | test - [11 5]
我们可以看到 StratifiedKFold
在训练数据集和测试数据集中保持类比(约1 / 10)。
下面是交叉验证行为的可视化。

RepeatedStratifiedKFold
可用于重复分层K折叠n次,每次重复中的随机化不同。
3.1.2.2.2. 分层洗牌拆分#
StratifiedShuffleSplit
的变型 ShuffleSplit ,它返回分层分裂, i.e. 其通过为每个目标类保留与完整集合中相同的百分比来创建分割。
下面是交叉验证行为的可视化。

3.1.2.3. 预定义的折叠拆分/验证集#
对于某些数据集,已经存在将数据预先定义的拆分为训练和验证折叠或多个交叉验证折叠。使用 PredefinedSplit
例如,在搜索超参数时可以使用这些折叠。
例如,当使用验证集时,请设置 test_fold
对于属于验证集一部分的所有样本,为0,对于所有其他样本,为-1。
3.1.2.4. 分组数据的交叉验证迭代器#
身份识别如果基础生成过程产生依赖样本组,则假设就被打破。
这样的数据分组是特定于领域的。一个例子是,从多个患者收集医疗数据,从每个患者采集多个样本。此类数据可能取决于个人群体。在我们的示例中,每个样本的患者ID将是其组标识符。
在这种情况下,我们想知道在特定组上训练的模型是否能很好地推广到未见的组。为了衡量这一点,我们需要确保验证折叠中的所有样本都来自配对训练折叠中根本没有代表的组。
可以使用以下交叉验证拆分器来实现这一目标。样本的分组标识符通过 groups
参数.
3.1.2.4.1. K折叠组#
GroupKFold
是K折叠的一种变体,可确保同一组不在测试和训练集中代表。例如,如果数据是从不同的受试者获得的,每个受试者有几个样本,并且如果模型足够灵活,可以从高度特定于个人的特征中学习,那么它可能无法推广到新的受试者。 GroupKFold
使得检测这种过度匹配的情况成为可能。
假设您有三个主题,每个主题都有一个从1到3的相关数字::
>>> from sklearn.model_selection import GroupKFold
>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d"]
>>> groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3]
>>> gkf = GroupKFold(n_splits=3)
>>> for train, test in gkf.split(X, y, groups=groups):
... print("%s %s" % (train, test))
[0 1 2 3 4 5] [6 7 8 9]
[0 1 2 6 7 8 9] [3 4 5]
[3 4 5 6 7 8 9] [0 1 2]
每个科目都处于不同的测试阶段,并且同一科目永远不会同时进行测试和培训。请注意,由于数据不平衡,折叠的大小并不完全相同。如果阶级比例必须在各阶层之间平衡, StratifiedGroupKFold
是一个更好的选择。
下面是交叉验证行为的可视化。

类似于 KFold
,测试集来自 GroupKFold
将形成所有数据的完整分区。
而 GroupKFold
尝试在每个折叠中放置相同数量的样本, shuffle=False
,当 shuffle=True
它尝试在每个折叠中放置相同数量的不同组(但不考虑组大小)。
3.1.2.4.2. StratifiedGroupKFold#
StratifiedGroupKFold
是一种交叉验证方案, StratifiedKFold
和 GroupKFold
.其想法是尝试保留每个分裂中的班级分布,同时将每个组保持在一个分裂中。当您有不平衡的数据集时,这可能很有用,以便仅使用 GroupKFold
可能会产生倾斜的裂缝。
示例::
>>> from sklearn.model_selection import StratifiedGroupKFold
>>> X = list(range(18))
>>> y = [1] * 6 + [0] * 12
>>> groups = [1, 2, 3, 3, 4, 4, 1, 1, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6]
>>> sgkf = StratifiedGroupKFold(n_splits=3)
>>> for train, test in sgkf.split(X, y, groups=groups):
... print("%s %s" % (train, test))
[ 0 2 3 4 5 6 7 10 11 15 16 17] [ 1 8 9 12 13 14]
[ 0 1 4 5 6 7 8 9 11 12 13 14] [ 2 3 10 15 16 17]
[ 1 2 3 8 9 10 12 13 14 15 16 17] [ 0 4 5 6 7 11]
实施说明#
使用当前的实现,在大多数情况下都不可能实现完全洗牌。当shuffle=True时,会发生以下情况:
所有小组都进行了洗牌。
使用稳定排序按类别的标准差对组进行排序。
排序的组被迭代并分配给折叠。
这意味着只有类别分布标准差相同的组才会被洗牌,当每个组只有一个类别时,这可能会很有用。
该算法将每个组随机分配给n_splits测试集之一,选择测试集,使测试集之间的类分布方差最小化。组分配从类频率中具有最高到最低方差的组进行,即首先分配在一个或几个类上达到峰值的大组。
从某种意义上说,这种分裂是次优的,即使完美分层是可能的,它也可能产生不平衡的分裂。如果每个组中的班级分布相对接近,请使用
GroupKFold
更好
以下是不平衡群体的交叉验证行为的可视化:

3.1.2.4.3. 排除一组#
LeaveOneGroupOut
是一种交叉验证方案,其中每次分裂都包含属于一个特定组的样本。组信息通过对每个样本的组进行编码的数组提供。
因此,每个训练集由除与特定组相关的样本之外的所有样本组成。这和 LeavePGroupsOut
与 n_groups=1
而同 GroupKFold
与 n_splits
等于传递给 groups
参数.
例如,在多个实验的情况下, LeaveOneGroupOut
可用于创建基于不同实验的交叉验证:我们使用所有实验的样本创建训练集,除了一个::
>>> from sklearn.model_selection import LeaveOneGroupOut
>>> X = [1, 5, 10, 50, 60, 70, 80]
>>> y = [0, 1, 1, 2, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3, 3]
>>> logo = LeaveOneGroupOut()
>>> for train, test in logo.split(X, y, groups=groups):
... print("%s %s" % (train, test))
[2 3 4 5 6] [0 1]
[0 1 4 5 6] [2 3]
[0 1 2 3] [4 5 6]
另一个常见的应用是使用时间信息:例如,组可能是样本收集的年份,因此允许针对基于时间的分割进行交叉验证。
3.1.2.4.4. 排除P群体#
LeavePGroupsOut
类似于 LeaveOneGroupOut
,但删除了与 \(P\) 每个训练/测试集的分组。的所有可能组合 \(P\) 组被排除在外,这意味着测试集将重叠 \(P>1\) .
Leave-2-Group Out示例::
>>> from sklearn.model_selection import LeavePGroupsOut
>>> X = np.arange(6)
>>> y = [1, 1, 1, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3]
>>> lpgo = LeavePGroupsOut(n_groups=2)
>>> for train, test in lpgo.split(X, y, groups=groups):
... print("%s %s" % (train, test))
[4 5] [0 1 2 3]
[2 3] [0 1 4 5]
[0 1] [2 3 4 5]
3.1.2.4.5. 分组洗牌拆分#
The GroupShuffleSplit
iterator behaves as a combination of
ShuffleSplit
and LeavePGroupsOut
, and generates a
sequence of randomized partitions in which a subset of groups are held
out for each split. Each train/test split is performed independently meaning
there is no guaranteed relationship between successive test sets.
以下是一个使用示例::
>>> from sklearn.model_selection import GroupShuffleSplit
>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 0.001]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "a"]
>>> groups = [1, 1, 2, 2, 3, 3, 4, 4]
>>> gss = GroupShuffleSplit(n_splits=4, test_size=0.5, random_state=0)
>>> for train, test in gss.split(X, y, groups=groups):
... print("%s %s" % (train, test))
...
[0 1 2 3] [4 5 6 7]
[2 3 6 7] [0 1 4 5]
[2 3 4 5] [0 1 6 7]
[4 5 6 7] [0 1 2 3]
下面是交叉验证行为的可视化。

当的行为 LeavePGroupsOut
是需要的,但组的数量足够大,可以生成所有可能的分区 \(P\) 扣留的团体的费用将高得令人望而却步。在这种情况下, GroupShuffleSplit
提供由生成的列车/测试拆分的随机样本(带有替换) LeavePGroupsOut
.
3.1.2.5. 使用交叉验证迭代器来拆分训练和测试#
上述组交叉验证函数也可以用于将数据集分成训练和测试子集。请注意, train_test_split
是一个包裹 ShuffleSplit
因此仅允许分层拆分(使用类别标签)并且无法考虑组。
要执行训练和测试拆分,请使用生成器输出产生的训练和测试子集的索引 split()
交叉验证拆分器的方法。例如::
>>> import numpy as np
>>> from sklearn.model_selection import GroupShuffleSplit
>>> X = np.array([0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 0.001])
>>> y = np.array(["a", "b", "b", "b", "c", "c", "c", "a"])
>>> groups = np.array([1, 1, 2, 2, 3, 3, 4, 4])
>>> train_indx, test_indx = next(
... GroupShuffleSplit(random_state=7).split(X, y, groups)
... )
>>> X_train, X_test, y_train, y_test = \
... X[train_indx], X[test_indx], y[train_indx], y[test_indx]
>>> X_train.shape, X_test.shape
((6,), (2,))
>>> np.unique(groups[train_indx]), np.unique(groups[test_indx])
(array([1, 2, 4]), array([3]))
3.1.2.6. 时间序列数据的交叉验证#
时间序列数据的特征是时间上接近的观测值之间的相关性( autocorrelation ).然而,经典的交叉验证技术,例如 KFold
和 ShuffleSplit
假设样本是独立和同分布的,并且将导致时间序列数据上的训练和测试实例之间的不合理相关性(产生泛化误差的差估计)。因此,在“未来”观测值上评估我们的时间序列数据模型非常重要,这些观测值与用于训练模型的观测值最不相似。为了实现这一点,提供了一种解决方案, TimeSeriesSplit
.
3.1.2.6.1. 时间序列分割#
TimeSeriesSplit
的变型 k-fold 哪个先返回 \(k\) 火车设置和 \((k+1)\) 作为测试集的第三倍。请注意,与标准交叉验证方法不同,连续训练集是之前训练集的超集。此外,它还将所有剩余数据添加到第一个训练分区,该分区始终用于训练模型。
此类可用于交叉验证以固定时间间隔观察的时间序列数据样本。事实上,折叠必须代表相同的持续时间,以便在折叠之间具有可比的指标。
对包含6个样本的数据集进行3分裂时间序列交叉验证的示例::
>>> from sklearn.model_selection import TimeSeriesSplit
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([1, 2, 3, 4, 5, 6])
>>> tscv = TimeSeriesSplit(n_splits=3)
>>> print(tscv)
TimeSeriesSplit(gap=0, max_train_size=None, n_splits=3, test_size=None)
>>> for train, test in tscv.split(X):
... print("%s %s" % (train, test))
[0 1 2] [3]
[0 1 2 3] [4]
[0 1 2 3 4] [5]
下面是交叉验证行为的可视化。

3.1.3. 关于洗牌的注释#
如果数据排序不是任意的(例如,具有相同类别标签的样本是连续的),则首先对其进行混洗可能对于获得有意义的交叉验证结果至关重要。然而,如果样本不是独立和相同分布的,则相反的情况可能成立。例如,如果样本对应于新闻文章,并按其发布时间排序,那么对数据进行洗牌可能会导致模型过拟合和膨胀的验证分数:它将在人为相似(时间上接近)的样本上进行测试训练样本。
一些交叉验证迭代器,例如 KFold
,有一个内置选项可以在拆分数据索引之前对它们进行洗牌。请注意:
这比直接洗牌数据消耗的内存更少。
默认情况下,不会发生洗牌,包括通过指定执行的(分层)K折叠交叉验证
cv=some_integer
到cross_val_score
、网格搜索等。请记住,train_test_split
仍然返回随机分裂。的
random_state
参数默认为None
,这意味着每次洗牌都会不同KFold(..., shuffle=True)
被迭代。然而,GridSearchCV
将对通过单次调用验证的每一组参数使用相同的洗牌fit
法要获得每次拆分相同的结果,请设置
random_state
转换为一个整。
For more details on how to control the randomness of cv splitters and avoid common pitfalls, see 控制随机性.
3.1.4. 交叉验证和模型选择#
交叉验证迭代器还可以用于使用网格搜索直接执行模型选择,以获取模型的最佳超参数。这是下一部分的主题: 调整估计器的超参数 .
3.1.5. 排列测试分数#
permutation_test_score
提供了另一种评估性能的方法 predictor .它提供了一个基于排列的p值,它代表偶然获得估计器观察到的性能的可能性。该测试中的零假设是,估计器未能利用特征和目标之间的任何统计依赖性来对遗漏的数据做出正确的预测。 permutation_test_score
通过计算生成空分布 n_permutations
数据的不同排列。在每个排列中,目标值被随机洗牌,从而消除特征和目标之间的任何依赖性。p值输出是交叉验证分数优于或等于没有置换目标的真实分数的置换分数。为了获得可靠的结果 n_permutations
通常应大于100并且 cv
3-10倍之间。
低p值提供证据表明数据集包含要素和目标之间的一些真正依赖性 and 估计器能够利用这种依赖性来获得良好的结果。相反,高p值可能是由于以下任何一个原因:
特征和目标之间缺乏依赖性(即,不存在系统性关系,任何观察到的模式都可能是随机机会造成的)
or 因为估计器不能使用数据中的依赖性(例如,因为它不适合)。
在后一种情况下,使用能够使用数据中结构的更合适的估计量将导致较低的p值。
交叉验证提供了有关估计器通过估计其预期分数的范围进行概括的信息。然而,在没有结构的多维数据集上训练的估计器在交叉验证中仍然可能表现得比预期更好,这只是偶然的。这通常会发生在样本少于几百个的小数据集上。 permutation_test_score
提供有关估计器是否已发现特征和目标之间的真实依赖关系的信息,并有助于评估估计器的性能。
值得注意的是,即使数据中只有弱结构,该测试也会产生较低的p值,因为在相应的置换数据集中绝对没有结构。因此,该测试只能显示模型是否可靠地优于随机猜测。
最后, permutation_test_score
是用蛮力和内部拟合来计算的 (n_permutations + 1) * n_cv
模型因此,它仅适用于小数据集,而小数据集的匹配单个模型非常快。使用 n_jobs
参数使计算并行化,从而加快了计算速度。
示例
引用#
奥贾拉和加里加。 Permutation Tests for Studying Classifier Performance . J·马赫。学习. 2010年研究。