1. 当准确率会骗人:数据不平衡的陷阱
我刚入行做机器学习时,曾经用准确率(Accuracy)评估过一个医疗诊断模型。在测试集上达到了95%的准确率,当时还沾沾自喜。直到临床医生问我:"这个模型能找出多少真正的癌症患者?"我才发现事情不对劲——原来数据集中健康人群占比95%,癌症患者只有5%。即便模型把所有样本都预测为健康,准确率也能达到95%!
这就是典型的数据不平衡问题。在实际业务中比比皆是:
- 金融风控中正常交易远多于欺诈交易
- 电商平台正常用户远多于恶意刷单用户
- 工业质检良品率通常高于不良品
传统准确率的计算公式是:
Accuracy = (TP + TN) / (TP + TN + FP + FN)当某一类样本占比极大时,模型只要"无脑"预测多数类就能获得虚高的准确率。这就像班级里90%学生考试及格,老师只要给所有人打及格分,判卷准确率就有90%,但完全失去了区分能力。
2. 平衡准确率:给少数派发言权
平衡准确率(Balanced Accuracy)的聪明之处在于:它给每个类别同等的权重,不管样本量多少。计算公式很简单:
Balanced Accuracy = (召回率_正类 + 召回率_负类) / 2或者更通用的多分类版本:
Balanced Accuracy = (1/n) * Σ(召回率_i)其中n是类别数量。
我用一个实际案例说明差异。假设我们有以下混淆矩阵:
| 预测正 | 预测负 | |
|---|---|---|
| 真实正 | 80 | 20 |
| 真实负 | 10 | 90 |
传统准确率 = (80+90)/200 = 85%
平衡准确率 = (80/100 + 90/100)/2 = 85%
看起来一样?再看这个极端案例:
| 预测正 | 预测负 | |
|---|---|---|
| 真实正 | 5 | 95 |
| 真实负 | 0 | 900 |
传统准确率 = (5+900)/1000 = 90.5%
平衡准确率 = (5/100 + 900/900)/2 = 52.5%
这才是真实情况——模型对正类的识别率只有5%!用Python计算很简单:
from sklearn.metrics import balanced_accuracy_score y_true = [0]*900 + [1]*100 # 900负样本,100正样本 y_pred = [0]*900 + [0]*100 # 全部预测为负 print(balanced_accuracy_score(y_true, y_pred)) # 输出0.53. 加权F1值:精准与召回的艺术
F1值是精确率(Precision)和召回率(Recall)的调和平均数。为什么要用调和平均?因为它惩罚极端值——只有当两者都高时F1才会高。
举个例子:
- 精确率=1.0,召回率=0.1 → F1=0.18
- 精确率=0.5,召回率=0.5 → F1=0.5
加权F1(Weighted F1)则进一步考虑了类别权重:
Weighted F1 = Σ(w_i * F1_i)其中w_i是类别i的样本占比。
医疗诊断场景就很典型。假设:
- 癌症检测:宁可误诊也不漏诊(高召回)
- 普通疾病筛查:减少误诊率(高精确)
用sklearn计算不同策略的F1:
from sklearn.metrics import f1_score # 3分类问题样例 y_true = [0, 1, 2, 0, 1, 2] y_pred = [0, 2, 1, 0, 0, 1] # 宏观平均(各类平等) print(f1_score(y_true, y_pred, average='macro')) # 加权平均(按样本量加权) print(f1_score(y_true, y_pred, average='weighted')) # 微观平均(合并统计) print(f1_score(y_true, y_pred, average='micro'))4. 实战中的选择策略
经过多个项目实践,我总结出以下经验:
优先使用平衡准确率当:
- 所有类别同等重要(如性别识别)
- 需要直观解释模型整体表现
- 类别数量较少且平衡差异大
优先使用加权F1当:
- 不同类别误判代价差异大(如欺诈检测)
- 需要精细控制假阳性/假阴性
- 多分类问题且样本分布不均
一个电商风控的实际案例:我们对比了两种指标:
| 模型 | 准确率 | 平衡准确率 | 加权F1 |
|---|---|---|---|
| 基线模型 | 98.7% | 65.2% | 0.723 |
| 优化模型 | 97.1% | 89.8% | 0.881 |
虽然优化模型准确率下降,但抓住了更多欺诈订单(F1提升22%)。最终选择优化模型,每月减少损失$150万。
5. 进阶技巧与避坑指南
样本量极少怎么办?
- 使用scikit-learn的
adjusted=True参数:
balanced_accuracy_score(y_true, y_pred, adjusted=True)这会修正随机猜测的基准值,避免在小样本下指标虚高。
多标签分类如何处理?
- 对于每个标签单独计算二分类指标
- 使用
sample_weight参数赋予关键标签更高权重
指标冲突时如何决策?建议制作代价矩阵量化不同错误的损失。例如:
| 错误类型 | 单位成本 |
|---|---|
| 漏诊癌症 | $10万 |
| 误诊癌症 | $1万 |
然后选择综合代价最小的模型。
最后分享一个血泪教训:曾有个项目只监控F1值,后来发现模型把所有不确定样本都预测为负类——因为负类F1容易优化。所以一定要多个指标组合观察!