Condados
← All posts The Computer Vision Metrics Handbook · Chapter 2 of 6 View all →

Multiclass and Multilabel Metrics: Micro, Macro, Weighted, and the Difference That Matters

How precision/recall/F1 scale past two classes — one-vs-rest, and the micro/macro/weighted averaging that decides whether rare classes count. Plus multilabel-specific metrics: subset accuracy, Hamming loss, and top-k — all on one running shelter-camera example, with scikit-learn.

Luis Condados · · Updated June 6, 2026
Multiclass and Multilabel Metrics: Micro, Macro, Weighted, and the Difference That Matters

Two words decide whether your multiclass F1 rewards the model for the rare classes or lets the common ones paper over them: “macro” vs “micro.” Pick the wrong average and a model that ignores your minority class can still look great. We’ll watch that happen on one running example — a shelter intake camera — and you can push the numbers around yourself.

This builds directly on the binary foundations — read that first if precision/recall/F1 aren’t second nature yet.

The running example: PetSpotter, the shelter intake camera

PetSpotter photographs each animal arriving at a small shelter and sorts it into one of three classes: Dog, Cat, or Bird. Last batch had 20 animals — and they’re imbalanced, like real intake: 12 dogs, 5 cats, 3 birds. The model loves dogs, is so-so on cats, and — the twist — never once says “bird”; every bird gets called a cat. Here’s the full 3×33\times3 confusion matrix (rows = what it really was, columns = what PetSpotter guessed):

predicted →DogCatBirdtruthDogCatBird1200230030
PetSpotter’s batch of 20. Green diagonal = correct; the red cells are the mistakes — 2 cats called dogs, and all 3 birds called cats. The whole “Bird” column is empty: the model never predicts bird.

Multiclass: one-vs-rest, then average

Intuition: score each class as its own yes/no question — “Dog vs not-Dog” — which turns the big matrix into three familiar binary problems, then combine.

With CC mutually exclusive classes you compute precision/recall/F1 per class by treating it as “this class vs everything else,” giving Pc,Rc,F1(c)P_c, R_c, F_1^{(c)}.

The only real question is how you collapse those three F1s into one number — and the choice changes the verdict completely.

Intuition — macro: every class gets one vote, no matter how rare.

Macro-F1=1Cc=1CF1(c).\text{Macro-}F_1 = \frac{1}{C} \sum_{c=1}^{C} F_1^{(c)}.

Intuition — micro: every sample gets one vote (pool all the counts first), so big classes dominate.

Precisionmicro=cTPcc(TPc+FPc),Recallmicro=cTPcc(TPc+FNc).\text{Precision}_{\text{micro}} = \frac{\sum_c TP_c}{\sum_c (TP_c + FP_c)}, \qquad \text{Recall}_{\text{micro}} = \frac{\sum_c TP_c}{\sum_c (TP_c + FN_c)}.

Intuition — weighted: macro, but each class’s vote is scaled by its support ncn_c.

Weighted-F1=c=1CncNF1(c).\text{Weighted-}F_1 = \sum_{c=1}^{C} \frac{n_c}{N}\, F_1^{(c)}.
0.92Dog0.55Cat0.00Birdmacro 0.49micro 0.75weighted 0.69
The bars are per-class F1; the lines are the three averages. Macro sits low because it can’t ignore the zero-height Bird bar.

Now you try

Edit any cell of PetSpotter’s matrix and watch the per-class scores and all three averages react. The lesson to confirm: give the Bird row a real diagonal (say move those 3 into the Bird column) and macro-F1 jumps far more than micro — because you just fixed the class micro was already ignoring.

Confusion matrix — rows = truth, columns = prediction. Edit any cell.
Per-class F1 (bars) vs the macro / micro / weighted averages (lines).

Top-k accuracy

Intuition: for hard, many-class problems, give credit if the right answer is in the running — among the model’s top kk guesses.

Multilabel: each photo can carry many labels

Now move PetSpotter outdoors, where one frame can hold several animals at once — a dog and a cat on the deck. Predictions and truth become binary vectors over L=3L=3 labels {\{dog, cat, bird}\}, and two metrics are unique to this setting.

Intuition — subset accuracy: all-or-nothing. The whole photo’s tag set must match exactly; one wrong tag fails it.

SubsetAcc=1Ni=1N1(Y^i=Yi).\text{SubsetAcc} = \frac{1}{N} \sum_{i=1}^{N} \mathbb{1}\big(\hat{Y}_i = Y_i\big).

Intuition — Hamming loss: partial credit. Score each label slot on its own and count the fraction wrong.

HammingLoss=1NLi=1Nj=1L1(y^ijyij).\text{HammingLoss} = \frac{1}{N L} \sum_{i=1}^{N} \sum_{j=1}^{L} \mathbb{1}\big(\hat{y}_{ij} \neq y_{ij}\big).
dogcatbirdFenceDeckBirdbath✓ •✓ •✓ •✓ = predicted • = true tag red border = mismatch
The two red-bordered slots are the only errors: a missed cat (Deck) and an extra cat (Birdbath). 7 of 9 slots right → Hamming 0.22, but only 1 of 3 photos perfect → subset 0.33.

Then the same micro / macro / weighted averages apply to P/R/F1 over the LL labels, plus a samples average (per-sample, then averaged). For ranking-style multilabel, label-ranking AP / per-label AP are the go-to.

Now you try

Toggle PetSpotter’s predicted tags (click the cells) and watch subset accuracy lurch while Hamming loss moves gently. Try fixing just the missed cat on “Deck” — subset accuracy leaps from 0.33 to 0.67 off a single slot, which is exactly its all-or-nothing temperament.

Dot = the true tag. Click a cell to toggle the model's prediction.

In scikit-learn

The same PetSpotter numbers, computed for real (labels 0=Dog, 1=Cat, 2=Bird):

import numpy as np
from sklearn.metrics import f1_score, classification_report, top_k_accuracy_score
from sklearn.metrics import hamming_loss, accuracy_score

# --- Multiclass: 20 animals (12 dog, 5 cat, 3 bird); birds all called cat ---
y_true = [0]*12 + [1]*5            + [2]*3
y_pred = [0]*12 + [0,0,1,1,1]      + [1,1,1]   # 2 cats->dog, all birds->cat
print("macro   ", f1_score(y_true, y_pred, average="macro"))      # 0.489
print("micro   ", f1_score(y_true, y_pred, average="micro"))      # 0.75 == accuracy
print("weighted", f1_score(y_true, y_pred, average="weighted"))   # 0.690
print(classification_report(y_true, y_pred, digits=3))            # per-class + all

# --- Multilabel: 3 backyard photos, labels [dog, cat, bird] ---
Y_true = np.array([[1, 0, 0], [1, 1, 0], [0, 0, 1]])
Y_pred = np.array([[1, 0, 0], [1, 0, 0], [0, 1, 1]])
print("subset accuracy", accuracy_score(Y_true, Y_pred))   # 0.333
print("hamming loss   ", hamming_loss(Y_true, Y_pred))     # 0.222
print("micro-F1       ", f1_score(Y_true, Y_pred, average="micro"))   # 0.75
print("macro-F1       ", f1_score(Y_true, Y_pred, average="macro"))   # 0.667

Caveats & common pitfalls

  • The averaging choice is a reporting decision, not a detail. Macro, micro, and weighted disagreed by 26 points on PetSpotter (0.49 vs 0.75) — never quote a bare “F1” without saying which average produced it [3].
  • Micro-F1 hides minority failure by construction. It pooled away the entire Bird failure into a healthy-looking 0.75. Read the per-class report [3].
  • Undefined precision is silent. Bird’s precision is 0/0 (never predicted); libraries quietly fill 0 and may warn — check, because it can swing macro a lot.
  • Top-k flatters large label sets. It credits “in the running,” right for shortlists, a vanity number if you only act on the top guess.
  • Multilabel thresholds are per-label. Subset accuracy and Hamming loss both swing with each label’s decision threshold; tune and report it [4].

Takeaways

  • Multiclass = one-vs-rest per class + an averaging choice. That choice is the whole story — PetSpotter scored 0.49, 0.69, or 0.75 depending on it.
  • Macro for fairness to rare classes; micro when every sample should count equally (and micro-F1 = accuracy for single-label multiclass); weighted is the frequency-weighted compromise.
  • Always read the per-class report, not just the headline average — it’s where the rare-class failures (Bird = 0.00) hide.
  • Multilabel adds subset accuracy (strict, 0.33) and Hamming loss (granular, 0.22); then reuse micro/macro/samples averaging.

Next: leaving pure classification for localization → Object detection metrics: IoU, AP, and mAP.

Further reading

References

[1] Sokolova, M., & Lapalme, G. (2009). A Systematic Analysis of Performance Measures for Classification Tasks. Information Processing & Management, 45(4), 427–437. DOI:10.1016/j.ipm.2009.03.002.

[2] Tsoumakas, G., & Katakis, I. (2007). Multi-Label Classification: An Overview. International Journal of Data Warehousing and Mining, 3(3), 1–13.

[3] Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.), ch. 4. Springer. Free PDF.

[4] Pedregosa, F., et al. (2011). Scikit-learn: Machine Learning in Python. JMLR, 12, 2825–2830. Docs: Multiclass & multilabel averaging.