ML Systems Lab Open interactive version →
Intermediate 50 min read imbalanceSMOTEclass weightsoversamplingundersampling

Class Imbalance

When 99% of examples are one class, accuracy is a lie — learn the techniques that actually work.

You are building a fraud detector. In your data there are 999 legitimate transactions for every 1 fraud. You train a model, it scores 99.9% accuracy, and your stakeholder is thrilled. You should not be — because a "model" that simply labels *everything* legitimate, learning nothing and looking at no features at all, *also* scores 99.9%. Accuracy is measuring the wrong thing: how often you agree with the majority class, which you can ace by ignoring the rare class entirely.

The rare class is not a flaw in your data — fraud genuinely *is* rare. The trouble is how ordinary training reacts to it. The loss adds up mistakes across all examples, and with 999 legit transactions per fraud, getting the legit ones right dominates the total 999-to-1. The gradient points almost entirely away from fraud, so the model learns to shrug it off. There are three places to fix this, at three points in the pipeline.


Fix 1 — reweight the loss (start here)

The cleanest first move is class weights: tell the loss that a mistake on a fraud example counts as much as roughly 999 mistakes on legit ones (set `class_weight='balanced'`, or `scale_pos_weight` in XGBoost). Now the rare class pulls on the gradient as hard as the common one. No data added or removed — just a reweighted loss.


Fix 2 — manufacture more minority examples (SMOTE), carefully

SMOTE takes a different tack: it invents new fraud examples by interpolating *between* real ones in feature space — pick two nearby frauds and drop a synthetic fraud on the line between them. This gives the model a denser minority region to learn a boundary from. But it has a real failure mode: when fraud and legit heavily *overlap*, interpolating between two frauds can plant a synthetic "fraud" right in the middle of legit territory — a contradictory, misleading training point. So SMOTE is not a default; it shines mainly when the minority class is genuinely sparse (a few hundred examples), and it needs care when the classes mix.


Fix 3 — move the decision threshold

This one does not touch training at all. A classifier outputs a *probability*; turning it into a yes/no needs a threshold, and the default 0.5 is almost never right here. If a missed fraud costs 10,000 and a false alarm costs 50 in review time — a 200:1 asymmetry — you should flag on much weaker suspicion: the cost-minimizing threshold works out to roughly 0.005, not 0.5. The threshold is a *business-cost* decision, not a modeling one: plot precision against recall across thresholds and pick the point that minimises your expected cost.


And above all: stop reporting accuracy

The deepest fix is the metric itself. On imbalanced data, use precision, recall, and PR-AUC, which actually measure how you do on the rare class. Even ROC-AUC can read a flattering 0.97 while the model catches almost no fraud, because the huge pile of true negatives swamps its denominator. Accuracy on an imbalanced problem is not a partial truth — it is actively misleading.


The fuller metric menu

Precision/recall/PR-AUC are the start; know the rest so you can pick the honest single number. Balanced accuracy (average recall across classes) doesn't reward always-predict-majority. MCC (Matthews correlation) uses all four confusion cells and is often the best single summary under imbalance. Macro/micro/weighted F1 average per-class F1 differently: macro treats classes equally, weighted scales by class size, and micro pools every prediction into one global count — which collapses to plain accuracy in binary classification, the exact number this module just told you not to trust. Specificity (TNR) and the FPR/FNR matter when the cost of each error type differs. And when action is capacity-limited — a fraud team reviews the top K — precision@K, recall@K, and lift@K are the right frame, because the model only has to rank the worst cases to the top.


A fair word on ROC-AUC — and how resampling breaks calibration

ROC-AUC isn't *useless* under imbalance — it's a valid ranking metric — but it's misleading because the huge true-negative pile keeps FPR tiny, so it can read 0.97 while precision is terrible; PR-AUC is usually more informative for rare positives. A subtler cost: class weighting and resampling distort probability calibration. Both change the class balance the model trains on, so its predicted probabilities come out too high for the minority class. If you need real probabilities (for cost-based thresholds), recalibrate afterward (Platt or isotonic) and check the reliability curve — the ranking may be fine while the numbers lie.


Validating rare events

With few positives, careless validation is noise. Use stratified splits so every fold holds enough positives (a random split can leave a fold with almost none), prefer repeated cross-validation to average out the high variance of a single split, and put confidence intervals around recall and precision — "recall 0.8" on 20 positives has an enormous interval. For fraud and any time-ordered data, use a temporal split (train on the past, test on the future), because a random split lets the model peek across the fraud timeline.


Sampling alternatives, and losses built for imbalance

Beyond plain class weights and vanilla SMOTE, know the toolkit: random undersampling (drop majority examples — fast, throws away data), random oversampling (duplicate minority — risks overfitting), the SMOTE + cleaning hybrids SMOTE-Tomek and SMOTE-ENN (oversample then remove the confusing points near the boundary), focal loss (down-weights easy majority examples so training focuses on the hard minority — popular in detection), and balanced random forests (each tree trained on a balanced bootstrap). Match the tool to the model and the imbalance severity rather than reaching for SMOTE reflexively.


The cost-sensitive formula, made explicit

"Pick the threshold from costs" has an exact form. Write the per-error costs and choose the threshold that minimises expected cost = FP·cost_FP + FN·cost_FN over the validation set (a true-positive/true-negative usually costs 0). Equivalently, when there's a hard review capacity, set the threshold to fill that queue with the highest-risk cases (precision@K). This turns "which threshold?" from a guess into an optimisation against numbers you can write down.


In production, imbalance keeps moving

Rare-event models need specific monitoring. Track alert volume (a spike means the model or the base rate shifted), precision drift (are the flags still real?), and base-rate drift (the fraud rate itself changes, which silently moves precision even if the model is unchanged). Account for delayed labels (confirmation arrives weeks later) and review capacity, and close the loop by retraining on confirmed cases as they come in. And at extreme imbalance (1:10,000+), stop treating it as one classifier: use a two-stage system — a high-recall candidate generator narrows millions to a manageable pool, then a precision-focused ranker or human-review queue orders that pool. Extreme rarity is a systems-design problem, not a loss-function tweak.

Key points

Takeaway

Accuracy on an imbalanced dataset measures how well the model predicts the majority class — which it can do by ignoring minority examples entirely. The fix starts with the metric, then the loss function, then the decision threshold. Resampling is a last resort, not a default.

Recap

Check your understanding

Q1. Your fraud model achieves 99.2% accuracy and your colleague is satisfied. What would you check?

Q2. Why is applying SMOTE to the full dataset before splitting into train and test sets invalid?

Q3. Compare class weighting and SMOTE for a 50:1 imbalanced tabular dataset. Which TWO of the following are true?

Q4. You lower classification threshold from 0.5 to 0.2 and recall increases 0.6 to 0.9 but precision drops 0.8 to 0.3. Is this an improvement?

Q5. What is the SMOTE failure mode when minority and majority classes heavily overlap in feature space?

Q6. You fix imbalance with class weighting, and the model's ranking (PR-AUC) is excellent, but downstream cost-based thresholding behaves oddly because the predicted probabilities seem systematically too high for the fraud class. What happened, and what do you do?

Q7. You have a 1:50,000 imbalance (a few hundred positives in tens of millions of rows) and single-classifier approaches keep failing. What overall design and validation approach fits?

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →