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。有了这些“类别级指标”,怎么汇总成一个整体数字?三种平均方式就登场了。

多分类平均方式总览 类别级 Precision 类别级 Recall 类别级 F1 Macro Average 直接算术平均(无权重) Micro Average 全局汇总再算指标 Weighted Average 按支持样本数加权 适合均衡类别 每个类别一样重要 样本量差别不大时 适合整体表现 不看类别,只看总量 类别不平衡时仍有参考 适合不平衡数据 按样本量加权 更符合线上分布

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 实战中的选择策略

用一个简单的流程帮你快速决策:

  1. 先看看样本分布——如果各类别样本数差距在 5 倍以内,macro 和 weighted 差距不大,我倾向用 macro,因为它更公平。
  2. 如果很不平衡(比如 100:1),weighted 是你的好朋友。它不会因为一个只有 10 样本的类别搞崩整体数字。
  3. 业务考核只看整体准确率? 那就直接用 micro,因为它刚好等于 accuracy。
  4. 需要向老板汇报——建议同时给出 weighted F1 和 macro F1,并说明为什么不一致。如果差异很大,本身就是重要的模型诊断信息。

一次排查经历

我曾经碰到一个模型,macro F1 0.72,weighted F1 0.91。我当时觉得“挺好”。后来排查发现,模型在大类上几乎 100% 正确,但有一个样本量很少的关键类别全部判错。macro 被拖低,但 weighted 完全掩盖了这个问题。从那以后,我养成了同时观察 macro 和 weighted 的好习惯。

好了,多分类平均指标就这些。记住核心:没有万能指标,只有最合适的组合。带着你的业务特点去选,比盲目抄别人的配置靠谱得多。