交叉验证是什么?
在模型建立中,通常有两个数据集:训练集(train)和测试集(test)。训练集用来训练模型;测试集是完全不参与训练的数据,仅仅用来观测测试效果的数据。
一般情况下,训练的结果对于训练集的拟合程度通常还是挺好的,但是在测试集总的表现却可能不行。比如下面的例子:
图一的模型是一条线型方程。 可以看到,所有的红点都不在蓝线上,所以导致了错误率很高,这是典型的不拟合的情况
图二 的蓝线则更加贴近实际的红点,虽然没有完全重合,但是可以看出模型表示的关系是正确的。
图三,所有点都在蓝线上,这时候模型计算出的错误率很低,(甚至将噪音都考虑进去了)。这个模型只在训练集中表现很好,在测试集中的表现就不行。 这是典型的‘过拟合’情况。
留一验证(LOOCV,Leave one out cross validation )
只从可用的数据集中保留一个数据点,并根据其余数据训练模型。此过程对每个数据点进行迭代,比如有n个数据点,就要重复交叉验证n次。例如下图,一共10个数据,就交叉验证十次。
优点:
适合小样本数据集
利用所有的数据点,因此偏差将很低
缺点:
重复交叉验证过程n次导致更高的执行时间
测试模型有效性的变化大。因为针对一个数据点进行测试,模型的估计值受到数据点的很大影响。如果数据点被证明是一个离群值,它可能导致更大的变化
- from sklearn.model_selection import LeavePOut
- lpo = LeavePOut(p=200)
- for k, (train_index, test_index) in enumerate(lpo.split(train)):
- train_data, test_data, train_target, test_target = train.iloc[
- train_index], train.iloc[test_index], target[train_index], target[
- test_index]
普通交叉验证:
交叉验证的步骤如下:
保留一个样本数据集, (取出训练集中20%的样本不用)
使用数据集的剩余部分训练模型 (使用另外的80%样本训练模型)
使用验证集的保留样本。(完成模型后,在20%的样本中测试)
如果模型在验证数据上提供了一个肯定的结果,那么继续使用当前的模型。
优点: 简单方便。直接将训练集按比例拆分成训练集和验证集,比如50:50。
缺点: 没有充分利用数据, 结果具有偶然性。如果按50:50分,会损失掉另外50%的数据信息,因为我们没有利用着50%的数据来训练模型。
- # 切分数据 训练数据80% 验证数据20%
- train_all, test_all, train_target, test_target = train_test_split(
- traindf, target, test_size=0.2, random_state=0)
K折交叉验证(k-fold cross validation)
针对上面通过train_test_split划分,从而进行模型评估方式存在的弊端,提出Cross Validation 交叉验证。
Cross Validation:简言之,就是进行多次train_test_split划分;每次划分时,在不同的数据集上进行训练、测试评估,从而得出一个评价结果;如果是5折交叉验证,意思就是在原始数据集上,进行5次划分,每次划分进行一次训练、评估(每次都是一个普通的交叉验证),最后得到5次划分后的评估结果,一般在这几次评估结果上取平均得到最后的 评分。k-fold cross-validation ,其中,k一般取5或10。
优点:
适合大样本的数据集
经过多次划分,大大降低了结果的偶然性,从而提高了模型的准确性。
对数据的使用效率更高。train_test_split,默认训练集、测试集比例为3:1。如果是5折交叉验证,训练集比测试集为4:1;10折交叉验证训练集比测试集为9:1。数据量越大,模型准确率越高。
缺点:未考虑类别的影响
对数据随机均等划分,不适合包含不同类别的数据集。比如:数据集有5类数据(ABCDE各占20%),抽取出来的也正好是按照类别划分的5类,第一折全是A,第二折全是B……这样就会导致,模型学习到测试集中数据的特点,用BCDE训练的模型去测试A类数据、ACDE的模型测试B类数据,这样准确率就会很低。
- from sklearn.datasets import load_digits
- from sklearn.model_selection import KFold
- import pandas as pd
- # 加载测试专用数据集
- train, target = load_digits(return_X_y=True)
- train = pd.DataFrame(train)
- target = pd.DataFrame(target)
- # K折交叉验证 K-fold CV
- # 5折交叉验证
- kf = KFold(n_splits=5)
-
- for k, (train_index, test_index) in enumerate(kf.split(train)):
- train_data, test_data, train_target, test_target = train.iloc[
- train_index], train.iloc[test_index], target.iloc[train_index], \
- target.iloc[
- test_index]
分层是重新将数据排列组合,使得每一折都能比较好地代表整体。
比如下面这个例子:在一个二分类问题上,原始数据一共有两类(F和M),F:M的数据量比例大概是 1:3;划分了5折,每一折中F和M的比例都保持和原数据一致(1:3)。
这样就避免了随机划分可能产生的的情况,像是一折全是F,其他3折都是M。
下图是标准交叉验证和分层交叉验证的区别:
标准交叉验证(即K折交叉验证):直接将数据分成几折;
分层交叉验证:先将数据分类(class1,2,3),然后在每个类别中划分三折。
StratifiedKFold用法类似Kfold,但是它是分层采样,确保训练集,验证集中各类别样本的比例与原始数据集中相同。因此一般使用StratifiedKFold。
- from sklearn.model_selection import StratifiedKFold
-
- kf = StratifiedKFold(n_splits=5)
- for k, (train_index, test_index) in enumerate(kf.split(train, target)):
- train_data, test_data, train_target, test_target = train.iloc[
- train_index], train.iloc[test_index], target[train_index], target[
- test_index]