专栏首页数据STUDIO图解机器学习中的 12 种交叉验证技术

图解机器学习中的 12 种交叉验证技术

大家好,我是云朵君!

今天我给大家盘点下机器学习中所使用的交叉验证器都有哪些,用最直观的图解方式来帮助大家理解他们是如何工作的。


数据集说明

数据集来源于kaggle M5 Forecasting - Accuracy[1]

该任务是尽可能精确地预测沃尔玛在美国销售的各种产品的单位销售额(demand)。本文将使用其中的一部分数据。

该数据样例如下。

数据集的划分需要根据交叉验证基本原理来操作。首先需要将所有数据集划分为训练集和测试集,再再训练集中利用交叉验证划分训练集和验证集,如下图所示。

首先按照日期date划分测试集和训练集,如下图所示。

为本次演示需求,创造了一些新的特征,最终筛选并使用了如下几个变量。

Training columns: 
['sell_price', 'year', 'month', 'dayofweek', 'lag_7', 
'rmean_7_7', 'demand_month_mean', 'demand_month_max',
'demandmonth_max_to_min_diff', 'demand_dayofweek_mean', 
'demand_dayofweek_median', 'demand_dayofweek_max']

设置如下两个全局变量,以及用来存储每种交叉验证得分结果的DataFrame

SEED = 888 # 为了再现
NFOLDS = 5 # 设置K折验证的折数
stats = pd.DataFrame(columns=['K-Fold Variation','CV-RMSE','TEST-RMSE'])

交叉验证

交叉验证(Cross Validation) 是在机器学习建立模型和验证模型参数时常用的方法。顾名思义,就是重复的使用数据,把得到的样本数据进行切分,组合为不同的训练集和测试集。用训练集来训练模型,测试集来评估模型的好坏。

交叉验证的目的

  1. 从有限的学习数据中获取尽可能多的有效信息。
  2. 交叉验证从多个方向开始学习样本的,可以有效地避免陷入局部最小值。
  3. 可以在一定程度上避免过拟合问题。

交叉验证的种类

根据切分的方法不同,交叉验证分为下面三种:

第一种是简单交叉验证

首先,随机的将样本数据分为两部分(比如:70%的训练集,30%的测试集),然后用训练集来训练模型,在测试集上验证模型及参数。接着再把样本打乱,重新选择训练集和测试集,继续训练数据和检验模型。最后选择损失函数评估最优的模型和参数。

第二种是K折交叉验证(K-Fold Cross Validation)

和第一种方法不同,

K

折交叉验证会把样本数据随机的分成

K

份,每次随机的选择

K-1

份作为训练集,剩下的1份做测试集。当这一轮完成后,重新随机选择

K-1

份来训练数据。若干轮(小于

K

)之后,选择损失函数评估最优的模型和参数。

第三种是留一交叉验证(Leave-one-out Cross Validation)

它是第二种情况的特例,此时

K

等于样本数

N

,这样对于

N

个样本,每次选择

N-1

个样本来训练数据,留一个样本来验证模型预测的好坏。此方法主要用于样本量非常少的情况,比如对于普通适中问题,

N

小于50时,一般采用留一交叉验证。

下面将用图解方法详细介绍12种交叉验证方法,主要参考scikit-learn官网[2]介绍。

交叉验证器

01 K折交叉验证--没有打乱

K

折交叉验证器 KFold,提供训练/验证索引以拆分训练/验证集中的数据。将数据集拆分为

K

个连续的折叠(默认情况下不改组)。然后将每个折叠用作一次验证,而剩余的

k - 1

个折叠形成训练集。

from sklearn.model_selection import KFold
KFold(n_splits= NFOLDS, shuffle=False, random_state=None) 
CV mean score:  23.64240, std: 1.8744.
Out of sample (test) score: 20.455980

不建议使用这种类型的交叉验证来处理时间序列数据,因为它忽略了数据的连贯性。实际的测试数据是将来的一个时期。

如下图所示,黑色部分为被用作的验证的一个折叠,而黄色部分为被用作训练的

K - 1

个折叠。

另外数据分布图是5折交叉验证中每个验证数据集(黑色部分),及实际用作验证模型的数据集的组合分布图。

02 K折交叉验证--打乱的

K折交叉验证器KFold设置参数shuffle=True

from sklearn.model_selection import KFold
KFold(n_splits= NFOLDS, random_state=SEED, shuffle=True)
CV mean score:  22.65849, std: 1.4224.
Out of sample (test) score: 20.508801

在每次迭代中,五分之一的数据仍然是验证集,但这一次它是随机分布在整个数据中。与前面一样,在一个迭代中用作验证的每个示例永远不会在另一个迭代中用作验证。

如下图所示,黑色部分为被用作验证的数据集,很明显,验证集数据是被打乱了的。

03 随机排列交叉验证

随机排列交叉验证器ShuffleSplit,生成索引以将数据拆分为训练集和验证集。

注意:与其他交叉验证策略相反,随机拆分并不能保证所有折叠都会不同,尽管对于大型数据集来说z这是很有可能。

from sklearn.model_selection import ShuffleSplit
ShuffleSplit(n_splits= NFOLDS, 
             random_state=SEED, 
             train_size=0.7, 
             test_size=0.2)
# 还有0.1的数据是没有被取到的
CV mean score:  22.93248, std: 1.0090.
Out of sample (test) score: 20.539504

ShuffleSplit将在每次迭代过程中随机抽取整个数据集,生成一个训练集和一个验证集。test_sizetrain_size参数控制每次迭代的验证和训练集的大小。因为我们在每次迭代中都是从整个数据集采样,所以在一次迭代中选择的值,可以在另一次迭代中再次选择。

由于部分数据未包含在训练中,该方法比普通的k倍交叉验证更快。

如下图所示,黑色部分为被用作验证的数据集,橙色是被用作训练的数据集,而白色部分为未被包含在训练和验证集中的数据集。

04 分层K折交叉验证--没有打乱

分层

K

折交叉验证器StratifiedKFold。

提供训练/验证索引以拆分训练/验证集中的数据。这个交叉验证对象是 KFold 的一种变体,它返回分层折叠。通过保留每个类别的样本百分比来进行折叠。

from sklearn.model_selection import StratifiedKFold
StratifiedKFold(n_splits= NFOLDS, shuffle=False)
CV mean score: 	22.73248, std: 0.4955.
Out of sample (test) score: 20.599119

就跟普通的

K

折交叉验证类似,但是每折包含每个目标样本的大约相同的百分比。更好地使用分类而不是回归。

其中有几点需要注意:

  • 生成验证集中,使每次切分的训练/验证集中的包含类别分布相同或尽可能接近。
  • shuffle=False时,将保留数据集排序中的顺序依赖关系。也就是说,某些验证集中来自类 k 的所有样本在 y 中是连续的。
  • 生成验证集大小一致,即最小和最大验证集数据数量,最多也就相差一个样本。

如下图所示,在没有打乱的情况下,验证集(图中黑色部分)分布是有一定的规律的。

且从下面的数据分布图可见,5折交叉验证数据密度分布曲线基本重合,说明虽然划分的样本不同,但其分布基本一致。

05 分层K折交叉验证--打乱的

对于每个目标,折叠包大约相同百分比的样本,但首先数据被打乱。这里需要注意的是,该交叉验证的拆分数据方法是一致的,仅仅是在拆分前,先打乱数据的排列,再进行分层

K

折交叉验证。

from sklearn.model_selection import StratifiedKFold 
StratifiedKFold(n_splits= NFOLDS, random_state=SEED, 
                shuffle=True)
CV mean score: 	22.47692, std: 0.9594.
Out of sample (test) score: 20.618389

如下图所示,打乱的分层K折交叉验证的验证集是没有规律、随机分布的。

该交叉验证的数据分布与未被打乱的分层K折交叉验证基本一致。

06 分组K折交叉验证

具有非重叠组的

K

折迭代器变体GroupKFold。

同一组不会出现在两个不同的折叠中(不同组的数量必须至少等于折叠的数量)。这些折叠是近似平衡的,因为每个折叠中不同组的数量是近似相同的。

可以从数据集的另一特定列(年)来定义组。确保同一组中不同时处于训练集和验证集中。

该交叉验证器分组是在方法split中参数groups来体现出来的。

from sklearn.model_selection import GroupKFold
groups = train['year'].tolist()
groupfolds = GroupKFold(n_splits=NFOLDS)
groupfolds.split(X_train,Y_train, groups=groups)
CV mean score: 	23.21066, std: 2.7148.
Out of sample (test) score: 20.550477

如下图所示,由于数据集原因(不是包含5个整年(组)),因此5折交叉验证中,并不能保证没次都包含相同数据数量的验证集。

在上一个示例中,我们使用年作为组,在下一个示例中使用月作为组。大家可以通过下面图可以很明显地看看有什么区别。

from sklearn.model_selection import GroupKFold
groups = train['month'].tolist()
groupfolds = GroupKFold(n_splits=NFOLDS)
groupfolds.split(X_train,Y_train, groups=groups)
CV mean score: 	22.32342, std: 3.9974.
Out of sample (test) score: 20.481986

如下图所示,每次迭代均是以月为组来取验证集。

07 分组K折交叉验证--留一组

留一组交叉验证器LeaveOneGroupOut。

根据第三方提供的整数组数组保留样本。此组信息可用于编码任意特定于域的预定义交叉验证折叠。

因此,每个训练集由除与特定组相关的样本之外的所有样本构成。

例如,组可以是样本收集的年份、月份等,因此允许针对基于时间的拆分进行交叉验证。

from sklearn.model_selection import LeaveOneGroupOut
groups = train['month'].tolist()
n_folds = train['month'].nunique()
logroupfolds = LeaveOneGroupOut()
logroupfolds.split(X_train,Y_train, groups=groups)
CV mean score:  22.48503, std: 5.6201.
Out of sample (test) score: 20.468222

在每次迭代中,模型都使用留一组之外的所有组的样本进行训练。如果以月份为组,则执行12次迭代。

由下图可以看到该分组K折交叉验证的拆分数据方法。

08 分组K折交叉验证--留N组

LeavePGroupsOut将 P 组留在交叉验证器之外,例如,组可以是样本收集的年份,因此允许针对基于时间的拆分进行交叉验证。

LeavePGroupsOut 和 LeaveOneGroupOut 的区别在于,前者使用所有样本分配到P不同的组值来构建测试集,而后者使用所有分配到相同组的样本。

通过参数n_groups设置要在测试拆分中排除的组数。

from sklearn.model_selection import LeavePGroupsOut
groups = train['year'].tolist()
lpgroupfolds = LeavePGroupsOut(n_groups=2)
lpgroupfolds.split(X_train,Y_train, groups=groups)
CV mean score: 	23.92578, std: 1.2573.
Out of sample (test) score: 90.222850

由下图可知,因K=5n_groups=2,所以共分为10种情况,每种划分的验证集均不相同。

09 随机排列的分组K折交叉验证

Shuffle-Group(s)-Out 交叉验证迭代器GroupShuffleSplit

GroupShuffleSplit迭代器为ShuffleSplit和LeavePGroupsOut的两种方法的结合,并生成一个随机分区序列,其中每个分区都会保留组的一个子集。

例如,组可以是样本收集的年份,因此允许针对基于时间的拆分进行交叉验证。

LeavePGroupsOut 和 GroupShuffleSplit 之间的区别在于,前者使用大小P唯一组的所有子集生成拆分,而 GroupShuffleSplit 生成用户确定数量的随机验证拆分,每个拆分都有用户确定的唯一组比例。

例如,与LeavePGroupsOut(p=10)相比,一个计算强度较小的替代方案是 GroupShuffleSplit(test_size=10, n_splits=100)

注意:参数test_sizetrain_size指的是组,而不是样本,像在 ShuffleSplit 中一样

定义组,并在每次迭代中随机抽样整个数据集,以生成一个训练集和一个验证集。

from sklearn.model_selection import GroupShuffleSplit
groups = train['month'].tolist()
rpgroupfolds = GroupShuffleSplit(n_splits=NFOLDS, train_size=0.7, 
                                 test_size=0.2, random_state=SEED)
rpgroupfolds.split(X_train,Y_train, groups=groups)
CV mean score:  21.62334, std: 2.5657.
Out of sample (test) score: 20.354134

从图中可见,断开(白色)部分为未取到的数据集,每一行中每段(以白色空白为界)中验证集(黑色)比例及位置都是一致的。而不同行之间验证集的位置是不同的。

10 时间序列交叉验证

时间序列数据的特征在于时间上接近的观测值之间的相关性(自相关)。然而,经典的交叉验证技术,例如 KFold 和 ShuffleSplit假设样本是独立的和同分布的,并且会导致时间序列数据的训练和测试实例之间不合理的相关性(产生对泛化误差的不良估计)。

因此,在“未来”观察中评估我们的模型的时间序列数据非常重要,这与用于训练模型的观察最不相似。为了实现这一点,提供了一种解决方案TimeSeriesSplit。

TimeSeriesSplit是KFold的变体,它首先返回

K

折叠成训练集和 第

K+1

折叠作为验证集。请注意,与标准交叉验证方法不同,连续训练集是它们之前的超集。此外,它将所有剩余数据添加到第一个训练分区,该分区始终用于训练模型。

from sklearn.model_selection import TimeSeriesSplit
timeSeriesSplit = TimeSeriesSplit(n_splits= NFOLDS)
CV mean score: 	24.32591, std: 2.0312.
Out of sample (test) score: 20.999613

这种方法建议用于时间序列数据。在时间序列分割中,训练集通常分为两部分。第一部分始终是训练集,而后一部分是验证集。

由下图可知,验证集的长度保持不变,而训练集随着每次迭代的不断增大。

11 封闭时间序列交叉验证

这是自定义的一种交叉验证方法。该方法函数见文末函数附录。

btscv = BlockingTimeSeriesSplit(n_splits=NFOLDS)
CV mean score: 		    22.57081, std: 6.0085.
Out of sample (test) score: 19.896889

由下图可见,训练和验证集在每次迭代中都是唯一的。没有值被使用两次。列车集总是在验证之前。由于在较少的样本中训练,它也比其他交叉验证方法更快。

12 清除K折交叉验证

这是基于_BaseKFold的一种交叉验证方法。在每次迭代中,在训练集之前和之后,我们会删除一些样本。

cont = pd.Series(train.index)
purgedfolds=PurgedKFold(n_splits=NFOLDS,
                        t1=cont, pctEmbargo=0.0)
CV mean score: 		    23.64854, std: 1.9370.
Out of sample (test) score: 20.589597

由下图可看出,训练集前后删除了一些样本。且其划分训练集和验证集的方法与基础不打乱的KFold一致。

embargo设置为大于0的值,将在验证集之后删除额外的样本。

cont = pd.Series(train.index)
purgedfolds=PurgedKFold(n_splits=NFOLDS,t1=cont,pctEmbargo=0.1)
CV mean score: 	23.87267, std: 1.7693.
Out of sample (test) score: 20.414387

由下图可看出,不仅在训练集前后删除了部分样本,在验证集后面也删除了一些样本,这些样本的大小将取决于参数embargo的大小。

各交叉验证结果比较

cm = sns.light_palette("green", as_cmap=True, reverse=True)
stats.style.background_gradient(cmap=cm)

附录

封闭时间序列交叉验证函数

class BlockingTimeSeriesSplit():
    def __init__(self, n_splits):
        self.n_splits = n_splits
    
    def get_n_splits(self, X, y, groups):
        return self.n_splits
      
    def split(self, X, y=None, groups=None):
        n_samples = len(X)
        k_fold_size = n_samples // self.n_splits
        indices = np.arange(n_samples)

        margin = 0
        for i in range(self.n_splits):
            start = i * k_fold_size
            stop = start + k_fold_size
            mid = int(0.9 * (stop - start)) + start
            yield indices[start: mid], indices[mid + margin: stop]

清除K折交叉验证函数

from sklearn.model_selection._split import _BaseKFold
class PurgedKFold(_BaseKFold):
    '''
    扩展KFold类以处理跨越间隔的标签
    在训练集中剔除了重叠的测试标记间隔
    假设测试集是连续的(shuffle=False),中间有w/o训练样本
    '''
    def __init__(self, n_splits=3, t1=None, pctEmbargo=0.1):
        if not isinstance(t1, pd.Series):
            raise ValueError('Label Through Dates must be a pd.Series')
        super(PurgedKFold,self).__init__(n_splits, shuffle=False, random_state=None)
        self.t1 = t1
        self.pctEmbargo = pctEmbargo

    def split(self,X,y=None,groups=None):
        X = pd.DataFrame(X)
        if (X.index==self.t1.index).sum()!=len(self.t1):
            raise ValueError('X and ThruDateValues must have the same index')
        indices = np.arange(X.shape[0])
        mbrg = int(X.shape[0] * self.pctEmbargo)
        test_starts=[(i[0],i[-1]+1) for i in np.array_split(np.arange(X.shape[0]), self.n_splits)]
        for i,j in test_starts:
            t0 = self.t1.index[i] # 测试集的开始
            test_indices = indices[i:j]
            maxT1Idx = self.t1.index.searchsorted(self.t1[test_indices].max())
            train_indices = self.t1.index.searchsorted(self.t1[self.t1<=t0].index)
            if maxT1Idx < X.shape[0]: # 右边的训练集带有 embargo)
                train_indices = np.concatenate((train_indices, indices[maxT1Idx+mbrg:]))
            yield train_indices,test_indices

参考资料

[1]

数据集: https://www.kaggle.com/c/m5-forecasting-accuracy

[2]

交叉验证: https://scikit-learn.org/stable/modules/classes.html

OK,今天的分享就到这里啦!

本文分享自微信公众号 - 数据STUDIO(jim_learning),作者:云朵君

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-09-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 机器学习中的交叉验证

    总第100篇 本篇讲讲机器学习中的交叉验证问题,并利用sklearn实现。 前言 在说交叉验证以前,我们先想一下我们在搭建模型时的关于数据切分的常规做法[直接利...

    张俊红
  • 算法研习:机器学习中的K-Fold交叉验证

    在我们训练机器学习模型时,为提高模型拟合效果,经常使用K-Fold交叉验证,这是提高模型性能的重要方法。在这篇文章中,我们将介绍K-Fold交叉验证的基本原理,...

    深度学习与Python
  • 机器学习面试题集 - 详解四种交叉验证方法

    它的基本思想就是将原始数据(dataset)进行分组,一部分做为训练集来训练模型,另一部分做为测试集来评价模型。

    杨熹
  • 机器学习中的超参数的选择与交叉验证

    1. 超参数有哪些   与超参数对应的是参数。参数是可以在模型中通过BP(反向传播)进行更新学习的参数,例如各种权值矩阵,偏移量等等。超参数是需要进行程序员自己...

    机器学习算法工程师
  • 学界 | 综述论文:机器学习中的模型评价、模型选择与算法选择

    机器之心
  • 5种常用的交叉验证技术,保证评估模型的稳定性

    你有没有想过是什么原因导致了这些排名的高差异?换句话说,为什么一个模型在私有排行榜上评估时会失去稳定性?

    deephub
  • 过关斩将打进Kaggle竞赛Top 0.3%,我是这样做的

    从下图可以看出,融合后的模型性能最好,RMSE 仅为 0.075,该融合模型用于最终预测。

    AI科技大本营
  • Python+sklearn使用三种交叉验证方法评估模型泛化能力

    本文使用的数据集格式请参考:使用Python预处理机器学习需要的手写体数字图像文件数据集

    Python小屋屋主
  • 一个完整的机器学习项目在Python中演练(四)

    磐创AI
  • 深度 | 机器学习中的模型评价、模型选择及算法选择

    作者:Sebastian Raschka 翻译:reason_W 编辑:周翔 简介 正确使用模型评估、模型选择和算法选择技术无论是对机器学习学术研究还是工业场景...

    AI科技大本营
  • 使用重采样评估Python中机器学习算法的性能

    你需要知道你的算法在看不见的数据上表现如何。

    老人雨何
  • 8种交叉验证类型的深入解释和可视化介绍

    交叉验证(也称为“过采样”技术)是数据科学项目的基本要素。它是一种重采样过程,用于评估机器学习模型并访问该模型对独立测试数据集的性能。

    deephub
  • 机器学习中需要知道的一些重要主题

    机器学习现在是一个热门话题,每个人都在尝试获取有关该主题的任何信息。有了关于机器学习的大量信息,人们可能会不知所措。在这篇文章中,我列出了你需要了解的一些机器学...

    磐创AI
  • 精华 | 12个关键词告诉你告诉你什么是机器学习(基础篇)

    键字全网搜索最新排名 【机器学习算法】:排名第一 【机器学习】:排名第一 【Python】:排名第三 【算法】:排名第四 源 | 小象 随着人工智能(AI)技术...

    昱良
  • 你真的知道什么是机器学习吗?

    随着人工智能(AI)技术对各行各业有越来越深入的影响,我们也更多地在新闻或报告中听到“机器学习”、“深度学习”、“增强学习”、“神经网络”等词汇,对于非专业人士...

    企鹅号小编
  • 12个关键词,告诉你到底什么是机器学习

    编者按:随着人工智能(AI)技术对各行各业有越来越深入的影响,我们也更多地在新闻或报告中听到“机器学习”、“深度学习”、“增强学习”、“神经网络”等词汇,对于...

    顶级程序员
  • 科普 | 12个关键词,告诉你到底什么是机器学习

    随着人工智能(AI)技术对各行各业有越来越深入的影响,我们也更多地在新闻或报告中听到“机器学习”、“深度学习”、“增强学习”、“神经网络”等词汇,对于非专业人士...

    CDA数据分析师
  • Kaggle大神带你上榜单Top2%:点击预测大赛纪实(上)

    大数据文摘
  • Wiztalk腾讯犀牛鸟项目学术分享之机器学习,报告将于今晚7点准时开始

    我们在Wiztalk CCF-腾讯犀牛鸟基金技术沙龙的基础上,全新推出Wiztalk 腾讯犀牛鸟项目学术分享。从本期报告起,我们将联动犀牛鸟专项基金为大家带来...

    腾讯高校合作

扫码关注云+社区

领取腾讯云代金券