2、分类问题评价指标(上):准确率、精确率、召回率、F1-Score的原理与计算
分类问题,说白了就是让模型做选择题。二分类是选A或B,多分类是选ABCD。但模型选完了,你怎么知道它选得好不好?
我刚开始做分类模型时,觉得只要准确率高就万事大吉。直到有一次,我做一个罕见病筛查模型——准确率99.5%,领导看了直点头。结果一上线,阳性病例一个都没抓住。嗯,从那以后我才明白:选对评价指标,比调参重要十倍。
这一章,咱们先把四个最基础的指标讲透:准确率、精确率、召回率、F1-Score。它们就像工具箱里的螺丝刀、扳手、钳子——各有各的用处,别拿扳手去拧螺丝。
2.1 混淆矩阵:一切评价的起点
聊指标之前,先得搞清楚混淆矩阵。它是个2×2的表格,记录着模型预测和真实标签的四种组合。
| 真实为正类 (Positive) | 真实为负类 (Negative) | |
|---|---|---|
| 预测为正类 | TP (True Positive) 正确预测的正例 |
FP (False Positive) 误报 |
| 预测为负类 | FN (False Negative) 漏报 |
TN (True Negative) 正确预测的负例 |
举个例子。假设有100个样本,其中10个是猫,90个是狗。模型预测出12个猫,其中8个是真猫,4个是狗被误认成猫。
- TP = 8:模型说它是猫,它确实是猫
- FP = 4:模型说它是猫,它其实是狗
- FN = 2:模型说它是狗,它其实是猫(漏掉了)
- TN = 86:模型说它是狗,它确实是狗
这四个数字,就是所有分类指标的原材料。
2.2 准确率(Accuracy)
准确率是最直观的指标。公式很简单:
Accuracy = (TP + TN) / (TP + TN + FP + FN)
用上面的例子:Accuracy = (8 + 86) / (8 + 86 + 4 + 2) = 94/100 = 94%。
看起来不错对吧?但问题来了——如果我把所有样本都预测成狗,准确率是多少?
TP=0, TN=90, FP=0, FN=10。Accuracy = 90/100 = 90%。
一个「永远说狗」的模型,准确率居然有90%?这就是准确率的陷阱:在类别极度不平衡时,准确率会骗人。
2.3 精确率(Precision)
精确率回答的问题是:「模型说它是正类,它真的是正类的概率有多大?」
Precision = TP / (TP + FP)
猫狗例子里:Precision = 8 / (8 + 4) = 8/12 ≈ 66.7%。
也就是说,模型预测的12个猫里,只有8个是真猫。剩下4个是狗。
什么时候该关注精确率?当「误报」的代价很高时。
- 垃圾邮件过滤:把正常邮件误判成垃圾邮件(FP),用户可能错过重要信息。宁可漏掉几封垃圾邮件,也别误伤正常邮件。这时候精确率就是核心指标。
- 内容审核:把正常内容误判成违规(FP),会引发用户投诉。精确率低了,运营团队得累死。
2.4 召回率(Recall)
召回率回答的问题是:「所有真正的正类,模型找出来了多少?」
Recall = TP / (TP + FN)
猫狗例子里:Recall = 8 / (8 + 2) = 8/10 = 80%。
10只真猫,模型找到了8只,漏了2只。
什么时候该关注召回率?当「漏报」的代价很高时。
- 癌症筛查:漏掉一个癌症患者(FN),可能耽误治疗时机。宁可多做几次检查(FP),也别放过一个病人。
- 金融风控:漏掉一笔欺诈交易(FN),直接就是经济损失。多拦截几笔正常交易(FP)可以人工复核。
2.5 F1-Score:精确率与召回率的调和平均
既然精确率和召回率经常打架,总得有个指标来综合评估吧?F1-Score就是干这个的。
F1 = 2 * (Precision * Recall) / (Precision + Recall)
猫狗例子里:F1 = 2 * (0.667 * 0.8) / (0.667 + 0.8) = 2 * 0.5336 / 1.467 ≈ 0.727。
为什么用调和平均,而不是算术平均?
你想想看,如果精确率是1.0,召回率是0.0,算术平均是0.5,但调和平均是0。调和平均对极端值更敏感——只有两个指标都高,F1才会高。这符合直觉:一个「要么全对要么全漏」的模型,综合能力就是不行。
| 场景 | 关注指标 | 原因 |
|---|---|---|
| 罕见病筛查 | 召回率 | 漏诊代价远高于误诊 |
| 垃圾邮件过滤 | 精确率 | 误伤正常邮件代价高 |
| 搜索引擎排序 | F1-Score | 既要相关(精确),又要全面(召回) |
| 通用模型对比 | F1-Score | 平衡评估,不受类别分布影响 |
2.6 知识结构图:四个指标的关系
下面这张图,帮你理清四个指标之间的逻辑关系。
2.7 代码实战:用Python计算四个指标
理论讲完了,咱们动手算一下。我习惯用sklearn,但也会手写一遍,加深理解。
import numpy as np
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
# 真实标签和预测标签
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0]
# 手算混淆矩阵
tp = sum((np.array(y_true)==1) & (np.array(y_pred)==1))
fp = sum((np.array(y_true)==0) & (np.array(y_pred)==1))
fn = sum((np.array(y_true)==1) & (np.array(y_pred)==0))
tn = sum((np.array(y_true)==0) & (np.array(y_pred)==0))
print(f"TP={tp}, FP={fp}, FN={fn}, TN={tn}")
# 手算指标
accuracy_manual = (tp + tn) / (tp + tn + fp + fn)
precision_manual = tp / (tp + fp) if (tp + fp) > 0 else 0
recall_manual = tp / (tp + fn) if (tp + fn) > 0 else 0
f1_manual = 2 * precision_manual * recall_manual / (precision_manual + recall_manual) if (precision_manual + recall_manual) > 0 else 0
print(f"手算结果:")
print(f"准确率={accuracy_manual:.3f}")
print(f"精确率={precision_manual:.3f}")
print(f"召回率={recall_manual:.3f}")
print(f"F1-Score={f1_manual:.3f}")
# 用sklearn验证
print(f"\nsklearn结果:")
print(f"准确率={accuracy_score(y_true, y_pred):.3f}")
print(f"精确率={precision_score(y_true, y_pred):.3f}")
print(f"召回率={recall_score(y_true, y_pred):.3f}")
print(f"F1-Score={f1_score(y_true, y_pred):.3f}")
输出结果:
TP=3, FP=1, FN=1, TN=5
手算结果:
准确率=0.800
精确率=0.750
召回率=0.750
F1-Score=0.750
sklearn结果:
准确率=0.800
精确率=0.750
召回率=0.750
F1-Score=0.750
2.8 多分类场景怎么办?
上面讲的都是二分类。多分类呢?比如手写数字识别,0到9共10类。
做法很简单:把某一类当成正类,其余所有类当成负类。然后对每个类别分别计算精确率、召回率、F1。
最后有两种汇总方式:
- 宏平均(Macro):每个类别的指标算出来,直接取算术平均。每个类别权重相同,不受样本数量影响。
- 微平均(Micro):把所有类别的TP、FP、FN加起来,再统一算指标。受大类别影响更大。
我个人习惯:类别均衡时用宏平均,类别不均衡时用微平均。但具体用哪个,还得看业务需求——你更关心小类别的表现,还是整体表现?
- 混淆矩阵是基础,先看它再谈指标
- 准确率在类别不平衡时会骗人
- 精确率管「准」,召回率管「全」
- F1是精确率和召回率的调和平均,综合评估
- 多分类用宏平均或微平均,看业务需求