为啥我的交叉验证始终比训练测试分割表现更好?

Posted

技术标签:

【中文标题】为啥我的交叉验证始终比训练测试分割表现更好?【英文标题】:Why does my cross-validation consistently perform better than train-test split?为什么我的交叉验证始终比训练测试分割表现更好? 【发布时间】:2021-12-25 20:11:10 【问题描述】:

我有下面的代码(使用 sklearn),它首先使用训练集进行交叉验证,然后使用测试集进行最终检查。但是,交叉验证始终表现更好,如下所示。我是否过度拟合了训练数据?如果是这样,最好调整哪个超参数来避免这种情况?

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
#Cross validation
rfc = RandomForestClassifier()
cv = RepeatedKFold(n_splits=10, n_repeats=5)   
scoring = 'accuracy', 'precision', 'recall', 'f1', 'roc_auc' 
scores = cross_validate(rfc, X_train, y_train, scoring=scoring, cv=cv)
print(mean(scores['test_accuracy']),
      mean(scores['test_precision']),
      mean(scores['test_recall']),
      mean(scores['test_f1']),
      mean(scores['test_roc_auc'])
      )

这给了我:

0.8536558341101569 0.8641939667622551 0.8392201023654705 0.8514895113569482 0.9264002192260914
现在使用整个训练+验证集重新训练模型,并使用从未见过的测试集对其进行测试 RFC = RandomForestClassifier() RFC.fit(X_train, y_train) y_pred = RFC.predict(X_test) 准确度 = 准确度分数(y_test,y_pred) 精度 = 精度分数(y_test,y_pred) 召回=召回分数(y_test,y_pred) f1 = f1_score(y_test, y_pred) y_pred_proba = RFC.predict_proba(X_test)[::,1] auc = roc_auc_score(y_test, y_pred_proba) 打印(准确度, 精确, 记起, f1, 奥克 )

现在它给了我下面的数字,显然更糟:

0.7809788654060067 0.5113236034222446 0.5044687189672294 0.5078730317420644 0.7589037004728368

【问题讨论】:

你可能想要使用正则化 您好,我认为比较结果是不对的,因为交叉验证分数是在training dataset 上计算的最佳分数,而第二个模型的分数是在testing dataset 上计算的. 【参考方案1】:

我可以使用Pima Indians Diabetes Dataset 重现您的场景。

您在预测指标中看到的差异并不一致,在某些运行中您甚至可能会注意到相反的情况,因为它取决于拆分期间 X_test 的选择 - 某些情况会更容易预测并且会给出更好的指标,反之亦然。虽然交叉验证在您轮换的整个集合上运行预测并聚合此效果,但单个 X_test 集将受到随机分裂的影响。

为了更好地了解这里发生的情况,我修改了您的实验并分为两个步骤:

1。交叉验证步骤:

我使用整个 X 和 y 集并按原样运行其余代码

rfc = RandomForestClassifier()
cv = RepeatedKFold(n_splits=10, n_repeats=5)
# cv = KFold(n_splits=10)
scoring = 'accuracy', 'precision', 'recall', 'f1', 'roc_auc'
scores = cross_validate(rfc, X, y, scoring=scoring, cv=cv)
print(mean(scores['test_accuracy']),
      mean(scores['test_precision']),
      mean(scores['test_recall']),
      mean(scores['test_f1']),
      mean(scores['test_roc_auc'])
      )

输出:

0.768257006151743 0.6943032069967433 0.593436328663432 0.6357667086829574 0.8221242747913622

2。经典的训练测试步骤:

接下来我运行普通的 train-test 步骤,但我使用不同的 train_test 拆分执行 50 次,并对指标进行平均(类似于交叉验证步骤)。

accuracies = []
precisions = []
recalls = []
f1s = []
aucs = []

for i in range(50):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    RFC = RandomForestClassifier()

    RFC.fit(X_train, y_train)
    y_pred = RFC.predict(X_test)

    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred)
    recall = recall_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    y_pred_proba = RFC.predict_proba(X_test)[::, 1]
    auc = roc_auc_score(y_test, y_pred_proba)
    accuracies.append(accuracy)
    precisions.append(precision)
    recalls.append(recall)
    f1s.append(f1)
    aucs.append(auc)

print(mean(accuracies),
      mean(precisions),
      mean(recalls),
      mean(f1s),
      mean(aucs)
      )

输出:

0.7606926406926405 0.7001931059992001 0.5778712922956755 0.6306501622080503 0.8207846633339568

正如预期的那样,预测指标是相似的。但是,交叉验证运行得更快,并使用整个数据集的每个数据点进行给定次数的测试(轮换)。

【讨论】:

谢谢,您是对的:CV 和重复的训练测试具有相同的结果,由于您提到的原因,在这种情况下 CV 是正确的方法。我犯的错误是我正在平衡我的训练数据(代码未显示),并将平衡的训练数据用于 CV,当然 CV 中的平衡数据比用于训练测试的原始不平衡数据集产生更好的结果分裂。

以上是关于为啥我的交叉验证始终比训练测试分割表现更好?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的交叉验证错误分类错误率与测试数据集成功率相矛盾

为啥交叉验证的性能比测试差?

Sklearn-CrossValidation 交叉验证

训练模型:交叉验证

交叉验证、留一交叉验证、自助法

5倍交叉验证如何理解