关于如何执行嵌套交叉验证的说明

机器算法验证 交叉验证 广义加法模型 方法比较 死亡
2022-03-17 03:28:23

我目前正在尝试根据死亡人数和人口数量来获取我们人口的生命表。我的第一个想法是遵循本文的方法,但经过一些讨论后,我们计划使用不同的模型,例如 GLMM 和 GAM,并探索包含不同的结和族。

作者建议使用训练集和测试集或交叉验证。我一直在阅读(这个网站上有很好的资源和问题!)我想知道我使用的方法是否合适,因为我不完全确定。

这是我的想法:

  1. 随机播放数据
  2. 将数据分为训练集(80%)和测试集(20%)。
  3. 测试训练数据中的不同参数,并使用测试数据进行比较。
  4. 一旦我有选择的家庭和结,再次洗牌数据。
  5. 对一个模型进行 5 倍交叉验证(大小数据集 = 3440 个观察值)的 n 次迭代(20 次?)以获得方法的性能(想法是在 4 中选择的模型能够更好地预测数据) .

在此处输入图像描述

现在,根据我上传的图,这是我的问题和困惑:

我应该使用嵌套交叉验证并在内部循环中测试模型参数(如图)吗?每个 k 折对应一组参数,或者我需要为每组参数重复嵌套 CV?理论结数可能太低,或者我可能需要为其中一个变量添加一个平滑因子等。我不确定我是否可以在内循环中使用这些参数。

这让我想到了另一个问题。我需要对每种方法进行一次 k 折交叉验证吗?如果我对具有泊松族的 GAM 和具有负二项式的 GAM 使用交叉验证,我可能处于该模型能够更好地预测数据的折叠处,而在另一个折叠处相同的模型可能表现不佳,因此,例如这组结可能不合适。

我不确定的另一件事是一个变量是日历年。改组数据时需要特别小心吗?也许确保每个折叠都有关于所有年份的信息或其他信息?我也想过使用 GLMM 或 GAMM,也许就足够了?

我的另一个问题与输出参数以及如何评估交叉验证有关。由于我的目标是预测,因此我计划查看每个模型的残差 AIC,并绘制预测值和观察值以查看哪种方法表现更好。我应该使用不同的参数吗?

感谢任何帮助或指导,因为我刚开始阅读有关交叉验证的内容并且没有经验。

编辑 - 关注@adrin 和@Gavin Simpson 的回答

如果我理解你的答案,基本上嵌套交叉验证将是这样的:

for each fold in the outer loop 
     for each parameter in the grid 
          run each parameter in the inner loop
          evaluate each parameter in each k-fold
     chose best parameter based on results from inner loop
     run best parameter in each k-fold of the outer loop

我想测试的参数是:

  1. 不同年龄的结(一些固定在边界上,但我想测试一些其他的用于中世纪的结)。

选择节的原因是由于边界处的极端值,因为第一点的死亡率很高(婴儿死亡率)和高于 85 的死亡率(因为高于 85 的数据被分组)。

我刚拿到 Simon Wood 的书,所以我可以了解更多关于 GAM 的信息。我考虑过使用三次样条,但细样条可能会很有趣。看看薄板样条与使用预定义结之间的区别会很有趣。如果薄板样条曲线很好地拟合数据,那将是一个不错的选择。

  1. 具有泊松和 NB 系列的 GLM 以及具有泊松和 NB 系列的 GAM。
  2. 我想检查是否包含区域的随机效应。
  3. 我还想测试性别和年龄、地区和年龄之间的相互作用是否会改善模型。

我没有考虑使用ti交互,而是沿着这些思路:

gam(deads ~ region*age + gender*age + calendar_year + ti(age,k=7,bs="cr") + offset(log(pop)), knots=list(age=list_knots), family=poisson, data=data, method="REML")

但我也想过尝试这样的事情:

gam(deads ~ s(age,bs="fs",by=region) + s(age,bs="fs",by=gender) + calendar_year + ti(age,k=7,bs="cr") + offset(log(pop)), knots=list(age=list_knots), family=poisson, data=data, method="REML")

显然,我需要阅读更多内容来检查什么是有意义的和更合适的。

  1. 最后,我想知道是否应该在因子变量中添加一些因子平滑。

现在,也许我应该对最终模型有更清晰的认识。让我听听你的想法。但是让所有这些参数进行测试是/是我对内部循环中交叉验证的困惑的一部分。

3个回答

我对这个问题给出了一般性答案,以下是适用于您的问题的答案:

训练和验证拆分

首先将输入拆分为训练和验证;但我也会考虑领域知识。在您的情况下,我会考虑该年份参数并考虑最近几年的数据(不确定您有多少年,假设您有 10 年,假设 2 年,如果您有 10 年)并假设这部分数据为你的验证集

嵌套交叉验证和参数搜索

现在你可以做你在图表中解释的事情了。假设您有一个方法,它接受输入数据和参数(例如,定义使用具有泊松族的 GAM 或具有负二项式的 GAM 的参数),并在数据上拟合相应的模型。让我们将您正在考虑的所有这些参数的集合称为参数 grid

现在对于每个外部折叠,您使用内部折叠进行整个网格搜索,以获得每个参数集的分数。然后使用最佳参数集对内部循环中提供给您的整个数据进行训练,并在外部循环的测试部分获得其性能。

假设您的参数网格总共有 3 个值(例如,具有泊松族的 GAM 或具有负二项式的 GAM,以及 [未正则化] 线性模型),并且不涉及其他参数。然后你会做这些训练:

5[outer loop]×(3[parameter grid]×4[inner loop]+1[best parameters])

用代码说话,它是这样的:

parameter_grid = {'param1: ['binomial', 'poisson'],
                  'smoothing': ['yes', 'no']}
scores = []
for train, test in outer_folds:
    model = GridSearchCV(estimator=my_custom_model,
                         param_grid=parameter_grid,
                         refit=True,
                         cv=4)
    model.fit(train.X, train.y)
    scores.append(model.score(test.X, test.y))
score = mean(scores)

为简单起见,我偏离了实际的 API,所以上面的代码更像是一个伪代码,但它给了你想法。

这让您了解参数网格将如何对您的数据执行。然后您可能会认为您想为您的线性模型添加一个正则化参数,或者排除您的一个 GAM,等等。您在此阶段对您的参数集进行所有操作。

最终评价

一旦你找到了一个你觉得舒服的参数网格,你就可以将它应用到你的整个火车数据上。您可以使用正常的 5 折交叉验证对整个火车数据进行网格搜索,而无需操纵参数网格来查找害虫参数集,在整个火车数据上使用这些参数训练模型,然后在验证中获得其性能放。该结果将是您的最终表现,如果您希望您的结果尽可能有效,此时您不应返回优化任何参数。

为了澄清现阶段的参数搜索,我从scikit-learn API获得帮助Python

parameter_grid = {'param1: ['binomial', 'poisson'],
                  'smoothing': ['yes', 'no']}
model = GridSearchCV(estimator=my_custom_model,
                     param_grid=parameter_grid,
                     refit=True,
                     cv=5)
model.fit(X_train, y_train)
model.score(X_validation, y_validation)

上面的代码对你的训练数据进行model.fit(...)了()5折交叉验证(cv=5),在整个数据上拟合了最好的模型(refit=True),最后给出了你在验证集上的分数(model.score(...))。

决定parameter_grid在这个阶段你要做什么是你在前一个阶段所做的。您可以包括/排除您在其中提到的所有参数并进行实验和评估。一旦确定了参数网格的选择,就可以进入验证阶段。

你想通过选择结来实现什么?作为确定模型中平滑项复杂性的一种方法?

一般来说,在现代 GAM 理论中选择结并没有什么好处。例如,薄板样条曲线是mgcv中的默认值,并且在每个唯一数据位置都有一个节点(随后的基被特征分解以给出较低等级的近似值),并且通过 ML 或 REML 策略选择平滑度通常可以很好地避免过度配件。如果不使用薄板样条,只需展开k1三次回归样条节点均匀分布在数据区间上或在k1数据的均匀分布的分位数。如果您正在使用其他基础,例如 ap 样条,则选择节点位置甚至可能没有意义,除了在数据间隔内均匀间隔它们。

关键的用户选择是为每个平滑选择足够大的基础维度,但有工具可以测试。假设初始基础扩展足够大/足够丰富以包含真实函数或与其接近的近似值,则对摆动的惩罚通常会完成其余部分。

GLM 是 GAM 的一个特例,其中所有项都完全平滑(线性)。因此,您可以使用平滑度选择来拟合 GAM,如果线性效应合适,则平滑度惩罚应该变大(摆动的惩罚更大)。或者,您可以按如下方式制定 GAM

gam(y ~ x + s(x, bs = "tp", m = c(2, 0)))

其中,平滑项专门测试x除了已经包含的线性效果之外的平滑效果。

至于选择族,模型诊断也可以激发这一点,参数的估计也可以通知负二项式在泊松分布上的额外变化。

对于随机效应,您可以将其拟合为随机效应平滑器 ingam()bam()via bs = "re",然后使用summary()输出来决定是否需要这种平滑 - 那里的测试对于随机效应项具有良好的属性。我也相信在最近的mgcv版本中实现的 AIC 适合与诸如此类的简单随机效应术语一起使用。

可以通过类似 ANOVA 的分解为“主效应”和“交互”,通过ti()术语使用推理来测试交互:

y ~ ti(x) + ti(z) + ti(x, z)

人们将专注于ti(x, z)确定是否需要交互。

至于因素平滑问题;使用所有数据拟合两个模型(一个有和没有那个因子水平的平滑),并通过 AIC 和summary()输出中报告的 Wald-like 检验比较模型拟合。

我在这里的一般观点是,在拟合从数据中学习的 GAM 时,使用所有数据来通知平滑度的估计可能会更好。然后使用统计推断来决定需要哪些术语。

或者只是适合最复杂的模型并包括额外的惩罚(通过select = TRUEwithmethod = "REML"method = "ML")为您进行选择。

让我补充一些要点来补充已经存在的好答案:

  • 关于嵌套交叉验证,我为自己做了一个描述,我发现它可以很容易地正确计算,而(通过)根本不将情况描述为嵌套
    基本思想是我需要两件事:一种将我的模型训练为我的数据的函数的方法,该方法负责所有自动调整/选择/超参数优化train_tuned_model (training_data),以及对通过train_tuned_model (training_data). 后者可以通过简单直接的交叉验证来完成。在内部,train_tuned_model (training_data)可能会使用另一种自举或交叉验证或我认为合适的任何启发式方法。
    这样,我仍然可以获得正确的计算,就像我通过嵌套交叉验证获得它们一样,但从概念上讲,我可以通过更简单和常用的构建块来描述整个事情。顺便说一句,这也回答了一个问题,即从整个数据集训练的最终模型是否应该使用嵌套交叉验证中的优化超参数集,或者是否应该再次优化超参数(我的回答:是)。
  • 关于你是否这样做:

    a)外 CV 循环 - 超参数循环 - 内 CV 循环
    b)外 CV 循环 - 内 CV 循环 - 超参数循环

    两者都是明智的。b) 是配对设计,a) 是它的非配对类似物。因此,由于可以进行配对设计,这可能会为您的优化提供更多动力。

  • 尚未提及的一点:交叉验证(两者)的成功关键取决于生成独立数据子集的拆分。您的数据结构比简单的“行 [假定] 独立”要复杂得多。你对这个结构了解很多。一定要考虑这个结构:

    • “日历年。我在洗牌时需要特别小心吗?” 一如既往:这取决于。

      • 在某些数据/模型中,年份实际上是一个随机因素(今年的收成数据根本无助于预测明年的收成,该模型将用于未知的未来年份)。在这种情况下,您需要确保测试年份独立于培训年份。
      • 其他数据/模型在本质上可能是纵向的(即了解过去几年有助于预测下一年)并且生产使用场景总是具有最后n可用年。在这种情况下,您需要进行拆分,以便训练始终是测试年之前的最后 n 年。(空间数据可能需要相同的原则)
      • 对于日历年,我已经没有幻想制定一个年份是正常固定因素的场景。也许一个近似值是您发现最好识别这一年是冷湿年、湿热年还是干热年。您不需要对固定因素采取任何特殊的预防措施。
    • 一般来说,拆分需要确保测试数据中的随机因子水平不会出现在训练数据中。

    • 固定因素在拆分中不需要特别注意,
    • 但是您需要小心并确保该因素确实是固定的。你的预测场景。数据收集中的固定因素可能是预测场景中的随机因素(见上文)。
    • 因子可以嵌套得很深:在这种情况下,在最高随机因子的正确拆分将自动导致较低级别的正确拆分。
    • 如果随机因素交叉,则拆分为独立的训练和测试级别意味着某些数据被排除在拆分的训练和测试之外。