为什么在交叉验证之前不应该上采样

数据挖掘 Python scikit-学习 交叉验证 阶级失衡 打击
2021-10-07 13:48:08

我有一个不平衡的数据集,我正在尝试不同的方法来解决数据不平衡问题。我发现这篇文章解释了使用 SMOTE 技术对数据进行过采样时交叉验证的正确方法。

我使用 AdaBoost 算法创建了一个模型,并设置了以下要在网格搜索中使用的参数:

    ada = AdaBoostClassifier(n_estimators=100, random_state=42)
    params = {
        'n_estimators': [50, 100, 200],
        'random_state': [42]
    }

根据文章,这是过采样的错误方法:

    X_train_upsample, y_train_upsample = SMOTE(random_state=42).fit_sample(X_train, y_train)
    
    # cross-validate using grid search
    
    grid_naive_up = GridSearchCV(ada, param_grid=params, cv=kf, 
                                 scoring='recall').fit(X_train_upsample, 
                                                       y_train_upsample)
    grid_naive_up.best_score_

0.6715940782827282

    # test set
    recall_score(y_test, grid_naive_up.predict(X_test))

0.2824858757062147

而过采样的正确方法是这样的:

    from imblearn.pipeline import Pipeline, make_pipeline
    
    imba_pipeline = make_pipeline(SMOTE(random_state=42), 
                                  AdaBoostClassifier(n_estimators=100, random_state=42))
    cross_val_score(imba_pipeline, X_train, y_train, scoring='recall', cv=kf)
    new_params = {'adaboostclassifier__' + key: params[key] for key in params}
    grid_imba = GridSearchCV(imba_pipeline, param_grid=new_params, cv=kf, scoring='recall',
                            return_train_score=True)
    grid_imba.fit(X_train, y_train);


    # How well do we do on our validation set?
    grid_imba.best_score_

0.29015614186873506

    # compare this to the test set:
    y_test_predict = grid_imba.predict(X_test)

0.2824858757062147

因此,根据文章,第一种方法是错误的,因为在交叉验证之前进行上采样时,验证召回不是测试召回的良好衡量标准(28.2%)。然而,当使用 imblearn 管道进行上采样作为交叉验证的一部分时,验证集召回率 (29%) 是对测试集召回率 (28.3%) 的良好估计。根据文章,这样做的原因是:

在交叉验证之前进行上采样时,您将选择最过采样的模型,因为过采样允许数据从验证折叠泄漏到训练折叠中。

谁能简单地向我解释过采样如何使数据泄漏到验证中并导致过拟合?为什么在 imblearn 管道中不会出现这个问题?

1个回答

为了清楚地了解为什么在 CV 之前进行上采样的过程是错误的并且会导致数据泄漏和其他不良后果,首先想象一下更简单的“基线”情况是有用的,在这种情况下,我们只是在没有 SMOTE 的情况下进行上采样(即创建重复样本)。

这种过程无效的第一个原因是,这样,由于上采样导致的一些重复将最终导致训练验证拆分(CV 折叠);结果是该算法使用在训练期间已经看到的一些样本进行了验证,这使验证集(折叠)的最基本要求无效,这实际上是数据泄漏的定义。有关更多详细信息,请参阅 SO 线程Process for oversampling data for不平衡二进制分类中的自己的答案从那里引用:

我曾经目睹过一个案例,建模者很难理解为什么他的测试准确率能达到 100%,远高于他的训练准确率;原来他的初始数据集充满了重复——这里没有类不平衡,但想法是相似的——其中一些重复在分割后自然而然地最终出现在他的测试集中,当然不是新的或看不见的数据......

但还有第二个原因:此过程在我们的验证折叠中显示了不再代表现实的有偏见的性能测量:请记住,我们希望我们的验证折叠代表真实的看不见的数据,这当然是不平衡的。在对结果进行上采样后执行 CV 也可以人为地平衡我们的验证折叠;这样做,并声称我们获得 X% 的准确率,而该准确率的很大一部分将归因于人为上采样的少数类是没有意义的,并且会产生误导性的印象。有关详细信息,请参阅交叉验证中的 SO 线程 Balance classes 中的自己的答案请注意,您链接到的帖子的作者(相当神秘,仅在括号中):

(我们足够聪明,不会对测试数据进行过度采样)

更多证实,这里是 Max Kuhn,R 包的创建者和(强烈推荐)应用预测建模教科书caret的合著者,在第 11 章:电子书的第 11 章:针对类不平衡的子采样:caret

你永远不想人为地平衡测试集;它的类频率应该与人们在“野外”看到的一致。


现在,上述情况确实适用于通过对少数类进行简单上采样来平衡的情况;但 SMOTE 并没有这样做——它使用插值来创建与真正的少数群体“足够接近”的合成样本。这将如何改变这种情况?

不多。

  • 上述第二个原因(验证折叠中的有偏差的性能测量)仍然完全适用 - 事实上,它独立于上采样的确切性质(重复样本或合成样本)。

  • 鉴于 SMOTE 生成的合成样本确实与真实样本高度相关,上述第一个原因导致的问题仍然在很大程度上存在,尽管有所改善。

相比之下,管道方法不会遇到这些问题,因为它首先分为训练和验证折叠,然后仅将 SMOTE 应用于训练折叠。