☰
多标签分类评估:Macro-F1与Micro-F1的计算、差异与选择
2026/10/14 1:25:02 网站建设 项目流程

多标签分类任务的评估很容易翻车。准确率看着还行,但换到 Macro-F1 和 Micro-F1 上,结果可能天差地别。这篇东西就是来聊聊这两个指标到底怎么算、各自在优化什么、以及实际用的时候容易踩哪些坑。

先给个结论:Macro-F1 对类别一视同仁,更关注少数类;Micro-F1 按样本量加权,更贴近整体正确率。具体怎么选,取决于你的业务到底在意什么。

1. 先搞清楚多标签任务的评估逻辑

多标签分类和单标签分类最大的区别是:一个样本可以同时属于多个类别。比如一张图片里既有猫又有狗,一封邮件既可能是工作相关又可能是紧急通知。这种场景下,传统的“预测一个标签,判断对错”的评估方式就不够用了。

多标签评估一般有两种粒度:

  • 样本粒度:对每个样本单独评估,计算它在所有标签上的表现,然后汇总。
  • 标签粒度:先把所有样本的预测结果按标签拆开,看作多个独立的二分类问题,然后分别计算每个标签的指标,再汇总。

Macro-F1 和 Micro-F1 都是在标签粒度上计算的,只是汇总方式不同。要理解这两个指标,得先明白 Precision、Recall 和 F1 是怎么在单个标签上算的。

1.1 单个标签的 Precision、Recall 和 F1

以标签 A 为例,把所有样本在这个标签上的预测结果分成四类:

  • TP:真实为正,预测为正
  • FP:真实为负,预测为正(误报)
  • FN:真实为正,预测为负(漏报)
  • TN:真实为负,预测为负

Precision 衡量的是“预测为正的样本里,有多少是真的正”,公式是 TP / (TP + FP)。Recall 衡量的是“真实为正的样本里,有多少被找出来了”,公式是 TP / (TP + FN)。F1 是两者的调和平均:2 * Precision * Recall / (Precision + Recall)。

为什么不直接用算术平均?因为 Precision 和 Recall 往往是此消彼长的。比如一个模型特别保守,只在自己非常有把握时才预测为正,那 Precision 会很高,但 Recall 很低。调和平均对低值更敏感,能更好地反映这种权衡。

1.2 Macro-F1 的计算方式

Macro-F1 的做法是:先对每个标签单独计算 F1,然后对所有标签的 F1 取算术平均。

公式如下:

Macro-F1 = (F1_label1 + F1_label2 + ... + F1_labelN) / N

其中 N 是标签总数。每个标签的 F1 权重完全相同,不管这个标签在数据里出现多少次。

举个例子,假设有三个标签 A、B、C,它们的 F1 分别是 0.9、0.8、0.3,那 Macro-F1 就是 (0.9 + 0.8 + 0.3) / 3 = 0.667。标签 C 的表现很差,会直接被拉到平均分里,哪怕 A 和 B 表现很好。

1.3 Micro-F1 的计算方式

Micro-F1 的做法是:先把所有标签的 TP、FP、FN 分别加总,然后基于这些汇总值计算全局的 Precision 和 Recall,最后得到 F1。

公式如下:

Micro-Precision = ΣTP / (ΣTP + ΣFP) Micro-Recall = ΣTP / (ΣTP + ΣFN) Micro-F1 = 2 * Micro-Precision * Micro-Recall / (Micro-Precision + Micro-Recall)

核心逻辑是“先把所有预测结果合并成一个大混淆矩阵,再算一次指标”。样本量大的标签,因为 TP、FP、FN 的绝对值大,对汇总值的影响就大,所以在 Micro-F1 中权重更高。

2. 核心差异:Macro 与 Micro 到底在优化什么

很多文章喜欢说“Macro 对类别不敏感,Micro 对类别敏感”,这个说法其实不够准确。用数学式子展开之后,差异会更清楚。

2.1 从数学展开看权重差异

把 Micro-F1 的公式稍微变形。Micro-Precision 的分母是所有标签的 (TP + FP) 之和,也就是所有预测为正的总数;分子是所有 TP 之和。换句话说,Micro-Precision 其实就是“在全体预测结果中,预测正确的比例”,等价于把所有样本拼接成一个大的二分类问题来算。

Macro-F1 则不同,它是先把每个标签单独算 F1,再取平均。每个标签在最终结果中的贡献是 1/N,即使这个标签只有 10 个样本,另一个标签有 10000 个样本,贡献也一样。

这带来的直接后果是:

  • Macro-F1 对少数类非常敏感。如果某个小类别几乎不被模型识别,F1 会很低,Macro-F1 会被显著拉低。
  • Micro-F1 对大类别更友好。大类别因为样本多,对整体 TP 贡献大,只要大类别分得好,Micro-F1 就不会太差。

2.2 一个直观例子

假设一个多标签分类任务有 4 个标签,测试集样本量分布如下:

  • 标签 A:5000 个样本
  • 标签 B:3000 个样本
  • 标签 C:1500 个样本
  • 标签 D:500 个标签

模型训练后,标签 A 的 F1 是 0.95,标签 B 是 0.90,标签 C 是 0.80,标签 D 只有 0.30。此时:

Macro-F1 = (0.95 + 0.90 + 0.80 + 0.30) / 4 = 0.7375 Micro-F1 需要把所有标签的混淆矩阵加起来算。

假设 Micro-Precision 和 Micro-Recall 算出来分别是 0.92 和 0.85,那 Micro-F1 大约是 0.884。差距很明显:Macro-F1 0.74 反映了小类别 D 的短板,Micro-F1 0.88 则主要由大类别 A、B 的正确率主导。

这两个数字都不“错”,只是回答的问题不同。Macro-F1 回答的是“每个类别平均表现如何”,Micro-F1 回答的是“所有预测结果放在一起看,正确率如何”。

2.3 哪种场景用哪个

  • 如果你关心所有类别都能被较好识别,尤其是少数类,选 Macro-F1。比如不少分类场景中,某些细分类别的样本量本来就少,漏掉一个都很严重,Macro-F1 能逼着模型照顾这些边缘类别。
  • 如果你更关注整体正确率,或者说大多数样本都被正确处理,选 Micro-F1。比如推荐场景中判断内容标签,大类别分错的影响远大于小类别,Micro-F1 更贴近业务体感。
  • 没有明确倾向时,把两个都打印出来。如果 Macro 和 Micro 差距过大,说明模型在类别间表现不均衡,这时候需要针对少数类单独优化,而不是只盯着某一个指标调参。

3. 多标签场景下的其他评估维度

除了 Macro-F1 和 Micro-F1,多标签分类还有一些专门的评估方式。这些指标虽然不如 F1 常见,但在特定场景下更有参考价值。

3.1 Hamming Loss

Hamming Loss 计算的是“被预测错误的标签对数量 / (总标签数 * 总样本数)”,包括漏报和误报,数值越小越好。

比如 5 个样本,每个样本 4 个标签,其中 3 个标签预测错了,那 Hamming Loss 就是 3 / (5 * 4) = 0.15。它和 Accuracy 有些类似,只不过把“完全正确”放宽为“单个标签正确”,对部分正确的样本给出了更细的反馈。

3.2 Exact Match Ratio

Exact Match Ratio 要求一个样本的全部标签都预测正确,才算该样本正确。这个指标非常苛刻,通常数值偏低,适合标签强关联的场景。

如果样本有 5 个标签,预测对了 4 个,在 Hamming Loss 看来已经很不错,但在 Exact Match Ratio 里依然算错。很多业务场景下,标签并不是独立存在的,比如一个文档可能需要同时标记“技术”和“教程”,漏掉其中一个,推荐系统给用户的气质就完全不对了。

3.3 one-error、Coverage 和 Ranking Loss

这些指标更多用在标签排序场景,即模型会给每个标签输出一个分数,评估时看排序质量。one-error 检查得分最高的标签是否在真实标签集中,Coverage 统计真实标签在排序列表中平均要到第几位才能全部覆盖,Ranking Loss 则度量错误排在前面的标签对数量占比。

如果你的模型输出的是 label 分数而不是截断成二值预测,这些指标会更敏感地反映排序质量。我在一些信息流推荐项目中,会把 one-error 和 Ranking Loss 作为辅助指标,和 F1 一起看,因为 F1 只看阈值固定后的结果,而排序指标能看出“即使这次没预测对,模型把正确标签排到第几位了”。

3.4 不同指标的信息量对比

下表可以快速看出各指标的关注重点:

指标计算粒度关注重点数值趋势适用场景
Macro-F1标签粒度类别平均表现越高越好关注少数类、类别均衡性
Micro-F1样本/全局粒度全体预测正确率越高越好关注整体准确率、大类别
Hamming Loss标签粒度标签错误率越低越好标签相对独立、允许部分正确
Exact Match Ratio样本粒度样本完全正确率越高越好标签强关联、严格要求全对
Ranking Loss排序粒度标签排序质量越低越好分数输出场景、排序需求

4. 代码实现:从零手写 Macro-F1 和 Micro-F1

理论和公式说完了,来看代码。很多框架里都内置了这两个指标,但自己手写一遍能更清楚地理解计算过程,排查问题也更快。

4.1 构造多标签测试数据

先准备一个简单的多标签二值化测试集。假设有 5 个样本、3 个标签,真实值 y_true 和预测值 y_pred 都用 0/1 矩阵表示。

import numpy as np y_true = np.array([ [1, 0, 1], [0, 1, 0], [1, 1, 1], [0, 0, 1], [1, 0, 0] ]) y_pred = np.array([ [1, 0, 1], [0, 0, 0], [1, 1, 1], [0, 0, 1], [0, 0, 0] ])

这里每一行代表一个样本,每一列代表一个标签。列维度上的 1 表示该样本命中该标签。

4.2 按标签计算混淆矩阵

逐列计算每个标签的 TP、FP、FN:

def per_label_confusion(y_true, y_pred): tp_list = [] fp_list = [] fn_list = [] for i in range(y_true.shape[1]): tp = np.sum((y_true[:, i] == 1) & (y_pred[:, i] == 1)) fp = np.sum((y_true[:, i] == 0) & (y_pred[:, i] == 1)) fn = np.sum((y_true[:, i] == 1) & (y_pred[:, i] == 0)) tp_list.append(tp) fp_list.append(fp) fn_list.append(fn) return np.array(tp_list), np.array(fp_list), np.array(fn_list) tp, fp, fn = per_label_confusion(y_true, y_pred) print("TP:", tp) print("FP:", fp) print("FN:", fn)

输出:

TP: [2 1 3] FP: [1 0 0] FN: [1 1 0]

解释一下:标签 0 有 2 个正确命中,1 个误报,1 个漏报;标签 1 有 1 个正确命中,0 个误报,1 个漏报;标签 2 有 3 个正确命中,0 个误报,0 个漏报。

4.3 计算 Macro-F1

def macro_f1(tp, fp, fn): eps = 1e-9 precision = tp / (tp + fp + eps) recall = tp / (tp + fn + eps) f1 = 2 * precision * recall / (precision + recall + eps) return np.mean(f1) print("Macro-F1:", macro_f1(tp, fp, fn))

注意这里加了 eps 防止除零。输出约 0.7778。具体计算过程:标签 0 的 F1 约 0.6667,标签 1 约 0.6667,标签 2 是 1.0,三者平均约 0.7778。

4.4 计算 Micro-F1

def micro_f1(tp, fp, fn): eps = 1e-9 tp_sum = np.sum(tp) fp_sum = np.sum(fp) fn_sum = np.sum(fn) precision = tp_sum / (tp_sum + fp_sum + eps) recall = tp_sum / (tp_sum + fn_sum + eps) return 2 * precision * recall / (precision + recall + eps) print("Micro-F1:", micro_f1(tp, fp, fn))

输出约 0.8571。TP 总和 6,FP 总和 1,FN 总和 2,所以 Precision = 6/7,Recall = 6/8,F1 约 0.8571。

4.5 使用 sklearn 验证

from sklearn.metrics import f1_score macro = f1_score(y_true, y_pred, average='macro') micro = f1_score(y_true, y_pred, average='micro') print("sklearn Macro-F1:", macro) print("sklearn Micro-F1:", micro)

输出和手写结果一致。平时直接用 sklearn 的 f1_score 就行,但要理解参数 average 的含义:可以取 macro、micro、weighted、samples。

其中 weighted 是 Macro-F1 的加权版本,权重是每个标签的样本数占比。如果你的标签分布很不均衡,weighted 会在 Macro 和 Micro 之间取一个折中,比 Macro 更平滑,比 Micro 更容易反映小类别的影响。我在看论文对比时,经常看到作者同时报 Macro 和 weighted 两个值,就是因为这两者的差异能暴露类别不均衡的程度。

5. 实际应用中的隐藏陷阱与经验总结

这一部分是我在实际项目中踩过的坑,比公式本身更有价值。

5.1 做多标签评估时先在少数类标签上人工检查

只盯着 F1 数值,很容易忽略一个现象:某些标签明明有大量正样本,模型却几乎不预测它们。这种情况下,Macro-F1 可能依然很高,因为多数类别表现很好,把平均分拉上去了。我现在的习惯是,每次实验除了打印 Macro-F1 和 Micro-F1,还会打印每个标签单独的 Precision、Recall、F1,以及每个标签在测试集中出现的次数。多标签类别不平衡会直接影响这两个指标的数值和对比结论,具体表现为:

  • 少数类 F1 很低时,Macro-F1 会被拉低,和 Micro-F1 差距拉大。
  • 少数类虽然指标低,但对 Micro-F1 的影响可能不到 0.01,肉眼根本看不出问题。
  • 这时候需要单独优化少数类的阈值,或者给少数类损失加权,而不是盲目调整个模型的架构。

5.2 小心数据泄漏和样本重叠

多标签分类任务里,一个常见错误是把训练集的统计信息带进测试集。比如你根据训练集每个标签的出现频率设置阈值,然后用这个阈值在测试集上预测,这其实是某种形式的泄漏。更隐蔽的是,同一原始内容被拆成多个样本后同时出现在训练集和测试集,导致模型“记住”了内容风格而显得性能很高,一旦上线跑真实新数据,性能立刻崩掉。做数据划分时,按原始内容 ID 分组,而不是按样本行分组,能有效避免这个问题。

5.3 不要盲目追求高 Macro-F1

在某些标签关联性强的场景中,Macro-F1 提高可能意味着模型牺牲了大类别的精度来换取小类别的召回。这种 trade-off 合不合理,要看业务需求。举个例子:标签“紧急”出现的概率可能只有 0.5%,但漏掉的影响非常严重;标签“普通新闻”出现概率 70%,错分几个影响不大。这种情况下,Macro-F1 比 Micro-F1 更能反映业务想要的效果。

我在实际项目中通常这样报告指标组合:

  • 主实验对比阶段:打印 Macro-F1、Micro-F1、weighted-F1。
  • 深入分析阶段:打印每个标签的 F1、以及和上个版本的差值。
  • 上线前评估:看少量人工评测结果,确认 F1 的提升不是数据分布变化引起的。

5.4 阈值调整对 F1 的影响非常大

很多新手忽略了一点:多标签模型的最终输出是概率或分数,F1 是依赖阈值的。默认阈值 0.5 不一定是最优选择。我习惯对每个标签单独搜阈值,在验证集上遍历 [0.1, 0.2, ..., 0.9],选择每个标签 F1 最高的阈值。这一步往往能提升 1-3 个点的 Macro-F1。

不过要留意:验证集阈值搜索本质上也是一种调参,需要在独立的测试集上确认最终效果,不然会过拟合验证集。数据量小的时候,甚至可以用交叉验证来选阈值,减少偶然性。

5.5 样本量极不平衡时的选择建议

当样本量差异达到 100 倍以上,比如一个标签出现 10 万次,另一个只有 500 次,Micro-F1 几乎被大标签主导,小标签的表现好坏在 Micro-F1 上几乎测不出来。此时你汇报“Micro-F1 87%”可能给业务方一个错误的安全感,因为最需要关注的细分类别实际上准确率很低。

这种情况我会建议:

  • 用 Macro-F1 作为主要模型对比指标,要求模型在小标签上也不能太差。
  • 同时报告小标签单独的表现,比如为小标签计算单独的 F1。
  • 如果小标签表现始终起不来,考虑用更多数据增强、过采样,或者把多标签拆成单标签二分类分别建模。

5.6 多标签和单标签场景的实施差异

多标签场景通常不需要保证每个样本至少一个标签。在代码里这个差异很容易被忽略,尤其使用框架内置指标的时候,模型输出的 sigmoid 概率全都在 0.5 以下,预测矩阵可能全是 0,此时 Micro-F1 会变得极低。这种失败模式不容易暴露在宏平均指标上,因为单个标签 F1 也可能很低。通常做法是对每行判断是否全 0,如果是,把最大概率位置置为 1,保证每个样本至少一个预测标签。但这个规则会对某些标签造成较多误报,需要根据验证集效果决定是否采用。

5.7 如何让两个指标的差异成为调试线索

Macro-F1 和 Micro-F1 的差值本身很有信息量。如果 Macro 明显低于 Micro,说明模型在少数类上表现较差,需要关注数据增强、类别权重或重采样。如果 Micro 明显低于 Macro,说明大类别出现系统性错误,比如大类别标签经常混在一起,此时需要检查标签定义是否清晰、标注质量是否有问题。

差值用于诊断比用于最终汇报更有价值。最终汇报还是应该明确告诉对方“这个指标代表什么”,不然业务方很容易对着一个数字做出错误解读。

5.8 什么时候用精确率召回率曲线

如果你的业务对漏报和误报的代价不对称,F1 这种单一数值可能不够。此时可以对每个标签单独画 PR 曲线,看曲线下面积,或者直接固定业务允许的误报上限,在验证集上寻找使召回率最高的阈值。比如医疗筛查场景中,漏报不可容忍,模型需要在一定误报率下尽量多找回真实病例,这时候 PR 曲线的意义比 F1 更直接。

在类别严重不平衡的数据集里,PR 曲线比 ROC 曲线更不容易产生误导,因为 ROC 受 TN 影响较大,在不平衡数据上往往会给出过于乐观的结果。

6. 写在最后的一点个人体会

做多标签分类这几年,我最大的感受是:指标是给业务“交代”的工具,也是给模型“找茬”的工具,但两者要分开用。给业务汇报时,挑一个最贴近业务诉求的指标,把计算口径讲清楚;分析模型时,别只看一个数字,要把标签维度的细节展开来看。Macro-F1 和 Micro-F1 的差异不是“谁对谁错”,而是“谁更适合解释你现在的问题”。

还有一个很小的建议:不管用什么指标,每次跑实验时固定随机种子,划分数据集时保证不同实验用的是完全相同的测试集。我遇到过因为数据划分不一致,导致某次改动明明没有效果,却因为换了一批测试样本看上去涨了两分的情况。这种假象比低分数更危险,因为它会误导整个团队往错误的方向使劲。

多标签评估没有万能指标,但把计算过程吃透、知道每个指标的局限,至少能保证你不被一个数字骗到。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询