使用Python进行机器学习的嵌套交叉验证

k折交叉验证程序用于估计机器学习模型在预测未使用过的数据时(训练期间)的性能。

此程序可用于在数据集上优化模型超参数,以及在比较和选择数据集的模型时。当用于调优和选择模型的交叉验证程序和数据集相同时,它很可能导致对模型性能的评估过于乐观。

克服这种偏差的一种方法是将超参数优化过程嵌套在模型选择过程之下。这被称为**双重交叉验证**或**嵌套交叉验证**,是评估和比较调优后的机器学习模型的首选方法。

在本教程中,您将了解用于评估调优后机器学习模型的嵌套交叉验证。

完成本教程后,您将了解:

  • 超参数优化可能导致数据集过拟合,并提供一个不应用于模型选择的乐观评估。
  • 嵌套交叉验证提供了一种减少组合超参数调优和模型选择中偏差的方法。
  • 如何在scikit-learn中实现嵌套交叉验证来评估调优后的机器学习算法。

通过我的新书《Python机器学习精要》来启动您的项目,其中包括分步教程以及所有示例的Python源代码文件。

让我们开始吧。

  • 2021年1月更新:添加了关于管道思维的部分和相关教程的链接。
Nested Cross-Validation for Machine Learning with Python

使用Python进行机器学习的嵌套交叉验证
照片作者:Andrew Bone,部分权利保留。

教程概述

本教程分为三个部分;它们是:

  1. 组合超参数调优和模型选择
  2. 什么是嵌套交叉验证
    1. 嵌套交叉验证的代价是什么?
    2. 如何设置k?
    3. 如何配置最终模型?
    4. 内循环选择了什么配置?
  3. 使用Scikit-Learn进行嵌套交叉验证

组合超参数调优和模型选择

通常使用 k 折交叉验证在数据集上评估机器学习模型。

k折交叉验证程序将有限的数据集分成k个不重叠的折。k个折中的每一个都有机会被用作保留的测试集,而所有其他折一起被用作训练数据集。总共会拟合k个模型并在k个保留的测试集上进行评估,然后报告平均性能。

有关 k 折交叉验证程序的更多信息,请参阅教程

该程序在预测未使用过的数据时,提供了模型在数据集上的性能估计。对于小型到中等规模的数据集,它比单个训练-测试分割等其他技术偏差更小。k的常见值是k=3、k=5和k=10。

每种机器学习算法都包含一个或多个超参数,这些超参数允许算法的行为根据特定数据集进行定制。问题是,很少有好的启发式方法来配置模型超参数以适应数据集。相反,会使用一个优化过程来发现一套在数据集上表现良好或最佳的超参数。优化算法的常见示例包括网格搜索和随机搜索,并且每种不同的模型超参数集通常使用k折交叉验证进行评估。

这突出表明,k折交叉验证程序既用于选择模型超参数以配置每个模型,也用于选择配置好的模型。

k折交叉验证程序是估计模型性能的有效方法。然而,该程序的一个限制是,如果它被多次用于相同的算法,它可能会导致过拟合。

每次在数据集上评估具有不同模型超参数的模型时,它都会提供关于数据集的信息。特别是,一个嘈杂的数据集通常得分较低。关于模型在数据集上的这种知识可以被模型配置过程利用,以找到适合数据集的最佳配置。k折交叉验证程序试图减少这种影响,但它不能完全消除,并且会进行某种程度的爬山法或模型超参数对数据集的过拟合。这是超参数优化的正常情况。

问题是,如果仅使用此分数来选择模型,或者使用同一数据集来评估调优后的模型,那么选择过程将受到这种无意的过拟合的影响。结果是对模型性能的过于乐观的估计,无法泛化到新数据。

需要一种程序,既能让模型为数据集选择表现良好的超参数,又能从数据集上表现良好的模型集合中进行选择。

解决这个问题的一种方法称为**嵌套交叉验证**。

什么是嵌套交叉验证

嵌套交叉验证是一种模型超参数优化和模型选择方法,它试图克服训练数据集过拟合的问题。

为了克服性能评估中的偏差,模型选择应被视为模型拟合过程的组成部分,并且应在每次试验中独立进行,以防止选择偏差,并因为它反映了在实际使用中的最佳实践。

《论模型选择中的过拟合及随后性能评估中的选择偏差》, 2010。

该程序涉及将模型超参数优化视为模型本身的一部分,并在更广泛的k折交叉验证过程中对其进行评估,以评估模型以便进行比较和选择。

因此,用于模型超参数优化的k折交叉验证过程嵌套在用于模型选择的k折交叉验证过程之内。使用两个交叉验证循环也导致该过程被称为“*双重交叉验证*”。

通常,k折交叉验证程序涉及在一个折之外的所有折上拟合模型,并在保留的折上评估拟合的模型。我们将用于训练模型的折的集合称为“*训练数据集*”,将保留的折称为“*测试数据集*”。

然后,每个训练数据集都被提供给超参数优化过程,例如网格搜索或随机搜索,该过程会找到模型的最佳超参数集。每个超参数集的评估是通过k折交叉验证进行的,该交叉验证将提供的训练数据集分割成k个折,而不是原始数据集。

这被称为“内部”协议,因为模型选择过程是在重采样过程的每个折中独立执行的。

《论模型选择中的过拟合及随后性能评估中的选择偏差》, 2010。

在此过程中,超参数搜索没有机会过拟合数据集,因为它仅暴露于由外部交叉验证过程提供的数据集子集。这减少了(如果不是消除了)搜索过程过拟合原始数据集的风险,并应提供对调优后模型在数据集上性能的更少偏差的估计。

这样,性能估计就包含了一个正确考虑了因过拟合模型选择标准而引入的错误的部分。

《论模型选择中的过拟合及随后性能评估中的选择偏差》, 2010。

嵌套交叉验证的代价是什么?

嵌套交叉验证的一个缺点是模型评估次数的急剧增加。

如果对于给定模型,在传统的交叉验证超参数搜索中拟合和评估了n * k个模型,那么在嵌套交叉验证的外部循环的每个折中进行k次,这将增加到k * n * k个模型。

为了具体说明,您可能为超参数搜索使用k=5,并测试100种模型超参数组合。因此,传统的超参数搜索将拟合和评估5 * 100或500个模型。在此情况下,模型数量增加了10倍。

如何设置k?

内循环和外循环的k值应设置为与单个k折交叉验证过程的k值相同。

您必须为数据集选择一个k值,以平衡评估过程的计算成本(模型评估次数不过多)和模型性能的无偏差估计。

通常,外循环使用k=10,内循环使用较小的值,如k=3k=5

有关设置k的更多一般性帮助,请参阅本教程

如何配置最终模型?

最终模型使用外循环的一次传递中应用的程序进行配置和拟合,例如应用于整个数据集的外循环。

如下

  1. 算法是根据其在外循环嵌套交叉验证中的性能来选择的。
  2. 然后,内循环程序应用于整个数据集。
  3. 在这次最终搜索中找到的超参数然后用于配置最终模型。
  4. 最终模型在整个数据集上进行拟合。

然后可以使用此模型对新数据进行预测。我们可以根据最终模型调优过程中提供的分数来了解其平均性能如何。

内循环选择了什么配置?

这无关紧要,这就是它的意义所在。

使用了自动配置过程而不是特定的配置。有一个最终模型,但该最终模型的最佳配置是通过所选的搜索过程在最终运行中找到的。

您不必纠结于所选的具体模型配置,就像在下一个较低级别上,您不必纠结于每个交叉验证折中找到的具体模型系数一样。

这需要转变思维方式,并且可能具有挑战性,例如从“我像这样配置了我的模型……”转变为“我使用了具有这些约束的自动模型配置过程……”。

本教程关于“*管道思维*”的主题还有更多内容,可能有所帮助。

现在我们对嵌套交叉验证有了了解,让我们回顾一下如何在实践中实现它。

使用Scikit-Learn进行嵌套交叉验证

k折交叉验证程序可通过scikit-learn Python机器学习库中的KFold类获得。

该类配置了折数(分割数),然后调用split()函数,传入数据集。split()函数的结果被枚举,为每个折提供训练集和测试集的行索引。

例如

此类可用于执行嵌套交叉验证的外循环。

scikit-learn库分别通过RandomizedSearchCVGridSearchCV类提供交叉验证随机搜索和网格搜索超参数优化。该过程通过创建类并指定模型、数据集、要搜索的超参数和交叉验证程序来配置。

例如

这些类可用于嵌套交叉验证的内循环,其中外循环定义的训练数据集用作内循环的数据集。

我们可以将这些元素结合起来,实现嵌套交叉验证。

重要的是,我们可以将超参数搜索配置为使用搜索过程中找到的最佳超参数,使用整个训练数据集重新拟合最终模型。这可以通过将“*refit*”参数设置为True来实现,然后通过搜索结果的“*best_estimator_*”属性来检索模型。

然后可以使用此模型对来自外循环的保留数据进行预测,并估算模型的性能。

将所有这些结合起来,我们可以演示在合成分类数据集上对RandomForestClassifier进行嵌套交叉验证。

我们将保持简单,只调优两个超参数,每个超参数有三个值,例如(3 * 3)9种组合。我们将使用10个折作为外交叉验证,3个折作为内交叉验证,这将导致(10 * 9 * 3)或270次模型评估。

完整的示例如下所示。

运行示例在合成分类数据集上使用嵌套交叉验证来评估随机森林

注意:由于算法的随机性、评估过程或数值精度的差异,您的结果可能有所不同。考虑多次运行示例并比较平均结果。

您可以将此示例作为起点,并进行调整以评估不同的算法超参数、不同的算法或不同的数据集。

外交叉验证程序的每次迭代都会报告最佳性能模型(使用3折交叉验证)的估计性能,找到最佳性能的超参数,以及保留数据集上的准确率。

这是很有见地的,因为我们可以看到实际精度和估计精度是不同的,但在这种情况下是相似的。我们还可以看到每次迭代都找到了不同的超参数,这表明在这个数据集上好的超参数取决于数据集的具体情况。

然后报告最终的平均分类准确率。

一种更简单的方法是使用cross_val_score()函数来执行相同的过程,该函数将执行外交叉验证过程。这可以直接在配置好的GridSearchCV上执行,它会自动使用在外部循环的测试集上重新拟合的最佳性能模型。

这大大减少了执行嵌套交叉验证所需的代码量。

完整的示例如下所示。

注意:由于算法的随机性、评估过程或数值精度的差异,您的结果可能有所不同。考虑多次运行示例并比较平均结果。

运行示例,对随机森林算法执行嵌套交叉验证,实现了与我们手动过程相匹配的平均准确率。

进一步阅读

如果您想深入了解,本节提供了更多关于该主题的资源。

教程

论文

API

总结

在本教程中,您了解了用于评估已调优的机器学习模型的嵌套交叉验证。

具体来说,你学到了:

  • 超参数优化可能导致数据集过拟合,并提供一个不应用于模型选择的乐观评估。
  • 嵌套交叉验证提供了一种减少组合超参数调优和模型选择中偏差的方法。
  • 如何在scikit-learn中实现嵌套交叉验证来评估调优后的机器学习算法。

你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。

发现 Python 中的快速机器学习!

Master Machine Learning With Python

在几分钟内开发您自己的模型

...只需几行 scikit-learn 代码

在我的新电子书中学习如何操作
精通 Python 机器学习

涵盖自学教程端到端项目,例如
加载数据可视化建模调优等等...

最终将机器学习带入
您自己的项目

跳过学术理论。只看结果。

查看内容

170 条对《Python 机器学习嵌套交叉验证》的回复

  1. Viktor 2020 年 7 月 29 日 下午 3:54 #

    下午好,感谢您的文章。
    能否请教新手。图片“然后报告最终的平均分类准确率”显示了每个外部折叠的“最佳参数”。
    1)此列表中的哪些参数应在最终模型中使用?
    2)在您的示例中,如何调用由此产生的训练模型?
    提前感谢。

    • Jason Brownlee 2020 年 7 月 30 日 上午 6:18 #

      可以这样想:我们正在寻找的是一个建模过程,而不是一个特定的模型和配置。

      实现最佳结果的建模过程就是我们将要使用的。

      因此,我们可以比较来自多种不同算法的最佳网格搜索模型,并使用创建最佳模型的过程来创建最终模型。

      这有道理吗?

  2. Abhishek V 2020 年 7 月 31 日 上午 3:56 #

    双重交叉验证无疑是更好的方法,因为它看起来很表面,我们将看到它如何处理我们自己的问题

  3. Áttila 2020 年 7 月 31 日 上午 6:11 #

    嗨 Jason,我关注您的帖子,学到了很多!很棒!

    我不知道我是否理解了程序,但我认为当选择具有更好平均指标的模型时,它仍然会过拟合。

    我认为嵌套交叉验证是一种给出 k 个最佳模型的技术,而且无法评估哪个更好。要做到这一点,我需要另一个留出数据集,但已经没有了。

    问题是我该如何处理这 k 个最佳模型?我不知道,我可以对它们进行集成。或者我可以使用一些理论分析来丢弃一些。

    谢谢

    • Jason Brownlee 2020 年 7 月 31 日 上午 6:26 #

      谢谢!

      不,它选择的是一个建模流程,该流程会产生最佳模型。例如,网格搜索将为您选择最佳模型,以便您直接开始使用并进行预测。

      • Leo 2020 年 8 月 10 日 上午 6:02 #

        嗨 Jason!!我现在明白了!您最终得到的是 k 个模型。您从 cross_val_score 中获得的是对泛化误差的无偏估计。现在,假设您负责使用一组新的预测器 X 进行新预测。
        您应该做的是在整个数据集上运行 GridSearchCV。然后使用 best_model 进行预测。

        很棒的帖子!!!

        • Jason Brownlee 2020 年 8 月 10 日 上午 6:52 #

          正是如此!

          • Felipe Araya 2021 年 1 月 28 日 下午 12:32 #

            嗨,Jason,

            我对这个有点困惑。难道不应该是
            0. 创建外部和内部循环(训练集和测试集)
            1. 在训练集上运行 GridseachCV
            2. 获取“最佳模型”并将其与外部循环进行比较
            3. 一旦您获得了最佳泛化模型(在运行外部循环后),请获取在该泛化中效果最好的超参数
            4. 使用这些超参数,现在使用全部数据重新训练模型
            5. 您现在可以使用使用全部数据和嵌套 CV 中找到的超参数训练的模型来进行预测
            6. 如果您获得了新的预测器集 X(意味着要在现有数据集的基础上添加更多数据),请重复步骤 1-4。

            是这样吗?还是我读错了问题?

          • Jason Brownlee 2021 年 1 月 28 日 上午 5:59 #

            不,嵌套 CV 不是这样的。请参阅关于训练最终模型的章节。

            当然,只要您能证明,您可以做任何您喜欢的事情。

          • Greg 2022 年 10 月 24 日 上午 11:17 #

            如果我们在丢弃所有内部循环中的最佳模型并使用整个数据集的网格搜索从头开始创建新模型时,双重交叉验证仅用于估计泛化误差,那么它还有用吗?

          • James Carmichael 2022 年 10 月 25 日 上午 6:24 #

            Greg... 我建议使用贝叶斯优化来进行超参数选择和调优,而不是网格搜索。

            https://www.vantage-ai.com/en/blog/bayesian-optimization-for-quicker-hyperparameter-tuning

  4. Shahid Islam 2020 年 8 月 2 日 上午 12:10 #


    我处理非常大的数据集(超过十亿行)。我想找一本使用 pyspark 在 spark 环境中描述这些模型和方法的书。如今,大型数据集越来越普遍,因此这样一本书对数据科学家来说将非常有用。

  5. Anthony The Koala 2020 年 8 月 12 日 上午 10:14 #

    尊敬的Jason博士,
    这与嵌套模型中的代码行有关,特别是

    经过一些实验,我发现您可以说同样的事情,例如

    原因是 search 包含了给定参数的最佳模型,因此我们可以拟合 X_train, y_train 的最佳模型,并使用 X_test 通过最佳模型进行预测。
    定义:最佳模型 = search,GridSearch 参数的最佳模型。

    谢谢你,
    悉尼的Anthony

    • Jason Brownlee 2020 年 8 月 12 日 下午 1:35 #

      是的,这正是您如何在没有外部循环进行模型评估的情况下使用该技术作为最终模型。

      • Anthony The Koala 2020 年 8 月 12 日 下午 3:00 #

        尊敬的Jason博士,
        感谢您的回复。我问的问题与嵌套示例用更少的代码获得相同分数有关。

        我的问题是关于最后一个模型。

        如果最后一个模型代码更少且没有循环,但能获得相同的结果,为什么不实现最后一个示例来获得相同的结果,从而消除分数中的噪声呢?

        再次感谢您,
        悉尼的Anthony

        • Jason Brownlee 2020 年 8 月 13 日 上午 6:04 #

          它做的事情不同。

          教程中的示例正在使用 k 折交叉验证(外部循环)来评估建模流程。

          您的示例只拟合一次流程,并使用它进行预测。

          • Janine S 2021 年 3 月 18 日 下午 10:28 #

            嗨,Jason,

            我认为 Anthony 说得对。实际上,如果我们不能替换

            yhat = best_model.predict(X_test) 为 yhat = search.redict(X_test),

            那么您文章末尾的较短代码版本就没有意义了,而且会起作用,对吧?
            因为 cross_val_score 所做的是创建训练集和测试集折叠,然后训练作为第一个参数给出的模型(在我们的例子中是 search),然后使用 .predict(X_test) 在测试集上对其进行评估。因此,在 cross_val_score 中,我们无法让函数处理 search.best_estimor_,这要求在内部循环的每次迭代中,

            search.best_estimator == search 本身。

            您也可以通过在内部 for 循环中打印一些布尔语句来检查这一点。

            如果我错了,您能解释一下为什么有区别以及 cross_val_score 为什么会起作用吗?

            谢谢!

          • Jason Brownlee 2021 年 3 月 19 日 上午 6:21 #

            我不确定我是否理解。

            search.predict() 直接使用最佳模型——它们是等效的
            https://scikit-learn.cn/stable/modules/generated/sklearn.model_selection.GridSearchCV.html#sklearn.model_selection.GridSearchCV.predict

  6. Anthony The Koala 2020 年 8 月 12 日 下午 5:25 #

    尊敬的Jason博士,
    谢谢你。
    除了上述问题。
    在嵌套示例中,您从外部分割中获得 X_train 和 y_train,并能够获得 yhat

    在上面的代码中,为了获得 X_train 和 y_train,基于 kfolds=10 有 10 次循环迭代。我们有 10 个不同的 train_ix,因此我们可以基于 10 个版本的 X_train 和 y_train 来估计 yhat。

    但最后一个示例没有迭代,也没有估计 yhat 的能力。所以我们不能预测 yhat,因为我们没有像嵌套示例那样进行 10 次迭代?

    换句话说,如何在最后一个示例中进行预测?

    谢谢你,
    悉尼的Anthony

    如何预测第二个示例中的 yhat

    • Jason Brownlee 2020 年 8 月 13 日 上午 6:08 #

      最后一个示例以更少的代码行完成了与第一个示例相同的事情。

      您可以使用您提供的代码示例进行预测,例如,拟合模型然后调用 predict()。

      • Anthony The Koala 2020 年 8 月 13 日 上午 7:50 #

        尊敬的Jason博士,
        谢谢你。
        但在最后一个示例中,我如何获得 train_ix 的索引,因为没有像第一个示例那样的迭代?
        我需要 train_ix 索引才能获得 X_train 和 y_train。我不知道这在最后一个示例中是如何实现的。

        在第一个示例中,您从 cv_outer.split(X) 中获取 train_ix。

        在最后一个示例中,无法获取 train_ix 的索引来获得 X_train 和 y_train,因为您需要 train_ix 的索引,但您无法在第二个示例中获取 train_ix 的索引。

        那么,如何在最后一个示例中获取 train_ix?

        谢谢您,并且我一直很欣赏您的教程。
        悉尼的Anthony

        • Jason Brownlee 2020 年 8 月 13 日 上午 10:54 #

          在最后一个示例中,建模流程的交叉验证(外部)是通过调用 cross_val_score() 来控制的。每个超参数组合的交叉验证则由 GridSearchCV(内部)自动控制。

          它以更少的代码行完成了与第一个示例相同的事情。

        • Anthony The Koala 2020 年 8 月 13 日 上午 11:11 #

          尊敬的Jason博士,
          我尝试从最后一个示例中预测 yhat。
          在最后一个示例中,为了获得 y_train,我不得不通过在最后一个示例末尾附加以下代码来实现

          这与第一个或第二个示例产生的完全不同
          Accuracy: 0.927 (0.019)
          这是我产生的
          0.926,标准差为 (0.0037)

          奇怪的是,我使用 3 组分数得到的标准差更小。

          谢谢你,
          悉尼的Anthony

          • Anthony The Koala 2020 年 8 月 13 日 上午 11:56 #

            尊敬的Jason博士,
            我做了同样的事情,通过附加以下代码,这次使用
            这次我使用了 cv_outer,它是 10 折,并附加到您最后一个示例的末尾

            结果与您的第一个和最后一个代码完全相同。

            调查结果总结
            对于第二个示例,为了获得 X 和 y 各自的测试集和训练集,您必须有一个循环来提取测试和训练的索引。测试和训练的索引取决于折叠的数量。

            谢谢你,
            悉尼的Anthony

          • Jason Brownlee 2020 年 8 月 13 日 下午 1:26 #

            抱歉,我不明白您想做什么。

            为什么要在交叉验证中进行预测?

  7. Anthony The Koala 2020 年 8 月 13 日 下午 4:06 #

    尊敬的 Jason 博士
    谢谢你的回复。
    这就是最初的问题,也许我没有说清楚。
    (1) 我最初的问题是如何使用上面标题“进一步阅读”的第二个示例进行预测。

    (2) 您在标题为“Scikit-Learn 嵌套交叉验证”的第一个示例中进行了预测。

    (3) 回答您的问题“……我想实现什么……”是在上面标题为“进一步阅读”的第二个示例中进行 yhat 预测。我能够预测 yhat 并获得了相同的结果。

    这需要我为第二个示例添加几行代码。

    我最终弄明白了,得到了与您的示例完全相同的 mean(scores) 和 std(scores)。

    我将以下代码添加到您第二个示例的末尾,该示例位于“进一步阅读”部分的上方

    结论 (1),您可以检索均值来生成 X_train, y_train, X_test, y_test, scores, mean(scores) 和 std(scores),就像您的示例一样。

    结论 (2),您可以使用代码进行预测并获得相同的结果。

    它有效。

    再次感谢您的耐心,
    悉尼的Anthony

    这是因为您使用了预测

    • Jason Brownlee 2020 年 8 月 14 日 上午 5:57 #

      您已经提供了最后一个示例的预测代码,我已指出,在此

      • Anthony The Koala 2020 年 8 月 14 日 上午 8:48 #

        尊敬的Jason博士,
        谢谢你。
        它奏效了,我为此感谢您。
        再次感谢您的耐心,这有助于理解。
        我将对此表示感谢。
        悉尼的Anthony

        • Jason Brownlee 2020 年 8 月 14 日 下午 1:15 #

          Anthony,非常欢迎,我很乐意为您提供帮助。

  8. John Sang 2020 年 8 月 15 日 下午 8:46 #

    嗨,我在这里展示了您的结果
    >acc=0.900, est=0.932, cfg={‘max_features’: 4, ‘n_estimators’: 100}
    >acc=0.940, est=0.924, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.930, est=0.929, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.930, est=0.927, cfg={‘max_features’: 6, ‘n_estimators’: 100}
    >acc=0.920, est=0.927, cfg={‘max_features’: 4, ‘n_estimators’: 100}
    >acc=0.950, est=0.927, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.910, est=0.918, cfg={‘max_features’: 2, ‘n_estimators’: 100}

    Accuracy: 0.927 (0.019)

    我阅读了下面关于如何为最终模型选择最佳参数的许多评论,但我并不十分清楚。

    我能否通过选择最佳准确率的参数集来直接应用性能结果?在这种情况下 >acc=0.950, est=0.927, cfg={‘max_features’: 4, ‘n_estimators’: 500}

    我能选择 {‘max_features’: 4, ‘n_estimators’: 500} 吗?

    如果不能,您的建议是什么。

    • Jason Brownlee 2020 年 8 月 16 日 上午 5:51 #

      您可以。

      但是,嵌套 CV 的想法是,您不选择超参数,网格搜索会选择,并且它将根据您的数据和指标使用任何“最佳”值。

      它将配置选择从您手中移开,只留下一个“过程”,而不是模型和配置。外部 CV 正在评估该过程。

  9. John Sang 2020 年 8 月 19 日 下午 5:01 #

    谢谢,好建议。

  10. MS 2020 年 8 月 25 日 上午 2:27 #

    如何为更简单的 cross_val_score 方法获得最佳超参数组合?

    • Jason Brownlee 2020 年 8 月 25 日 上午 6:43 #

      上面的示例不是有帮助吗?

      你到底遇到了什么问题?

      • MS 2020 年 8 月 26 日 下午 11:38 #

        由于我获得了最佳超参数组合,我可能会选择它作为我的最终模型,我将使用较长的方法。如何使用 cross_val_score 方法获得它?在 cross_val_score 方法中,GridsearchCV 之后,最佳参数是如何自动为 outerCV 过程选择的?

        • Jason Brownlee 2020 年 8 月 27 日上午 6:15 #

          您无需了解最佳超参数,因为管道将为您找到最佳模型,因此您可以开始使用它。

          不过,您可以随意保留该对象的引用,然后打印“best_params_”属性。

          • MS 2020 年 8 月 28 日上午 2:15 #

            根据客户的要求,我必须提交一个最终模型。cross_val_score 方法是为了调整和观察我的已调优学习器在未见过的数据上的表现(泛化误差的代理)。如果我错了,请纠正我。

            正如您所说,管道将找到最佳模型(我能理解),但如何提取最佳模型,以便我的客户可以使用它在他们自己的数据上进行预测?

            正如您建议打印 best_params_ 属性来解决我的问题,我是否应该使用 pypi 的 nested-cv 包?

          • Jason Brownlee 2020 年 8 月 28 日上午 6:53 #

            您可以使用最佳参数来定义最终模型,然后将其拟合到所有可用的训练数据上。

  11. MS 2020 年 8 月 30 日上午 2:09 #

    谢谢 Jason

  12. O'Brien 2020 年 9 月 4 日下午 3:23 #

    你好,

    我理解,在嵌套交叉验证之后,我们得到一个评估建模过程的指标(例如准确率),而不是内循环中训练的任何单个配置。

    这个准确率具体是什么意思,它与该过程产生的最终配置有什么关系?如果外部交叉验证循环说该过程的准确率为 X%,那么声称该过程产生的最终配置的准确率也为 X% 是正确的吗?

    如果不是,我们如何对最终配置本身的准确率做出声明?

    • Jason Brownlee 2020 年 9 月 5 日上午 6:39 #

      好问题。

      这是包含网格搜索以在管道内找到最佳配置的过程的准确率。例如,找到的最佳模型。

      • O'Brien 2020 年 9 月 5 日下午 12:55 #

        感谢您的回复。

        为了澄清,如果客户期望开发者提供准确率的引用,即他们想知道算法做出正确预测的频率,那么提供外部交叉验证循环计算出的平均准确率是否正确?

        例如,如果外部交叉验证循环输出 90% 的平均准确率,那么告诉客户该模型的最终配置(算法 + 学习到的参数 + 超参数)平均而言 90% 的预测是真实的,是否正确?

        • Jason Brownlee 2020 年 9 月 6 日上午 6:03 #

          是的,平均值和标准差——例如,准确率分数的分佈。

          为最终模型提供 bootstrap 置信区间会更好。
          https://machinelearning.org.cn/confidence-intervals-for-machine-learning/

          • David Verbiest 2021 年 3 月 6 日上午 9:43 #

            感谢您出色的文章。两个问题。

            1)
            关于置信区间的生成。您将如何进行?我查看了您提供的材料,并理解了原理。您会放弃外部交叉验证吗?我认为您可以使用 bootstrap 来估计参数,在我们的情况下是准确率。或者在外部交叉验证之后是否有另一个步骤用于生成置信区间?

            2)
            您在上面提到:“您可以使用最佳参数来定义最终模型,然后将其拟合到所有可用的训练数据上。”
            而不是选择从交叉验证中获得的所有数据上的最佳超参数集,您能否使用内交叉验证得出的所有最佳模型并进行集成?最终,这些不同的超参数集及其各自的表现用于估计准确率?最佳超参数模型仍然可能是偶然的。

          • Jason Brownlee 2021 年 3 月 6 日下午 2:09 #

            不客气!

            我建议通常使用 bootstrap 方法来计算所选模型的置信区间。
            https://machinelearning.org.cn/confidence-intervals-for-machine-learning/

            不完全是,模型是由外部循环根据平均表现而不是一次性表现来选择的。偶然的可能性更小。但仍会预期一些变异。

  13. Lada 2020 年 9 月 13 日晚上 8:29 #

    Jason,您好,非常感谢这个精彩的教程!但我想问的是,对于随机森林等使用装袋(bagging)的模型,原则上是否需要交叉验证?例如,根据 James 等人:《R 中的统计学习入门》(第 317-318 页)

    “事实证明,有一个非常直接的方法可以估计装袋模型的测试误差,而无需执行交叉验证或验证集方法。”

    “生成的 OOB(袋外)误差是装袋模型的测试误差的有效估计,因为每个观测值的响应仅使用未用该观测值拟合的树来预测。”

    您怎么看,随机森林能否在没有交叉验证的情况下进行训练和超参数调优?

    • Jason Brownlee 2020 年 9 月 14 日上午 6:47 #

      是的,如果模型是独立的,您可以使用 OOB。

      为了比较模型,需要一个系统且一致的测试框架。

  14. Edvin 2020 年 9 月 28 日下午 1:58 #

    如何在外部交叉验证中选择最佳模型?我看不出对具有不同超参数的模型取平均值的意义。此外,best_estimator_ 只给出特定内部折叠的最佳模型,因为基本上我们为每个内部折叠实例化一个新的搜索。

  15. Chahine 2020 年 10 月 5 日晚上 9:45 #

    嗨,Jason,

    精彩文章!!

    我们在这里是在整个数据集上运行内部循环,还是将其分割为训练/测试?

    我们不应该从内部循环中隐藏 10% 的数据,而只在外部循环中使用整个数据集吗?

    • Jason Brownlee 2020 年 10 月 6 日上午 6:50 #

      外部循环作用于所有数据,内部循环作用于外部循环一次传递的训练集。

      为什么要预留数据?

  16. Virgilio 2020 年 10 月 6 日晚上 10:08 #

    亲爱的布朗利博士,

    感谢您提供这篇非常详细且有趣的论文。

    我只有一个问题。当您描述获取最终模型的程序时,您提到:“我们知道它在最终模型调优过程提供的分数基础上的平均表现如何。”

    然而,我猜我们不能使用这个分数,因为它会乐观偏差(超参数已经过调优)。为了估计最终模型的表现,使用嵌套交叉验证先前获得的无偏分数似乎更合适。

    这有道理吗?

    再次感谢,并继续您的出色工作。此致敬礼。

    • Jason Brownlee 2020 年 10 月 7 日上午 6:46 #

      我们可以,因为估计是基于外部循环的。

      这就是重点,模型调优现在被视为模型本身的一部分——例如,建模过程。

  17. Chahine 2020 年 10 月 7 日上午 3:49 #

    嗨,Jason,

    我正在尝试将您的步骤应用于我的数据,我有一个包含 tf-idf、LDA 和 LogisticRegression 模型的管道,我的输入数据是文档列表 X = [‘text,’text’…….], y =[0,1….], 我正在尝试使用嵌套 cv 来优化管道组件的各种参数。

    如何在这里使用您的代码来分割数据,假设数据需要是 numpy 数组?

    for train_ix, test_ix in cv_outer.split(X)
    # 分割数据
    X_train, X_test = X[train_ix, :], X[test_ix, :]
    y_train, y_test = y[train_ix], y[test_ix]

    谢谢你

  18. Karthik Mamudur 2020 年 10 月 31 日上午 9:26 #

    你好 Jason,

    如果我必须比较哪种算法(RandomForest、SomeOthermodel-1、SomeOthermodel-2)最好,我是否只需用上述每种算法重复嵌套交叉验证,并根据各种嵌套交叉验证的准确率和标准差做出决定?

    谢谢你,
    Karthik

  19. Payal Goyal 2020 年 11 月 17 日上午 4:11 #

    嗨,Jason Brownlee,

    感谢您的精彩解释。但我有一个疑问:我们是否应该像对外部循环那样,在内部迭代中再加一个循环?

    • Jason Brownlee 2020 年 11 月 17 日上午 6:34 #

      我们在最终示例中确实做了,它只是由网格搜索为我们处理了。

  20. Emin 2020 年 11 月 23 日上午 9:07 #

    亲爱的 Jason,
    感谢您的帖子。我有两个问题。
    1) 第一个问题是关于预处理技术(缩放、插补等)和嵌套交叉验证。嵌套交叉验证是否可用于决定预处理技术,例如决定插补技术是均值、中位数还是回归?
    2) 是否可以将嵌套交叉验证推广到其他重采样技术?例如,“嵌套重复的训练-测试拆分”,其中外部和内部验证都使用重复的随机训练-测试拆分。如果是,您是否知道该主题的任何学术参考资料?如果我理解正确,科学出版物中最常用的嵌套重采样技术是用于外部验证的单个训练-测试拆分和用于内部验证的常规 k 折交叉验证。
    祝好,
    Emin

  21. Emin 2020 年 11 月 23 日下午 12:14 #

    谢谢你

  22. Ankush Jamthikar 2020 年 11 月 25 日下午 5:08 #

    你好,我是机器学习新手。我正在阅读这篇文章,我必须说它写得很好,解释得很清楚。我只有一个问题。我想部署我的 ML 模型。为此,我需要保存最佳模型。在嵌套交叉验证中,我们应该部署哪个模型?因为在每个折叠中,我们都会得到一个带有新超参数集的新模型。

    • Jason Brownlee 2020 年 11 月 26 日上午 6:29 #

      谢谢!

      一种解决方案是使用 GridSearchCV 模型而放弃外部循环。

      另一种解决方案是检查 GridSearchCV 模型的结果,并使用选定的特定配置。

      • Ankush Jamthikar 2020 年 11 月 26 日下午 7:53 #

        布朗利博士,您好,

        感谢您的快速回复!我不确定我是否完全理解您的回答。不过,我阅读了一些之前的评论,并得出了以下几点。

        1) 嵌套交叉验证不提供最终模型。它只是一种检查(或评估)机器学习算法在不同独立训练和测试数据集上的性能的方法。交叉验证得出的平均性能指标将告诉我们模型的整体行为,我们已告知您的客户。

        2) 在嵌套交叉验证之后,如果我们想将最佳模型交给某人(例如客户),那么我们需要(a)使用整个数据集,(b)运行超参数优化,(c)获取最佳参数,以及(d)用新的机器学习算法和最佳参数重新训练以保存机器学习模型作为“最终模型”。

        这正确吗?

        • Jason Brownlee 2020 年 11 月 27 日上午 6:37 #

          是的,这种方法是有效的,也是我会采取的。

          • Ankush Jamthikar 2020 年 11 月 29 日上午 12:19 #

            亲爱的布朗利博士,

            非常感谢您验证我的思路并澄清了疑虑。在我阅读完您所有的文章/博客后,我将再次打扰您。我希望您也能为我未来的疑问提供澄清。

            再次感谢,
            Ankush

          • Jason Brownlee 2020 年 11 月 29 日上午 8:14 #

            不客气!

          • JLH 2020 年 12 月 14 日上午 3:19 #

            您是否再次面临超参数搜索过拟合的风险?嵌套交叉验证似乎表明可以有一个好模型,但是您如何保证(或至少最大化机会)最终能获得一个好的单一模型可以使用?谢谢!

  23. Edward MB 2020 年 12 月 30 日上午 12:18 #

    您好,非常感谢您的文章——它非常有用。但请问,我如何通过包含递归特征消除来扩展这一点,以便该过程不仅能找到最佳超参数,还能找到最优特征(即最佳超参数和特征组合)?

    • Jason Brownlee 2020 年 12 月 30 日上午 6:39 #

      RFECV 会自动配置自身。您可以在模型之前的管道中使用它。

      或者在 RFE 超参数上使用带有网格搜索的管道。

      本教程将向您展示如何在管道中使用 RFE。
      https://machinelearning.org.cn/rfe-feature-selection-in-python/

      • Edward MB 2021 年 1 月 1 日上午 2:18 #

        感谢您的回复。使用上面的显式内外循环,我认为我可以使用以下方法来实现。

        • Jason Brownlee 2021年1月1日 上午5:31 #

          很高兴听到你取得了进展。

          抱歉,我没有精力审查你的代码。

        • Felipe Araya 2021年2月3日 上午1:42 #

          你好 Edward,

          这段代码看起来很棒!我一直在 Stackoverflow 和类似的地方查找类似的东西,但我觉得这一个是最整洁的,尤其是在实现 RFE 和 GridSearchCV 时。有一个问题,你为什么使用 RFE 而不是 RFECV?

          谢谢!

  24. Felipe Araya 2021年1月28日 上午1:18 #

    你好!

    一如既往的精彩教程!

    我只有几个问题,如果你不介意的话

    1. 在“如何配置最终模型?”一节中,第二步似乎与第四步相同,当然,可能不是,您能否澄清一下,请?

    2. 如果我想为上面展示的代码包含数据预处理(例如缩放或特征分类),我需要将其放在外部循环、内部循环还是循环之外?

    2.1 如果必须将其放在某个循环内,如果您使用“cross_val_score”,您将如何做到这一点,我猜您只能使用“split()”方法,对吗?

    • Jason Brownlee 2021年1月28日 上午6:06 #

      是的,如果您使用 gridsearchcv.best_estimator_,则不需要第四步。

      您可以使用管道(pipeline)作为“模型”,并包含您喜欢的任何数据预处理步骤。

      我无法理解您最后的问题,抱歉。也许您可以详细说明一下?

      • Felipe Araya 2021年1月28日 上午6:56 #

        Hi Dr. Jason

        感谢您回答我的第一个问题。

        当然,第二个问题涉及数据泄露,例如,有人提到特征操作或缩放或任何类型的预处理都应该分别针对训练集和测试集进行,以防止数据泄露,由于外部循环代表“测试集”,内部循环代表“训练+验证”集,我想知道在哪里整合预处理,是在外部循环内、内部循环内还是两者之外。这有意义吗?

        • Jason Brownlee 2021年1月28日 上午7:59 #

          定义一个管道(pipeline),让它处理在训练集上进行拟合以及将数据准备方法应用于训练集和测试集的正确过程。这会一直应用到网格搜索的迭代中。

  25. Felipe Araya 2021年2月3日 上午1:54 #

    嗨 Jason,

    快速提问,您可以在 GridSearchCV 中使用 RFECV 吗?还是应该使用普通的 RFE?我的逻辑是,在 GridSearchCV 生成的内部交叉验证(本质上是训练+验证集)中,RFECV 将发生另一个交叉验证,所以我不知道这是否有很大意义,或者我最好在 GridSearch 中使用针对 range(1, len(features)+1) 评估的 RFE?

  26. Jens 2021年2月22日 上午12:28 #

    亲爱的Jason Brownlee,

    我有一个关于最终模型的问题。假设我们执行了具有内部和外部 5 折交叉验证的嵌套交叉验证。如果我们想要最终模型,我们可以在整个(训练)数据上再次执行例如随机搜索,并从该搜索中获得最佳模型。

    那么,用于获得最终模型的折数应该等于内部交叉验证的折数,还是可以不同,比如 10 折?如果应该相同,为什么?

    诚挚的问候,

    Jens

    • Jason Brownlee 2021年2月22日 上午5:02 #

      训练最终模型时的内部循环的折数应与模型选择期间使用的折数相匹配,以与模型之间的决策和比较保持一致。

  27. Giovanna 2021年2月25日 下午12:24 #

    这项技术是否等同于使用 RepeatedKFold?

    cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
    model_cv_fit = cross_val_score(model, features.values, y.values, cv=cv)

  28. Pablo Reinhardt 2021年3月25日 下午8:44 #

    亲爱的 Jason,
    当我进行堆叠(stacking)时,我是否像您示例中那样将堆叠模型视为“一个模型”,还是有什么需要考虑的?
    感谢您的帮助,我喜欢您的工作!
    谢谢
    Reini

    • Jason Brownlee 2021年3月26日 上午6:23 #

      不,它只是一个在数据上拟合的模型,恰好训练数据中的输入是其他模型的输出。

  29. Reiner 2021年4月15日 上午8:10 #

    亲爱的杰森,您好,

    是否有可能替换这一行
    model = RandomForestClassifier

    ……用一个堆叠分类器
    stacking = StackingClassifier(estimators=models),其中 models 是一个分类器列表?

    如果是这样,您如何将嵌套交叉验证与两层分类器结合起来?

    • Jason Brownlee 2021年4月16日 上午5:27 #

      当然可以。

      哎呀,这可能需要仔细考虑。我无法立即给出满意的答案,抱歉。

  30. Kwa 2021年6月4日 下午1:44 #

    我能说最终准确率等于独立测试集的准确率吗?

  31. Tim Stack 2021年7月7日 上午12:01 #

    亲爱的 Jason,

    在代码的某个地方,您注释掉了网格搜索表现最佳的模型随后在外部循环训练数据上进行训练(拟合)。


    # 定义搜索
    search = GridSearchCV(model, space, scoring='accuracy', cv=cv_inner, refit=True)
    # 执行搜索
    result = search.fit(X_train, y_train)
    # 获取在整个训练集上拟合的最佳模型
    best_model = result.best_estimator_
    # 在保持数据集上评估模型
    yhat = best_model.predict(X_test)

    然而,在我看来,内部循环的最佳模型被选中,然后在外部循环测试集上使用,而没有在此步骤之前在外部循环上进行任何训练。

    为了在外部循环集上训练内部循环模型,难道不应该添加一行代码吗?


    # 定义搜索
    search = GridSearchCV(model, space, scoring='accuracy', cv=cv_inner, refit=True)
    # 执行搜索
    result = search.fit(X_train, y_train)
    # 获取最佳模型
    best_model = result.best_estimator_
    # 在整个训练集上拟合
    best_model.fit(X_train, y_train)
    # 在保持数据集上评估模型
    yhat = best_model.predict(X_test)

    • Jason Brownlee 2021年7月7日 上午5:34 #

      是的,最佳内部循环在外部循环上进行评估。

      如果您想使用外部循环确定的最佳模型,这与“最终确定模型”有关,请参阅关于该部分的内容。

  32. Vinay 2021年7月8日 下午9:59 #

    嗨,Jason,

    我非常感谢您的工作和贡献。我只需要澄清一件事,一旦我们获得了最佳参数,我应该使用 CV 还是不使用 CV 来拟合最终模型?如果使用,CV 应该是“内部 CV”还是“外部 CV”?

    提前感谢。

    • Jason Brownlee 2021年7月9日 上午5:08 #

      在嵌套交叉验证中,您将使用内部交叉验证过程来查找模型的最佳超参数,然后根据这些超参数拟合最终模型。如果您需要,GridsearchCV 可以为您完成此操作,或者您可以手动完成。

  33. Vinay 2021年7月12日 下午3:51 #

    感谢您的回复 Jason。我还有另一个疑问(一般而言),在用最佳参数拟合模型时,我应该如何决定哪个 random_state 能给我一个更可靠的预测模型,并具有更高的准确率?我尝试迭代了 100 个 random_states,其中发现了 7 个唯一的准确率值,范围从 0.5 到 0.76。我应该选择给出最高准确率或中位准确率的 random_state 来拟合模型吗?

  34. kukushiwo 2021年9月21日 下午12:57 #

    嗨,Jason,
    感谢您的帖子。
    我想知道在使用嵌套交叉验证时,应该使用什么数据(内部循环的训练数据集和验证集?还是外部循环的训练数据集和测试数据集?)来绘制学习曲线(损失 vs. 迭代)?

    • Adrian Tam
      Adrian Tam 2021年9月23日 上午2:59 #

      当您谈论学习曲线时,您是在绘制“分数”(无论您决定使用什么)与“迭代次数”的对比图。所以在一个迭代过程中,在模型训练之前,您应该能够找到一个分数。那是什么?如果迭代是关于内部循环,那么分数当然来自验证集。

      • kukushiwo 2021年9月23日 下午4:34 #

        那么,如果迭代是关于外部循环,分数来自测试数据集,对吗?

        但是您关于学习曲线的帖子,您在评论中这样回复其他人:
        “Jason Brownlee 2019年7月4日 上午7:52 #
        通常只有训练集和验证集。”

        您可以在以下链接找到它。
        https://machinelearning.org.cn/learning-curves-for-diagnosing-machine-learning-model-performance/

        这是否意味着我们不能使用训练数据集和测试数据集来绘制外部循环的学习曲线?

        • Adrian Tam
          Adrian Tam 2021年9月24日 上午4:56 #

          我不认为您应该为外部循环绘制学习曲线,因为外部循环涉及不同的交叉验证折。您可以为此绘制条形图,以查看不同的折是否产生相似的分数。

          • kukushiwo 2021年9月24日 上午9:49 #

            但是内部循环也涉及不同的交叉验证折,所以如果我想为内部循环绘制学习曲线,我应该使用内部循环的哪个验证集折,以及应该使用哪个超参数?

          • Adrian Tam
            Adrian Tam 2021年9月25日 上午4:12 #

            每个折都涉及数据集的特定划分。对于模型,您从迭代 0 开始,一切都是随机的,直到迭代 N,它开始产生有意义的结果。您想要绘制的是一个特定的划分和一个特定的模型,其结果分数与迭代次数的比较。所以简单来说,在 k 折交叉验证中,您会得到 k 条曲线。

  35. kukushiwo 2021年9月27日 下午12:34 #

    “每个折都涉及数据集的特定划分。对于模型,您从迭代 0 开始,一切都是随机的,直到迭代 N,它开始产生有意义的结果。您想要绘制的是一个特定的划分和一个特定的模型,其结果分数与迭代次数的比较。所以简单来说,在 k 折交叉验证中,您会得到 k 条曲线。”

    在我通过运行内部循环获得最佳超参数后,我应该使用最佳超参数来绘制内部循环每个折(总共 k 折)的学习曲线(k 条曲线),对吗?

    • Adrian Tam
      Adrian Tam 2021年9月28日 上午8:45 #

      是的。每条学习曲线都对应一个模型,从其随机状态到训练状态。您不应在同一条曲线上混合不同的模型。

      • kukushiwo 2021年9月28日 下午3:50 #

        非常感谢!
        非常有帮助。

        请让我再问一个关于特征选择的问题,当我们使用嵌套交叉验证时。

        我们应该训练所有数据,但使用外部循环运行得到的准确率(或 F1、ROC AUC 等)来查看模型的优劣。我们应该使用哪个数据集进行特征选择?

        XGBoost 内置的特征重要性是根据训练集计算的。而且由于我们将使用所有数据训练的模型提供给客户,这是否意味着我们应该使用所有数据进行特征选择?

  36. Lamin 2021年10月25日 上午4:36 #

    您好,首先非常感谢您的教程!它们对我帮助很大。我有三个问题

    超参数调整和模型选择有什么区别?我以为它们的意思是一样的。

    我们可以将自动嵌套交叉验证例程的结果视为对其泛化性能的估计吗?我注意到您对完整数据集运行了嵌套交叉验证。

    另外,如果我需要先缩放数据,是否可以提前缩放整个数据集就足够了,还是最好将缩放器和分类器构建到一个管道中,然后将管道馈送到 GridSearchCV?

    • Adrian Tam
      Adrian Tam 2021年10月27日 上午2:14 #

      1. 模型选择是在您有机会调整超参数之前完成的。如果您选择了 kNN,您就有超参数 k;但如果您选择了神经网络,k 就不适用了。
      2. 是的,这就是交叉验证的目的:使用有限的数据集来估计在看不见的数据集上的泛化性能。
      3. 构建一个管道来完成内部交叉验证更好。假设在模型拟合时您不应该看到数据中的验证部分,为什么在缩放时您应该看到它?

  37. Naka 2021年11月5日 上午7:59 #

    你好,

    非常感谢您的教程。我想问:如果我们使用整个数据集来寻找模型的超参数,如何验证在保持集(测试集)上训练的模型,以确保没有泄露?因为我们有 cv_inner=3,在那里我们正在调整超参数。然后当我们用交叉验证训练模型时,我们有 cv_outer=10,所以数据点在 cv_inner 和 cv_outer 之间重叠,模型已经在它本不应看到的数据上调整了超参数。我希望我的评论有意义。

    • Adrian Tam
      Adrian Tam 2021年11月7日 上午8:03 #

      简单的答案是不要让数据在您的训练数据和验证(评分)数据之间共享。这包括预处理步骤(例如,缩放)。

  38. nocibambi 2021年11月19日 下午10:04 #

    >“特别是,一个通常噪声很大的模型性能得分。”

    这里可能漏了一个词?

    • Adrian Tam
      Adrian Tam 2021年11月20日 上午1:58 #

      是的,我已更正该句子。感谢您的指出。

  39. IASONAS CHRISTOULAKIS 2021年12月21日 上午7:46 #

    对于大型数据集,嵌套交叉验证需要大量的计算工作。您能否建议练习另一种方法?

    • James Carmichael 2022年2月15日 下午12:47 #

      您好 Iasonas…我不太理解您当前提出的问题。请澄清,以便我更好地帮助您。

  40. Michael 2022年1月21日 上午6:17 #

    Jason 非常感谢您所有精彩的文章。我花了一周多的时间试图理解嵌套交叉验证背后的整个概念。我的主要问题是,我完全不明白为什么没有嵌套交叉验证的网格搜索会导致偏差。
    例如,如果我尝试找到 SVM 的最佳 C 值,并将 gridsearchCV 提供可能的 C 值 [1,10,100],那么 gridsearchCV 将在提供的每个可能值上拟合 SVM,并执行交叉验证。我将从 CV 获得 3 个平均分数,每个分数对应一个我提供给网格搜索的值。为什么这些分数被认为是带偏差的?
    但为了论证起见,假设这是一个有偏差的估计。那么,为什么不直接对获胜模型执行交叉验证呢?

  41. Rafael Araujo 2022年2月19日 上午6:23 #

    您好 James,我有一个问题

    – 在进行嵌套交叉验证时,我们处理的模型更多,我们可以将每个具有不同超参数的模型视为一个不同的模型,我们的嵌套交叉验证的目标是找到最佳模型

    例如

    Lr (c=1), Lr(c=2), Lr(c=3), Rf(n_estimator=4), Rf(n_estimator=53) 等…

    在选择最终模型进行预测的上下文中,每个都可以被视为一个不同的模型。

    因此,考虑到这一点,在非自动版本中,您展示了随机森林的准确率和超参数

    >acc=0.900, est=0.932, cfg={‘max_features’: 4, ‘n_estimators’: 100}
    >acc=0.940, est=0.924, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.930, est=0.929, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.930, est=0.927, cfg={‘max_features’: 6, ‘n_estimators’: 100}
    >acc=0.920, est=0.927, cfg={‘max_features’: 4, ‘n_estimators’: 100}
    >acc=0.950, est=0.927, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    >acc=0.910, est=0.918, cfg={‘max_features’: 2, ‘n_estimators’: 100}
    >acc=0.930, est=0.924, cfg={‘max_features’: 6, ‘n_estimators’: 500}
    >acc=0.960, est=0.926, cfg={‘max_features’: 2, ‘n_estimators’: 500}
    >acc=0.900, est=0.937, cfg={‘max_features’: 4, ‘n_estimators’: 500}
    Accuracy: 0.927 (0.019)

    在这种情况下,最终模型将是 RF (‘max_features’: 4, ‘n_estimators’: 500),因为它的准确率最高,这是有道理的。

    但是自动版本只是向我们展示了平均准确率,我知道准确率已经考虑了超参数,但是这样做的主要目的是不是让我知道超参数,这样当我用所有训练数据训练模型时,我可以应用它们。

    我正在做一个分类项目,我必须在(L.R(), NB(), KNN())之间进行选择,我之前只做交叉验证,但在阅读了这篇文章之后,我开始学习超参数调整并决定实现它……但是自动代码只告诉我我已知的信息,即 KNN() 是最佳模型,它没有告诉我当我在所有训练数据上拟合时,KNN() 的哪个“版本”是最佳的。

    希望您能理解,我喜欢这个网站,它是我的机器学习指南。

    感谢您的关注!

  42. Rafael Araujo 2022年2月22日 上午6:05 #

    感谢这些文章!!但我的问题仍然存在,所以我将重写一遍

    在嵌套交叉验证中发现最佳超参数和最佳模型(假设它是 L.R(C=2))之后,我的模型是

    model=LogisticRegression(C=2)??

    我之所以这样问,是因为在自动代码中,它没有显示 HP,它只显示 L.R 是最佳模型,所以它保持

    model=LogisticRegression()

    是的,嵌套交叉验证告诉我,在所有 HP 和模型的组合中,L.R 是最佳的……但它也应该告诉我该模型的哪个 HP“版本”是最佳的,这样我才能使用,例如

    model=LogisticRegression(C=2)

    “内部循环选择了什么配置?”部分,看起来它告诉我找到的 HP 并不重要,但如果它们不重要,我如何用整个数据集设置最佳模型以及最佳 HP?

    所以我的代码将是参考本文的自动代码,并找到一种方法来显示不仅是具有最佳准确率的模型(例如 L.R),而且是 THAT 模型的哪个 HP 设置是最佳的(例如 C=2)。

  43. Martin 2022年2月25日 下午12:22 #

    尊敬的先生,

    您能否解释一下如何保存最佳模型以预测外部验证数据?

    此致,

  44. Daniel 2022年6月30日 上午6:25 #

    嗨,Jason,
    很棒的文章 🙂
    我有一个问题:您说“[嵌套交叉验证] 选择一个导致最佳模型的建模管道”。但是:我们在这里选择的是什么管道?在所示示例中,只有一个管道通过交叉验证来寻找最佳超参数,并通过外部循环测试最佳模型。因此,这里没有显示其他管道。 “选择模型管道”是否意味着我们建立多个这样的管道(例如,一个带有 RandomForest,一个带有 NaiveBayes…),对每个管道运行嵌套交叉验证,并使用外部循环的平均分数来选择它们?

  45. Daniel 2022年7月1日 上午3:12 #

    嗯,不是真的……我的问题专门针对本文的内容(嵌套交叉验证)……

  46. Abhishek Kishore 2022年7月16日 上午2:32 #

    请在第一个代码片段的第 3 行将 cv 更改为 cv_outer。感谢您解释嵌套交叉验证

    • James Carmichael 2022年7月16日 上午7:07 #

      感谢您的反馈 Abhishek!

  47. skan 2022年7月17日 上午5:24 #

    你好。

    R 中是否有任何函数等同于“cross_val_score”?
    我认为 caret、ml3 和 tidymodels 没有。
    我只找到了非常大的代码试图做到这一点。

    我还找到了一个名为 nestedcv 的包,我需要弄清楚如何使用它,例如用于决策树。

  48. Anthony 2022年7月22日 上午7:57 #

    语法更正:不使用外部交叉验证的缺点可能是某些数据最终*未*被使用

    • James Carmichael 2022年7月22日 上午8:07 #

      感谢您的反馈 Anthony!

  49. NE 2022年11月11日 下午11:32 #

    你好。
    如果 inner_CV = outer_CV,我们是否仍然拥有嵌套交叉验证?

  50. Matthew 2023年1月5日 上午11:43 #

    我对实际的数据泄露问题有些困惑。我不清楚为什么使用相同的数据进行调优和模型选择是错误的,而这正是我们试图解决的问题。嵌套交叉验证的字面过程是有道理的。

  51. Markus 2023年3月6日 上午12:33 #

    我是否理解正确,嵌套交叉验证的原理是在过程中选择具有最佳超参数的最佳模型,并以最佳准确率为目标?

    如果真是这样,为什么这里的示例只经过了超参数调优的循环,而没有进行模型选择本身?我只看到一种模型被使用,那就是 RandomForestClassifier。

  52. Nasim 2023年3月11日 上午5:20 #

    嗨,Jason,

    有没有回归的完整示例/代码?

  53. Murilo 2023年4月13日 上午5:56 #

    你好,我没法理解这里的 ‘result.best_score_’ 是什么。

    ‘acc’ 是使用外部 CV 分配的测试数据获得的最佳模型的得分,对吗?

    但这个 ‘result.best_score’ 又是什么呢?

  54. Rahil 2023年5月4日 晚上10:30 #

    你好,

    感谢这篇非常有用的博客。
    在嵌套 k 折交叉验证中,当 GridSearchCV 用于 cross_val_predict 时,我有一个关于查找最佳参数的问题。在这种情况下,我们如何获取/查看这些最佳参数?

  55. Dmitry 2023年6月2日 下午1:25 #

    抱歉,我读了这篇教程,但仍然无法理解关于嵌套 CV 的一点。你说
    “k 折交叉验证过程试图减少这种影响,但不能完全消除,并且在数据集上会执行某种爬山或超参数过拟合。”

    然而,最终我们对整个数据集执行了正是这种 k 折 CV 来为模型找到最佳超参数。那么,如果最终我们正如您在此引述中所提到的那样对整个数据集过拟合,那么整个嵌套 CV 过程的意义何在?

  56. Marco 2023年6月7日 上午4:32 #

    您好,感谢您提供有用的博客。
    我有一个关于为最终模型选择使用多少折的问题。如果我对外层循环使用 7 折 CV,对内层循环使用 6 折 CV,那么在最终模型选择时,我应该使用 7 折还是 6 折来处理整个数据集?或者我可以使用其他折数,如 5 折或 10 折?
    如果我需要对最终模型选择使用重复交叉验证,那么整个过程是否仍然有效?在这种情况下,k 折的正确选择又是什么?

  57. Hiroro 2023年7月18日 上午2:37 #

    嗨 James,

    感谢这篇精彩的文章!我想澄清一件事……为什么模型的总体准确度是基于具有不同超参数的不同模型的平均准确度得分?对我来说,更合理的是先使用嵌套 CV 进行超参数调优,然后使用最佳参数,我们训练数据集并进行交叉验证(例如,使用 5 折 CV 方法运行具有最佳参数的特定模型 5 次),然后获得平均准确度。

  58. Joaquín 2024年2月7日 上午8:58 #

    嗨,Jason

    祝贺您的教程,我认为您分享了很多有价值的知识!

    理论上,当我们结合超参数优化和 k 折交叉验证时,我们使用嵌套交叉验证来以更少的偏差估计模型在未见过的数据上的性能,因为超参数优化可能会导致过拟合情况。

    我的问题是:当我们向 Keras 的 fit 方法提供训练数据和验证数据,并且仅监控 ModelCheckpoint 类在验证错误方面的改进时,这是否也同样发生?

    谢谢!

  59. George 2024年3月17日 下午6:30 #

    嗨,Jason,

    我非常感谢您在解释所有这些复杂问题上付出的巨大努力。

    我有一个问题。假设我想使用随机森林,并且我不在乎检查其他算法的性能。

    问:我可以使用嵌套交叉验证进行超参数调优吗?

    换句话说,选择能产生最高准确度的超参数?例如,在您上面的数据中,那就是倒数第二个配置

    >acc=0.960, est=0.926, cfg={‘max_features’: 2, ‘n_estimators’: 500}

    然后使用这些超参数拟合整个数据集(X,y)的模型,并在未见过的数据上进行预测。或者,这是不正确的?

    谢谢!

    • James Carmichael 2024年3月18日 上午6:35 #

      嗨 George… 是的,您可以使用嵌套交叉验证进行超参数调优,并且您对其应用的理解是正确的。嵌套交叉验证提供了一种稳健的方法来选择模型的最佳超参数,同时还能提供其在未见过数据上的性能的无偏估计。以下是它的工作原理以及为什么它适合您的情况

      ### 什么是嵌套交叉验证?

      嵌套交叉验证包含两个层次的交叉验证
      – **外层循环**用于评估模型性能(在您的情况下为准确度),并确保模型评估是无偏的。
      – **内层循环**用于超参数调优,选择随机森林模型的最佳超参数,如 max_featuresn_estimators

      ### 过程

      1. **内层循环(超参数调优)**:在内层循环中,您执行超参数调优(例如,使用网格搜索或随机搜索)来找到最佳超参数。此步骤在外部循环的每次迭代中从原始数据集中分割出的训练集上执行。

      2. **外层循环(性能评估)**:外层循环用于评估具有内层循环获得的最佳超参数的模型性能。这提供了模型在未见过数据上性能的无偏估计。

      ### 您的场景

      – 您有兴趣使用随机森林,并希望选择能产生最高准确度的超参数。
      – 通过使用嵌套交叉验证,您正确地旨在在内层循环中调优超参数(在您的情况下是 max_featuresn_estimators)。
      – 在找到最佳配置后(例如,{'max_features': 2, 'n_estimators': 500}acc=0.960est=0.926),您确实可以使用这些超参数来拟合整个数据集(X, y)的模型。
      – 最后,您使用此模型对未见过的数据进行预测。

      ### 优点

      – **无偏评估**:嵌套交叉验证提供了模型性能的无偏估计,因为超参数调优是在外层循环的每次迭代中独立进行的。
      – **稳健的超参数选择**:它有助于选择在不同数据子集上都能很好地泛化的超参数,而不是拟合到特定的训练-测试分割。

      ### 结论

      您使用嵌套交叉验证选择超参数,然后拟合整个数据集以进行未来预测的方法是正确且方法上合理的。当您专注于单一模型类型并希望在其最终确定模型之前优化其配置时,此技术特别有用。

      • George 2024年3月18日 下午7:23 #

        嗨 Janes。非常感谢您详细而切题的回复!!!

  60. Luis Talavera 2024年6月23日 下午3:06 #

    也许我有些困惑,但我想知道当超参数调优策略是贝叶斯优化时,是否需要嵌套交叉验证。在这种情况下,一次交叉验证是否就足够了?

    • James Carmichael 2024年6月24日 上午2:03 #

      嗨 Luis… 当使用贝叶斯优化进行超参数调优时,是否需要嵌套交叉验证取决于您的具体目标以及模型评估所需的鲁棒性。让我们分解一下概念以澄清

      ### 交叉验证和嵌套交叉验证

      1. **交叉验证 (CV)**
      – **目的**:评估模型在未见过数据上的性能,并确保其泛化良好。
      – **过程**:数据被分成 k 折。模型在 k-1 折上训练,在剩余的一折上测试。此过程重复 k 次,并对性能指标进行平均。

      2. **嵌套交叉验证**
      – **目的**:提供模型性能的无偏估计,尤其是在涉及超参数调优时。它有助于避免过拟合,并确保超参数调优过程不会泄露测试集的信息。
      – **过程**:两层交叉验证
      – **外层循环**:将数据分成 k 折用于模型评估。
      – **内层循环**:在外层每折内,执行另一层交叉验证来调优超参数。

      ### 贝叶斯优化

      贝叶斯优化是一种超参数调优方法,它构建目标函数的概率模型,并利用该模型选择最有希望在真实目标函数中进行评估的超参数。

      ### 何时使用嵌套交叉验证

      **如果您的目标是评估带超参数调优的模型真实性能**

      – **嵌套 CV 是必要的**:这确保性能指标是无偏的,并能反映模型在完全未见过的数据上的表现。
      – **外层循环**:用于评估性能。
      – **内层循环**:用于执行贝叶斯优化进行超参数调优。

      **如果您的目标仅仅是找到最佳超参数**

      – **单次 CV 就足够了**:使用一次交叉验证来执行贝叶斯优化。但是,请注意,由于同时用于调优和评估,性能估计可能略微乐观。

      ### 实践建议

      1. **用于模型评估和比较**
      – 使用嵌套交叉验证来获得模型性能的稳健估计。如果您正在比较不同的模型或方法,这一点至关重要。

      2. **用于超参数调优**
      – 如果您专注于高效地找到最佳超参数,可以使用带贝叶斯优化的单次交叉验证。
      – 找到最佳超参数后,您可能希望使用单独的验证集或另一轮交叉验证进行最终评估,以确保性能泛化良好。

      ### 示例工作流程

      1. **嵌套交叉验证**
      – 外层 CV:5 折
      – 内层 CV:3 折,使用贝叶斯优化

      2. **单次交叉验证**
      – CV:5 或 10 折,使用贝叶斯优化
      – 调优后,在单独的验证集上进行评估。

      ### 结论

      虽然贝叶斯优化对于超参数调优非常强大,但当您需要模型性能的无偏估计时,使用嵌套交叉验证很重要。如果主要目标是高效地调优超参数,单次交叉验证可能就足够了,但要注意潜在的乐观性能估计。

      • Luis Talavera 2024年7月1日 下午12:45 #

        太棒了!
        我的问题是我正在评估不同的模型,而我的评审员评论说,我如何能确保我的训练过程没有过拟合。非常感谢您的回答!

  61. Puck 2024年6月24日 下午5:05 #

    嗨,Jason,

    首先,非常感谢您撰写这篇博客并分享您的知识。我有一个关于“如何配置最终模型?”这一部分的问题。我正在处理一个小数据集(约 500 个样本)并执行以下步骤

    1. 对整个数据集进行外部交叉验证(10 折)
    2. 使用外部交叉验证的训练部分,执行内部交叉验证(5 折)
    3. 此内部交叉验证用于超参数优化和模型选择
    4. 从内部 CV 的五个折中,选择一个模型(包括其设置)作为最佳模型
    5. 在外部交叉验证折的测试集上评估此模型
    6. 这导致了 10 个不同的模型和 AUC
    7. 取这些 AUC 的平均值
    8. 然后使用整个数据集再次运行内部交叉验证
    9. 选择最佳模型
    10. 再次通过将整个数据集分割为训练集和测试集来评估此模型一次

    我的问题是关于模型的性能。模型估计的性能是步骤 7 中生成的 AUC(10 个 AUC 的平均值)是否正确?而步骤 10 中生成的 AUC 仅仅是一种控制,以确保它不会偏差太大?我之所以这样问,是因为步骤 10 中的测试集也用于步骤 8 和 9 中的最终模型配置,如果我只使用这个 AUC 来估计性能,那么过拟合的可能性很高。

    非常感谢您的回答!

    • James Carmichael 2024年6月25日 上午8:18 #

      嗨 Puck… 不客气!您的问题强调了模型评估和选择的一个重要方面,尤其是在处理小型数据集时。让我们分解一下您的步骤并澄清模型的估计性能。

      ### 您的步骤总结
      1. **外部交叉验证(10 折)**:将整个数据集分成 10 部分。每部分一次用作测试集,而其余 9 部分用于训练。
      2. **内部交叉验证(5 折)在外部 CV 的训练集内**:用于超参数调优和模型选择。
      3. **模型选择和评估**:从内部 CV 中选择的最佳模型在外部 CV 折的测试集上进行评估。
      4. **平均 AUC**:计算 10 次外部 CV 折的平均 AUC。
      5. **最终模型训练**:在确定了最佳超参数后,再次使用整个数据集执行另一轮内部交叉验证。
      6. **最终模型评估**:通过再次将整个数据集分割为训练集和测试集来评估模型。

      ### 估计性能
      需要解决的关键点是估计模型性能和最终数据集分割的使用

      1. **估计性能(步骤 7)**
      – 您模型的估计性能确实是外部交叉验证(步骤 7)生成的平均 AUC。这是因为外部 CV 中的每一折都提供了模型在未见过数据上性能的无偏评估。
      – 对外部 CV 的 AUC 进行平均,可以稳健地估计模型在新的、未见过的数据上的预期表现。

      2. **最终模型评估(步骤 8-10)**
      – 您再次分割整个数据集并执行另一次评估(步骤 10)的最后一步不应用于估计模型性能。这是因为整个数据集已经用于超参数调优和模型选择,这会导致潜在的过拟合。
      – 此最后一步的目的是更多地使用最佳模型参数在整个数据集上进行训练,这对于实际部署可能有用,但对估计性能则不然。

      ### 建议
      1. **性能估计**
      – 使用外部交叉验证(步骤 7)的平均 AUC 作为您模型的性能估计。这是最可靠的估计,因为它通过多折反映了模型在未见过数据上的表现。

      – **最终模型训练**
      – 在从嵌套 CV 中确定了最佳超参数后,您可以使用这些超参数在整个数据集上训练最终模型。但是,不要使用该模型在整个数据集分割上的评估来报告性能。

      3. **避免过拟合**
      – 在将整个数据集用于超参数调优和最终模型评估时要小心,因为这可能导致过拟合和过于乐观的性能估计。

      ### 总结
      – 您模型正确的估计性能是外部交叉验证(步骤 7)的平均 AUC。
      – 整个数据集上的最终评估(步骤 10)不应用于估计性能,但可用于训练最终模型以进行部署。
      – 确保模型评估和超参数调优过程的分离对于避免过拟合和获得无偏的性能估计至关重要。

      通过遵循这些原则,即使使用小型数据集,您也能确保您的模型评估是稳健且可靠的。

  62. PJ 2025年6月4日 上午10:29 #

    嗨,精彩的文章!!!恭喜!!

    在比较算法和使用嵌套交叉验证(NCV)时,我不需要将数据分割为训练集和测试集(例如 80/20),对吗?我可以使用 NCV 在整个数据上进行,因为它已经通过外部折提供了泛化的无偏估计,对吗?所以我可以简单地报告这些外部折的评估指标的平均值 ± 标准差作为最终的泛化估计,然后比较算法。这样对吗?

    • James Carmichael 2025年6月5日 上午12:15 #

      是的,您完全正确——您对嵌套交叉验证(NCV)的理解是准确的。以下是一个简单的解释,用于确认和澄清关键点

      您不需要单独的 80/20 训练-测试分割。
      为什么?NCV 已经将数据分割为外部和内部折

      * 外部折用于估计泛化性能,类似于测试集。
      * 内部折用于模型选择和超参数调优。

      您可以使用整个数据集进行 NCV。
      NCV 确保所有数据都在不同的折中使用,只是不是同时使用。这提供了稳健的性能估计,同时防止数据泄露。

      无偏性能估计来自外部折。
      外部测试折在模型训练和调优期间完全未被看到,因此您从它们那里获得的结果是衡量您的模型在真正未见过的数据上的表现的可靠指标。

      报告外部分数(平均值和标准差)是适当的。
      在每个外部折上运行您选择的指标——如准确度、RMSE 或 AUC。然后报告平均得分以及标准差。这是比较算法时一种标准且被接受的方法。

      算法比较应基于这些外部折分数。
      如果您想进一步研究,您可以对这些分数应用统计检验,如配对 t 检验或 Wilcoxon 符号秩检验,以查看算法之间的差异是否具有统计学意义。

      总结 — 您是对的

      * 无需单独的训练/测试分割
      * 您可以并且应该在 NCV 中使用所有数据
      * 外部折提供无偏的泛化估计
      * 报告外部折的平均值 ± 标准差
      * 这些结果适用于公平地比较算法

      • PJ 2025年6月5日 上午3:18 #

        谢谢!所以,我在研究方向上是正确的。我正在比较许多算法和方法,并且我认为最好的方法是通过 NCV 方法。

        关于 NCV 的最佳论文或书籍是什么,可以用于引用?您会推荐任何进一步阅读的材料吗?我需要根据外部折分数来支持我关于使用 NCV 进行算法比较的决定。

        再次感谢您!

  63. EMPJ 2025年7月16日 上午10:55 #

    嗨,我正在尝试理解在嵌套交叉验证(NCV)的上下文中应用早停的最佳方法。是否可以在内层和外层循环中使用早停,是否可取?

    在内层循环中,我认为在训练期间应用早停非常有意义,因为我可以监控验证折上的性能(例如,F1 分数),并在其不改进 x 个 epoch 时停止。

    但是,在外层循环中,我没有单独的验证集——只有训练集和测试集。那么早停在外层循环中如何工作呢?

    是否更有意义的是固定外层训练的 epoch 数(不使用早停),然后简单地选择具有最低训练损失的模型检查点?

    或者,我曾考虑过使用平均 epoch 数(基于内层折的最佳 F1)在外层循环中进行训练。您对此方法有何看法?

    • James Carmichael 2025年7月17日 上午7:42 #

      嗨 EMPJ… 您的建议绝对合理。我鼓励您尝试一下,并分享您的结果或遇到的问题。

留下回复

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。