页面树结构

版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

学习预测函数的参数并在相同的数据上进行测试是一个方法上的错误:一个只会重复其刚刚看到的样本的标签的模型将具有完美的分数,但是无法预测任何有用的,看不见的数据。这种情况称为过拟合。为了避免这种情况,通常的做法是执行(监督)机器学习实验以将部分可用数据作为测试集 。请注意,“实验”一词并不意味着仅表示学术用途,因为即使在商业设置中,机器学习通常从实验开始。 。请注意,“实验”一词并不意味着仅表示学术用途,因为即使在商业环境中,机器学习通常从实验研究开始。X_test, y_test

在scikit中,学习随机分组到训练和测试集中,可以使用train_test_split辅助函数快速计算。我们加载虹膜数据集,以适应线性支持向量机:

代码块
languagepy
>>> import numpy as np
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import datasets
>>> from sklearn import svm

>>> iris = datasets.load_iris()
>>> iris.data.shape, iris.target.shape
((150, 4), (150,))

我们现在可以快速抽样培训,同时拿出40%的测试数据(评估)我们的分类器:

代码块
languagepy
>>> X_train, X_test, y_train, y_test = train_test_split(
...     iris.data, iris.target, 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必须为SVM手动设置的设置时,仍然有可能在测试集上过度拟合 因为可以调整参数直到估计器执行最佳。这样,关于测试集的知识可以“泄漏”到模型中,并且评估指标不再报告泛化性能。为了解决这个问题,数据集的另一部分可以被称为所谓的“验证集”:训练在训练集上进行,之后对验证集进行评估,当实验似乎成功时,可以在测试集上进行最终评估。

然而,通过将可用数据分为三组,我们大大减少了可用于学习模型的样本数,结果可能取决于一对(训练,验证)集合的特定随机选择。

解决这个问题的方法是一个称为 交叉验证 (简称简历)的过程。一个测试集仍然需要进行最终评估,但是在做CV时不再需要验证集。在基本方法中,称为k- fold CV,训练集被分为k个较小的集合(其他方法如下所述,但通常遵循相同的原则)。对于k “折叠”中的每一个,遵循以下过程:

  • 使用折叠作为训练数据训练模型;
  • 所得到的模型在数据的剩余部分被验证(即,它被用作测试集来计算性能度量,例如精度)。

然后通过k倍交叉验证报告的绩效指标是循环中计算的值的平均值。这种方法在计算上可能是昂贵的,但是不会浪费太多的数据(如固定任意测试集的情况),这是问题的主要优点,例如反向推理,其中样本数量非常小。

 

计算交叉验证的度量

使用交叉验证的最简单的方法是cross_val_score在估计器和数据集上调用 帮助函数。

以下示例演示如何通过分割数据,拟合模型和连续计算分数(每次不同分割)来计算虹膜数据集上的线性内核支持向量机的准确性:

代码块
languagepy
>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1)
>>> scores = cross_val_score(clf, iris.data, iris.target, cv=5)
>>> scores                                              
array([ 0.96...,  1.  ...,  0.96...,  0.96...,  1.        ])

因此,得分估计的平均分数和95%置信区间由下式给出:

代码块
languagepy
>>> print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
Accuracy: 0.98 (+/- 0.03)

默认情况下,每个CV迭代计算的分数是score 估计量的方法。可以通过使用评分参数来改变它:

代码块
languagepy
>>> from sklearn import metrics
>>> scores = cross_val_score(
...     clf, iris.data, iris.target, cv=5, scoring='f1_macro')
>>> scores                                              
array([ 0.96...,  1.  ...,  0.96...,  0.96...,  1.        ])

参见评分参数:详细定义模型评估规则。在Iris数据集的情况下,样本在目标类别之间进行平衡,因此精度和F1分数几乎相等。

cv参数为整数时,默认情况下cross_val_score使用 KFoldStratifiedKFold策略,如果估计器派生出来,则使用后者ClassifierMixin

通过传递交叉验证迭代器也可以使用其他交叉验证策略,例如:

代码块
languagepy
>>> from sklearn.model_selection import ShuffleSplit
>>> n_samples = iris.data.shape[0]
>>> cv = ShuffleSplit(n_splits=3, test_size=0.3, random_state=0)
>>> cross_val_score(clf, iris.data, iris.target, cv=cv)
...                                                     
array([ 0.97...,  0.97...,  1.        ])

数据转换带有数据

同样重要的是测试从训练数据中预测的数据,预处理(如标准化,特征选择等)和类似的数据变换也应该从训练集中学到并应用于预测数据:

代码块
languagepy
>>> from sklearn import preprocessing
>>> X_train, X_test, y_train, y_test = train_test_split(
...     iris.data, iris.target, 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...

Pipeline使得编写估计器更容易,在交叉验证下提供此行为:

代码块
languagepy
>>> from sklearn.pipeline import make_pipeline
>>> clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
>>> cross_val_score(clf, iris.data, iris.target, cv=cv)
...                                                 
array([ 0.97...,  0.93...,  0.95...])

参见管道和FeatureUnion:组合估计

通过交叉验证获取预测

该函数与输入中的每个元素cross_val_predict具有类似的接口 cross_val_score,但返回,当该元素在测试集中时为该元素获得的预测。只有交叉验证策略才能将所有元素精确地分配给测试集一次(否则引发异常)。

然后可以使用这些预测来评估分类器:

代码块
languagepy
>>> from sklearn.model_selection import cross_val_predict
>>> predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)
>>> metrics.accuracy_score(iris.target, predicted) 
0.966...

注意,该计算的结果可能与使用的结果略有不同,cross_val_score因为元素以不同的方式分组。

可用的交叉验证迭代器在以下部分中介绍。

交叉验证迭代

以下部分列出了用于生成可用于根据不同交叉验证策略生成数据集拆分的索引的实用程序。

交叉验证迭代器独立同分布的数据

假设一些数据是独立的,相同分布的(iid)假定所有样本来源于相同的生成过程,假设生成过程没有过去生成的样本的记忆。

在这种情况下可以使用以下交叉验证器。

注意

虽然数据是机器学习理论中的一个常见假设,但在实践中很少存在。如果知道样本是使用时间相关过程生成的,使用时间序列感知交叉验证方案<time_series_cv>更为安全。 同样,如果我们知道生成过程具有组结构(来自不同的收集的样本科目,实验,测量设备)使用群组交叉验证更安全<group_cv>

K-fold

KFold将样本ķ组中的所有样本(称为折叠(如果等于相同于离开策略))分成相同大小(如果可能)。使用k  -  1折叠学习预测功能,并将折叠的折叠用于测试。

具有4个样本的数据集的2倍交叉验证示例:

代码块
languagepy
>>> 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]

每个折叠由两个阵列组成:第一个与训练集相关 ,第二个与测试集相关。因此,可以使用numpy索引创建训练/测试集:

代码块
languagepy
>>> 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]

    

离开一个(LOO)

LeaveOneOut(或LOO)是一个简单的交叉验证。每个学习集是通过取除除了一个之外的所有样本创建的,测试集被删除。因此,对于ñ样本,我们有ñ不同的训练集和ñ不同的测试集。这种交叉验证过程不会浪费太多数据,因为只有一个样本从培训集中删除:

代码块
languagepy
>>> 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用于模型选择的潜在用户应该重视一些已知的注意事项。当与ķ交叉验证相比,一个ññ样本构建模型而不是ķ模型,其中。而且,每一个都是针对n  -  1样本而不是 。在这两种方式中,假设ķ不是太大,而且LOO在计算上比ķ交叉验证更昂贵。

在准确性方面,LOO通常导致高方差作为测试误差的估计。直观地,由于n  -  1所述的ñ样本被用于建立每个模型,从褶皱构造模型几乎彼此相同,并从整个训练集构建的模型。

然而,如果学习曲线对于所讨论的训练大小陡峭,则5或10倍交叉验证可以高估泛化误差。

作为一般规则,大多数作者和实证证据表明,5或10倍交叉验证应优于LOO。

参考文献:

离开P(LPO)

LeavePOutLeaveOneOut通过p从完整集合中移除样本创建所有可能的训练/测试集非常相似。对于ñ样品,这产生列车测试对。不像LeaveOneOutKFold,测试集将用于重叠

具有4个样本的数据集上的Leave-2-Out示例:

代码块
languagepy
>>> 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]

 

随机排列交叉验证aka Shuffle&Split

ShuffleSplit

所述ShuffleSplit迭代器将产生独立列车/测试数据集分割的一个用户定义的编号。样品首先洗牌,然后分成一对火车和测试仪。

可以通过明确种子random_state伪随机数发生器来控制结果的重现性的随机性。

以下是一个使用示例:

代码块
languagepy
>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.arange(5)
>>> ss = ShuffleSplit(n_splits=3, test_size=0.25,
...     random_state=0)
>>> for train_index, test_index in ss.split(X):
...     print("%s %s" % (train_index, test_index))
...
[1 3 4] [2 0]
[1 4 3] [0 2]
[4 0 2] [1 3]

ShuffleSplit因此是KFold交叉验证的一个很好的替代方案,允许对列车/测试拆分的每一侧的迭代次数和采样比例进行更精细的控制。

 

交叉验证迭代器,基于类标签分层

一些分类问题可能会在目标类别的分布上表现出很大的不平衡:例如,比正面样本可能有多倍的阴性样本。在这种情况下,建议使用分层抽样,StratifiedKFold并 StratifiedShuffleSplit确保相应的班次频率在每个列车和验证阶段大致保留。

分层的k-fold

StratifiedKFoldk折的变体,它返回分层 折叠:每个集合包含与完整集合大致相同的每个目标类别的样本百分比。

在具有10个来自两个稍微不平衡的类别的样本的数据集上分层3倍交叉验证的示例:

代码块
languagepy
>>> from sklearn.model_selection import StratifiedKFold

>>> X = np.ones(10)
>>> y = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
>>> skf = StratifiedKFold(n_splits=3)
>>> for train, test in skf.split(X, y):
...     print("%s %s" % (train, test))
[2 3 6 7 8 9] [0 1 4 5]
[0 1 3 4 5 8 9] [2 6 7]
[0 1 2 4 5 6 7] [3 8 9]

 

分层随机拆分

StratifiedShuffleSplitShuffleSplit的一个变体,它返回分层分割,通过保留与完整集中相同的每个目标类的百分比来创建分割。

用于分组数据的交叉验证迭代器

如果潜在的生成过程产生依赖样本组,则iid假设被破坏。

这样的数据分组是域特定的。一个例子是当从多个患者收集到医学数据时,每个患者取出多个样本。而这些数据很可能取决于个别群体。在我们的示例中,每个样本的患者ID将是其组标识符。

在这种情况下,我们想知道一个针对特定组的训练的模型是否能够对未知的组进行概括。为了衡量这一点,我们需要确保验证折叠中的所有样品来自在配对训练折叠中完全没有表现的组。

以下交叉验证分隔符可用于执行此操作。通过groups 参数指定样本的分组标识符。

群组k-fold

class:GroupKFold是k-fold的变体,它确保了同一组在测试和训练集中都不被表示。例如,如果数据是从具有每个主题的几个样本的不同主题获得的,并且如果模型具有足够的灵活性以从高度个人特定的特征中学习,则可能无法将其推广到新的主题。类:GroupKFold可以检测到这种过拟合情况。

想象一下,您有三个科目,每个科目的相关编号从1到3:

代码块
languagepy
>>> 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]

每个科目都处于不同的测试阶段,同样的科目从不在测试和培训中。请注意,由于数据不平衡,折叠的尺寸不完全相同。

离开一个组

LeaveOneGroupOut是交叉验证方案,其根据第三方提供的整数组数组来保存样本。该组信息可用于编码任意域特定的预定义交叉验证折叠。

因此,每个训练集合除了与特定组相关的样本之外的所有样本构成。

例如,在多个实验的情况下,LeaveOneGroupOut 可以用于基于不同的实验创建交叉验证:我们使用所有实验的样本创建训练集,除了一个:

代码块
languagepy
>>> 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]

另一个常见的应用是使用时间信息:例如,组可以是采样的年份,从而允许基于时间分割的交叉验证。

离开P组

LeavePGroupsOut类似于LeaveOneGroupOut,但删除与P每个训练/测试集相关的样本。

离开2组的例子:

代码块
languagepy
>>> 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]

 

组随机拆分

GroupShuffleSplit

所述GroupShuffleSplit迭代器的操作与一个组合 ShuffleSplitLeavePGroupsOut,并且生成其中烷基的子集被伸出为每个分割随机分区的序列。

以下是一个使用示例:

代码块
languagepy
>>> 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

 

预定义折叠/验证集

对于某些数据集,已经存在将数据预先定义为训练和验证的折叠或多个交叉验证折叠。使用PredefinedSplit它可以使用这些折叠,例如在搜索超参数时。

例如,当使用验证集时,将test_fold所有作为验证集的一部分的样本设置为0,对所有其他样本设置为-1。

 

时间序列数据的交叉验证

时间序列数据的特征在于时间近似的观测值之间的相关性(自相关)。然而,经典的交叉验证技术,例如KFoldShuffleSplit假设样本是独立的,相同分布的,并且将导致训练和测试实例之间的不合理的相关性(产生差的广义误差估计)对时间序列数据。因此,非常重要的是评估我们的模型对“未来”观察的时间序列数据,最不像用于训练模型的那些。为此,提供了一个解决方案TimeSeriesSplit

时间序列分割

TimeSeriesSplitk-fold的变体,其返回ķ作为列车组的第一折叠,并且作为测试集返回第三折。请注意,与标准的交叉验证方法不同,连续的训练集是超越他们的训练集。此外,它将所有剩余数据添加到始终用于训练模型的第一训练分区。

该类可用于交叉验证以固定时间间隔观察的时间序列数据样本。

具有6个样本的数据集上的3分割时间序列交叉验证示例:

代码块
languagepy
>>> 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(n_splits=3)
>>> 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]

关于shuffling的注释

如果数据排序不是任意的(例如具有相同类别标签的样本是连续的),首先对其进行混洗可能是获得有意义的交叉验证结果所必需的。然而,如果样本不是独立和相同分布的,则可能是相反的。例如,如果样本对应于新闻文章,并按照其出版时间进行排序,那么混洗数据可能会导致一个超额的模型和一个膨胀的验证分数:它将在人为相似的样本上进行测试及时)培训样品。

一些交叉验证迭代器,例如KFold,具有内置的选项,以便在分割数据索引之前混洗数据索引。注意:

  • 这消耗的记忆比数据直接更改。
  • 缺省情况下没有发生重排,包括对(分层)K倍交叉通过指定执行验证cv=some_integer来 cross_val_score,格点搜索,等等请注意, train_test_split仍然返回一个随机分裂。
  • random_state参数默认为None,这意味着每次重复的混洗将不同。但是,对于通过单个调用其方法验证的每组参数,将使用相同的混洗。KFold(..., shuffle=True)GridSearchCVfit
  • 为了确保结果是可重复的(在同一平台上),请使用固定值random_state

 

交叉验证和模型选择

交叉验证迭代器也可用于使用Grid Search直接执行模型选择以获得模型的最佳超参数。这是下一节的主题:调整估计器的超参数