multiclass accuracy
sensitivity
specificity
performance metrics
machine learning evaluation

How to calculate multiclass overall accuracy, sensitivity and specificity?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Overall accuracy in multiclass classification is straightforward, but sensitivity and specificity are not single direct formulas the way they are in a binary problem. The standard approach is to compute them one class at a time using a one-versus-rest view, then decide explicitly how to average those class-wise values.

Start With the Confusion Matrix

For multiclass metrics, the confusion matrix is the cleanest source of truth. Accuracy is simply the sum of the diagonal divided by the total number of samples.

python
1import numpy as np
2from sklearn.metrics import confusion_matrix, accuracy_score
3
4y_true = np.array([0, 0, 1, 1, 2, 2, 2, 1, 0, 2])
5y_pred = np.array([0, 1, 1, 1, 2, 0, 2, 1, 0, 2])
6
7labels = np.array([0, 1, 2])
8cm = confusion_matrix(y_true, y_pred, labels=labels)
9
10print(cm)
11print("accuracy:", accuracy_score(y_true, y_pred))

Accuracy is easy because it ignores the class-by-class tradeoffs. Sensitivity and specificity require more structure.

Compute Sensitivity and Specificity Per Class

For a given class i, pretend that class is the positive class and all other classes are negative. Then derive:

  • true positive as cm[i, i]
  • false negative as the rest of row i
  • false positive as the rest of column i
  • true negative as everything else

That gives the binary-style formulas:

  • sensitivity = TP / (TP + FN)
  • specificity = TN / (TN + FP)
python
1def per_class_sens_spec(cm):
2    total = cm.sum()
3    rows = []
4
5    for i in range(cm.shape[0]):
6        tp = cm[i, i]
7        fn = cm[i, :].sum() - tp
8        fp = cm[:, i].sum() - tp
9        tn = total - tp - fn - fp
10
11        sensitivity = tp / (tp + fn) if (tp + fn) else 0.0
12        specificity = tn / (tn + fp) if (tn + fp) else 0.0
13
14        rows.append((i, sensitivity, specificity))
15
16    return rows
17
18stats = per_class_sens_spec(cm)
19for cls, sens, spec in stats:
20    print(f"class={cls} sensitivity={sens:.4f} specificity={spec:.4f}")

This is the key step most explanations skip. There is no single native multiclass sensitivity without first deciding how class-wise sensitivity should be combined.

Choose the Averaging Rule Explicitly

Once you have per-class values, you need to choose how to summarize them. The common options are:

  • macro average: plain mean across classes
  • weighted average: mean weighted by class support
  • micro-style pooling: aggregate counts first, then compute ratios
python
1supports = cm.sum(axis=1)
2sensitivities = np.array([row[1] for row in stats])
3specificities = np.array([row[2] for row in stats])
4
5macro_sens = sensitivities.mean()
6macro_spec = specificities.mean()
7
8weighted_sens = np.average(sensitivities, weights=supports)
9weighted_spec = np.average(specificities, weights=supports)
10
11print("macro sensitivity:", round(macro_sens, 4))
12print("macro specificity:", round(macro_spec, 4))
13print("weighted sensitivity:", round(weighted_sens, 4))
14print("weighted specificity:", round(weighted_spec, 4))

Macro averaging treats each class equally. Weighted averaging gives bigger classes more influence. Neither is "the" correct answer in all settings. The right one depends on whether rare classes matter as much as common ones.

Why Accuracy Alone Is Not Enough

In imbalanced problems, accuracy can look healthy even when one class is being ignored. Per-class sensitivity exposes whether the model is actually finding the positives for each class. Specificity tells you whether the model is incorrectly assigning other classes to that class too often.

One subtle point: specificity can look artificially high in multiclass settings because each one-versus-rest comparison includes many true negatives. That is why reporting only one high specificity number can be misleading without the accompanying sensitivity and confusion matrix context.

Handle Edge Cases Deliberately

Evaluation code should define behavior for awkward but real cases:

  • one class is absent from y_true
  • one class is never predicted
  • the denominator for a metric becomes zero

Your metric code should not silently reorder labels either. Always pass an explicit labels array into the confusion matrix so row and column meaning stay stable across runs.

If the label order changes between one script and another, you can generate a perfectly valid confusion matrix with completely wrong human interpretation.

Common Pitfalls

The most common mistake is computing overall accuracy and then calling it enough. That hides poor class-level behavior.

Another common issue is reporting "multiclass sensitivity" or "multiclass specificity" without saying whether the value is macro, weighted, or something else. Developers also often compute specificity incorrectly by forgetting the one-versus-rest formulation and using only row-based counts. Finally, implicit label ordering can produce reports that look correct numerically while labeling the classes wrong.

Summary

  • Compute overall accuracy directly from the multiclass confusion matrix.
  • Compute sensitivity and specificity one class at a time using a one-versus-rest interpretation.
  • Choose an averaging rule such as macro or weighted and report it explicitly.
  • Keep label ordering fixed so confusion matrix rows and columns stay meaningful.
  • Do not rely on accuracy alone when class imbalance or rare classes matter.

Course illustration
Course illustration

All Rights Reserved.