三、召回率与F1分数:平衡的艺术
前面我们聊了精确率,也就是模型预测为正例的那些样本里,到底有多少是真正正确的。但光看精确率够吗?不够。我举个例子你就明白了。
假设我们做一个癌症筛查模型。如果模型只把最有把握的那一个样本判为阳性,其他全判阴性,精确率可能是100%。但这模型有用吗?没用。因为它漏掉了几乎所有真正的病人。
所以,我们需要另一个指标——召回率。
3.1 召回率的深度解析
召回率衡量的是:所有真正的正例中,模型找出来了多少。
公式很简单:
Recall = TP / (TP + FN)
TP是正确找出来的正例,FN是漏掉的正例。说白了,召回率就是模型对正例的「抓全率」。
核心理解:
- 精确率:模型说「是」的,有多少真「是」?—— 关注预测的准确性
- 召回率:真正的「是」里,模型找出了多少?—— 关注正例的覆盖率
我在项目中遇到过这样一个场景:做金融欺诈检测时,业务方要求「宁可错杀一千,不可放过一个」。为什么?因为漏掉一笔欺诈交易,损失可能是几十万。这时候,召回率就是第一优先级。
但召回率也不是越高越好。你想想看,如果模型把所有样本都判为正例,召回率就是100%。但精确率会跌到惨不忍睹。这就是精确率和召回率之间的矛盾关系。
| 场景 | 优先指标 | 原因 |
|---|---|---|
| 癌症筛查 | 召回率 | 漏诊后果严重,宁可误诊也要找全 |
| 垃圾邮件过滤 | 精确率 | 误删正常邮件比漏掉垃圾邮件更糟糕 |
| 商品推荐 | 两者平衡 | 既要推荐准,又不能漏掉用户可能喜欢的 |
避坑指南:
我曾经在一个二分类项目中只看精确率,结果模型在测试集上精确率95%,上线后业务反馈「怎么漏了这么多?」。一查才发现,正例只占5%,模型学成了「保守派」——只预测最有把握的少数样本。从那以后,我养成了同时看精确率和召回率的习惯。
3.2 F1分数:调和平均的智慧
既然精确率和召回率各有侧重,能不能用一个数字综合衡量?这就是F1分数的由来。
F1是精确率和召回率的调和平均:
F1 = 2 * (Precision * Recall) / (Precision + Recall)
为什么用调和平均,而不是算术平均?我解释一下。
假设精确率=1.0,召回率=0.0。算术平均是0.5,看起来还行。但调和平均呢?
F1 = 2 * (1.0 * 0.0) / (1.0 + 0.0) = 0.0
F1直接归零。这才是真实情况——召回率为0的模型,根本不能用。
调和平均的特点:
- 对极端值更敏感:一个指标差,F1就低
- 鼓励两者均衡:只有精确率和召回率都高,F1才高
- 比算术平均更「诚实」
我的经验:
做模型对比时,我习惯先看F1。如果两个模型F1差不多,再分别看精确率和召回率。F1就像一个「体检总评分」,帮你快速筛掉明显不合格的模型。
3.3 Precision-Recall曲线:动态视角
前面我们讨论的都是固定阈值下的指标。但实际应用中,阈值是可以调整的。比如逻辑回归输出的是概率,默认阈值0.5。调高到0.7,精确率可能上升,召回率下降。调低到0.3,情况反过来。
怎么全面评估模型在不同阈值下的表现?Precision-Recall曲线(PR曲线)就是干这个的。
PR曲线的横轴是召回率,纵轴是精确率。每个点对应一个阈值下的(Recall, Precision)对。把所有点连起来,就是PR曲线。
我画了一张图,帮你直观理解:
从图上可以看到:
- 阈值高(如0.9)时,精确率很高,但召回率很低
- 阈值低(如0.1)时,召回率很高,但精确率很低
- 曲线越靠近右上角,说明模型在不同阈值下都能保持较高的精确率和召回率
PR曲线还有一个重要用途:比较不同模型。如果模型A的PR曲线完全包住模型B的曲线,那A肯定优于B。如果曲线有交叉,就需要结合具体业务场景来选。
PR曲线 vs ROC曲线:
很多人问我,为什么不用ROC曲线?我的回答是:当正负样本严重不平衡时(比如正例只占1%),PR曲线比ROC曲线更敏感。ROC曲线可能会给你一种「模型还不错」的错觉,但PR曲线会如实反映模型在少数类上的表现。所以,处理不平衡数据集时,我优先看PR曲线。
3.4 实战:用Python绘制PR曲线
光说不练假把式。下面我给出一个完整的PR曲线绘制代码,你可以直接拿去用。
from sklearn.metrics import precision_recall_curve, average_precision_score
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
# 生成不平衡数据集
X, y = make_classification(n_samples=1000, n_classes=2,
weights=[0.9, 0.1], random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42)
# 训练模型
model = LogisticRegression()
model.fit(X_train, y_train)
# 预测概率
y_scores = model.predict_proba(X_test)[:, 1]
# 计算PR曲线
precisions, recalls, thresholds = precision_recall_curve(
y_test, y_scores)
# 计算平均精确率(AP)
ap = average_precision_score(y_test, y_scores)
# 绘制PR曲线
plt.figure(figsize=(8, 6))
plt.plot(recalls, precisions, 'b-', linewidth=2,
label=f'PR曲线 (AP = {ap:.3f})')
plt.xlabel('召回率 (Recall)')
plt.ylabel('精确率 (Precision)')
plt.title('Precision-Recall 曲线')
plt.legend(loc='lower left')
plt.grid(True, alpha=0.3)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.show()
# 查看不同阈值下的指标
print("阈值\t精确率\t召回率")
for i in range(0, len(thresholds), 50):
print(f"{thresholds[i]:.3f}\t{precisions[i]:.3f}\t{recalls[i]:.3f}")
实用技巧:
代码中的average_precision_score返回的是AP值,也就是PR曲线下的面积。AP值越接近1,模型越好。我一般用AP作为模型选择的参考指标之一。
3.5 本章小结
好了,这一章的内容就这些。我们聊了三个核心概念:
- 召回率:衡量模型找全正例的能力,和精确率是一对「冤家」
- F1分数:用调和平均综合精确率和召回率,比算术平均更合理
- PR曲线:展示模型在不同阈值下的表现,特别适合不平衡数据集
记住一句话:没有完美的指标,只有适合业务的指标。选哪个,取决于你更在意「误报」还是「漏报」。