一、交叉验证是何方神圣?
在机器学习的广袤天地里,对模型的性能评估可是重中之重。传统的验证方式,是将数据集简单粗暴地划分为训练集和测试集。这就好比你只用了一部分数据去训练模型,然后拿另一小部分数据来测试,却妄想着用这个测试结果能解释模型在所有数据上的表现,这是不是有点自欺欺人、管中窥豹?因为这样得到的评估结果往往不太稳定,也不太全面,就好像你只看了冰山一角,很难判断整座冰山的真实模样。
而交叉验证,就像是一位严谨的裁判,它会将数据集进行多次划分,分成几个互不干扰的子集,然后用这些子集来交替进行模型的训练与验证。打个比方,就像是一场多轮的知识竞赛,每一轮都用不同的题目组合来考验选手(模型),最后综合所有轮次的表现来评判选手的真实水平。这种方式能让我们更全面、更稳定地了解模型的性能,避免因为单次划分数据而带来的片面性和偶然性。
二、交叉验证的核心思想
如上图所示,交叉验证的核心思想,就在于对数据集进行多次划分,然后对多次评估的结果取平均值,这样就能消除单次划分时数据划分不平衡而造成的不良影响。尤其在小规模数据集上,这种数据划分不平衡的问题更容易出现,所以交叉验证在小规模数据集上的优势就更加明显。
比如说,我们要评估一个学生的学习成果,如果只看他一次考试的成绩,可能会因为这次考试的题目难度、他当时的考试状态等因素而有偏差。但如果让他参加多次不同的考试,综合这些考试的成绩来评估,就能更准确地了解他的真实水平。交叉验证就如同让模型参加多场不同的 “考试”,以此来获得更可靠的性能评估。
如上图所示,机器学习中的数据集通常被分为三类:训练数据集(Training dataset)、验证数据集(Validation dataset)和测试数据集(Testing dataset 或 Test dataset)。
训练集是用于训练模型的数据集。模型通过学习训练集中的数据特征和标签(如果是监督学习)之间的关系来调整自身的参数,把模型比作一个学生,就好比学生通过数据学习知识,
验证集是用于在模型训练过程中调整模型超参数或者评估模型在不同训练阶段的性能的数据集。它是从原始数据集中划分出来的一部分,和训练集是相互独立的,就好比学生(模型)通过课后练习验证自己的知识理解程度或掌握水平。
测试集是用于评估最终模型性能的数据集。它是在模型训练和超参数调整完成后,用来模拟模型在实际应用中的性能,就好比学生(模型)参加期末考试,考试中出现的题都是自己没见过的,才能检验出学生是否真的学到了知识。
下图展示了训练集、验证集、测试集在模型建模和评估中的作用:训练集用于帮助模型拟合数据,验证集帮助模型不断缩小观测值与预测值之间的误差,当模型在测试集和验证集上的表现取得平衡(通常用于防止产生欠拟合或者过拟合)后,用测试集来评估模型在面对新数据集时的表现。
一个形象的比喻:
假设你正在开发一个图像分类器,用于识别猫、狗和马的图片。你有10000张带标签的图片。
-
训练数据集:你可以拿出6000张图片来训练你的模型。模型会学习这些图片中的特征,比如猫的耳朵形状、狗的眼睛大小等。
-
验证数据集:然后,你可以从剩下的图片中取出2000张作为验证数据集。在这个阶段,你可能尝试不同的模型(比如决策树,神经网络等)或者调整模型的超参数(比如神经网络的层数,决策树的深度等),然后根据模型在验证数据集上的表现来选择最好的模型和参数组合的配置。
-
测试数据集:最后,你可以用剩下的2000张图片作为测试数据集。这个数据集可以用来评估你选择的模型在未见过的数据上的表现,这将是你模型的最终评估得分。
小结:
数据集划分 | 描述 | 用途 |
---|---|---|
训练数据集 | 用于训练机器学习模型。模型尝试学习此数据集中的模式和关系,并调整其参数以最小化预测误差。 | 用于“学习”和调整模型参数 |
验证数据集 | 在模型训练过程中,用于评估模型的表现并进行超参数优化。这部分数据不参与模型的训练,但是可以帮助我们做出如何调整模型复杂性等决策。 | 用于模型选择和超参数调整 |
测试数据集 | 在模型训练和验证完成后,测试数据集用于评估模型在未见过的数据上的表现。这可以给我们一个更真实的模型性能估计 | 用于最终评估模型性能 |
三、交叉验证的七种方式
3.1 简单交叉验证
简单交叉验证(Cross Validation)是对原始数据集进行 1 次随机(默认随机)的划分,划分为两组,一组作为训练集,另一组作为验证集。这样会得到 1 个模型,通常划分 30% 的数据作为测试数据。
比如说,我们有一个数据集,就像一个装满各种水果的篮子,里面有苹果、香蕉、橘子等。我们把这个 “水果篮子” 里的水果随机分成两堆,一堆用来学习如何识别水果(训练集),另一堆用来检验我们学得怎么样(验证集)。假如我们发现用这两堆水果得到的模型对苹果的识别不太准确,那我们可以把水果重新打乱,再分一次,看看这次的模型效果会不会更好。通过多次这样的尝试,我们就能找到一个相对较好的模型啦。
下面是简单交叉验证的示意图:
训练集(70%) ------ 模型训练 ------ 模型评估 ------ 验证集(30%)
3.2 随机打乱交叉验证
随机打乱交叉验证(Shuffle Cross Validation)是对原始数据集进行 n 次随机划分,每次划分为两组,一组作为训练集,另一组作为验证集。这样会得到 n 个模型,最终,将 n 个模型的性能指标的平均值作为该种模型的性能指标。
这就好比我们有一群小朋友在玩猜数字游戏,每次游戏时,我们都把小朋友们随机分成两组,一组小朋友负责想数字(训练集),另一组小朋友负责猜数字(验证集)。玩了 n 次之后,我们把每次游戏中小朋友们猜数字的准确率平均一下,就得到了一个相对更可靠的准确率指标,以此来判断这群小朋友在猜数字游戏上的整体水平。
随机打乱交叉验证的示意图如下:
随机打乱交叉验证的示意图如下:
第1次划分:训练集1 ------ 模型训练 ------ 模型评估 ------ 验证集1
第2次划分:训练集2 ------ 模型训练 ------ 模型评估 ------ 验证集2
...
第n次划分:训练集n ------ 模型训练 ------ 模型评估 ------ 验证集n
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型n性能指标) / n
3.3 K 折交叉验证
K 折交叉验证(K-Fold Cross Validation)是将原始数据集分为 K 组(一般是均分为 K 个连续的折叠),然后将每个子集数据分别做一次验证集,其余 K - 1 组子集数据作为训练集。这样会得到 K 个模型,最终,将 K 个模型的性能指标的平均值作为该种模型的性能指标。通常 K 大于或等于 3。
想象我们有一本厚厚的漫画书,要把它分成 K 个小部分。我们先把第一小部分拿出来当作验证集,其余的 K - 1 个小部分用来学习漫画里的故事和绘画风格(训练集),然后根据这个训练结果来评估对第一小部分漫画的理解程度。接着,我们再把第二小部分拿出来当验证集,重复前面的过程,直到每一小部分都当过一次验证集。最后,把这 K 次评估的结果平均一下,就知道我们对这本漫画书的整体理解程度有多高啦。
以下是 K 折交叉验证的示例图:
第1折:训练集(第2 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(第1折)
第2折:训练集(第1, 3 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(第2折)
...
第K折:训练集(第1 - K - 1折) ------ 模型训练 ------ 模型评估 ------ 验证集(第K折)
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型K性能指标) / K
3.4 分层 K 折交叉验证
分层 K 折交叉验证(StratifiedKFold Cross Validation)适用于目标变量是具有类别标签的分类问题。它根据目标变量的类别分布来划分数据集,尽量确保每个折叠中的类别比例与整个数据集中的类别比例大致相似,避免目标变量的不同类别标签的数量分布不均衡。这样会得到 K 个模型,最终,将 K 个模型的性能指标的平均值作为该种模型的性能指标。
例如,我们有一个动物数据集,里面有猫、狗、兔子等不同种类的动物,而且它们的数量分布不太均匀。如果直接用普通的 K 折交叉验证,可能会导致某些折里全是猫,而另一些折里几乎没有猫,这样模型在训练和评估时就会出现偏差。分层 K 折交叉验证就像是一个聪明的分类大师,它会先仔细数清楚每种动物的数量,然后按照比例把它们均匀地分配到 K 个折叠中,这样每个折叠里都有差不多比例的猫、狗、兔子等动物。然后再进行交叉验证,这样得到的模型评估结果就更能反映模型在不同类别动物上的真实表现啦。
分层 K 折交叉验证的示意图如下:
第1折:训练集(按类别比例划分,第2 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(按类别比例划分,第1折)
第2折:训练集(按类别比例划分,第1, 3 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(按类别比例划分,第2折)
...
第K折:训练集(按类别比例划分,第1 - K - 1折) ------ 模型训练 ------ 模型评估 ------ 验证集(按类别比例划分,第K折)
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型K性能指标) / K
3.5 分组 K 折交叉验证
分组 K 折交叉验证(GroupKFold Cross Validation)适用于某些特征变量为类别标签的数据集。它根据指定的分组数据,将样本分为不同的组(一般是按照某个指定的特征变量),尽量确保相同特征的样本在同一个折叠中。这样会得到 K 个模型,最终,将 K 个模型的性能指标的平均值作为该种模型的性能指标。
假设我们有一个学生的考试成绩数据集,这些学生来自不同的班级。分组 K 折交叉验证会把同一个班级的学生数据放在一起,形成一个组。然后在进行交叉验证时,确保每个折叠里要么全是这个班级的学生数据,要么全是那个班级的学生数据,不会把不同班级的学生数据混在一起。这样,我们就可以评估模型在不同班级学生数据上的表现,看看这个模型是不是对每个班级的学生都能很好地预测成绩,还是只对某些班级的学生效果好。
分组 K 折交叉验证的图形解释如下:
第1组:训练集(其他组,第2 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(第1组,第1折)
第2组:训练集(其他组,第1, 3 - K折) ------ 模型训练 ------ 模型评估 ------ 验证集(第2组,第2折)
...
第K组:训练集(其他组,第1 - K - 1折) ------ 模型训练 ------ 模型评估 ------ 验证集(第K组,第K折)
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型K性能指标) / K
3.6 留一法交叉验证
留一法交叉验证(Leave-One-Out Cross Validation,LOO-CV)是将原始数据集的 1 个样本作为验证集,其余 N - 1 个样本作为训练集。这样会得到 N 个模型,最终,将 N 个模型的性能指标的平均值作为该种模型的性能指标。
就好像我们有一个装满星星的魔法盒子,每次我们从盒子里拿出一颗星星当作验证集,剩下的星星都用来研究这些星星的魔法属性(训练集),然后看看我们对这颗单独星星的魔法属性预测得准不准。因为每次只拿出一颗星星,所以我们要重复这个过程 N 次,直到每颗星星都被单独拿出来当过一次验证集。最后把这 N 次的预测结果综合起来,就能知道我们对整个盒子里星星的魔法属性了解得怎么样啦。
留一法交叉验证的示意图如下:
第1个样本:训练集(第2 - N个样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第1个样本)
第2个样本:训练集(第1, 3 - N个样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第2个样本)
...
第N个样本:训练集(第1 - N - 1个样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第N个样本)
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型N性能指标) / N
3.7 留 P 法交叉验证
留 P 法交叉验证(Leave-P-Out Cross Validation,LPO-CV)与留一法交叉验证类似,是将原始数据集的 P 个样本作为验证集,其余 N - P 个样本作为训练集。这样会得到C(N,k)= 个模型,最终,将 个模型的性能指标的平均值作为该种模型的性能指标。
这就好比我们有一群小精灵,我们每次选出 P 个小精灵站出来当作验证集,剩下的 N - P 个小精灵在后面研究魔法技能(训练集),然后对这 P 个小精灵的魔法能力进行评估。因为从 N 个小精灵中选 P 个小精灵的组合方式有 种,所以我们要进行 次这样的过程,最后把所有的评估结果平均起来,就得到了关于这群小精灵魔法能力评估的一个综合指标。
留 P 法交叉验证的图形解释如下:
第1组P个样本:训练集(其余样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第1组P个样本)
第2组P个样本:训练集(其余样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第2组P个样本)
...
第C(N,k)组P个样本:训练集(其余样本) ------ 模型训练 ------ 模型评估 ------ 验证集(第C(N,k)组P个样本)
最终性能指标 = (模型1性能指标 + 模型2性能指标 +... + 模型C(N,k)性能指标) / C(N,k)
四、交叉验证的类名、作用、主要参数和主要属性或函数
在sklearn中,有关交叉验证的类均在model_selection
子模块中。下面是一个表格,总结了上述 7 种交叉验证方法的类名、作用、主要参数和主要属性或方法:
类名 | 作用 | 主要参数 | 主要属性或方法 |
---|---|---|---|
train_test_split() | 简单交叉验证 | arrays: 输入的数据集,可以是一个或多个数组(特征矩阵和目标变量)。 test_size:测试集的大小,默认为0.25。此外,train_size表示训练集的大小,为1-test_size。 shuffle:是否在划分前对数据进行随机打乱,默认为True。 random_state:随机种子,用于控制划分过程的随机性。指定相同的随机种子会得到相同的划分结果。 | 无 |
ShuffleSplit() | 随机交叉验证 | n_splits:数据集被划分的次数。其余参数同上。 | get_n_splits():获取数据集被划分的次数,即模型的个数。 |
KFold() | K折交叉验证 | n_splits:数据集被划分(折叠)的次数K,默认为5。其余参数同上。 | 同上。 |
StratifiedKFold() | 分层交叉验证 | 参数同上。 | 同上。 |
GroupKFold() | 分组交叉验证 | 参数同上。 | 同上。 |
LeaveOneOut() | 留1法交叉验证 | 无 | 同上。 |
LeavePOut() | 留P法交叉验证 | p:验证集中样本的数量p。 | 同上。 |
注意:
- 上述7种交叉验证中,只有train_test_split()仅对数据集进行1次划分,训练出1个模型。其余6个都会对数据集进行多次划分,训练出多个模型。
- 上述7种交叉验证中,只有train_test_split()可以直接对数据集进行划分,并返回训练特征集、测试特征集、训练目标集、测试目标集。其余6个都必须先**进行实例化,然后通过split()方法对数据集进行划分,并且不直接返回数据集,而是返回划分数据集的索引**。
- 除train_test_split()外,其余6个都通过split()方法对数据集进行划分,并通过**get_n_splits()方法**获取数据集被划分的次数,即模型的个数。
五、使用各种交叉验证方法的注意事项:
- 数据平衡性:对于不平衡数据集,分层 K折交叉验证通常是更好的选择,可避免某些类别在验证集中缺失或占比过低,导致模型评估有偏差。例如在医学图像分类中,病变样本较少,若普通 K折交叉验证可能使某些折里没有病变样本,分层 K 折交叉验证能保证每个折都有一定比例病变样本。
- 数据集大小:小数据集使用留一法交叉验证或分层 K 折交叉验证可能更合适,能充分利用数据;大数据集则可考虑 K折交叉验证或随机打乱交叉验证,计算成本和时间更合理。如在处理大规模电商交易数据时,K折交叉验证效率较高;而在研究稀有疾病数据(样本少)时,留一法交叉验证可更精准评估模型。
- 数据顺序:时间序列数据不能用随机打乱的交叉验证方法(如普通 K折交叉验证、随机打乱交叉验证),应采用专门的时间序列交叉验证方法或考虑数据顺序的分层 K折交叉验证(若类别与时间相关)。例如股票价格预测,不能随机打乱数据顺序,需按时间顺序划分训练集和验证集。
- 计算成本:留一法交叉验证和留 P法交叉验证计算成本高,因为要训练大量模型。数据集大或模型训练复杂时,可能导致计算资源耗尽或时间过长。比如在训练深度神经网络处理海量图像数据时,留一法交叉验证几乎不可行,而K 折交叉验证更实用。
六、案例展示
6.1 简单交叉验证
使用 train_test_split 函数将数据集划分为训练集和测试集,测试集占比 30%。
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, ShuffleSplit, KFold, StratifiedKFold, GroupKFold, LeaveOneOut, LeavePOut# 数据
df = pd.DataFrame({'Height': [162, 182, 176, 170, 166, 181, 181, 176, 184, 179, 180, 175],'Weight': [50, 72, 69, 62, 59, 71, 80, 60, 67, 63, 69, 60],'Gender': ['F', 'M', 'M', 'F', 'M', 'F', 'U', 'F', 'U', 'F', 'M', 'U'],'Athletic_Ability':['L1', 'L1', 'L1', 'L1', 'L1', 'L2', 'L3', 'L2', 'L3', 'L3', 'L2', 'L1']
})
# 提取特征数据,存放在X中,这里的特征有身高、体重、性别
X = df[['Height', 'Weight', 'Gender']]
# 提取目标数据,也就是运动能力等级,存放在y中
y = df['Athletic_Ability'] # 简单交叉验证:train_test_split()
# 将数据集X和对应的标签y按照7:3的比例划分为训练集和测试集
# shuffle=True表示在划分前先打乱数据顺序,random_state=1用于固定随机数种子,保证每次运行代码划分结果一致
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3,shuffle=True,random_state=1)
print(X_train, X_test, y_train, y_test)
输入结果如下:
( Height Weight Gender1 182 72 M6 181 80 U0 162 50 F7 176 60 F11 175 60 U9 179 63 F8 184 67 U5 181 71 F,Height Weight Gender2 176 69 M3 170 62 F4 166 59 M10 180 69 M,1 L16 L30 L17 L211 L19 L38 L35 L2Name: Athletic_Ability, dtype: object,2 L13 L14 L110 L2Name: Athletic_Ability, dtype: object)
可以明显看出,训练集有8个样本,约为总样本的70%;测试集有4个样本,约为总样本的30%。
6.2 随机交叉验证
使用 S h u f f l e S p l i t {{ShuffleSplit}} ShuffleSplit 进行随机交叉验证,将数据集进行 4 次划分,每次划分中测试集占比 30%。
# 2、随机交叉验证:ShuffleSplit()
# 创建一个ShuffleSplit对象,n_splits=4表示将数据集划分4次,test_size=0.3表示测试集占比30%,random_state=1用于固定随机种子
ss = ShuffleSplit(n_splits=4, test_size=0.3, random_state=1)
# 调用split方法,它会生成一个生成器,这个生成器能用来遍历每次划分的训练集和测试集的索引
ss.split(X, y) # 遍历生成器,enumerate函数同时给出索引i和每次划分对应的索引元组data_indices
for i, data_indices in enumerate(ss.split(X, y)): # 打印当前划分的轮次i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集被划分的总次数,也就是最终会训练和评估的模型个数
print(ss.get_n_splits())
运行结果:
0 [ 1 6 0 7 11 9 8 5] [ 2 3 4 10]
1 [ 8 10 5 11 4 2 9 6] [1 7 0 3]
2 [6 0 8 1 9 7 4 2] [11 5 3 10]
3 [ 3 5 7 11 10 0 1 9] [6 8 2 4]
可见,进行了4次随机交叉,每次交叉训练集有8个样本,约为总样本的70%;测试集有4个样本,约为总样本的30%。
6.3 K 折交叉验证
使用 K F o l d {{KFold}} KFold 进行 3 折交叉验证,将数据集划分为 3 个子集,每个子集轮流作为验证集。
# 3、K折交叉验证:KFold()
# 创建一个KFold对象,参数3表示将数据集划分为3折
kf = KFold(3)
# 遍历KFold对象生成的生成器,每次迭代会给出折数索引i,以及当前折划分下的训练集和测试集的索引data_indices
for i, data_indices in enumerate(kf.split(X, y)): # 打印当前折数i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集被划分的折数,这里是3折
print(kf.get_n_splits())
运行结果:
0 [ 4 5 6 7 8 9 10 11] [0 1 2 3]
1 [ 0 1 2 3 8 9 10 11] [4 5 6 7]
2 [0 1 2 3 4 5 6 7] [ 8 9 10 11]
可见,进行了3折交叉,每个验证集中的样本数量为总样本数量的1/3,也就是12/3 = 4个,而且是以“组”的形式进行交换的。
6.4 分层K折交叉验证
使用 S t r a t i f i e d K F o l d {{StratifiedKFold}} StratifiedKFold 进行分层交叉验证,将数据集划分为 3 折,确保每折的类别比例与原始数据集相似,适用于分类问题。
# 4、分层K折交叉验证:StratifiedKFold()
# 创建StratifiedKFold对象,参数3表示将数据集划分为3折
# 它会尽量保持各折中目标变量(y)的类别分布与原始数据集一致
skf = StratifiedKFold(3)
# 遍历StratifiedKFold对象生成的生成器,enumerate会给出当前折数索引i
# 以及对应的训练集和测试集的索引data_indices
for i, data_indices in enumerate(skf.split(X, y)): # 打印当前折数i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集被划分的折数
print(skf.get_n_splits())
运行结果:
0 [ 2 3 4 7 8 9 10 11] [0 1 5 6]
1 [ 0 1 4 5 6 9 10 11] [2 3 7 8]
2 [0 1 2 3 5 6 7 8] [ 4 9 10 11]
观察一下标签的分布:[‘L1’, ‘L1’, ‘L1’, ‘L1’, ‘L1’, ‘L2’, ‘L3’, ‘L2’, ‘L3’, ‘L3’, ‘L2’, ‘L1’],其中L1、L2、L3的占比为2:1:1,这个比例在分层K折交叉验证的各个子集中也依然适用。
对比一下,我们发现分层K折之后的每个组内,样本分类的比例保持着不变。
6.5 分组交叉验证
使用 G r o u p K F o l d {{GroupKFold}} GroupKFold 进行分组交叉验证,根据Gender特征将数据分为 3 组,确保相同Gender的样本在同一组中,用于特定的分组数据场景。
# 5、分组交叉验证:GroupKFold()
# 创建GroupKFold对象,参数3表示将数据集划分为3组
gkf = GroupKFold(3)
# 遍历GroupKFold生成的生成器,enumerate获取轮次索引i
# split方法传入特征X、标签y和分组依据df['Gender'],确保同一分组不会同时出现在训练集和测试集中
for i, data_indices in enumerate(gkf.split(X, y, groups=df['Gender'])): # 打印轮次i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集被划分的组数
print(gkf.get_n_splits())
运行结果:
0 [ 1 2 4 6 8 10 11] [0 3 5 7 9]
1 [ 0 3 5 6 7 8 9 11] [ 1 2 4 10]
2 [ 0 1 2 3 4 5 7 9 10] [ 6 8 11]
很明显,在这里是根据[‘Gender’]性别来进行分组的。分组的结果如下图所示:
显而易见,交叉分组是按照[‘Gender’]中的不同的值进行分组的。
6.6 留一法交叉验证
使用 L e a v e O n e O u t {{LeaveOneOut}} LeaveOneOut 进行留一法交叉验证,每次将一个样本作为验证集,其余样本作为训练集,适用于小数据集。
# 6、留一法交叉验证:LeaveOneOut()
# 创建LeaveOneOut对象,该方法每次从数据集中留出一个样本作为测试集
loo = LeaveOneOut()
# 遍历LeaveOneOut生成的生成器,enumerate用来获取当前轮次索引i
# split方法划分出每次的训练集和测试集的索引data_indices
for i, data_indices in enumerate(loo.split(X, y)): # 打印当前轮次i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集划分的总次数,对于留一法,划分次数等于样本总数
print(loo.get_n_splits(X))
运行结果:
0 [ 1 2 3 4 5 6 7 8 9 10 11] [0]
1 [ 0 2 3 4 5 6 7 8 9 10 11] [1]
2 [ 0 1 3 4 5 6 7 8 9 10 11] [2]
3 [ 0 1 2 4 5 6 7 8 9 10 11] [3]
4 [ 0 1 2 3 5 6 7 8 9 10 11] [4]
5 [ 0 1 2 3 4 6 7 8 9 10 11] [5]
6 [ 0 1 2 3 4 5 7 8 9 10 11] [6]
7 [ 0 1 2 3 4 5 6 8 9 10 11] [7]
8 [ 0 1 2 3 4 5 6 7 9 10 11] [8]
9 [ 0 1 2 3 4 5 6 7 8 10 11] [9]
10 [ 0 1 2 3 4 5 6 7 8 9 11] [10]
11 [ 0 1 2 3 4 5 6 7 8 9 10] [11]
很明显,留一法中,每次留出一个样本做本验证集,其他的样本作为训练集,共有n次验证。
6.7 留P法交叉验证
使用 L e a v e P O u t {{LeavePOut}} LeavePOut 进行留 P 法交叉验证,这里设置p = 4,即每次将 4 个样本作为验证集,其余样本作为训练集。
# 7、留P法交叉验证:LeavePOut()
# 创建LeavePOut对象,参数p = 4表示每次从数据集中留出4个样本作为测试集
lpo = LeavePOut(p = 4)
# 遍历LeavePOut生成的生成器,enumerate用于获取当前轮次的索引i
# split方法会划分出每次的训练集和测试集的索引data_indices
for i, data_indices in enumerate(lpo.split(X, y)): # 打印当前轮次i,训练集索引data_indices[0],测试集索引data_indices[1]print(i, data_indices[0], data_indices[1]) # 获取数据集划分的总次数,划分次数由样本总数和p值共同决定
print(lpo.get_n_splits(X))
运行结果
0 [ 4 5 6 7 8 9 10 11] [0 1 2 3]
1 [ 3 5 6 7 8 9 10 11] [0 1 2 4]
2 [ 3 4 6 7 8 9 10 11] [0 1 2 5]
3 [ 3 4 5 7 8 9 10 11] [0 1 2 6]
... ... ...
492 [0 1 2 3 4 5 6 9] [ 7 8 10 11]
493 [0 1 2 3 4 5 6 8] [ 7 9 10 11]
494 [0 1 2 3 4 5 6 7] [ 8 9 10 11]
可见,留P法,当p=4时,每次留出4个元素作为验证集,剩下的8个样本作为测试集,这样的组合有495种。
七、总结
交叉验证在机器学习模型评估、调参和特征选择等方面具有重要的作用。通过不同类型的交叉验证方法,我们可以根据数据集的特点和问题的需求,灵活地选择合适的方法来优化模型,提高模型的质量和可靠性。在实际应用中,我们应该充分理解各种交叉验证方法的原理和适用场景,合理地运用它们来解决实际问题。