如何正确应用交叉验证和/或拆分数据集?

数据挖掘 机器学习 神经网络 深度学习 数据集 交叉验证
2022-03-08 18:04:21

我有一个特殊的问题,现在真的不知道如何在这种情况下正确验证我的实验。

  • 有一个包含 100.000 个样本的大数据集,99.000 y=0, 1.000 y=1
  • 每个样本有 1.000 个特征
  • 有 10 种不同的特征组合子集必须进行评估,以获得有关或多或少具有表现力的特征组的信息
  • 由于每个子集的特征数量和类型不同,因此必须手动找出适当的模型架构
  • 必须评估几种不同模型的性能
  • 必须评估几种不同采样技术的性能
  • 必须应用 10 倍交叉验证
  • 必须对每个实验配置应用随机搜索(子集 + 模型 + 采样技术的组合)

这些事实导致以下实验程序:

train, val = splitDataset()
iterate each featureSubset:
    iterate each modelType:
        model = manuallySearchGoodArchitecture(featureSubset, modelType, train, val)

        iterate each samplingTechnique:
            train_temporary = applySampling(samplingTechnique)
            HPs = HPOptimization(model, train_temporary, val)
            result =  k-FOldCV(model, HPs, samplingTechnique, train)
        end
    end
end

所以,现在我不确定是否正确应用了 CrossValidation。因为必须验证的模型已经看到train数据(在manuallySearchGoodArchitecture和期间HPOptimization),并且这些数据也用于 CV 期间的测试目的。

  1. 那么,这个套路有问题吗?
  2. 在应用适当的验证技术的同时执行这些实验的正确方法是什么?
  3. 数据集必须如何拆分?
1个回答

我不确定我是否了解该过程的每个部分,但它有一个明显的问题:因为 CV 应用于内部循环,因此存在严重的模型过度拟合其他参数(特征子集)的风险,模型类型,采样技术)。根据目标,这不一定是错误的,但相应地解释结果很重要。例如,在这种情况下,结果将显示不同的特征子集在数据上的表现如何,但子集之间的这种性能差异不应该被认为是可靠的:一个特定的子集可能碰巧比另一个子集更好。

这是一个相当复杂的设置,我假设需要考虑效率限制。如果可能的话,最可靠的结果将通过使用不同的数据子集进行几个阶段的 CV(或其他技术,例如bagging )来获得。例如,您可以运行整个过程几次,每次使用不同的随机实例子集:通过这种方式,您可以平均性能并查看特定的功能子集是否始终优于另一个(例如,对于其他功能的相同想法)参数)。


[已编辑] 免责声明:我不知道是否有任何标准方法可以进行这样的复杂多级设置,我的建议仅基于我对一些大致相似的案例的经验。

通常的想法是,每一个选择都可以被视为一个超参数,包括特征子集、模型类型、架构、采样技术。因此,我认为理想情况下应该在每个级别进行交叉验证,即将每个循环级别放在一个函数中,并使用不同的数据子集调用这个函数 k 次。它看起来像这样:

train1, val1 = splitDataset()
iterate each featureSubset:
    train2, val2 = splitData(train1)
    resultTrain2 = kfoldCV(processLevel2, train2)
    resultLevel2 = apply(resultTrain2, val2)
resultLevel1 = apply(resultLevel2, val1)

processLevel2:
    iterate each modelType:
        train3, val3 = splitData(train2)
        resultTrain3 = kfoldCV(processLevel3, train3)
        ...

备注:我不是 100% 确定算法,也许我过于复杂了。我认为它给出了一般的想法。

但是当然按照这个逻辑计算复杂度变得太高了,所以你可能不得不走一些捷径。我过去成功尝试过的一件事是使用遗传学习来同时优化不同的参数:这意味着拥有代表不同参数(特征子集、模型类型等)的不同“基因”,每个都有它的一组值,然后运行应该收敛到所有参数的一组最佳值的遗传过程(每次评估特定的参数组合时我都使用 CV)。但是我不知道这是否是一种非常正统的方法:)


[编辑2]

经过更多思考,我想我会尝试做这样的事情:

innerData, valOuter = splitDataset() // keep large amount for validation set, say between 20-40%
train, valInner = splitDataset(innerData)
iterate each featureSubset:
    iterate each modelType:
        model = manuallySearchGoodArchitecture(featureSubset, modelType, train, val)

        iterate each samplingTechnique:
            train_temporary = applySampling(samplingTechnique)
            HPs = HPOptimization(model, train_temporary, valInner)
            result =  k-FOldCV(model, HPs, samplingTechnique, train)
        end
    end
end

bestHPCombinations = select top N HPCombinations from first stage
for each HPCombination in bestHPCombinations
     model = train(innerData)
     result = apply(model, valOuter)
end

它更简单:这个想法只是对一些新数据重新评估第一阶段的结果,以避免过度拟合(这里 HPCombination 包括 featuresSubset、modelType 等)。第二阶段还可以包括 CV 或 bagging,以获得更可靠的结果。但总的来说,我认为这个选项是相当可靠的,因为第一阶段的最佳模型不太可能只是偶然地成为第二阶段的最佳模型。