# 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曲线就像一个"能力地图",展示了模型在各种标准下的表现,让我们能根据实际需求做出最佳选择。