2025年【译】交叉验证(Crossd_validation)

【译】交叉验证(Crossd_validation)按自己的理解对 scikit learn 中的模型选择中的交叉验证部分 进行了简单的翻译 供大家参考 学习 Cross validation 评估算法的表现 在同一个数据集上学习算法的参数和进行预测是方法性的错误 很有可能会导致 Overfitting 避免过拟合的常用方法是将一部分数据作为测试集 用以对算法进行测试

大家好,我是讯享网,很高兴认识大家。

按自己的理解对scikit-learn中的模型选择中的交叉验证部分,进行了简单的翻译,供大家参考、学习。

Cross validation:评估算法的表现

在同一个数据集上学习算法的参数和进行预测是方法性的错误。很有可能会导致Overfitting。避免过拟合的常用方法是将一部分数据作为测试集,用以对算法进行测试。

在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 iris = datasets.load_iris() iris.data.shape, iris.target.shape

讯享网
讯享网((150, 4), (150,)) 

利用train_test_split将数据分出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. 

当评估不同的参数设置,对算法表现的影响时,仍然存在则过拟合的风险。因为在调整参数,优化测试集的算法表现时,测试集的信息已经泄漏进模型中了。

为了解决这个问题,仍需要一部分数据作为验证集(Validation set)。这样,用训练集(Train set)的数据训练模型;用验证集对模型参数调剂,如上述程序中的C值;最后,算法的评价在测试集(Test set)上完成。

这样的做法会带来两个新的问题,一是,将数据分成了三分,降低了训练模型的数据量;二是,最后算法的表现会较依赖三个数据集的划分。

可以采用交叉验证集(Cross validation set)来解决上述两个问题。测试集仍然划分出来,用以最后对算法进行评价。但单独的验证集不再需要了。将训练集划分为k个小的数据集,称之为k-fold CV。对每个fold进行下列过程:
* 1 用其他k-1 folds作为训练数据,训练模型
* 2 模型的结果用剩下的fold评价

模型的性能用上述循环中的k-fold交叉验证集的平均值表现。这种做法增加了计算量,但提高了数据的利用效率。

计算交叉验证的度量指标(metrics)

最为简单的方法是调用cross_val_score

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., 1. , 0., 0., 1. ]) 

默认给出的是estimator的score值,可以对其进行修改:

from sklearn import metrics scores = cross_val_score(clf, iris.data, iris.target, cv = 5, scoring = 'f1_macro') scores
讯享网array([ 0., 1. , 0., 0., 1. ]) 

但cv的取值是整型时,cross_val_score默认采用KFoldStratifiedKFold str的策略。

也可以同通过传入交叉验证迭代器,以采用其他交叉验证的策略。

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., 0., 1. ]) 

cross_validate函数

cross_val_score相比,cross_validate有两方面的不同:
* 1 允许指定多个评价指标;
* 2 除测试分数之外,还返回包括训练分数、拟合时间和评分时间的字典

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, return_train_score=False) sorted(scores.keys())
讯享网['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro'] 
scores['test_recall_macro']
讯享网array([ 0., 1. , 0., 0., 1. ]) 

cross_val_predict函数

cross_val_predictcross_val_validate的接口是相似的,但其返回的是所有输入的正确率。

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. 

交叉验证迭代器(iterators)

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

假设一些数据是独立的并且服从同样的分布(即数据由相同的过程产生,但其产生过程不含前数据集的信息)。这种数据是理论上的,在实际中,很难找到满足条件的真实数据。

K-fold

KFold将所有的样本分为k组(fold),模型用k-1组学习参数,用剩下的一组测试。下面是一个4个样本的数据集,将其分为2个folds。


讯享网

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] 

每个fold由两个数组构成,第一个是与训练集相关,第二个这是与测试集相关。因此可以通过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]
讯享网X_train
array([[ 0., 0.], [ 1., 1.]]) 
讯享网y_train
array([0, 1]) 
讯享网X_test
array([[-1., -1.], [ 2., 2.]]) 
讯享网y_test
array([0, 1]) 

Repeated K-Fold

Repeated K-Fold重复K-Foldn次,每次重复产生不同的划分方式。

讯享网import numpy as np from sklearn.model_selection import RepeatedKFold X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]]) random_state =  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] 

去掉一个样本的策略(Leave One Out, LOO)

讯享网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比KFold的计算成本高。

LOO策略经常会导致模型会有较高的反差。从直觉上来看,每次用n-1的样本去学习模型参数,每次的训练集几乎是相同的,与在整个训练集上训练模型没有太大的差别。

通常来说,分为5-10个fold较为可靠。

去掉P个样本的策略(Leave P Out, LPO)

LPOLOO相似,从整个数据集中,移去p个样本,产生训练、测试样本对。

讯享网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] 

随机排列策略

讯享网from sklearn.model_selection import ShuffleSplit X = np.arange(5) ss = ShuffleSplit(n_splits=5, test_size=0.40, 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] [2 4 0] [3 1] [3 1 0] [4 2] 

ShuffleSplitKFold一个良好的替代策略,可以控制迭代的次数和训练集、测试集的比例。

不平衡数据集的交叉验证迭代器

一些分类问题的数据集是不平衡的,例如肿瘤良性、恶性分类问题,肿瘤是恶性的样本量占总的样本量的比例很低。此时就需要使用StratifiedKFold* StratifiedShuffleKFold*,使得在每个训练、测试fold中,各类样本的比例大致相同。

StratifiedKFold

讯享网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] 

StratifiedShuffleKFold

是由ShuffleSplit演变而来。

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

如果数据是分组的,在同一个组中的样本彼此不独立,而在不同组的样本是独立的。例如从患者身上采集的医疗数据,在同一个患者上采集的多个样本是不独立的,属于同一组,在不同患者上采集的数据则是独立的。这类数据应有组别的属性信息。

对于这种数据,就需要保证所有在测试集中的样本的所属的组别不来自于该训练集样本所属组别。

Group k-fold

样本的组别信息以groups体现。

讯享网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] 

GroupKFold,就不会有同一组别的样本既出现在训练集中,又出现在测试集中的情况。

去除p组的策略

讯享网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] 

随机分组的策略

讯享网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.2, random_state=0) for train, test in gss.split(X, y, groups=groups): print("%s %s" % (train, test)) 
[0 1 2 3 6 7] [4 5] [2 3 4 5 6 7] [0 1] [0 1 2 3 4 5] [6 7] [0 1 4 5 6 7] [2 3] 

时间序列数据的交叉验证

TimeSeriesSplit

讯享网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) for train, test in tscv.split(X): print("%s %s" % (train, test)) 
TimeSeriesSplit(max_train_size=None, n_splits=3) [0 1 2] [3] [0 1 2 3] [4] [0 1 2 3 4] [5] 

打乱注意点

当样本是独立同分布时,随机打乱是可以得到更有意义的正交验证结果。但当样本不服从独立同分布时,可能会导致相反的结果。

一些交叉验证迭代器,例如KFold,内部有在划分数据之前是否打乱数据顺序的选项。

note:
* 这样比直接打乱,要节省内存;
* 默认没有打乱,但train_test_split返回随机的划分结果
* random_state默认是None,意味着每次打乱都是不同的(KFold(…, shuffle=True)), 但网格搜索将会通过仅调用fit方法,使用的是同样的划分
* 如果需要每次划分相同,将random_state设为一个整数

小讯
上一篇 2025-03-19 10:00
下一篇 2025-03-04 23:52

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/12929.html