实战指南:用Python绘制分类模型的PR与ROC曲线
在机器学习项目中,评估分类模型性能时,很多开发者习惯性地依赖单一准确率指标,这往往会导致对模型真实效果的误判。特别是在样本分布不均衡的场景下,准确率可能给出极具误导性的乐观结果。本文将带你用Python实战演练两种更可靠的评估工具——PR曲线和ROC曲线,通过可视化手段全面把握模型在不同阈值下的表现差异。
1. 环境准备与数据加载
首先确保你的Python环境已安装以下核心库:
# 基础数据处理与建模 import numpy as np import pandas as pd from sklearn.datasets import make_classification # 模型训练与评估 from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier # 评估指标与可视化 from sklearn.metrics import precision_recall_curve, roc_curve, auc import matplotlib.pyplot as plt import seaborn as sns我们使用make_classification生成模拟数据,刻意构造样本不均衡场景:
# 生成1000个样本,其中正类仅占20% X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.8, 0.2], random_state=42) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42) print(f"正样本比例 - 训练集: {y_train.mean():.2%}, 测试集: {y_test.mean():.2%}")提示:实际项目中建议使用
class_weight='balanced'参数或过采样技术处理样本不均衡问题
2. 训练基准分类模型
我们选择逻辑回归和随机森林作为对比模型:
# 初始化模型(逻辑回归设置class_weight以处理样本不均衡) lr = LogisticRegression(class_weight='balanced', random_state=42) rf = RandomForestClassifier(class_weight='balanced_subsample', random_state=42) # 训练模型 lr.fit(X_train, y_train) rf.fit(X_train, y_train) # 获取预测概率(注意使用predict_proba而非predict) lr_probs = lr.predict_proba(X_test)[:, 1] # 正类的预测概率 rf_probs = rf.predict_proba(X_test)[:, 1]关键点说明:
class_weight参数帮助模型关注少数类- 必须使用
predict_proba获取概率值而非硬分类结果 - 概率值将作为绘制曲线的阈值调节依据
3. PR曲线绘制与解读
PR曲线展示的是精确率(Precision)与召回率(Recall)之间的权衡关系:
def plot_pr_curve(y_true, y_probs, model_name): precision, recall, _ = precision_recall_curve(y_true, y_probs) pr_auc = auc(recall, precision) plt.plot(recall, precision, label=f'{model_name} (AUC = {pr_auc:.2f})') plt.xlabel('Recall') plt.ylabel('Precision') plt.title('PR Curve') plt.legend() plt.grid(True) plt.figure(figsize=(10, 6)) plot_pr_curve(y_test, lr_probs, 'Logistic Regression') plot_pr_curve(y_test, rf_probs, 'Random Forest') # 添加基准线(正样本比例) baseline = y_test.mean() plt.axhline(y=baseline, color='gray', linestyle='--', label=f'Baseline ({baseline:.2%})') plt.legend() plt.show()PR曲线的关键特征:
| 特征 | 解释 | 实际意义 |
|---|---|---|
| 曲线位置 | 越靠近右上角越好 | 模型在精确率和召回率间取得更好平衡 |
| AUC值 | 曲线下面积(0-1) | 综合评估指标,值越大性能越好 |
| 基准线 | 正样本比例 | 随机猜测模型的性能水平 |
典型应用场景:
- 欺诈检测(关注少数类)
- 疾病筛查(不能漏检病例)
- 推荐系统(确保推荐内容精准)
4. ROC曲线绘制与对比分析
ROC曲线展示的是真正率(TPR)与假正率(FPR)的关系:
def plot_roc_curve(y_true, y_probs, model_name): fpr, tpr, _ = roc_curve(y_true, y_probs) roc_auc = auc(fpr, tpr) plt.plot(fpr, tpr, label=f'{model_name} (AUC = {roc_auc:.2f})') plt.plot([0, 1], [0, 1], 'k--') # 随机猜测线 plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('ROC Curve') plt.legend() plt.grid(True) plt.figure(figsize=(10, 6)) plot_roc_curve(y_test, lr_probs, 'Logistic Regression') plot_roc_curve(y_test, rf_probs, 'Random Forest') plt.show()ROC曲线与PR曲线的核心区别:
| 特性 | ROC曲线 | PR曲线 |
|---|---|---|
| 关注点 | 整体分类性能 | 正类识别能力 |
| 横坐标 | FPR (假正率) | Recall (召回率) |
| 纵坐标 | TPR (真正率) | Precision (精确率) |
| 样本不均衡影响 | 相对稳定 | 非常敏感 |
| 适用场景 | 均衡数据集 | 不均衡数据集 |
注意:当正样本比例低于10%时,优先参考PR曲线评估模型
5. 高级技巧与实战建议
5.1 多模型对比可视化
将PR和ROC曲线组合展示更直观:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 6)) # PR曲线 for model, probs in [('LR', lr_probs), ('RF', rf_probs)]: precision, recall, _ = precision_recall_curve(y_test, probs) ax1.plot(recall, precision, label=f'{model} (AUC={auc(recall, precision):.2f})') ax1.set_title('PR Curve Comparison') ax1.legend() # ROC曲线 for model, probs in [('LR', lr_probs), ('RF', rf_probs)]: fpr, tpr, _ = roc_curve(y_test, probs) ax2.plot(fpr, tpr, label=f'{model} (AUC={auc(fpr, tpr):.2f})') ax2.plot([0, 1], [0, 1], 'k--') ax2.set_title('ROC Curve Comparison') ax2.legend() plt.show()5.2 阈值选择策略
通过曲线确定最佳分类阈值:
# 寻找PR曲线上F1分数最大的阈值 def find_optimal_threshold(y_true, y_probs): precision, recall, thresholds = precision_recall_curve(y_true, y_probs) f1_scores = 2 * (precision * recall) / (precision + recall + 1e-8) optimal_idx = np.argmax(f1_scores) return thresholds[optimal_idx], f1_scores[optimal_idx] lr_threshold, lr_f1 = find_optimal_threshold(y_test, lr_probs) rf_threshold, rf_f1 = find_optimal_threshold(y_test, rf_probs) print(f"逻辑回归最佳阈值: {lr_threshold:.4f} (F1={lr_f1:.2f})") print(f"随机森林最佳阈值: {rf_threshold:.4f} (F1={rf_f1:.2f})")5.3 实际应用中的陷阱
常见问题与解决方案:
曲线波动剧烈
- 检查样本量是否足够
- 尝试使用平滑技术
AUC值异常高
- 验证是否存在数据泄露
- 检查特征工程合理性
测试集与训练集表现差异大
- 确保数据分布一致
- 考虑使用交叉验证
在电商用户流失预测项目中,我们发现当正样本比例低于5%时,ROC曲线的AUC值仍然保持在0.85以上,但PR曲线的AUC仅为0.3,这提示模型的实际业务价值可能被高估。通过调整分类阈值和引入代价敏感学习,最终将召回率从0.6提升到0.8,虽然精确率有所下降,但更符合业务需求。