# Summary plt.xlabel('假阳性率(误招普通人的比例)', fontsize=14) plt.ylabel('真阳性率(找到人才的比例)', fontsize=14) 1. 完美模型 ```python # ROC曲线:直角形 # 意义:存在一个阈值,能完美区分所有样本 """ 1.0 ┌────────┐ │ │ TPR │ │ │ │ 0 └────────┘ 0 1.0 FPR """ ``` 2. 无用模型 ```python # ROC曲线:对角线 # 意义:和抛硬币一样 """ 1.0 ┌────────/ │ / TPR │ / │ / 0 └/───────┘ 0 1.0 FPR """ ``` 3. 实际模型 ```python # ROC曲线:向左上凸起 # 意义:比随机好,但不完美 """ 1.0 ┌───────⟋ │ ⟋ TPR │ ⟋ │ ⟋ 0 └────────┘ 0 1.0 FPR """ ``` # Cues [[混淆矩阵]] # Notes 我来用通俗的方式介绍ROC曲线,通过几个生活化的例子让你彻底理解。 ## 什么是ROC曲线? ROC(Receiver Operating Characteristic)曲线是一条展示模型在**不同标准下**表现的曲线。 ### 生活化比喻:招聘面试 想象你是HR,要从100个候选人中筛选程序员: - 其中30人是真正的好程序员 - 70人能力一般 你给每个人打分(0-100分),现在要决定:**多少分以上才算通过?** ## ROC曲线展示什么? ### 不同"录取分数线"的结果 ```python # 候选人得分分布 candidates = [ # (姓名, 得分, 是否优秀) ("张三", 95, True), # 优秀程序员,高分 ("李四", 88, True), # 优秀程序员,高分 ("王五", 82, False), # 一般,但分数不低 ("赵六", 75, True), # 优秀,但面试紧张 ("钱七", 68, False), # 一般 ("孙八", 61, False), # 一般 ("周九", 55, True), # 优秀,但不善表达 ("吴十", 45, False), # 一般 # ... 更多候选人 ] ``` ### 设置不同分数线的后果 #### 分数线 = 90分 ```Java 通过:只有张三 结果: ✓ 录取的都是人才(精准度100%) ✗ 错过了很多人才(只找到1/30) ``` #### 分数线 = 70分 ```Java 通过:张三、李四、王五、赵六 结果: ✓ 找到了更多人才(3/30) ✗ 也录取了一些普通人(王五) ``` #### 分数线 = 40分 ```Java 通过:几乎所有人 结果: ✓ 没错过任何人才(30/30) ✗ 也录取了大量普通人 ``` ## ROC曲线的两个关键指标 ### 1. 真阳性率(TPR)= 灵敏度 **"找到了多少真人才"** ```Java TPR = 找到的人才数 / 总人才数 分数线90分:TPR = 1/30 = 3.3% 分数线70分:TPR = 3/30 = 10% 分数线40分:TPR = 30/30 = 100% ``` ### 2. 假阳性率(FPR) **"误招了多少普通人"** ```Java FPR = 误招的普通人 / 总普通人数 分数线90分:FPR = 0/70 = 0% 分数线70分:FPR = 1/70 = 1.4% 分数线40分:FPR = 65/70 = 92.8% ``` ## 画出ROC曲线 ```python import matplotlib.pyplot as plt import numpy as np # 模拟不同分数线的结果 thresholds = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0] tpr = [0, 0.033, 0.067, 0.10, 0.20, 0.40, 0.60, 0.80, 0.90, 0.97, 1.0] fpr = [0, 0, 0.014, 0.014, 0.057, 0.20, 0.40, 0.65, 0.85, 0.95, 1.0] plt.figure(figsize=(10, 8)) plt.plot(fpr, tpr, 'b-', linewidth=3, marker='o', markersize=8) # 标注几个关键点 plt.annotate('分数线=90\n(严格)', (0, 0.033), xytext=(0.1, 0.15), arrowprops=dict(arrowstyle='->'), fontsize=12) plt.annotate('分数线=70\n(平衡)', (0.014, 0.10), xytext=(0.2, 0.3), arrowprops=dict(arrowstyle='->'), fontsize=12) plt.annotate('分数线=40\n(宽松)', (0.65, 0.80), xytext=(0.5, 0.6), arrowprops=dict(arrowstyle='->'), fontsize=12) plt.plot([0, 1], [0, 1], 'k--', alpha=0.5, label='随机选择') plt.xlabel('假阳性率(误招普通人的比例)', fontsize=14) plt.ylabel('真阳性率(找到人才的比例)', fontsize=14) plt.title('ROC曲线:展示不同录取标准的效果', fontsize=16) plt.grid(True, alpha=0.3) ``` ## 实际案例解读 ### 案例1:医院疾病筛查 ```python # 场景:筛查1000人中的10个癌症患者 # 问题:设置什么标准? # 保守策略(阈值高) """ 类似分数线90分 - 只把非常可疑的人判定为患病 - 优点:不会让健康人虚惊一场 - 缺点:可能漏诊真正的患者 位置:ROC曲线左下角 """ # 激进策略(阈值低) """ 类似分数线40分 - 稍有异常就判定为需要进一步检查 - 优点:不会漏诊 - 缺点:很多健康人要做不必要的检查 位置:ROC曲线右上角 """ ``` ### 案例2:垃圾邮件过滤 ```python # 场景:识别垃圾邮件 # 严格过滤(阈值高) """ 后果: ✓ 收件箱很干净 ✗ 重要邮件可能被误删 用户抱怨:"我的offer邮件哪去了?!" """ # 宽松过滤(阈值低) """ 后果: ✓ 不会错过重要邮件 ✗ 垃圾邮件很多 用户抱怨:"这过滤器有用吗?" """ ``` ## 如何选择工作点? ### 不同场景的选择 ```python # 1. 癌症筛查:宁可错杀,不可放过 optimal_point = "高TPR,即使FPR较高" # ROC曲线右上方 # 2. 垃圾邮件:平衡准确性 optimal_point = "TPR和FPR的平衡点" # ROC曲线中间 # 3. 高精度要求:宁缺毋滥 optimal_point = "低FPR,即使TPR不高" # ROC曲线左下方 ``` ### 可视化不同选择 ```python plt.figure(figsize=(10, 8)) # 绘制ROC曲线 plt.plot(fpr, tpr, 'b-', linewidth=3) # 标记不同的工作点 plt.plot(0.05, 0.6, 'ro', markersize=12) plt.text(0.05, 0.55, '保守策略\n(高精度)', fontsize=12) plt.plot(0.3, 0.85, 'go', markersize=12) plt.text(0.3, 0.80, '平衡策略', fontsize=12) plt.plot(0.7, 0.95, 'mo', markersize=12) plt.text(0.7, 0.90, '激进策略\n(高召回)', fontsize=12) ``` ## ROC曲线的优势 ### 1. 展示所有可能性 ```python # 不用提前决定阈值 # 可以看到所有阈值下的表现 for threshold in range(0, 101, 10): print(f"阈值{threshold}分: TPR={tpr}, FPR={fpr}") ``` ### 2. 不受样本比例影响 ```python # 场景A:100人中10人患病(10%) # 场景B:10000人中10人患病(0.1%) # ROC曲线形状相同! ``` ## 总结 **ROC曲线告诉我们**: 1. **没有免费的午餐**:想找到更多真阳性,就得接受更多假阳性 2. **可以做权衡**:根据实际需求选择工作点 3. **模型能力的全貌**:不只是一个点,而是所有可能性 **记住关键点**: - **左下角**(0,0):什么都不预测为正 - **右上角**(1,1):什么都预测为正 - **左上角**(0,1):完美预测(理想状态) - **对角线**:随机猜测 **实际应用**: - 医疗诊断:通常选择高TPR(不漏诊) - 金融风控:通常选择低FPR(不误杀) - 推荐系统:通常选择平衡点 ROC曲线就像一个"能力地图",展示了模型在各种标准下的表现,让我们能根据实际需求做出最佳选择。