文章目录
交叉验证迭代器
迭代器根据不同的交叉验证策略用于生成数据下标,进而实现数据集的划分,生成需要的测试集和验证集。
独立且完全相同分布数据
iid数据,independent and identically distributed data,以下方式可用于这种数据的划分。
note:机器学习中假设数据是iid的,然而实际上并不可能。对于已知数据结构的数据,比如时间序列的,采用 time-series aware cross-validation scheme可能更合适;同理,对于拥有分组结构的数据(如从同一个对象上采集的数据,同一个实验中获取的数据),采用group-wise cross-validation更合适。
K-fold
将数据划随机分为K份,利用每次利用其中K-1份进行训练,利用剩余的一份进行验证,循环K次将数据遍历。
import numpy as np
from sklearn.model_selection import KFold
X = np.array(['a','c','f','c','a','a','f'])
kf = KFold(n_splits = 2)
for train,test in kf.split(X):
print(train,test)
[4 5 6] [0 1 2 3]
[0 1 2 3] [4 5 6]
可以看出,K fold并不受数据分布和组、类的影响,有可能把同一组或者类的元素分到不同的样本中。
重复K flod
即将K-fold重复进行n次,可以用于数据量较少时,为了在训练过程中充分利用数据。感觉重复的次数n × K n\times Kn×K应该小于数据量,不然会存在重复划分的数据。同理,RepeatedStratifiedKFold是进行n次重复的StratifiedKFold划分。
import numpy as np
from sklearn.model_selection import RepeatedKFold
X = np.array(['a','c','f','c','a','a','f'])
random_state = 30
rkf = RepeatedKFold(n_splits = 2,n_repeats = 2, random_state = random_state)
for train,test in rkf.split(X):
print(train,test)
[4 5 6] [0 1 2 3]
[0 1 2 3] [4 5 6]
[1 3 6] [0 2 4 5]
[0 2 4 5] [1 3 6]
留一法(Leave one out,LOO)
对于n个数据样本,每次选取其中n-1个作为训练集,剩余的一个作为验证集,重复n次,一点儿都不浪费数据,然而,貌似运算量比较大。一般认为,5-10 fold交叉验证优于LOO法。
同理,Leave P out是留下p个样本作为验证集,对应为LeavePOut。
from sklearn.model_selection import LeaveOneOut
X = np.array(['a','c','f','c','a','a','f'])
loo = LeaveOneOut()
for train,test in loo.split(X):
print(train,test)
[1 2 3 4 5 6] [0]
[0 2 3 4 5 6] [1]
[0 1 3 4 5 6] [2]
[0 1 2 4 5 6] [3]
[0 1 2 3 5 6] [4]
[0 1 2 3 4 6] [5]
[0 1 2 3 4 5] [6]
随机排列的分类 shuffle&split
首先随机打乱数据,然后根据设置的参数划分训练集和测试集。通过设置伪随机数可以控制每次随机打乱的顺序是一样的。ShuffleSplit是K fold的一个比较好的替代。
from sklearn.model_selection import ShuffleSplit
random_state = 30
X = np.array(['a','c','f','c','a','a','f'])
ss = ShuffleSplit(n_splits = 5, test_size = 0.25, random_state=random_state)
for train_index, test_index in ss.split(X):
print(train_index,test_index)
[2 0 4 6 5] [3 1]
[4 5 6 1 3] [0 2]
[4 2 3 6 5] [1 0]
[3 2 0 6 4] [5 1]
[4 2 0 5 6] [3 1]
基于类标签的分层交叉验证迭代器
一些分类问题中数据的分布可能并不是很平衡,例如阴性样本数量比阳性样本数量多很多,这种情况下推荐的方式是使用分层采样的做法: StratifiedKFold或者 StratifiedShuffleSplit,这可以确保在每个训练fold和测试fold中,阴性样本和阳性样本所占的比例是和数据集中的比例基本相同的。
Stratified K fold
Stratified K fold是K fold的一种变化形式,返回分层的folds:每一层中各类样本的比例基本和所有样本中各类的比例相同。
#对比StratifiedKFlod与KFold
import numpy as np
from sklearn.model_selection import StratifiedKFold,KFold
X,y = ([1]*50),np.hstack(([0]*45,[1]*5))
skf = StratifiedKFold(n_splits = 3)
for train_index,test_index in skf.split(X,y):
print('train - {} | test - {}'.format(np.bincount(y[train_index]),np.bincount(y[test_index])))
train - [30 3] | test - [15 2]
train - [30 3] | test - [15 2]
train - [30 4] | test - [15 1]
kf = KFold(n_splits = 3)
for train_index,test_index in kf.split(X,y):
print('train - {} | test - {}'.format(np.bincount(y[train_index]),np.bincount(y[test_index])))
train - [28 5] | test - [17]
train - [28 5] | test - [17]
train - [34] | test - [11 5]
可以看出,使用Stratified的分组,分组中样本比例基本保持在9:1左右,与总样本中比例保持接近。而未使用Stratified的分类,样本中比例比较随机。同理ShuffleSplit也可以使用StratifiedShuffleSplit。
分组数据的交叉验证迭代器
当数据为分组数据时,在划分训练集和测试集的时候,要确保同一组的数据不能同时出现在训练集和测试集。例如采集的不同病人的数据,每一个病人的数据应该为一组,而病人的id可以作为组的标志。
Group k-fold
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_index, test_index in gkf.split(X, y, groups=groups):
print(train_index,test_index)
[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,LeavePGroupsOut,GroupShuffleSplit等。
时间序列数据的交叉验证
TimeSeriesSplit是K-Fold的一个变化形式,他使用前K个folds作为训练集,第K+1个fold作为测试集。
from sklearn.model_selection import TimeSeriesSplit
X = np.array([1,2,3,4,5,6])
tscv = TimeSeriesSplit(n_splits=3)
for train_index, test_index in tscv.split(X):
print(train_index,test_index)
[0 1 2] [3]
[0 1 2 3] [4]
[0 1 2 3 4] [5]