欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

交叉验证:评估估算器的表现 - sklearn的使用方法

程序员文章站 2022-07-06 22:29:31
...

0. 准备工作…

学习预测函数的参数,并在相同数据集上进行测试是一种错误的做法: 一个仅给出测试用例标签的模型将会获得极高的分数,但对于尚未出现过的数据它则无法预测出任何有用的信息。 这种情况称为 overfitting(过拟合). 为了避免这种情况,在进行(监督)机器学习实验时,通常取出部分可利用数据作为 test set(测试数据集) X_test, y_test。需要强调的是这里说的 “experiment(实验)” 并不仅限于学术(academic),因为即使是在商业场景下机器学习也往往是从实验开始的。下面是模型训练中典型的交叉验证工作流流程图。通过网格搜索可以确定最佳参数。
交叉验证:评估估算器的表现 - sklearn的使用方法
利用 scikit-learn 包中的 train_test_split 辅助函数可以很快地将实验数据集划分为任何训练集(training sets)和测试集(test sets)。 下面让我们载入 iris 数据集,并在此数据集上训练出线性支持向量机:

>>> 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% 作为测试集,从而测试(评估)我们的分类器:

>>> 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...

1. 计算交叉验证的指标

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

下面的示例展示了如何通过分割数据,拟合模型和计算连续 5 次的分数(每次不同分割)来估计 linear kernel 支持向量机在 iris 数据集上的精度:

>>> 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% 置信区间由此给出:

>>> print("Accuracy: {} (+/- {})".format(scores.mean(), scores.std() * 2))
Accuracy: 0.98 (+/- 0.03)

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

>>> 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.        ])

详情请参阅 scoring 参数: 定义模型评估规则 。 在 Iris 数据集的情形下,样本在各个目标类别之间是平衡的,因此准确度和 F1-score 几乎相等。

当 cv 参数是一个整数时, cross_val_score 默认使用 KFoldStratifiedKFold 策略,后者会在估计器派生自 ClassifierMixin 时使用。

也可以通过传入一个交叉验证迭代器来使用其他交叉验证策略,比如:

>>> from sklearn.model_selection import ShuffleSplit
>>> n_samples = iris.data.shape[0]
>>> cv = ShuffleSplit(n_splits=5, test_size=0.3, random_state=0)
>>> cross_val_score(clf, iris.data, iris.target, cv=cv)  
array([0.977..., 0.977..., 1.  ..., 0.955..., 1.        ])

另外一种可选方案是使用一个可迭代生成器作为索引数组产生(train, test) 划分,比如:

>>> 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(iris.data)
>>> cross_val_score(clf, iris.data, iris.target, cv=custom_cv)
array([1.        , 0.973...])

保留数据的数据转换

正如在训练集中保留的数据上测试一个 predictor (预测器)是很重要的一样,预处理(如标准化,特征选择等)和类似的 data transformations 也应该从训练集中学习,并应用于预测数据以进行预测:

>> 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 可以更容易地组合估计器,在交叉验证下使用如下:

>> 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...])

1.1. cross_validate 函数和多度量评估

cross_validate 函数与 cross_val_score 在下面的两个方面有些不同:

  • 它允许指定多个指标进行评估.
  • 除了测试得分之外,它还会返回一个包含训练得分,拟合次数, score-times (得分次数)的一个字典。 It returns a dict containing training scores, fit-times and score-times in addition to the test score.

对于单个度量评估,其中 scoring 参数是一个字符串,可以调用或 None , keys 将是 - [‘test_score’, ‘fit_time’, ‘score_time’]

而对于多度量评估,返回值是一个带有以下的 keys 的字典 - [‘test_<scorer1_name>’, ‘test_<scorer2_name>’, ‘test_<scorer…>’, ‘fit_time’, ‘score_time’]

return_train_score 默认设置为 True 。 它增加了所有 scorers(得分器) 的训练得分 keys 。如果不需要训练 scores ,则应将其明确设置为 False 。

你还可以通过设置return_estimator=True来保留在所有训练集上拟合好的估计器。

可以将多个测度指标指定为list,tuple或者是预定义评分器(predefined scorer)的名字的集合:

>>> 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, iris.data, iris.target, scoring=scoring,
...                         cv=5)
>>> 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.        ])

或作为一个字典 mapping 得分器名称预定义或自定义的得分函数:

>>> from sklearn.metrics.scorer import make_scorer
>>> scoring = {'prec_macro': 'precision_macro',
...            'rec_macro': make_scorer(recall_score, average='macro')}
>>> scores = cross_validate(clf, iris.data, iris.target, 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...])

这里是一个使用单一指标的 cross_validate 的示例:

>>> scores = cross_validate(clf, iris.data, iris.target,
...                         scoring='precision_macro', cv=5,
...                         return_estimator=True)
>>> sorted(scores.keys())
['estimator', 'fit_time', 'score_time', 'test_score']

1.2. 通过交叉验证获取预测

除了返回结果不同,函数 cross_val_predict 具有和 cross_val_score 相同的接口, 对于每一个输入的元素,如果其在测试集合中,将会得到预测结果。交叉验证策略会将可用的元素提交到测试集合有且仅有一次(否则会抛出一个异常)

警告 : 交叉预测可能使用不当
cross_val_predict函数的结果可能会与cross_val_score函数的结果不一样,因为在这两种方法中元素的分组方式不一样。函数cross_val_score在所有交叉验证的折子上取平均。但是,函数cross_val_predict只是简单的返回由若干不同模型预测出的标签或概率。因此,cross_val_predict不是一种适当的泛化错误的度量。

函数cross_val_predict比较适合做下列事儿:

  • 从不同模型获得的预测结果的可视化。
  • 模型混合: 在集成方法中,当一个有监督估计量的预测被用来训练另一个估计量时

2. 交叉验证迭代器

接下来的部分列出了一些用于生成索引标号,用于在不同的交叉验证策略中生成数据划分的工具。

2.1. 交叉验证迭代器–循环遍历数据

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

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

2.1.1. K 折

KFold 将所有的样例划分为 k 个组,称为折叠 (fold) (如果 k = n, 这等价于 Leave One Out(留一) 策略),都具有相同的大小(如果可能)。预测函数学习时使用 k - 1 个折叠中的数据,最后一个剩下的折叠会用于测试。

在 4 个样例的数据集上使用 2-fold 交叉验证的示例:

>>> import numpy as np
>>> from sklearn.model_selection import KFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([1, 2, 3, 4])
>>> kf = KFold(n_splits=2)
>>> kf.get_n_splits(X)
2
>>> print(kf)
KFold(n_splits=2, random_state=None, shuffle=False)
>>> for train_index, test_index in kf.split(X):
...     print("TRAIN:", train_index, "TEST:", test_index)
...     X_train, X_test = X[train_index], X[test_index]
...     y_train, y_test = y[train_index], y[test_index]
TRAIN: [2 3] TEST: [0 1]
TRAIN: [0 1] TEST: [2 3]

方法:

get_n_splits(self[, X, y, groups]):返回交叉验证器中的拆分迭代次数。
split(self, X[, y, groups]):生成索引以将数据分为训练和测试集。

交叉验证:评估估算器的表现 - sklearn的使用方法
每个折叠由两个 arrays 组成,第一个作为 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]

2.1.2. 重复 K-折交叉验证

RepeatedKFold 重复 K-Fold n 次。当需要运行时可以使用它 KFold n 次,在每次重复中产生不同的分割。

2折 K-Fold 重复 2 次的示例:

>>> import numpy as np
>>> from sklearn.model_selection import RepeatedKFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([0, 0, 1, 1])
>>> rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=2652124)
>>> for train_index, test_index in rkf.split(X):
...     print("TRAIN:", train_index, "TEST:", test_index)
...     X_train, X_test = X[train_index], X[test_index]
...     y_train, y_test = y[train_index], y[test_index]
...
TRAIN: [0 1] TEST: [2 3]
TRAIN: [2 3] TEST: [0 1]
TRAIN: [1 2] TEST: [0 3]
TRAIN: [0 3] TEST: [1 2]

方法:

get_n_splits(self[, X, y, groups]):返回交叉验证器中的拆分迭代次数
split(self, X[, y, groups]):生成索引以将数据分为训练和测试集。

类似地, RepeatedStratifiedKFold 在每个重复中以不同的随机化重复 n 次分层的 K-Fold 。

示例:

>>> import numpy as np
>>> from sklearn.model_selection import RepeatedStratifiedKFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([0, 0, 1, 1])
>>> rskf = RepeatedStratifiedKFold(n_splits=2, n_repeats=2,
...     random_state=36851234)
>>> for train_index, test_index in rskf.split(X, y):
...     print("TRAIN:", train_index, "TEST:", test_index)
...     X_train, X_test = X[train_index], X[test_index]
...     y_train, y_test = y[train_index], y[test_index]
...
TRAIN: [1 2] TEST: [0 3]
TRAIN: [0 3] TEST: [1 2]
TRAIN: [1 3] TEST: [0 2]
TRAIN: [0 2] TEST: [1 3]

方法:

get_n_splits(self[, X, y, groups]):返回交叉验证器中的拆分迭代次数
split(self, X[, y, groups]):生成索引以将数据分为训练和测试集。

2.1.3. 随机排列交叉验证 a.k.a. Shuffle & Split

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

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

这是一个使用的小示例:

>>> import numpy as np
>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [3, 4], [5, 6]])
>>> y = np.array([1, 2, 1, 2, 1, 2])
>>> rs = ShuffleSplit(n_splits=5, test_size=.25, random_state=0)
>>> rs.get_n_splits(X)
5
>>> print(rs)
ShuffleSplit(n_splits=5, random_state=0, test_size=0.25, train_size=None)
>>> for train_index, test_index in rs.split(X):
...     print("TRAIN:", train_index, "TEST:", test_index)
TRAIN: [1 3 0 4] TEST: [5 2]
TRAIN: [4 0 2 5] TEST: [1 3]
TRAIN: [1 2 4 0] TEST: [3 5]
TRAIN: [3 4 1 0] TEST: [5 2]
TRAIN: [3 5 1 0] TEST: [2 4]
>>> rs = ShuffleSplit(n_splits=5, train_size=0.5, test_size=.25,
...                   random_state=0)
>>> for train_index, test_index in rs.split(X):
...     print("TRAIN:", train_index, "TEST:", test_index)
TRAIN: [1 3 0] TEST: [5 2]
TRAIN: [4 0 2] TEST: [1 3]
TRAIN: [1 2 4] TEST: [3 5]
TRAIN: [3 4 1] TEST: [5 2]
TRAIN: [3 5 1] TEST: [2 4]

3. A note on shuffling

如果数据的顺序不是任意的(比如说,相同标签的样例连续出现),为了获得有意义的交叉验证结果,首先对其进行 打散是很有必要的。然而,当样例不是独立同分布时打散则是不可行的。例如:样例是相关的文章,以他们发表的时间 进行排序,这时候如果对数据进行打散,将会导致模型过拟合,得到一个过高的验证分数:因为验证样例更加相似(在时间上更接近) 于训练数据。

一些交叉验证迭代器, 比如 KFold ,有一个内建的在划分数据前进行数据索引打散的选项。注意:

  • 这种方式仅需要很少的内存就可以打散数据。
  • 默认不会进行打散,包括设置 cv=some_integer (直接)k 折叠交叉验证的 cross_val_score , 表格搜索等。注意 train_test_split 会返回一个随机的划分。
  • 参数 random_state 默认设置为 None ,这意为着每次进行 KFold(…, shuffle=True) 时,打散都是不同的。 然而, GridSearchCV 通过调用 fit 方法验证时,将会使用相同的打散来训练每一组参数。
  • 为了保证结果的可重复性(在相同的平台上),应该给 random_state 设定一个固定的值。

4. 交叉验证和模型选择

交叉验证迭代器可以通过网格搜索得到最优的模型超参数,从而直接用于模型的选择。 这是另一部分 调整估计器的超参数 的主要内容。


强烈建议有时间要看 ↑

相关标签: 机器学习 python