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
相关产品推荐
相关产品推荐

