从混淆矩阵到加权F1:用Python一步步拆解多分类模型的好坏
当你训练完一个新闻分类模型,看着输出报告里密密麻麻的准确率、召回率、F1值,是不是感觉像在看天书?别担心,今天我们就用最直白的方式,从最基础的混淆矩阵开始,手把手带你理解这些指标背后的数学逻辑,特别是为什么加权F1(Weighted F1)在不平衡数据集中如此重要。
1. 从三分类案例理解混淆矩阵
假设我们有一个简单的三分类问题:将文本情感分为正面、中性、负面。模型在测试集上的预测结果如下:
import numpy as np from sklearn.metrics import confusion_matrix y_true = np.array([0, 1, 2, 0, 1, 2, 0, 1, 2]) # 真实标签 y_pred = np.array([0, 1, 2, 0, 1, 2, 0, 2, 2]) # 预测标签用confusion_matrix生成混淆矩阵:
cm = confusion_matrix(y_true, y_pred) print(cm)输出结果:
[[3 0 0] [0 2 1] [0 0 2]]这个3x3矩阵的解读其实很简单:
- 行代表真实类别(0=正面,1=中性,2=负面)
- 列代表预测类别
- 对角线上的数字表示正确分类的样本数
混淆矩阵的四个关键指标:
- TP(True Positive):对角线上的值
- FP(False Positive):列总和减去TP
- FN(False Negative):行总和减去TP
- TN(True Negative):其他所有正确拒绝的样本
2. 手动计算各类别指标
2.1 查准率(Precision)
查准率回答的是:"模型预测为A类的样本中,有多少真的是A类?"
计算公式:
Precision = TP / (TP + FP)对于我们的例子:
- 类别0:3/(3+0+0) = 1.0
- 类别1:2/(2+0+1) ≈ 0.67
- 类别2:2/(2+0+1) ≈ 0.67
2.2 召回率(Recall)
召回率回答的是:"所有真实的A类样本中,模型找出了多少?"
计算公式:
Recall = TP / (TP + FN)计算各类别:
- 类别0:3/(3+0+0) = 1.0
- 类别1:2/(2+1+0) ≈ 0.67
- 类别2:2/(2+0+1) ≈ 0.67
2.3 F1分数
F1是查准率和召回率的调和平均数,兼顾两者表现:
F1 = 2 * (Precision * Recall) / (Precision + Recall)各类别F1:
- 类别0:2*(1.0*1.0)/(1.0+1.0) = 1.0
- 类别1:2*(0.67*0.67)/(0.67+0.67) ≈ 0.67
- 类别2:2*(0.67*0.67)/(0.67+0.67) ≈ 0.67
3. 宏观 vs 微观 vs 加权F1
当我们需要用一个数字来概括多分类模型的整体表现时,有三种主要方法:
| 平均方法 | 计算方式 | 特点 |
|---|---|---|
| 宏观F1 | 各类别F1的简单平均 | 平等对待所有类别 |
| 微观F1 | 全局TP、FP、FN计算 | 受大类别影响大 |
| 加权F1 | 按样本量加权的F1平均 | 平衡类别重要性 |
3.1 宏观F1(Macro F1)
直接对各类别F1取平均:
Macro F1 = (F1_class0 + F1_class1 + F1_class2) / 3 = (1.0 + 0.67 + 0.67) / 3 ≈ 0.78适用场景:当所有类别同等重要时,比如医疗诊断中的罕见病检测。
3.2 微观F1(Micro F1)
先汇总所有类别的TP、FP、FN,再计算:
总TP = 3 + 2 + 2 = 7 总FP = (0+0) + (0+1) + (0+1) = 2 总FN = (0+0) + (1+0) + (0+1) = 2 Micro Precision = 7 / (7 + 2) ≈ 0.78 Micro Recall = 7 / (7 + 2) ≈ 0.78 Micro F1 = 2*(0.78*0.78)/(0.78+0.78) ≈ 0.78特点:在平衡数据集中,Micro F1 ≈ Accuracy。
3.3 加权F1(Weighted F1)
这是最常用的多分类评估指标,考虑了类别不平衡:
计算各类别样本权重:
- 类别0:3个样本
- 类别1:3个样本
- 类别2:3个样本 (本例中各类别样本数相同)
加权平均:
Weighted F1 = (3*1.0 + 3*0.67 + 3*0.67) / 9 ≈ 0.78为什么加权F1最常用:
- 自动适应类别不平衡
- 业务上更合理(大类别权重高)
- 与sklearn默认的
average='weighted'一致
4. 加权F1与平衡准确率的对比
平衡准确率(Balanced Accuracy)是另一个处理不平衡数据的指标:
Balanced Accuracy = (Recall_class0 + Recall_class1 + Recall_class2) / 3 = (1.0 + 0.67 + 0.67) / 3 ≈ 0.78关键区别:
| 指标 | 计算基础 | 考虑因素 | 适用场景 |
|---|---|---|---|
| 加权F1 | F1分数 | 查准率和召回率 | 需要平衡精确性和覆盖率 |
| 平衡准确率 | 召回率 | 各类别检测能力 | 更关注少数类表现 |
实际项目中,我通常会同时观察这两个指标。比如在客户流失预测中:
- 如果更关注识别高风险客户(即使有误报),侧重加权F1
- 如果要求平等对待所有客户群体,看平衡准确率
5. 用Python实现完整评估流程
下面是一个完整的评估示例,包含我们讨论的所有指标:
from sklearn.metrics import (precision_score, recall_score, f1_score, balanced_accuracy_score) # 计算各类别指标 print("类别0 - Precision:", precision_score(y_true, y_pred, average=None)[0]) print("类别1 - Recall:", recall_score(y_true, y_pred, average=None)[1]) # 计算各种平均F1 print("Macro F1:", f1_score(y_true, y_pred, average='macro')) print("Micro F1:", f1_score(y_true, y_pred, average='micro')) print("Weighted F1:", f1_score(y_true, y_pred, average='weighted')) # 平衡准确率 print("Balanced Accuracy:", balanced_accuracy_score(y_true, y_pred))输出解读技巧:
- 先看加权F1是否达到业务要求
- 检查各类别F1是否均衡
- 对比平衡准确率与加权F1的差异
- 如果某些类别表现特别差,检查数据分布和特征工程
6. 处理极端不平衡数据的实战技巧
在实际项目中,数据往往比我们的例子不平衡得多。比如:
- 欺诈检测中正样本可能只有1%
- 某些产品品类占比极低
应对策略:
重采样技术:
- 过采样少数类(如SMOTE)
- 欠采样多数类
类别权重调整:
# 在模型训练时设置class_weight from sklearn.svm import SVC model = SVC(class_weight='balanced')阈值调整:
- 默认0.5可能不是最优
- 用ROC曲线或PR曲线找到最佳阈值
分层抽样:
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, stratify=y, test_size=0.2)集成方法:
- 对少数类使用boosting算法
- 使用专门处理不平衡数据的算法如RUSBoost
在我的一个电商项目中,某个品类只有总样本的2%,通过组合SMOTE和类别权重,最终将该品类的F1从0.3提升到了0.65,而整体加权F1保持在0.82以上。