别再只看准确率了!用sklearn的balanced_accuracy_score和f1_score搞定不平衡数据集评估

记得第一次做信用卡欺诈检测项目时,我训练出的模型准确率高达99.5%,兴奋地汇报给主管后,却被反问:"你知道欺诈交易占数据集的多少吗?"——原来这个"高精度"模型只是把所有交易都预测为正常,而漏掉了全部0.5%的欺诈案例。这个教训让我明白: 在不平衡数据集上,传统准确率是最危险的谎言

1. 为什么准确率会欺骗我们?

假设你正在开发一个医疗诊断系统,数据集中健康样本占95%,患者仅占5%。如果一个模型将所有样本都预测为健康,它的准确率会高达95%,但对患者而言这个模型完全失效。这就是 准确率悖论 ——当类别分布严重失衡时,多数类会主导评估结果。

1.1 不平衡数据的典型场景

  • 金融风控 :正常交易远多于欺诈交易(通常<1%)
  • 医疗诊断 :健康样本远多于患病样本
  • 工业质检 :合格品远多于缺陷品
  • 自然语言处理 :常见词频远高于罕见词

提示:当少数类占比低于20%时,就应考虑使用平衡指标评估模型

2. 平衡准确率:给每个类别平等发言权

balanced_accuracy_score 的核心思想很简单: 计算每个类别召回率的算术平均值 。这意味着一个包含100个正样本和900个负样本的数据集,正负类的权重都是50%,而非原始比例。

2.1 数学原理与sklearn实现

平衡准确率的计算公式为:

balanced_accuracy = (TP/(TP+FN) + TN/(TN+FP)) / 2

用sklearn计算只需两行代码:

from sklearn.metrics import balanced_accuracy_score
balanced_acc = balanced_accuracy_score(y_true, y_pred)

2.2 实际案例对比

我们用一个信用卡欺诈数据集演示:

评估指标 模型A(全预测负类) 模型B(合理预测)
传统准确率 99.5% 98.7%
平衡准确率 50.0% 92.3%

这个对比清晰显示: 传统准确率会奖励作弊行为,而平衡准确率能识别真正有价值的模型

3. F1分数:精确率与召回率的和谐统一

F1分数是精确率(Precision)和召回率(Recall)的 调和平均数 ,特别适合评估对少数类的识别能力。在sklearn中, f1_score average 参数有几种关键选择:

3.1 多分类场景下的计算方式

from sklearn.metrics import f1_score

# 宏观平均:所有类别权重相同
macro_f1 = f1_score(y_true, y_pred, average='macro')

# 加权平均:按类别样本量加权
weighted_f1 = f1_score(y_true, y_pred, average='weighted')

3.2 为什么推荐weighted F1?

下表对比了三种平均方式在电商评论情感分析中的应用:

情感类别 样本占比 模型F1 macro-F1贡献 weighted-F1贡献
正面 70% 0.85 0.85 0.85×0.7=0.595
中性 20% 0.60 0.60 0.60×0.2=0.120
负面 10% 0.40 0.40 0.40×0.1=0.040
总分 0.62 0.62 0.755

可以看到, weighted F1更贴近实际业务价值 ——它让模型在多数类上的表现贡献更大。

4. 实战:从数据到评估的全流程

让我们用Python完整走一遍处理不平衡数据的流程:

4.1 数据准备与特征工程

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# 生成不平衡数据集(正负样本比例1:99)
X, y = make_classification(n_samples=10000, weights=[0.99], flip_y=0.01)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# 使用SMOTE过采样
from imblearn.over_sampling import SMOTE
smote = SMOTE()
X_res, y_res = smote.fit_resample(X_train, y_train)

4.2 模型训练与评估

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (accuracy_score, 
                            balanced_accuracy_score,
                            f1_score)

model = RandomForestClassifier()
model.fit(X_res, y_res)
preds = model.predict(X_test)

print(f"准确率: {accuracy_score(y_test, preds):.3f}")
print(f"平衡准确率: {balanced_accuracy_score(y_test, preds):.3f}") 
print(f"加权F1: {f1_score(y_test, preds, average='weighted'):.3f}")

4.3 结果可视化技巧

使用混淆矩阵热力图能直观展示模型在不同类别上的表现差异:

import seaborn as sns
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, preds)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')

5. 进阶技巧与常见陷阱

5.1 阈值调整的艺术

默认0.5的分类阈值可能不适合不平衡数据。通过PR曲线找到最佳阈值:

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

probs = model.predict_proba(X_test)[:, 1]
precision, recall, thresholds = precision_recall_curve(y_test, probs)

plt.plot(thresholds, precision[:-1], label='Precision')
plt.plot(thresholds, recall[:-1], label='Recall')
plt.legend()

5.2 避免这些常见错误

  • 数据泄露 :在过采样/欠采样前拆分训练测试集
  • 指标误用 :在极度不平衡数据上使用micro-F1
  • 盲目调参 :先理解业务需求再选择优化方向

5.3 其他实用评估工具

  • ROC-AUC :对类别不平衡相对稳健
  • Cohen's Kappa :考虑随机猜测的基准
  • Matthews系数 :适用于二分类的平衡指标

在最近一个客户流失预测项目中,我们通过组合balanced_accuracy和weighted F1,发现了一个有趣现象:虽然随机森林的准确率比逻辑回归低2%,但它的平衡准确率高出15%,最终这个发现帮助我们选择了真正有效的模型,将高价值客户留存率提升了40%。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐