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

Why does my cross-validation consistently perform better than train-test split?

我有下面的代码(使用 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 = 随机森林分类器() RFC.fit(X_train, y_train) y_pred = RFC.predict(X_test) 精度 = accuracy_score(y_test, y_pred) 精度 = precision_score(y_test, y_pred) 召回 = 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) 打印(准确性, 精确, 记起, f1, 拍卖会 )

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

0.7809788654060067 0.5113236034222446 0.5044687189672294 0.5078730317420644 0.7589037004728368

我可以用 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 分割做了 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

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