4. 多分类指标宏平均与微平均:macro、micro、weighted 平均
多分类场景里,光看总准确率往往不够。你要分别看每个类别的表现,但又不想看十几个数字头晕。这时候就要靠「平均」手段来浓缩信息。今天聊聊三种最常见的平均方式:macro(宏平均)、micro(微平均)、weighted(加权平均)。
我刚开始做多分类时,经常只用 macro,觉得它“公平”。后来遇到严重不平衡的数据,才发现 macro 有时会把人带偏。嗯,咱们把底层逻辑拆清楚。
4.1 先回忆一下:多分类混淆矩阵
二分类我们只有 TP、FP、TN、FN。多分类呢?其实每个类别都可以看成“自己 vs 其他”。比如三分类(类别 A/B/C),对于类别 A:
- TP_A:真实 A,预测 A
- FN_A:真实 A,预测成 B 或 C
- FP_A:真实 B 或 C,预测成 A
- TN_A:真实 B 或 C,预测成 B 或 C(但不是 A)
每个类别都能算出一组 precision、recall、f1。有了这些“类别级指标”,怎么汇总成一个整体数字?三种平均方式就登场了。
4.2 Macro Average(宏平均)
做法很简单:把你所有类别的某个指标(比如 F1)加起来,除以类别数。举个例子,三个类别的 F1 分别是 0.80、0.75、0.55,那 macro F1 就是 (0.80+0.75+0.55)/3 = 0.70。
关键点:每个类别的权重一样,不管这个类别只有 10 个样本还是 10000 个样本。
什么时候用 macro?
当你想让模型在“小类别”上也有好表现,而不是被大类别牵着鼻子走的时候。我个人在医疗影像的罕见病分类中几乎必看 macro F1,因为罕见类样本极少,但漏掉一个代价极高。
避坑提醒
我曾经在一个严重不平衡的数据上只看 macro F1,模型对大类几乎全对但对小类乱猜,macro F1 居然还可以。后来我加了 weighted 才发现问题。
macro 对小类非常敏感,如果小类由于偶然涨了 10 个点,整体 macro 会被拉高,但它可能并不稳定。
4.3 Micro Average(微平均)
微平均的思路不一样——它先不管类别,把所有的 TP、FP、FN 汇总到一起,然后统一算 precision、recall、F1。
具体来说:
# 多分类二值化后,全局汇总
total_TP = sum(TP_i)
total_FP = sum(FP_i)
total_FN = sum(FN_i)
micro_precision = total_TP / (total_TP + total_FP)
micro_recall = total_TP / (total_TP + total_FN)
micro_f1 = 2 * micro_precision * micro_recall / (micro_precision + micro_recall)
你会发现 micro F1 其实就等于 overall accuracy(当预测只有一标签时)。为什么?因为 micro 把每个样本是否预测正确直接累加,相当于算的是全局正确率。
适用场景
如果你只关心模型在全部样本上的整体表现,不关心个别差类别,那 micro 是最直接的数字。比如垃圾邮件过滤,你只关心整体误判率,大概率类别的表现就足够了。
但我个人一直觉得,光看 micro 会掩盖小类的问题。假如大类比小类多 100 倍,micro F1 几乎完全由大类决定,小类即使全军覆没,整体数字也很难看出来。
4.4 Weighted Average(加权平均)
加权平均就是 macro 的改良版。它仍然先算每个类别的指标,但在求平均时按该类别的“样本数”(support)加权。样本多的类别影响力大,样本少的类别影响力小。
数学上:
weighted_precision = ∑(precision_i * support_i) / total_samples
weighted_recall = ∑(recall_i * support_i) / total_samples
weighted_f1 = ∑(f1_i * support_i) / total_samples
加权平均保留了每个类别的内部指标,但权重和样本分布一致。说白了,更符合线上真实的数据分布。
我的经验
在实际项目中,我一般默认先看 weighted F1,除非有特殊需求才切到 macro 或 micro。比如在做用户分层时,每个层级的样本量差异很大,weighted 能公平反映模型在主力层级上的表现,同时又不完全忽略小层级。
4.5 三种方式的对比
| 维度 | Macro | Micro | Weighted |
|---|---|---|---|
| 类别权重 | 相等 | 不按类别,按样本贡献 | 按样本数 |
| 对小类的敏感度 | 高 | 很低 | 中等 |
| 是否等于 accuracy | 否 | 是(单标签多分类) | 否 |
| 受数据不平衡影响 | 可能高估不均 | 偏向大类别 | 与分布一致 |
| 常用场景 | 各类别同等重要 | 只看整体准确率 | 默认推荐 |
4.6 代码示例:scikit-learn 中一键计算
不用自己手算,sklearn 的 classification_report 或 f1_score 都支持。看一下:
from sklearn.metrics import f1_score, recall_score, precision_score
y_true = [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]
y_pred = [0, 2, 1, 0, 1, 1, 0, 1, 2, 2]
# Macro
print("Macro F1:", f1_score(y_true, y_pred, average='macro'))
# Micro
print("Micro F1:", f1_score(y_true, y_pred, average='micro'))
# Weighted
print("Weighted F1:", f1_score(y_true, y_pred, average='weighted'))
# 查看每个类别的支持数
from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred))
输出会非常清晰。我习惯先跑个 classification_report 看一眼每类的 precision/recall/f1 和 support,再决定用哪种平均方式。
小技巧
在调参阶段,我经常同时记录三个平均 F1,画折线图对比趋势。如果 macro 和 weighted 的 gap 突然拉大,说明模型在少数类上表现特别差或特别好,可以针对性做数据增强或重采样。
4.7 实战中的选择策略
用一个简单的流程帮你快速决策:
- 先看看样本分布——如果各类别样本数差距在 5 倍以内,macro 和 weighted 差距不大,我倾向用 macro,因为它更公平。
- 如果很不平衡(比如 100:1),weighted 是你的好朋友。它不会因为一个只有 10 样本的类别搞崩整体数字。
- 业务考核只看整体准确率? 那就直接用 micro,因为它刚好等于 accuracy。
- 需要向老板汇报——建议同时给出 weighted F1 和 macro F1,并说明为什么不一致。如果差异很大,本身就是重要的模型诊断信息。
一次排查经历
我曾经碰到一个模型,macro F1 0.72,weighted F1 0.91。我当时觉得“挺好”。后来排查发现,模型在大类上几乎 100% 正确,但有一个样本量很少的关键类别全部判错。macro 被拖低,但 weighted 完全掩盖了这个问题。从那以后,我养成了同时观察 macro 和 weighted 的好习惯。
好了,多分类平均指标就这些。记住核心:没有万能指标,只有最合适的组合。带着你的业务特点去选,比盲目抄别人的配置靠谱得多。