sklearn-交叉验证

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Forlogen/article/details/85223042

交叉验证:评估模型的表现

如果我们训练出的模型只在训练集上表现极好,但在未知的数据上效果很差,说明出现了过拟合,为了避免这种现象的出现,我们需要验证集来评估我们的模型。

当我们在训练集上训练好一个模型后,现在验证集上对模型进行,如果验证集上的效果比较好时,再到测试集上就行最后的评估。但是单纯的将数据集分为三部分,会大大减少模型学习的数据量(因为有时数据是很难获取的,数目可能会比较少),并且最后模型的效果也依赖于我们对于数据集的划分。这时我们就可以使用交叉验证,很好解决这个问题。

sklearn中的交叉验证

利用 scikit-learn 包中的 train_test_split 辅助函数可以很快地将实验数据集划分为任何训练集(training sets)和测试集(test sets)。

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

clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)  #cv为迭代次数。
print(scores) 

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

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

scaler = preprocessing.StandardScaler().fit(X_train) X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(kernel='linear', C=1).fit(X_train_transformed, y_train)X_test_transformed = scaler.transform(X_test)
print('归一化后测试集的准确度:',clf.score(X_test_transformed, y_test))

此外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)

cross_validate函数和多度量评估

此外还可以使用cross_validate函数进行评估,相比于cross_val_score有如下不同:
• 允许指定多个指标进行评估
• 除了测试得分之外,它还会返回一个包含训练得分,拟合次数, score-times (得分次数)的一个字典。
对于单个度量评估,其中 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 。可以将多个指标指定为 predefined scorer names(预定义的得分器的名称) list ,tuple 或者 set

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())
scores['test_recall_macro'] 

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

scoring = {'prec_macro': 'precision_macro','rec_micro': make_scorer(recall_score, average='macro')}

也可使用单一的指标

scores = cross_validate(clf, iris.data, iris.target,scoring='precision_macro', cv=5,
 return_estimator=True)
sorted(scores.keys())

通过交叉验证获取预测

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

 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) 

交叉验证迭代器-循环遍历数据
这里我们假设一些数据是独立的和相同分布的 (i.i.d) 假定所有的样本来源于相同的生成过程,并假设生成过程没有记忆过去生成的样本。

  1. K折交叉验证: KFold 将所有的样例划分为 k 个组,称为折叠 (fold) (如果 k = n, 这等价于 Leave One Out(留一) 策略),都具有相同的大小(如果可能)。预测函数学习时使用 k - 1 个折叠中的数据,最后一个剩下的折叠会用于测试。K-折交叉验证得出的性能指标是循环计算中每个值的平均值。 该方法虽然计算代价很高,但是它不会浪费太多的数据(如固定任意测试集的情况一样), 在处理样本数据集较少的问题(例如,逆向推理)时比较有优势。

下面是交叉验证行为的可视化。注意,KFold不受类或组的影响。
在这里插入图片描述

  1. K折重复多次: RepeatedKFold 重复 K-Fold n 次。当需要运行时可以使用它 KFold n 次,在每次重复中产生不同的分割。类似地, RepeatedStratifiedKFold 在每个重复中以不同的随机化重复 n 次分层的 K-Fold

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

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

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

  2. 随机排列交叉验证: ShuffleSplit 迭代器将会生成一个用户给定数量的独立的训练/测试数据划分。样例首先被打散然后划分为一对训练测试集合。 可以通过设定明确的 random_state ,使得伪随机生成器的结果可以重复。

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

下面是交叉验证行为的可视化。注意,ShuffleSplit不受类或组的影响。
在这里插入图片描述

基于类标签、具有分层的交叉验证迭代器

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

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

RepeatedStratifiedKFold 可用于在每次重复中用不同的随机化重复分层 K-Fold n 次。

下面是交叉验证行为的可视化。
在这里插入图片描述

  1. 分成随机split:StratifiedShuffleSplit 是 ShuffleSplit 的一个变种,会返回直接的划分,比如: 创建一个划分,但是划分中每个类的比例和完整数据集中的相同。

下面是交叉验证行为的可视化。
在这里插入图片描述

用于分组数据的交叉验证迭代器
如何进一步测试模型的泛化能力? 留出一组特定的不属于测试集和训练集的数据。有时我们想知道在一组特定的 groups 上训练的模型是否能很好地适用于看不见的 group 。为了衡量这一点,我们需要确保验证对象中的所有样本来自配对训练折叠中完全没有表示的组。下面的交叉验证分离器可以用来做到这一点。 样本的 grouping identifier (分组标识符) 通过 groups 参数指定。

  1. GroupKFold 是 k-fold 的变体,它确保同一个 group 在测试和训练集中都不被表示。 例如,如果数据是从不同的 subjects 获得的,每个 subject 有多个样本,并且如果模型足够灵活以高度人物指定的特征中学习,则可能无法推广到新的 subject 。 GroupKFold 可以检测到这种过拟合的情况。

下面是交叉验证行为的可视化。
在这里插入图片描述
2. LeaveOneGroupOut 是一个交叉验证方案,它根据第三方提供的 array of integer groups (整数组的数组)来提供样本。这个组信息可以用来编码任意域特定的预定义交叉验证折叠。

每个训练集都是由除特定组别以外的所有样本构成的。

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

  2. GroupShuffleSplit 迭代器是 ShuffleSplit 和 LeavePGroupsOut 的组合,它生成一个随机划分分区的序列,其中为每个分组提供了一个组子集。

下面是交叉验证行为的可视化。
在这里插入图片描述

交叉验证再时间序列数据中的应用

时间序列数据的特点是时间 (autocorrelation(自相关性)) 附近的观测之间的相关性。 然而,传统的交叉验证技术,例如 KFold 和 ShuffleSplit 假设样本是独立的且分布相同的,并且在时间序列数据上会导致训练和测试实例之间不合理的相关性(产生广义误差的估计较差)。 因此,对未来观测的时间序列数据模型的评估至少与用于训练模型的观测模型非常重要。为了达到这个目的,一个解决方案是由 TimeSeriesSplit 提供的。

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

下面是交叉验证行为的可视化。
在这里插入图片描述

一个综合的例子:

from sklearn.model_selection import train_test_split,cross_val_score,cross_validate from sklearn.model_selection import KFold,LeaveOneOut,LeavePOut,ShuffleSplitfrom sklearn.model_selection import StratifiedKFold,StratifiedShuffleSplit
from sklearn.model_selection import GroupKFold,LeaveOneGroupOut,LeavePGroupsOut,GroupShuffleSplit 
from sklearn.model_selection import TimeSeriesSplit # 时间序列分割
from sklearn import datasetsfrom sklearn import svm  
from sklearn import preprocessing
from sklearn.metrics import recall_score

iris = datasets.load_iris()  # 加载数据集
print('样本集大小:',iris.data.shape,iris.target.shape)

# 我们使用鸢尾花数据集来看一下train_test_split函数的使用
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)  # 交叉验证划分训练集和测试集.test_size为测试集所占的比例
print('训练集大小:',X_train.shape,y_train.shape)
print('测试集大小:',X_test.shape,y_test.shape)  
clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train) # 使用训练集训练模型,这里使用支持向量机的方法
print('准确率:',clf.score(X_test, y_test))  # 计算测试集的度量值(准确率)


#  如果涉及到归一化,则在测试集上也要使用训练集模型提取的归一化函数。
scaler = preprocessing.StandardScaler().fit(X_train)  # 通过训练集获得归一化函数模型,在训练集和测试集上都使用这个归一化函数
X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(kernel='linear', C=1).fit(X_train_transformed, y_train) # 使用训练集训练模型
X_test_transformed = scaler.transform(X_test)
print('归一化后测试集的准确度:',clf.score(X_test_transformed, y_test))  # 计算测试集的度量值(准确度)

# ===================================直接调用交叉验证评估模型==========================
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)  #cv为迭代次数。
print(scores)  # 打印输出每次迭代的度量值(准确度)
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))  # 获取置信区间。(也就是均值和方差)

# ===================================多种度量结果======================================
scoring = ['precision_macro', 'recall_macro'] # precision_macro为精度,recall_macro为召回率
scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=True)
sorted(scores.keys())
print('测试结果:',scores)  # scores类型为字典。包含训练得分,拟合次数, score-times (得分次数)


# ==================================K折交叉验证、留一交叉验证、留p交叉验证、随机排列交叉验证==========================================
# k折划分子集
kf = KFold(n_splits=2)
for train, test in kf.split(iris.data):
    print("k折划分:%s %s" % (train.shape, test.shape))
    break

# 留一划分子集
loo = LeaveOneOut()
for train, test in loo.split(iris.data):
    print("留一划分:%s %s" % (train.shape, test.shape))
    break

# 留p划分子集
lpo = LeavePOut(p=2)
for train, test in loo.split(iris.data):
    print("留p划分:%s %s" % (train.shape, test.shape))
    break

# 随机排列划分子集
ss = ShuffleSplit(n_splits=3, test_size=0.25,random_state=0)
for train_index, test_index in ss.split(iris.data):
    print("随机排列划分:%s %s" % (train.shape, test.shape))
    break

# ==================================分层K折交叉验证、分层随机交叉验证==========================================
skf = StratifiedKFold(n_splits=3)  #各个类别的比例大致和完整数据集中相同
for train, test in skf.split(iris.data, iris.target):
    print("分层K折划分:%s %s" % (train.shape, test.shape))
    break

skf = StratifiedShuffleSplit(n_splits=3)  # 划分中每个类的比例和完整数据集中的相同
for train, test in skf.split(iris.data, iris.target):
    print("分层随机划分:%s %s" % (train.shape, test.shape))
    break


# ==================================组 k-fold交叉验证、留一组交叉验证、留 P 组交叉验证、Group Shuffle Split==========================================
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]

# k折分组
gkf = GroupKFold(n_splits=3)  # 训练集和测试集属于不同的组
for train, test in gkf.split(X, y, groups=groups):
    print("组 k-fold分割:%s %s" % (train, test))

# 留一分组
logo = LeaveOneGroupOut()
for train, test in logo.split(X, y, groups=groups):
    print("留一组分割:%s %s" % (train, test))

# 留p分组
lpgo = LeavePGroupsOut(n_groups=2)
for train, test in lpgo.split(X, y, groups=groups):
    print("留 P 组分割:%s %s" % (train, test))

# 随机分组
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))


# ==================================时间序列分割==========================================
tscv = TimeSeriesSplit(n_splits=3)
TimeSeriesSplit(max_train_size=None, n_splits=3)
for train, test in tscv.split(iris.data):
    print("时间序列分割:%s %s" % (train, test))

结果
在这里插入图片描述

参见:
sklearn交叉验证中文文档
cross_validation
交叉验证

猜你喜欢

转载自blog.csdn.net/Forlogen/article/details/85223042