Skip to content
AI360Xpert
Core ML

Multi-Label Classification

Softmax forces every class to compete for one probability budget. Real tagging often needs several labels at once, which is a different architecture entirely.

Four independent sigmoid heads can each fire above threshold at once, unlike a softmax row where raising one class necessarily takes probability away from the rest
Four independent sigmoid heads can each fire above threshold at once, unlike a softmax row where raising one class necessarily takes probability away from the rest

Why Does This Exist?

Softmax regression assumes exactly one right answer per example, and builds that assumption straight into the arithmetic: every class's share of probability is taken from the others, because they all have to sum to one. Most tagging systems don't work that way, and forcing them through softmax anyway produces a model that looks fine on paper and quietly can't do its job.

Here's the case we'll carry down the page. A support ticket says the app crashed and the customer wants a refund. That's two tags at once — bug and refund — out of a label set that also includes billing, feature-request, and account-access. Nothing about that ticket is ambiguous or uncertain; it genuinely belongs to two categories simultaneously, and a system that can only assign one is wrong by design, not by mistake.

Multi-label classification is the name for that task shape: any number of labels can apply to one example, including none and including all of them, and the model's job is to say which subset.

Think of It Like This

A ballot where you can circle more than one box

A single-choice ballot forces exactly one mark: circling a second candidate spoils the vote, because the format itself enforces mutual exclusion, and every candidate's chance is taken directly from the others. That's softmax.

A different ballot — "select all that apply" for which amenities a property has — has no such rule. Circling pool takes nothing away from parking; a property can have both, one, or neither, and each box gets judged entirely on its own evidence. That's multi-label: KK independent yes-or-no questions, not one KK-way choice, and the model has to be built to actually allow that rather than fake it with a workaround.

How It Actually Works

The architecture is one line different, and it changes everything downstream

Swap softmax's single competing output for KK independent sigmoid outputs, one per label, each producing its own probability between 0 and 1 with no constraint tying them together:

pk=σ(zk)=11+ezkp_k = \sigma(z_k) = \frac{1}{1 + e^{-z_k}}

Nothing here forces kpk=1\sum_k p_k = 1. A ticket can score 0.91 on bug and 0.87 on refund simultaneously, because each zkz_k is scored on its own evidence with no other label competing for the same probability budget. The loss changes to match: binary cross-entropy summed independently over every label, rather than one categorical cross-entropy over a forced choice.

A threshold per label, not one shared cut

Because each label is its own sigmoid, each one needs its own decision threshold — the same threshold selection problem, applied KK times over. refund might warrant a lower cut than bug if a missed refund request is costlier than a missed bug report; nothing requires the same 0.5 everywhere, and defaulting to 0.5 for every label is the same unexamined default that plagues binary classification, multiplied by KK.

Averaging disagrees with itself, for the same reason it does in the single-label case

Once predictions exist, scoring them multiplies the classification metrics averaging problem by however many labels you have, plus a new axis: averaging can happen per label (score bug across every ticket, then refund across every ticket, then average those) or per sample (score all labels on ticket one, then all labels on ticket two, then average those). Macro-per-label gives a rare label like account-access a full vote in the average regardless of how few tickets carry it. Micro pools every label-ticket pair into one bucket and follows whichever labels are most common. Sample-based averaging asks "how well did we do on this ticket, across its own labels" and rewards getting an easy, single-label ticket right the same as a genuinely hard multi-tag one. None of the three is the neutral choice; each answers a different question about the same predictions.

Show Me the Code

Fifty-six percent of these tickets carry more than one true tag. Score the same predictions two ways: independent sigmoids that can raise several labels at once, and a softmax head that's structurally forced to pick just one.

import numpy as np
rng = np.random.default_rng(11)n, k = 2000, 4  # 2000 tickets, 4 tags: billing, bug, crash, refund -- any subset can applylogits = rng.normal(size=(n, k))truth = (logits + rng.normal(0.0, 1.0, (n, k)) > 0.3).astype(int)  # co-occurring tags
sigmoid_pred = (1 / (1 + np.exp(-logits)) > 0.5).astype(int)          # k independent yes/no callssoftmax_p = np.exp(logits) / np.exp(logits).sum(axis=1, keepdims=True)  # forced to sum to 1softmax_pred = (softmax_p == softmax_p.max(axis=1, keepdims=True)).astype(int)  # only the top tag fires
def recall(pred: np.ndarray) -> float:    return float((pred & truth).sum() / max(truth.sum(), 1))
multi_tag_share = float((truth.sum(axis=1) > 1).mean())print(f"tickets carrying more than one true tag: {multi_tag_share:.0%}")print(f"recall across all true tags: sigmoid heads={recall(sigmoid_pred):.2f}  softmax head={recall(softmax_pred):.2f}")# -> tickets carrying more than one true tag: 56%# -> recall across all true tags: sigmoid heads=0.81  softmax head=0.44

The softmax head structurally cannot raise a second tag once the first has claimed the probability budget, and recall shows the cost directly: it catches fewer than half the true tags the independent sigmoid heads catch, on identical underlying scores.

Watch Out For

Using softmax on labels that can co-occur, and not noticing

A softmax-trained tagger can look healthy on aggregate accuracy and still be structurally incapable of raising a second true label, because accuracy on the top label doesn't measure the labels it never had the chance to raise. The tell is decent top-1 accuracy paired with poor recall once you check per-label — that gap is architectural, not a training problem, and no amount of extra data or epochs fixes a softmax head being asked to do a multi-label job.

Reporting one averaged score across wildly uneven label frequencies

A single micro-F1 across four labels where one holds 70% of the tickets and another holds 3% reports almost entirely on the frequent label, and a team can ship a model that's quietly useless on the rare-but-important tag — often the costliest one, like account-access — while the headline number looks fine. Report per-label precision and recall alongside whichever average you lead with, the same discipline classification metrics already asks for in the single-label case.

The Quick Version

  • Multi-label classification allows any number of labels per example, including several at once, unlike single-label multiclass where exactly one applies.
  • The architecture swaps one competing softmax output for KK independent sigmoid outputs, one per label, with no constraint forcing them to sum to one.
  • Each label needs its own decision threshold; there's no reason every label shares the same cut.
  • Averaging metrics across labels disagrees with itself the same way single-label averaging does — macro, micro, and per-sample answer different questions about the same predictions.
  • A softmax head on genuinely co-occurring labels caps recall structurally, not just in practice, because raising one label spends probability the others needed.

Related concepts