Evaluation Metrics for Classification

Imagine a hospital deploys a model to flag patients at risk of sepsis. The model is 99% accurate, so it must be excellent. In reality, only 1% of patients develop sepsis. The model simply predicts "no sepsis" for everyone. Accuracy hides a catastrophic failure to detect the one outcome that actually matters. This is not a hypothetical curiosity. It is the single most common mistake made when machine learning systems are evaluated and shipped. Choosing the right evaluation metric is as important as choosing the right model. This is because the metric defines what "good" means. A fraud-detection system, a spam filter, and a cancer screener all face the same binary classification structure. Yet they demand entirely different notions of success. A metric that rewards the wrong behavior will silently steer Model Selection toward a model that looks strong on paper. Such a model fails in production. Understanding accuracy, precision, recall, and F1 is essential. Knowing precisely when each one is the right lens is a prerequisite for any serious use of classifiers. This article builds these metrics from first principles. It starts with the four possible outcomes of a binary decision. It shows algebraically why accuracy breaks down under class imbalance. Precision and recall do not break down this way. It connects the metrics to the practical tension between false positives and false negatives. It shows how F1 arises as a specific, principled way to trade one against the other. Throughout, we tie the discussion back to the gap between Training Error vs Test Error. A metric computed on training data tells you nothing about deployment performance. By the end, you will be able to compute each metric by hand from raw predictions. You will know exactly which one to optimize for a given business or scientific cost structure. You will understand the pitfalls that trip up even experienced practitioners. These include imbalanced data, multi-class averaging, and threshold sensitivity. This sets up the natural next tools. The Confusion Matrix is the single object all these metrics are derived from. ROC Curves and AUC offers threshold-free evaluation.

Key takeaways
  • Accuracy is the fraction of correct predictions; it is misleading when classes are imbalanced.
  • Precision measures how many predicted positives are truly positive; it penalizes false positives.
  • Recall measures how many actual positives were found; it penalizes false negatives.
  • F1 is the harmonic mean of precision and recall, balancing both errors into one number.
  • Metric choice must match the real-world cost of false positives versus false negatives.
  • All four metrics are computed from the same four counts: TP, FP, FN, TN.
The core insight

No single number captures classifier quality — precision and recall expose the two distinct ways a classifier can fail, and the right metric depends on which failure costs more.

The idea

A binary classifier maps each input to a predicted label, positive or negative. Comparing predictions to ground truth produces exactly four disjoint outcomes: a true positive (TP) is a correctly predicted positive, a false positive (FP) is a negative incorrectly predicted positive, a false negative (FN) is a positive incorrectly predicted negative, and a true negative (TN) is a correctly predicted negative. Every classification metric discussed here is a function of these four counts, tallied over a labeled evaluation set — commonly organized into a table called the Confusion Matrix.

Accuracy is the simplest summary: ``` accuracy = (TP + TN) / (TP + FP + FN + TN) ``` It answers "what fraction of predictions were correct?" This is intuitive but treats both error types and both classes as equally important, which is rarely true in practice, especially when one class is rare.

Precision (also called positive predictive value) asks: of everything the model flagged as positive, how much was actually positive? ``` precision = TP / (TP + FP) ``` High precision means few false alarms. It is the metric to prioritize when false positives are costly — flagging an innocent email as spam and losing an important message, or falsely accusing a legitimate transaction of fraud.

Recall (also called sensitivity or true positive rate) asks: of everything that was actually positive, how much did the model find? ``` recall = TP / (TP + FN) ``` High recall means few missed cases. It is the metric to prioritize when false negatives are costly — missing a tumor, missing a fraudulent transaction, missing a structural defect. Precision and recall exist in tension: a classifier that predicts "positive" for everything achieves perfect recall (it never misses a true positive) but catastrophic precision (nearly every prediction is a false alarm). A classifier that only predicts "positive" when supremely confident achieves high precision but low recall, missing many true cases. F1 score is the harmonic mean of the two, chosen specifically because the harmonic mean punishes imbalance between precision and recall more severely than the arithmetic mean: ``` F1 = 2 (precision recall) / (precision + recall) ``` F1 is high only when both precision and recall are reasonably high; a model that is excellent at one and terrible at the other scores poorly, which is exactly the behavior you want from a single combined metric.

How it works

Computing these metrics requires only predicted labels, true labels, and a pass to tally the four counts. The algorithm below computes all four base metrics, and generalizes to multi-class problems via two standard averaging schemes. ``` FUNCTION compute_confusion_counts(y_true, y_pred, positive_class): TP, FP, FN, TN = 0, 0, 0, 0 FOR i in range(length(y_true)): actual = (y_true[i] == positive_class) predicted = (y_pred[i] == positive_class) IF actual AND predicted: TP += 1 ELSE IF NOT actual AND predicted: FP += 1 ELSE IF actual AND NOT predicted: FN += 1 ELSE: TN += 1 RETURN TP, FP, FN, TN FUNCTION binary_metrics(TP, FP, FN, TN): accuracy = (TP + TN) / (TP + FP + FN + TN) precision = TP / (TP + FP) IF (TP + FP) > 0 ELSE undefined recall = TP / (TP + FN) IF (TP + FN) > 0 ELSE undefined f1 = 2 precision recall / (precision + recall) IF (precision + recall) > 0 ELSE 0 RETURN accuracy, precision, recall, f1 FUNCTION multiclass_metrics(y_true, y_pred, classes, average):

# average in {"macro", "micro", "weighted"} per_class_precision = [] per_class_recall = [] per_class_f1 = [] per_class_support = [] # number of true instances of each class FOR c in classes: TP, FP, FN, TN = compute_confusion_counts(y_true, y_pred, c) _, p, r, f = binary_metrics(TP, FP, FN, TN) append p to per_class_precision append r to per_class_recall append f to per_class_f1 append (TP + FN) to per_class_support IF average == "macro": RETURN mean(per_class_precision), mean(per_class_recall), mean(per_class_f1) IF average == "weighted": total = sum(per_class_support) w_p = sum(p_i s_i for p_i, s_i in zip(per_class_precision, per_class_support)) / total w_r = sum(r_i s_i for r_i, s_i in zip(per_class_recall, per_class_support)) / total w_f = sum(f_i * s_i for f_i, s_i in zip(per_class_f1, per_class_support)) / total RETURN w_p, w_r, w_f IF average == "micro":

# pool TP, FP, FN across all classes first, then compute once total_TP, total_FP, total_FN = 0, 0, 0 FOR c in classes: TP, FP, FN, TN = compute_confusion_counts(y_true, y_pred, c) total_TP += TP; total_FP += FP; total_FN += FN _, p, r, f = binary_metrics(total_TP, total_FP, total_FN, 0) RETURN p, r, f ``` The key design choice is the averaging strategy for multi-class problems, since precision and recall are natively defined per class (treating that class as "positive" and all others as "negative"). Macro-averaging treats every class equally regardless of size, exposing poor performance on rare classes. Weighted averaging scales each class's contribution by its frequency, closer in spirit to accuracy. Micro-averaging pools all TP/FP/FN counts globally before computing one precision/recall pair, which — for single-label multi-class problems where every instance gets exactly one predicted label — makes micro-precision, micro-recall, and accuracy coincide exactly.

Figure
The precision–recall tradeoff as a decision threshold slides
Sliding the decision threshold predicted score, low → high negatives positives low threshold high recall, lower precision high threshold high precision, lower recall Predict "positive" for everything right of the threshold line.

Complexity & correctness

Time complexity. Computing the four base counts requires a single linear pass over the n evaluation examples, comparing predicted and true labels: `O(n)` time. Deriving accuracy, precision, recall, and F1 from the counts is `O(1)` arithmetic. For a multi-class problem with `k` classes, the naive per-class loop above is `O(nk)` because each class requires its own pass; this is easily reduced to `O(n + k)` by tallying a full confusion matrix once (one pass, incrementing `matrix[true_class][pred_class]`) and then computing all row/column sums in `O(k^2)` — negligible when `k ≪ n`, which holds in essentially every practical setting.

Space complexity. The base binary metrics need only `O(1)` additional space beyond the input arrays. The multi-class confusion matrix requires `O(k^2)` space to store all class-pair counts, which is the standard and efficient representation.

Correctness argument. The four counts TP, FP, FN, TN form a partition of all `n` predictions — every example falls into exactly one of the four categories, since "actual" and "predicted" are each binary and the four combinations are exhaustive and mutually exclusive. This is precisely what an invariant check gives us: at every iteration of the loop, `TP + FP + FN + TN` equals the number of examples processed so far, and after the loop it equals `n`. This invariant guarantees accuracy's denominator is always `n` and that precision/recall denominators (`TP+FP` and `TP+FN`) are well-defined subsets of that total. The harmonic mean underlying F1 is not an arbitrary choice. For two positive numbers `p` and `r`, the harmonic mean satisfies: ``` HM(p, r) = 2pr / (p + r) ≤ sqrt(pr) ≤ (p + r) / 2 ``` with equality throughout only when `p = r`. This means F1 is always less than or equal to the arithmetic average of precision and recall, and the gap grows as the two diverge — so a classifier with precision 0.99 and recall 0.01 gets an arithmetic mean of 0.50 (misleadingly decent) but an F1 of roughly 0.0198 (correctly reflecting near-total failure to find positives). This is the mathematical reason F1 is preferred over a simple average when you want a single number that punishes lopsided performance.

Honest tradeoffs. All of these metrics depend on a fixed decision threshold (typically 0.5 on a predicted probability). Changing the threshold changes every metric simultaneously, which is why threshold-free tools like ROC Curves and AUC exist for comparing models independent of any single operating point. Additionally, precision and recall are class-specific in multi-class settings, so a single scalar always requires an averaging choice — there is no free lunch, only a decision about which failure mode matters most.

Worked example

Consider a binary email spam filter evaluated on 20 labeled emails. The confusion counts, tallied by the algorithm above, are:

Predicted SpamPredicted Not-Spam
Actual SpamTP = 6FN = 4
Actual Not-SpamFP = 2TN = 8

Step through the computation by hand: ``` accuracy = (TP + TN) / n = (6 + 8) / 20 = 14 / 20 = 0.70 precision = TP / (TP + FP) = 6 / (6 + 2) = 6 / 8 = 0.75 recall = TP / (TP + FN) = 6 / (6 + 4) = 6 / 10 = 0.60 F1 = 2 precision recall / (precision + recall) = 2 0.75 0.60 / (0.75 + 0.60) = 0.90 / 1.35 = 0.667 ``` Interpretation: precision of 0.75 means that of every email the filter labeled spam, 75% actually were spam (2 legitimate emails were wrongly blocked). Recall of 0.60 means the filter only caught 60% of actual spam — 4 spam emails slipped through to the inbox. Accuracy (0.70) sits between the two and, notably, hides the asymmetry entirely: it treats the 2 false positives and the 4 false negatives as equally weighted contributions to error, even though for a spam filter, blocking a legitimate email (FP) is often considered far worse than letting one spam message through (FN). Now contrast with an imbalanced medical-screening scenario: 950 healthy patients, 50 diseased patients, and a lazy model that predicts "healthy" for everyone. ``` TP = 0, FN = 50, FP = 0, TN = 950 accuracy = (0 + 950) / 1000 = 0.95 precision = 0 / (0 + 0) = undefined (no positive predictions made) recall = 0 / (0 + 50) = 0.00 F1 = 0 (recall is 0, so the harmonic mean collapses to 0) ``` A 95% accuracy looks superb, but recall of 0 reveals the model caught zero diseased patients — the exact failure mode accuracy is structurally blind to when one class dominates. This is the central, recurring lesson: accuracy is only trustworthy when classes are roughly balanced and error types are equally costly; otherwise precision, recall, and F1 must be examined directly.

Common pitfalls & traps

PitfallWhy it happensFix
Trusting accuracy on imbalanced dataMajority-class prediction inflates accuracy while ignoring the minority class entirelyReport precision/recall per class, or use balanced accuracy / F1
Undefined precision or recall (division by zero)No positive predictions made (precision) or no actual positives exist (recall)Explicitly define a convention (often 0) and flag it, don't silently crash
Averaging precision/recall arithmetically instead of harmonically for a summaryArithmetic mean masks large imbalance between precision and recallUse F1 (harmonic mean) when one combined number is required
Confusing macro vs. micro vs. weighted averaging in multi-class reportsEach answers a different question about class importanceState explicitly which averaging scheme is used, and why
Evaluating on the training setMetrics look excellent but do not reflect generalizationAlways compute metrics on a held-out test or validation split, consistent with Training Error vs Test Error
Picking F1 without considering asymmetric costsF1 assumes precision and recall matter equally, which is often falseUse a weighted F-beta score, or optimize precision/recall directly against business cost
Ignoring threshold dependenceAll four metrics are computed at one fixed threshold, hiding how performance shifts elsewhereInspect a precision-recall curve across thresholds before finalizing a threshold

Pioneers & history

The precision–recall framework did not originate in machine learning; it was formalized in the mid-20th century within information retrieval, where the task of "finding relevant documents in a large corpus" required exactly this kind of two-sided error accounting. The terminology and mathematical apparatus were later absorbed wholesale into statistics, medicine (as sensitivity/specificity), and eventually machine learning classification benchmarks.

Origins · 1 minCalvin N. Mooers and how this was discovered
CN
Calvin N. Mooers
1919–1994 · Information retrieval pioneer who helped formalize recall as a measure of a retrieval system's completeness

History
1950
s: precision and recall formalized in information retrieval research for document search systems.
1955
statistical sensitivity and specificity formalized in medical diagnostic testing literature.
1979
van Rijsbergen's Information Retrieval popularizes the F-measure as a weighted harmonic combination of precision and recall.
1990
s: precision, recall, and F1 become standard reporting metrics in text classification and early machine learning benchmarks.
2000
s–present: F1 and macro/micro averaging become default evaluation practice across NLP, computer vision, and imbalanced-data machine learning tasks.

Where this matters

Search engines report precision and recall internally when tuning ranking algorithms, since users care both about not missing relevant results (recall) and not being shown irrelevant ones (precision). Spam and fraud detection systems tune thresholds around precision, since false positives (blocking legitimate mail or transactions) carry direct user cost. Medical screening and diagnostic AI systems are evaluated primarily on recall (sensitivity), since a missed disease case can be fatal, even at the cost of more false alarms requiring follow-up testing. Named-entity recognition, object detection, and other structured prediction tasks in NLP and computer vision report F1 as the standard leaderboard metric, since it balances both the tendency to over-detect and under-detect. Every major machine learning library (scikit-learn's `classification_report`, TensorFlow's metrics module, PyTorch's `torchmetrics`) implements exactly the counting and averaging logic described here, making this one of the most universally deployed pieces of evaluation infrastructure in applied machine learning.

Ask the tutor: How does changing the decision threshold move a model along a precision-recall curve, and how does that connect to computing ROC-AUC?

Why is accuracy considered misleading for imbalanced datasets?

Accuracy weights every example equally, so if 95% of examples belong to one class, a model can achieve 95% accuracy by always predicting that class — without ever correctly identifying a minority-class example. The metric doesn't distinguish between errors on the majority class and errors on the minority class, even though the minority class (fraud, disease, defects) is often the one that matters most. Precision and recall, computed with respect to a specific class of interest, expose this failure directly because they isolate performance on that class rather than blending it into an overall average.

When should I prioritize precision over recall, and vice versa?

Prioritize precision when false positives carry high cost — for example, wrongly flagging a legitimate transaction as fraud and blocking a customer, or over-diagnosing a healthy patient and triggering unnecessary invasive treatment. Prioritize recall when false negatives carry high cost — missing an actual tumor, missing a security intrusion, or missing a safety-critical defect. Many real systems weigh these costs explicitly using an F-beta score, where beta greater than 1 favors recall and beta less than 1 favors precision, rather than assuming they are equally important as F1 does.

What does it mean for precision or recall to be "undefined," and how should it be handled?

Precision is undefined when the model makes zero positive predictions (division by zero in TP/(TP+FP)), and recall is undefined when there are zero actual positive examples in the evaluation set. Most libraries adopt a convention of returning 0 in these cases and issuing a warning, since a model that predicts nothing positive has, practically speaking, achieved zero useful precision. It's important to check for and explicitly handle this edge case rather than let it silently propagate as a NaN into downstream averaging or model comparison.

How do macro, micro, and weighted averaging differ in multi-class classification?

Macro-averaging computes the metric independently for each class and takes an unweighted mean, treating rare and common classes as equally important — useful for exposing poor performance on minority classes. Micro-averaging pools raw TP, FP, FN counts across all classes before computing one global precision/recall, which for single-label problems equals overall accuracy. Weighted-averaging computes per-class metrics but weights each by class frequency (support), giving a summary closer to what a majority-class-dominated accuracy would show, while still respecting per-class computation.

Is F1 always the right single metric to report?

No. F1 assumes precision and recall are equally important, which is a strong and often false assumption. If a business problem clearly values recall more (e.g., disease screening) or precision more (e.g., spam filtering), the F-beta score generalizes F1 by weighting recall beta times more than precision, and should be preferred. Additionally, in multi-class problems, F1 requires an averaging choice (macro, micro, weighted), so "the F1 score" is ambiguous unless that choice is stated explicitly alongside the number.

How do these metrics relate to the confusion matrix and ROC curves?

The confusion matrix is the raw tabulation of TP, FP, FN, TN counts from which accuracy, precision, recall, and F1 are all derived arithmetically — it is the single source object underlying every metric in this article. ROC curves extend this idea by plotting true positive rate (recall) against false positive rate across every possible decision threshold, rather than fixing one threshold as precision/recall/F1 do. This makes ROC-AUC a threshold-independent summary of ranking quality, complementary to the threshold-specific metrics covered here.

Why does training error not tell us anything about these metrics in deployment?

A model can achieve near-perfect precision, recall, and F1 on the data it was trained on simply by memorizing patterns specific to that dataset, a phenomenon tied directly to the gap between Training Error vs Test Error. These metrics are only meaningful as estimates of real-world performance when computed on a held-out test set the model never saw during training or Model Selection. Reporting training-set precision/recall is a common and serious evaluation error, since it systematically overstates how well the model will perform on new data.

Before this

Where this leads