交叉验证AUROC绘图、最优阈值确定及AUC计算准确性问询
我在做机器学习模型交叉验证评估时遇到两个核心问题:
- 已经能绘制每折的AUROC和对应阈值,但不确定如何准确绘制所有折的平均AUROC及其对应阈值,自己加了代码但拿不准正确性。
- 发现用
np.mean()计算的平均AUC和sklearn.metrics.auc()基于平均ROC曲线计算的AUC有差异,想知道哪种方法更精准。
以下是我修改后的代码:
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42) cv = StratifiedKFold(n_splits=10) classifier = SVC(kernel='sigmoid',probability=True,random_state=0) tprs = [] aucs = [] optimal_thresholds = [] mean_fpr = np.linspace(0, 1, 100) plt.figure(figsize=(10,10)) i = 0 for train, test in cv.split(X, y): probas_ = classifier.fit(X[train], y[train]).predict_proba(X[test]) # Compute ROC curve and area the curve fpr, tpr, thresholds = roc_curve(y[test], probas_[:, 1]) # 我添加的代码: optimal_threshold_index = np.argmax(tpr-fpr) optimal_threshold = thresholds[optimal_threshold_index] optimal_thresholds.append(optimal_threshold) # tprs.append(np.interp(mean_fpr, fpr, tpr)) tprs[-1][0] = 0.0 roc_auc = auc(fpr, tpr) aucs.append(roc_auc) plt.plot(fpr, tpr, lw=1, alpha=0.3, label='ROC fold %d (AUC = %0.4f)' % (i, roc_auc)) i += 1 plt.plot([0, 1], [0, 1], linestyle='--', lw=2, color='r', label='Chance', alpha=.8) mean_tpr = np.mean(tprs, axis=0) mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) # 我添加的代码: np_mean_AUC = np.mean(aucs) # print(f"np_mean_AUC={np_mean_AUC},mean_auc={mean_auc}") # std_auc = np.std(aucs) plt.plot(mean_fpr, mean_tpr, color='b', label=r'Mean ROC (AUC = %0.4f $\pm$ %0.4f)' % (np_mean_AUC, std_auc), lw=2, alpha=.8) # 我添加的代码: mean_optimal_threshold_index = np.argmax(mean_tpr-mean_fpr) plt.annotate(f'Mean Optimal Threshold ({np.mean(optimal_thresholds):.2f})', xy=(mean_fpr[mean_optimal_threshold_index], mean_tpr[mean_optimal_threshold_index]), xytext=(5, -5), textcoords='offset points', arrowprops=dict(facecolor='red', arrowstyle='wedge,tail_width=0.7', shrinkA=0, shrinkB=10), color='red') # std_tpr = np.std(tprs, axis=0) tprs_upper = np.minimum(mean_tpr + std_tpr, 1) tprs_lower = np.maximum(mean_tpr - std_tpr, 0) plt.fill_between(mean_fpr, tprs_lower, tprs_upper, color='grey', alpha=.2, label=r'$\pm$ 1 std. dev.') plt.xlim([-0.01, 1.01]) plt.ylim([-0.01, 1.01]) plt.xlabel('False Positive Rate',fontsize=18) plt.ylabel('True Positive Rate',fontsize=18) plt.title('Cross-Validation ROC of SVM',fontsize=18) plt.legend(loc="lower right", prop={'size': 15}) plt.show()
输出的ROC曲线如图所示:
请帮忙判断:
- 我添加的代码是否能准确绘制交叉验证的平均ROC曲线及对应阈值?
- 标注的AUC值是否合理?
一、关于平均ROC与阈值的绘制正确性
你添加的代码逻辑整体合理,但有几个细节需要明确:
1. 单折最优阈值的计算
你用np.argmax(tpr-fpr)找最优阈值,这是基于Youden指数的常用阈值选择策略,逻辑没问题。收集每折最优阈值后取平均,是一种合理的汇总方式,但要注意:
- 不同折的阈值分布可能差异较大,取平均可能掩盖个体差异,若想更严谨,可补充绘制阈值分布箱线图辅助说明。
2. 平均ROC上的阈值标注
你在平均ROC曲线上用np.argmax(mean_tpr-mean_fpr)找到的点,是平均ROC曲线上的Youden指数最大值点,但这个点和你取平均的optimal_thresholds没有直接对应关系——因为平均ROC是各折TPR在统一FPR网格上插值后取平均得到的虚拟曲线,本身没有原始阈值序列。
你当前把“各折最优阈值的平均值”和“平均ROC上的Youden点”绑定标注的方式,可作为近似展示,但需要明确:这个阈值是各折最优阈值的统计平均值,而非平均ROC曲线本身对应的阈值。
如果想更严谨地关联平均ROC与阈值,可尝试对各折阈值进行插值对齐:
# 在循环中添加阈值插值 interp_thresholds = np.interp(mean_fpr, fpr, thresholds) threshold_list.append(interp_thresholds) # 之后计算平均阈值 mean_thresholds = np.mean(threshold_list, axis=0) # 找到平均ROC上Youden点对应的平均阈值 mean_opt_thresh = mean_thresholds[np.argmax(mean_tpr - mean_fpr)] # 标注时用这个值 plt.annotate(f'Mean Optimal Threshold ({mean_opt_thresh:.2f})', ...)
但要注意,插值后的阈值是近似值,因为原始阈值和FPR的对应关系是非线性的。
二、两种AUC计算方式的差异与精准性
np.mean(aucs)和auc(mean_fpr, mean_tpr)的差异是正常的,二者统计意义不同:
np.mean(aucs):各折AUC的算术平均值- 逻辑:先计算每折测试集的ROC曲线AUC,再取平均。
- 意义:直接反映模型在不同数据子集上的AUC表现平均水平,是交叉验证中评估模型泛化能力的标准方式。
auc(mean_fpr, mean_tpr):平均ROC曲线的AUC- 逻辑:先对各折TPR在统一FPR网格上插值得到平均TPR曲线,再计算该曲线的AUC。
- 意义:仅反映聚合后的虚拟ROC曲线的面积,不对应任何实际测试集的模型表现,更多用于可视化展示整体趋势。
哪种更精准?
若目标是评估模型泛化能力,np.mean(aucs)是更合理、精准的选择——它直接基于每折真实测试集的评估结果,符合交叉验证估计模型真实性能的核心目的。你当前在图例中标注np_mean_AUC是合理的,建议保留。
三、代码优化建议
- 添加最优阈值分布可视化:用
plt.boxplot(optimal_thresholds)展示各折阈值的离散程度,让结果更完整。 - 若追求平均ROC与阈值的严格对应,可采用上述阈值插值对齐的方式,但需接受插值带来的近似误差。
内容的提问来源于stack exchange,提问作者Alexander Xie

