用于特征选择和超参数优化的嵌套交叉验证

机器算法验证 物流 特征选择 随机森林 模型选择 超参数
2022-03-26 02:26:33

我花了好几个小时试图理解嵌套交叉验证并尝试自己实现——我真的不确定我是否做对了,我不知道如何测试我是否做对了除了问专家,如果我确实做对了。

我正在尝试优化我的特征选择器的超参数。最初,我陷入了在我的模型评估中引入选择偏差的陷阱,类似于这里描述的,即我试图使用我选择特征的相同数据来优化我的超参数。

这是我对嵌套交叉验证的理解以及我尝试使用它的方式:(为简单起见,使用折叠数 = 5):

  1. 所有数据分成 5 组,其中 4 组用于训练,1 组用于测试。
  2. 创建我要搜索的参数网格,并使用“外部”训练数据对参数网格进行 5 折交叉验证的网格搜索,并存储最能最大化 ROC 分数的参数集(使用 sklearn 的 GridSearchCV,这就是为什么这一切都是一步完成的)。
  3. 现在我有了随机森林的最佳参数(次迭代的最佳参数),将该随机森林模型拟合到“外部”训练数据,并转换外部训练和测试数据(即,使用获得的最佳特征选择器通过(2)实际选择特征)。
  4. 使用转换后的外部数据拟合我的“主要”模型(逻辑回归)
  5. 使用转换后的外部测试数据,使用(4)中的拟合逻辑回归模型进行预测,然后找到这些预测的ROC分数。
  6. 存储 (3) 中找到的最佳参数和 (5) 中找到的分数,并从 (1) 重新开始,使用与原始 5 不同的集合作为测试集,另外 4 个作为训练集。
  7. 最后,与(6)中最高分相关的参数应该用作我的随机森林特征选择过程的参数。

在我研究这个主题的过程中,我看到了“内部验证还不够!”这样的短语。并且经常“不要使用用于训练的数据进行验证”。通过内部验证,我认为他们指的是我在(2)中所做的事情,并且通过对我从中获得的结果进行更大的交叉验证,我认为我正在解决“不够”的部分并使用不同的数据通过这个外部验证来训练和适应。我认为我还通过在外部 cv 的每次迭代中重复特征选择来解决选择偏差。

我错过了什么吗?在查看其他人这样做的示例时,似乎他们使用嵌套交叉验证来优化超参数进行特征选择。这让我觉得我应该在某个地方再建一个窝,但我不知道它会在哪里工作。此外,随机森林的“随机”部分让我不确定我正在做任何可概括的事情,而不是完全依赖机会的事情。

另外,当人们说“使用内部 CV 选择参数”时,我不太确定这是什么意思,因为内部 CV 运行了多次,不是吗?

1个回答

我想出了我的理解在哪里,我想我应该回答我的问题,以防其他人偶然发现它。

首先,sklearn 使嵌套交叉验证看似简单。我一遍又一遍地阅读他们的例子,但直到我查看了这个问题的答案中给出的非常有用的伪代码,才得到它。

简而言之,这是我必须做的(这几乎是 scikit-learn 给出的示例的副本):

  1. 初始化两个交叉验证生成器,内部外部为此,我使用了 StratifiedKFold() 构造函数。
  2. 创建一个 RandomizedSearchCV 对象(比整个网格搜索快得多——我认为可以轻松地使用 sklearn 对象来计算贝叶斯信息准则并制作一个更酷/更快/更智能的超参数优化器,但这超出了我的知识范围,我只是听说Andreas Mueller 曾经在一些讲座中谈到过)将内部交叉验证器作为cv参数,其余的东西、估计器、评分函数等都是正常的。
  3. 适合您的训练集 (X) 和标签 (y)。您想要拟合它,因为下一步需要一个估计器(即,使用管道中的估计器转换 X 和 y 后得到的估计器 + 使用最终估计器拟合 X 和 y 以产生拟合估计器)。
  4. 使用cross_val_score并为其提供新安装的 RandomizedSearchCV 对象、X、y 和外部交叉验证器。我将其输出分配给一个名为scores的变量,并返回一个元组,该元组由一个具有最佳分数的元组和随机搜索(rs._best_paramsrs._best_score)和score变量给出的最佳参数组成。我对我到底需要什么有点模糊并且有点懒惰,所以这可能是返回的信息比必要的多。

在代码中,它看起来是这样的:

def nestedCrossValidation(X, y, pipe, param_dist, scoring, outer, inner):
    rs = RandomizedSearchCV(pipe, param_dist, verbose=1, scoring=scoring, cv=inner)
    rs.fit(X, y)
    scores = cross_val_score(rs, X, y, cv=outer)
    return ((rs._best_score, rs.best_params), scores)

cross_val_score 将拆分为一个训练/测试集并对该训练集进行随机搜索,该训练集本身拆分为一个测试/训练集,生成分数,然后返回到 cross_val_score 进行测试并继续进行下一个测试/训练放。

完成此操作后,您将获得一堆交叉验证分数。我最初的问题是:“你现在得到/做什么?” 嵌套交叉验证不适用于模型选择我的意思是,您并没有试图获得对最终模型有益的参数值。这就是内部 RandomizedSearchCV 的用途。

但是,当然,如果您在管道中使用 RandomForest 之类的东西进行特征选择,那么您每次都会期望一组不同的参数!那么你真正得到了什么有用的东西?

嵌套交叉验证是对您的方法论/一系列步骤的好坏给出一个公正的估计. 什么是好的”?好是由超参数的稳定性和您最终获得的交叉验证分数来定义的。假设你像我一样得到数字:我得到的交叉验证分数为:[0.57027027, 0.48918919, 0.37297297, 0.74444444, 0.53703704]。所以根据我做事方法的心情,我可以得到 0.37 到 0.74 之间的 ROC 分数——显然这是不可取的。如果您查看我的超参数,您会发现“最佳”超参数变化很大。然而,如果我得到一致的高交叉验证分数,并且最佳超参数都在同一个范围内,我可以相当自信地选择选择特征和建模我的数据的方式非常好。

如果您有不稳定因素——我不确定您能做什么。我还是新手——这个板上的专家可能有更好的建议,而不是盲目地改变你的方法。

但如果你有稳定,下一步是什么?这是我忽略理解的另一个重要方面:由您的训练数据创建的非常好的、可预测的和可推广的模型并不是最终模型。但它很接近。最终模型使用了你所有的数据,因为你已经完成了测试、优化和调整(是的,如果你尝试用你曾经适合它的数据交叉验证一个模型,你会得到一个有偏差的结果,但是你为什么要在这一点上交叉验证它?你已经这样做了,希望不存在偏见问题)——你给它所有你能提供的数据,这样它就可以做出最明智的决定,并且下一次你会看到你的模型有多好,是它在野外的时候,

我希望这可以帮助别人。出于某种原因,我花了很长时间来解决这个问题,这里有一些我曾经理解的其他链接:

http://www.pnas.org/content/99/10/6562.full.pdf — 重新检查其他遗传学论文得出的数据和结论的论文,这些论文不使用嵌套交叉验证进行特征选择/超-参数选择。知道即使是超级聪明和有成就的人也会时不时地被统计数据所欺骗,这有点令人欣慰。

http://jmlr.org/papers/volume11/cawley10a/cawley10a.pdf — iirc,我看到该作者的一位作者在此论坛上回答了大量有关此主题的问题

交叉验证后使用完整数据集进行训练?— 上述作者之一以更通俗的方式回答了类似的问题。

http://scikit-learn.org/stable/auto_examples/model_selection/plot_nested_cross_validation_iris.html — sklearn 示例