如何在嵌套交叉验证中获取超参数?

机器算法验证 交叉验证 scikit-学习 超参数
2022-02-15 17:59:23

我已阅读以下有关嵌套交叉验证的帖子,但仍不能 100% 确定我将如何处理具有嵌套交叉验证的模型选择:

为了解释我的困惑,让我尝试一步一步地介绍使用嵌套交叉验证方法的模型选择。

  1. 使用 K-Fold 创建一个外部 CV 循环。这将用于估计“赢得”每个内部 CV 循环的超参数的性能。
  2. 使用 GridSearchCV 创建一个内部 CV 循环,其中在每个内部循环中,GSCV 会遍历参数空间的所有可能组合并得出最佳参数集。
  3. 在 GSCV 在内循环中找到最佳参数后,使用外循环中的测试集对其进行测试,以获得性能估计。
  4. 然后外循环更新到下一个折叠作为测试集,其余作为训练集,并重复 1-3 次。总可能的“获胜”参数是在外循环中指定的折叠数。因此,如果外循环是 5 倍,那么您将对具有5 组不同超参数的算法进行性能估计,而不是一组特定超参数的性能。

这种方法在 SKLearn 的示例页面上进行了说明:http: //scikit-learn.org/stable/auto_examples/model_selection/plot_nested_cross_validation_iris.html

问题:4.之后,您如何确定哪些超参数效果最好?我了解您想在最后使用 COMPLETE 数据集来训练您的算法(例如逻辑回归、随机森林等)。但是如何确定哪些超参数在嵌套交叉验证中效果最好?我的理解是,对于每个内部循环,一组不同的超参数将获胜。对于外循环,您将获得对 GridSearchCV 性能的估计,但您没有获得任何一组特定的超参数。那么,在最终的模型创建中,您如何知道要使用哪些超参数?这是我从其他方面难以理解的缺失逻辑。

提前感谢您的任何提示,特别是如果@Dikran Marsupial 和@cbeleites 可以加入!

编辑:如果可以,请在您的回答中使用“算法”和“超参数”等术语。我认为让我感到困惑的一个原因是人们使用术语“模型”或“模型选择”。我很困惑他们是否在谈论选择使用哪种算法或使用哪些超参数。

编辑 2:我创建了一个笔记本,显示了两种进行嵌套交叉验证的方法。第一种方式是 SKLearn 示例中显示的方式,另一种较长的方式是我编写的方式。SKLearn 中显示的方式并没有公开“获胜”超参数,但我更长的方式可以。但问题仍然是一样的。完成嵌套交叉验证后,即使暴露了超参数,我现在该怎么办?正如您从笔记本末尾的超参数中看到的那样,它们变化很大。

3个回答

(我确定我已经在某个答案中写了大部分内容 - 但现在找不到。如果有人偶然发现该答案,请链接它)。我在这里看到了两种略有不同的方法,我认为这两种方法都是明智的。

但首先是一些术语:

  • 来自应用领域,对我来说(拟合/训练的)模型是现成的。即模型包含为新数据生成预测所需的所有信息。因此,模型还包含超参数正如您将看到的,这种观点与下面的方法 2 密切相关。
  • OTOH,根据我的经验,训练算法在以下意义上没有得到很好的定义:为了获得(拟合的)模型,不仅需要完成“正常”模型参数的 - 我们称之为“初级拟合”,而且超参数也需要固定。从我的应用程序的角度来看,参数和超参数之间并没有太大区别:两者都是模型的一部分,需要在训练期间进行估计/决定。
    我猜它们之间的差异与开发新训练算法的人之间的差异有关,他们通常会描述一类训练算法以及一些转向参数(超参数)在没有应用程序/领域知识的情况下很难/不可能修复(或至少修复应该如何决定/估计它们)。

方法一:需要稳定的优化结果

使用这种方法,“模型训练”是对“正常”模型参数的拟合,并给出参数。内部例如交叉验证负责超参数优化。

解决应该选择谁的超参数集的困境的关键步骤/假设是要求优化是稳定的用于验证目的的交叉验证假设所有代理模型都与最终模型足够相似(通过应用于整个数据集的相同训练算法获得)以允许将它们视为平等(在它们之间以及最终模型之间)。如果这个假设被打破并且

  1. 代理模型彼此之间仍然相等(或等价),但与最终模型不相等,我们谈论的是众所周知的交叉验证的悲观偏差。

  2. 如果代理模型彼此不相等/等价,我们就会遇到不稳定的问题。

对于内循环的优化结果,这意味着如果优化是稳定的,那么选择超参数就没有冲突如果在内部交叉验证结果中观察到相当大的变化,则优化是不稳定的不稳定的训练情况比仅仅决定选择哪个超参数集有更糟糕的问题,我真的建议在这种情况下退后一步,重新开始建模过程。

但是,这里有一个例外:优化中可能有几个局部最小值,从而为实际目的产生相同的性能。还要求它们之间的选择是稳定的可能是不必要的强要求 - 但我不知道如何摆脱这种困境。

请注意,如果并非所有模型都产生相同的获胜参数集,则不应在此处使用外循环估计作为泛化错误:

  • 如果您声称参数的泛化错误p,所有进入验证的代理模型实际上都应该使用这些参数。
    (想象一下有人告诉你他们用 C = 1 和线性内核对模型进行了交叉验证,你发现一些分割是用 rbf 内核评估的!)
  • 但是除非不涉及决策,因为所有拆分都产生相同的参数,否则这将破坏外循环中的独立性:每个拆分的测试数据已经进入决策,因为它是所有其他拆分中的训练数据,因此使用来优化参数。

方法 2:将超参数调整作为模型训练的一部分

这种方法连接了“训练算法开发者”和训练算法应用用户的观点。

训练算法开发人员提供“裸”训练算法model = train_naked (trainingdata, hyperparameters)作为应用用户的需求 tunedmodel = train_tuned (trainingdata),它还负责修复超参数。

train_tuned例如,可以通过在裸训练算法周围包装基于交叉验证的优化器来实现train_naked

train_tuned然后可以像任何其他不需要超参数输入的训练算法一样使用,例如,它的输出tunedmodel可以进行交叉验证。现在检查超参数的稳定性,就像应该检查“正常”参数的稳定性一样,作为交叉验证评估的一部分。

如果您对所有获胜模型的性能进行平均,而不管它们各自的参数集如何,这实际上就是您在嵌套交叉验证中所做和评估的事情。


有什么不同?

我们最终可能会采用这两种方法得到不同的最终模型:

  • 方法 1 中的最终模型将是train_naked (all data, hyperparameters from optimization)
  • 而方法 2 将使用train_tuned (all data)and - 因为它会在更大的数据集上再次运行超参数优化 - 这可能最终会得到一组不同的超参数。

但同样的逻辑也适用:如果我们发现最终模型的参数与交叉验证代理模型有很大不同,那就是假设 1 被违反的症状。所以恕我直言,我们再次没有冲突,而是检查我们的(隐式)假设是否合理。如果他们不是,我们无论如何都不应该对最终模型的性能有一个很好的估计。


我有很多人认为嵌套交叉验证做方法 1 的印象(也是从这里看到的类似问题/混淆的数量)。但是泛化误差通常根据方法 2 估计,所以这就是要走的路最终模型也是如此。


虹膜示例

总结:优化基本没有意义。可用的样本量不允许区分此处任何参数集的性能。

然而,从应用程序的角度来看,结论是您选择 4 个参数集中的哪一个并不重要——这并不是什么坏消息:您发现了一个相对稳定的参数平台。这是调整模型的适当嵌套验证的优势:虽然您不能声称它是最佳模型,但您仍然可以声称使用方法 2 建立在整个数据上的模型将具有大约 97 % 的准确率(150 个测试用例中 145 个正确的 95 % 置信区间:92 - 99 %)

请注意,方法 1 也没有看起来那么遥远 - 见下文:您的优化意外地错过了一个相对明显的“赢家”,因为平局(这实际上是样本量问题的另一个非常明显的症状)。

虽然我对 SVM 的了解还不够深入,无法“看到” C = 1 在这里应该是一个不错的选择,但我会选择限制性更强的线性内核。此外,正如您所做的优化一样,即使您知道所有参数集都会导致几乎相同的性能,选择获胜的参数集也没有错。

但是,将来,请考虑您的经验是否会对您可以预期的性能产生粗略的猜测,以及大致哪种模型是一个不错的选择。然后构建该模型(使用手动固定的超参数)并计算其性能的置信区间。使用它来决定尝试优化是否明智。(我可能会补充一点,我主要处理的数据是获得 10 个以上独立案例并不容易——如果你在一个独立样本量很大的领域,事情看起来会好得多)

长版:

至于iris数据集上的示例结果。iris有 150 个案例,考虑具有 2 x 2 个参数(2 个内核,惩罚 2 个数量级C)网格的 SVM。

内循环分为 129 (2x) 和 132 (6x) 案例。“最佳”参数集在线性或 rbf 内核之间未确定,两者都具有 C = 1。但是,内部测试精度都94 - 98.5 % 的观察精度内(包括始终丢失的 C = 10)。我们在其中一个拆分中的最大差异是 rbf 的 3 对 8 个错误,其中 C = 1 对 10。

这不可能是一个显着的差异。我不知道如何提取 CV 中个别案例的预测,但即使假设 3 个错误是共享的,并且 C = 10 模型又产生了 5 个错误:

> table (rbf1, rbf10)
         rbf10
rbf1      correct wrong
  correct     124     5
  wrong         0     3

> mcnemar.exact(rbf1, rbf10)

    Exact McNemar test (with central confidence intervals)

data:  rbf1 and rbf10
b = 5, c = 0, p-value = 0.0625
alternative hypothesis: true odds ratio is not equal to 1

请记住,在 2 x 2 网格中有 6 个成对比较,因此我们还需要对多重比较进行校正。


方法一

在 rbf “赢得”线性内核的 4 个外部拆分中的 3 个中,它们实际上具有相同的估计准确度(我猜在 tie 的情况下 min 返回第一个合适的索引)。

将网格更改为 params = {'kernel':['linear', 'rbf'],'C':[1,10]} 产量

({'kernel': 'linear', 'C': 1}, 0.95238095238095233, 0.97674418604651159)
({'kernel': 'rbf', 'C': 1}, 0.95238095238095233, 0.98449612403100772)
({'kernel': 'linear', 'C': 1}, 1.0, 0.97727272727272729)
({'kernel': 'linear', 'C': 1}, 0.94444444444444442, 0.98484848484848486)
({'kernel': 'linear', 'C': 1}, 0.94444444444444442, 0.98484848484848486)
({'kernel': 'linear', 'C': 1}, 1.0, 0.98484848484848486)
({'kernel': 'linear', 'C': 1}, 1.0, 0.96212121212121215)

方法二:

clf是你的最终模型。random_state = 2, rbf 与 C = 1 获胜

In [310]: clf.grid_scores_
[...snip warning...]
Out[310]: 
[mean: 0.97333, std: 0.00897, params: {'kernel': 'linear', 'C': 1},
 mean: 0.98000, std: 0.02773, params: {'kernel': 'rbf', 'C': 1},
 mean: 0.96000, std: 0.03202, params: {'kernel': 'linear', 'C': 10},
 mean: 0.95333, std: 0.01791, params: {'kernel': 'rbf', 'C': 10}]

(大约 5 次中的 1 次,6 次中的 1 次,linear并且rbfC = 1排名第 1 位并列)

我已经阅读了您的问题和上面的答案 2 次(第一次 3 个月前)。我很感兴趣,也想找到对我的数据进行交叉验证的绝对合适的方法。经过大量思考和阅读,我似乎找到了漏洞,这是我的修复:

为了解释我的困惑,让我尝试一步一步地介绍使用嵌套交叉验证方法的模型选择。

  1. 使用 K-Fold 创建一个外部 CV 循环。这将用于估计“赢得”每个内部 CV 循环的超参数的性能。
  2. 使用 GridSearchCV 创建一个内部 CV 循环,其中在每个内部循环中,GSCV 会遍历参数空间的所有可能组合并得出最佳参数集。(注意:这里我假设:内循环的数据 = 外循环的训练数据。您可能会问:为什么?答案:https ://stackoverflow.com/questions/42228735/scikit-learn-gridsearchcv-with-multiple-repetitions/ 42230764#42230764阅读 Vivek Kumar 第 4 步的答案部分)
  3. 在 GSCV 在内循环中找到“最佳参数”后(我们称其为内循环),在外循环中使用测试集对其进行测试以获得性能估计(我们称其为 outer_fold_score1)。
  4. 然后外循环更新到下一个折叠作为测试集,其余作为训练集(以评估外循环上的“内部获胜者”),使用新测试集(outer_fold_score2)再次测试“内部获胜者”。然后外循环再次更新到下一个折叠,直到循环完成。每次折叠的得分(outer_fold_score 1,2..)将取平均值,以获得外循环的“内部获胜者”得分(outer_score)
  5. 然后外部循环更新到下一个折叠作为测试集,其余作为训练集(以找到下一个“内部获胜者”,并重复1-4 次(请注意,当我们重复 1 时,我们不会创建新的 K- fold 但我们每次都使用相同的外 Kfold)。在 1-4 个循环中,我们得到一个带有 outer_score 的“最佳参数”(或“内部获胜者”)。具有最佳 outer_score 的那个将是获胜者获胜者

推理:

  • 基本上,您的问题涉及外循环有许多“获胜参数”。问题是你没有完成外部循环来评估并找到“外部赢家”。您的第 4 步仅在外循环的 1 折中评估“内部赢家”,但您没有“循环”。因此我需要用我的第 4 步替换它 - “循环”外部循环中的评估步骤并获得外部分数(通过平均)
  • 您的第 5 步确实在外循环中完成了“循环”工作,但这只是为了建立另一个“内在赢家”。它没有在外循环中循环这个“内部赢家”的“评估”

不使用嵌套交叉验证来选择算法的超参数,此方法用于估计模型构建过程的泛化误差。通过模型构建过程,我希望您应用的所有步骤都能达到您将在现场使用的最终模型。
模型构建过程可以由您应用的规则组成,以决定对数据应用哪些预处理、使用哪些特征以及最终使用哪些超参数。将此视为一种“元算法”,它接收特定数据集作为输入D并产生一个“算法”作为输出,由一组固定的预处理转换、特征和最后的超参数值组成。

例如,假设您有 X,y作为设计矩阵和目标,你想训练一个分类器:
1. 只使用第一个x特点X相关性最高的y.
2. 通过最小化 10 倍交叉验证误差估计来选择超参数值。

如果您将这两个步骤应用于特定的一对X,y您将获得具有一组明确特征和固定超参数的特定算法,这些算法不一定与您获得的相同X,y尽管模型构建过程是相同的,即:步骤 1 + 2,它们不依赖于任何特定的数据集。
假设您完成了上述所有操作而没有将数据拆分为训练测试,因为您有一个小数据集,您如何估计刚刚创建的分类器的泛化误差?您可以使用在步骤 2 的交叉验证中发现的最佳错误吗?
,第一个大问题是在步骤 1 中,您使用所有数据来选择要使用的功能。因此,即使您在第 2 步中进行了交叉验证,这些功能也会在每次交叉验证运行时看到并记住测试折叠中存在的一些信息。结果将是对测试错误的过于乐观的估计,这被称为特征选择偏差为了在您的估计中考虑到这一点,您需要将特征选择步骤放在步骤 2 的交叉验证循环中。
好的,现在我们好吗?在循环内使用特征选择步骤进行交叉验证时发现的最佳错误是对泛化错误的公平估计吗?
理论上答案仍然是否定的,问题是您的超参数已被选择以最小化您可以使用的特定数据集上的交叉验证错误,因此从某种意义上说,您正在将超参数拟合到您的数据中过度拟合它们,这称为模型选择偏差. 但这在实践中是否值得关注?这取决于具体的应用:当数据集较小且要调整的超参数数量相对较大时,它可能会变得更加严重,因为训练中的过度拟合。为了在估计泛化误差时考虑到这一点,您将按照您的描述应用嵌套交叉验证,然后您可以正确估计泛化误差。
最后回答您的最后一个问题,在通过嵌套交叉验证对您的“模型构建过程”泛化错误进行公平估计之后,您只需将该过程(步骤 1+2)应用于整个数据集,从而获得具有固定集合的模型特征和设置超参数值,但请记住,我们期望该模型对看不见的数据产生的错误是嵌套交叉验证估计