从混淆矩阵到AUC:5步代码实战绘制ROC与PR曲线对比
从混淆矩阵到AUC:5步代码实战绘制ROC与PR曲线对比
在机器学习模型的评估过程中,分类性能的量化分析是核心环节。传统理论讲解往往让初学者陷入公式迷宫,而本文将带您通过5个可执行的代码步骤,从混淆矩阵基础出发,最终完成ROC与PR曲线的对比可视化,并深入解读AUC/AP值的实际意义。
1. 环境准备与模拟数据生成
任何模型评估都需要标准化的测试环境。我们首先生成一个适用于二分类问题的模拟数据集,确保正负样本比例可控(3:7),以模拟真实场景中的类别不均衡情况:
from sklearn.datasets import make_classification import matplotlib.pyplot as plt import numpy as np # 生成1000个样本,特征维度20,其中5个为有效特征 X, y = make_classification( n_samples=1000, n_features=20, n_informative=5, n_classes=2, weights=[0.7, 0.3], random_state=42 ) # 查看样本分布 print(f"负样本数: {sum(y==0)}, 正样本数: {sum(y==1)}")提示:通过调整
weights参数可以模拟不同程度的类别不平衡,这对后续曲线分析至关重要。
为验证模型效果,我们使用逻辑回归进行训练,并输出预测概率而非硬分类结果:
from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split # 划分训练集与测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 训练模型并获取预测概率 model = LogisticRegression(max_iter=1000) model.fit(X_train, y_train) y_scores = model.predict_proba(X_test)[:, 1] # 取正类概率2. 混淆矩阵的动态构建
混淆矩阵不是静态概念,其数值随分类阈值的变化而改变。下面代码展示如何根据阈值动态生成矩阵:
from sklearn.metrics import confusion_matrix def dynamic_confusion_matrix(y_true, y_scores, threshold=0.5): y_pred = (y_scores >= threshold).astype(int) tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel() return { "Threshold": threshold, "TP": tp, "FP": fp, "TN": tn, "FN": fn, "Precision": tp/(tp+fp) if (tp+fp)>0 else 0, "Recall": tp/(tp+fn) if (tp+fn)>0 else 0 } # 测试不同阈值下的矩阵变化 thresholds = [0.2, 0.5, 0.8] for thresh in thresholds: print(f"阈值={thresh}:", dynamic_confusion_matrix(y_test, y_scores, thresh))输出示例:
阈值=0.2: {'Threshold': 0.2, 'TP': 82, 'FP': 147, 'TN': 153, 'FN': 18, 'Precision': 0.358, 'Recall': 0.82} 阈值=0.5: {'Threshold': 0.5, 'TP': 65, 'FP': 45, 'TN': 255, 'FN': 35, 'Precision': 0.591, 'Recall': 0.65} 阈值=0.8: {'Threshold': 0.8, 'TP': 28, 'FP': 5, 'TN': 295, 'FN': 72, 'Precision': 0.848, 'Recall': 0.28}关键观察点:
- 阈值降低:召回率上升但精确率下降(捕获更多正例,代价是误报增加)
- 阈值升高:精确率提升但召回率降低(预测更保守,漏检增多)
3. ROC曲线的生成与解读
ROC曲线通过系统遍历所有可能阈值,展示模型在不同误报容忍度下的性能。使用sklearn.metrics可快速计算关键指标:
from sklearn.metrics import roc_curve, auc fpr, tpr, thresholds = roc_curve(y_test, y_scores) roc_auc = auc(fpr, tpr) # 绘制曲线 plt.figure(figsize=(10, 6)) plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC曲线 (AUC = {roc_auc:.3f})') plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('假正例率 (FPR)') plt.ylabel('真正例率 (TPR)') plt.title('ROC曲线分析') plt.legend(loc="lower right") # 标记最佳阈值点 optimal_idx = np.argmax(tpr - fpr) plt.scatter(fpr[optimal_idx], tpr[optimal_idx], marker='o', color='red') plt.annotate(f'最佳阈值: {thresholds[optimal_idx]:.2f}', (fpr[optimal_idx]+0.05, tpr[optimal_idx]-0.05)) plt.show()曲线特征解读:
- 对角线:表示随机猜测模型的性能(AUC=0.5)
- 左上方凸起:模型区分能力越强,AUC值越高
- 最佳阈值点:TPR与FPR差值最大处,通常作为业务平衡点
4. PR曲线的绘制与对比分析
在类别不平衡场景下,PR曲线比ROC曲线更能反映模型真实性能。下面是实现代码:
from sklearn.metrics import precision_recall_curve, average_precision_score precision, recall, _ = precision_recall_curve(y_test, y_scores) ap = average_precision_score(y_test, y_scores) plt.figure(figsize=(10, 6)) plt.plot(recall, precision, color='blue', lw=2, label=f'PR曲线 (AP = {ap:.3f})') plt.xlabel('召回率 (Recall)') plt.ylabel('精确率 (Precision)') plt.title('PR曲线分析') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.legend(loc="upper right") # 标记平衡点 plt.scatter(recall[optimal_idx], precision[optimal_idx], marker='o', color='red') plt.annotate(f'平衡点: P={precision[optimal_idx]:.2f}, R={recall[optimal_idx]:.2f}', (recall[optimal_idx]+0.05, precision[optimal_idx]-0.05)) plt.show()ROC与PR曲线的选择指南:
| 场景特征 | 推荐曲线 | 原因分析 |
|---|---|---|
| 类别分布均衡 | ROC | 整体性能评估更全面 |
| 负样本远多于正样本 | PR | 避免负样本主导评价指标 |
| 关注假正例成本 | PR | 精确率直接反映误报比例 |
| 需要比较不同模型 | 两者均需 | 综合评估泛化与细分能力 |
5. 关键指标的系统对比与业务应用
最后我们整合所有指标,形成可落地的业务决策工具:
from sklearn.metrics import classification_report # 按最佳阈值生成最终预测 y_pred = (y_scores >= thresholds[optimal_idx]).astype(int) # 输出完整评估报告 print(classification_report(y_test, y_pred, target_names=['负类', '正类'])) # 构建指标对比表格 metrics = { 'AUC': roc_auc, 'AP': ap, 'F1': f1_score(y_test, y_pred), 'Balanced Acc': balanced_accuracy_score(y_test, y_pred) } pd.DataFrame.from_dict(metrics, orient='index', columns=['值'])典型业务场景的阈值选择策略:
金融风控(低FP容忍)
- 选择PR曲线上精确率>90%的阈值
- 可接受召回率降低以减少误拦截
疾病筛查(低FN优先)
- 选择ROC曲线上TPR>95%的阈值
- 适当放宽FPR以提高病例检出
推荐系统(平衡体验)
- 选择F1分数最大的阈值
- 精确率与召回率的调和平衡
# 保存完整评估结果到CSV results = pd.DataFrame({ 'Threshold': thresholds, 'FPR': fpr, 'TPR': tpr, 'Precision': precision[:-1], # 移除最后一个冗余点 'Recall': recall[:-1] }) results.to_csv('model_evaluation_metrics.csv', index=False)通过这5个步骤,我们不仅实现了从理论到实践的跨越,更建立了可复用的模型评估框架。在实际项目中,建议将此流程封装为Python类,方便在不同模型中快速调用对比。
