You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cross_validate与RocCurveDisplay计算AUC结果差异原因咨询

两种AUC计算结果差异的原因分析

问题背景

训练Random Forest分类器后,采用两种方式计算AUC分数时结果差异极大:

  • 通过cross_validate得到的AUC为0.72
  • 通过RocCurveDisplay绘制ROC曲线并计算的AUC为0.97
    更换分类器后,SVM也存在类似差异(0.53 vs 0.71),但Naive Bayes的结果较为接近(0.66 vs 0.68)。

第一种计算方式代码(cross_validate)

numeric_transformer = make_pipeline(
    IterativeImputer(estimator=RandomForestRegressor(),random_state=0),
    StandardScaler()
)
preprocessor = make_column_transformer(
    (numeric_transformer, numeric_cols)
)


pipe = Pipeline(steps=[
    ('preprocessor', preprocessor),
    ('clf', RandomForestClassifier())
])

scoring = {
    'AUC': 'roc_auc', 
    'ACCURACY': 'accuracy',
    'F1_SCORE': 'f1',
    'PRECISION': 'precision',
    'RECALL': 'recall'
}
print(scoring)

cv = StratifiedKFold(n_splits=5)
cv_scores_RF = cross_validate(pipe, X, y, cv=cv, scoring=scoring, return_estimator=True)

print("Random forest metrics")
print(f"AUC: {abs(cv_scores_RF['test_AUC']).mean()}")
print(f"ACCURACY: {cv_scores_RF['test_ACCURACY'].mean()}")
print(f"F1 SCORE: {abs(cv_scores_RF['test_F1_SCORE']).mean()}")
print(f"PRECISION: {abs(cv_scores_RF['test_PRECISION']).mean()}")
print(f"RECALL: {abs(cv_scores_RF['test_RECALL']).mean()}")

第二种计算方式代码(RocCurveDisplay)

import matplotlib.pyplot as plt
from sklearn.metrics import RocCurveDisplay


plt.figure(figsize=(8, 6))


tprs = []
aucs = []


for i, estimator in enumerate(cv_scores_RF['estimator']):
    viz = RocCurveDisplay.from_estimator(estimator, X, y, ax=plt.gca(), name=f'ROC fold {i+1}')
    
    roc_auc = auc(viz.fpr, viz.tpr)
    aucs.append(roc_auc)

    interp_tpr = np.interp(mean_fpr, viz.fpr, viz.tpr)
    interp_tpr[0] = 0.0
    tprs.append(interp_tpr)

mean_tpr = np.mean(tprs, axis=0)
mean_auc = np.mean(aucs)

plt.plot(mean_fpr, mean_tpr, color='b', linestyle='--', lw=2, label=f'Mean ROC (AUC = {mean_auc:.2f})')

plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) - RF')
plt.legend(loc='lower right')

plt.show()

print(f"Mean AUC: {mean_auc:.2f}")

核心原因

两种方法的数据使用逻辑完全不同,这是差异的根源:

  • cross_validate的逻辑:严格遵循交叉验证规则,每个fold的模型仅在对应训练集上训练,随后在未见过的测试集上计算AUC,最终取所有fold测试集AUC的平均值,这是评估模型泛化能力的标准做法。
  • RocCurveDisplay的逻辑:代码中直接用每个fold的模型在整个数据集X上做预测并计算AUC,相当于让模型在它已经见过的训练数据上做评估,存在严重的数据泄露,得到的AUC是过于乐观的结果。

不同分类器差异的额外原因

  • Random Forest和SVM:这类模型对训练数据的拟合能力强,在见过的数据上的预测表现远优于未见过的数据,因此数据泄露带来的偏差极大。
  • Naive Bayes:模型本身假设较强,拟合能力弱,即使在训练数据上评估,结果和测试集的差异也较小,所以两者结果接近。

修正方法

要让第二种方法的结果与cross_validate一致,需在交叉验证的每个fold中,用对应模型在该fold的测试集上计算ROC和AUC,调整后的代码示例:

import matplotlib.pyplot as plt
from sklearn.metrics import RocCurveDisplay, auc
import numpy as np

plt.figure(figsize=(8, 6))

tprs = []
aucs = []
mean_fpr = np.linspace(0, 1, 100)

# 遍历交叉验证的每个fold,获取训练/测试索引
for i, (train_idx, test_idx) in enumerate(cv.split(X, y)):
    # 获取当前fold的模型
    estimator = cv_scores_RF['estimator'][i]
    # 用模型在当前fold的测试集上预测
    viz = RocCurveDisplay.from_estimator(
        estimator, X.iloc[test_idx], y.iloc[test_idx], 
        ax=plt.gca(), name=f'ROC fold {i+1}'
    )
    
    roc_auc = auc(viz.fpr, viz.tpr)
    aucs.append(roc_auc)

    interp_tpr = np.interp(mean_fpr, viz.fpr, viz.tpr)
    interp_tpr[0] = 0.0
    tprs.append(interp_tpr)

mean_tpr = np.mean(tprs, axis=0)
mean_auc = np.mean(aucs)

plt.plot(mean_fpr, mean_tpr, color='b', linestyle='--', lw=2, label=f'Mean ROC (AUC = {mean_auc:.2f})')

plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) - RF')
plt.legend(loc='lower right')

plt.show()

print(f"Mean AUC: {mean_auc:.2f}")

调整后,第二种方法的AUC结果会与cross_validate一致,因为都是在未见过的测试集上评估模型泛化能力。


内容的提问来源于stack exchange,提问作者Yuno Gasai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 10:05:19