使用欠采样对高度不平衡数据进行交叉验证

数据挖掘 机器学习 scikit-学习 交叉验证 采样 阶级失衡
2021-10-01 06:38:27

在我的问题中,我正在处理一个高度不平衡的数据集,比如每个正类都有 10000 个负类。训练模型的正常启动方法是对数据进行欠采样。在此过程中,在欠采样数据上训练我们的模型并检查对保留的模型评估(来自原始数据 - 没有欠采样)非常重要。

现在问题来了。KFold-cross 验证实际上将欠采样的训练集拆分为 K 个段,并将其中的一个折叠作为测试集(现在是欠采样的测试集)。我相信对于模型评估,我们实际上需要计算非欠采样测试集的感兴趣指标(对吗?或者我在这里误解了某事?)。如果是,是否可以按如下方式进行交叉验证?

  1. 将数据拆分为 K 个段。
  2. 将第一个 Segment 作为测试集,对其余的 Folds 进行欠采样(例如 K=1 作为测试集,K=2,3,4,5 作为训练集)
  3. 在欠采样的训练数据上拟合模型并计算测试集上感兴趣的指标。
  4. 考虑另一个折叠作为测试集(这次例如,K=2),其余的作为训练集(K=1,3,4,5)。对训练集进行欠采样并继续执行步骤 3。
  5. 对其余的折叠继续此过程。

当我们对数据进行欠采样时,这是一种正确的交叉验证方式吗?如果是的话,可以用标准库来做吗?


2 月 21 日编辑: 感谢 @Wes,我想知道以下代码是否是 KFold 交叉验证在高度不平衡数据集上的正确实现。

import numpy as np
from statistics import mean, stdev
from collections import Counter

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import KFold
from sklearn.metrics import roc_auc_score

from imblearn.under_sampling import RandomUnderSampler
from imblearn.pipeline import make_pipeline

# initial parameters
RANDOM_STATE = 42
RATIO = 0.033
N_SAMPLES = 1000000
K_FOLD = 5

# Generate the dataset
X, y = make_classification(n_classes=2, class_sep=1, 
                           n_features=10, n_redundant=2,
                           weights=[0.9999, 0.0001], n_informative=5,
                           flip_y=0.0, n_samples=N_SAMPLES,
                           random_state=RANDOM_STATE)

print('Number of samples in each class %s' % Counter(y))

rus = RandomUnderSampler(random_state=RANDOM_STATE, ratio = RATIO)
rfc = RandomForestClassifier(random_state=RANDOM_STATE, n_estimators=100)
pipeline = make_pipeline(rus, rfc)

auc_roc = []
kf = KFold(n_splits=K_FOLD)
for train_index, test_index in kf.split(X):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    y_pred = pipeline.fit(X_train, y_train).predict_proba(X_test)[:,1]

    auc_roc.append(roc_auc_score(y_test, y_pred))


print('ROC_AUC = {} +/- {}'.format(np.round(mean(auc_roc),4),
                                   np.round(stdev(auc_roc),4)))

这样我就得到了以下结果ROC_AUC = 0.9374 +/- 0.037

1个回答

您应该始终对未过采样/欠采样的数据进行模型性能评估。您可以使用 scikit-learn 设置管道以对训练集执行欠采样,然后按照您的描述对每次迭代的非欠采样数据折叠进行评估。