Model Drift Detection
Models freeze the past while the world keeps moving. Drift detection compares live traffic to training data on a schedule, so a shifted input or a changed relationship raises an alert before users pay for it.
Why Does This Exist?
A recommendation model trained on summer buying keeps serving in December. No exception fires, latency stays flat, and revenue slides for weeks before anyone asks whether the model still matches the world. The model did not break. The world moved: incomes shifted with inflation, tastes changed, a sensor aged. A frozen function of a moving world decays by default.
Drift detection is the scheduled comparison that catches that move. A job pulls recent inference rows, compares their distributions and the prediction mix against the training baseline, and pages when the distance passes a threshold. It answers one question nightly: should we retrain today. Without it, the first detector is a customer complaint.
This page covers the comparison itself: data drift, concept drift, and the alerting loop. Testing gates before deploy are ML testing strategies. Watching nulls and schema live is data quality monitoring. Owning the whole serving picture is model monitoring. When an alert fires, ML metadata names the exact training snapshot to compare against.
Basics first: call the input distribution , which means how often each feature value appears. Call the relationship , which means the right answer given those features. Drift moves one or both, and the two moves need different responses.
Think of It Like This
The river survey and the old chart
A shipping company charts a river: deep channel here, sandbar there. Ships run fine for months. Storms shift the bed, but the chart on the bridge never changes. Captains who trust it run aground on a bar that moved 200 meters east.
Drift detection sends a scout boat daily to sound the channel and overlay the readings on the old chart. A small wiggle earns a note. A moved bar earns an alarm and a re-survey, which is retraining. The chart is the model, the river is live traffic, and the scout is the scheduled job.
Where it stops: soundings show the bar moved, not whether ships can still pass. Distance alone never proves harm, which is why alerts route to accuracy checks before they route to retraining.
How It Actually Works
Two drift kinds share one pipeline. Data drift moves while the relationship holds: average income rises, but income still predicts risk the same way. Concept drift moves itself: the same income now means different risk because behavior changed. Prediction drift watches the output mix as a proxy when true labels arrive weeks late.
Distance with PSI over shared bins
Bin one feature the same way in training and live traffic, then compare shares. Call expected share , which means the training fraction in bin . Call actual share , which means the live fraction in the same bin. The Population Stability Index is , summed over bins. Teams commonly treat below 0.10 as noise, 0.10 to 0.25 as review, and above 0.25 as a major shift. Thresholds differ by team, but the arithmetic is shared.
The nightly loop with four steps
Pull the last window of inference rows with inputs and predictions. Compute distances against the saved baseline on guarded features plus the prediction mix. Compare each score to its threshold, with seasonality handled by comparing against the same season last year or a rolling window. On breach, page with the top drifting feature and route to accuracy checks, then to retraining when harm is confirmed.
Worked example
One income feature uses three bins. Training shares over 1,000 rows are 0.30, 0.50, and 0.20, which means counts of 300, 500, and 200 since . Live shares over 500 rows are 0.20, 0.45, and 0.35, which means counts of 100, 225, and 175.
Per-bin contributions are , plus , plus . The sum is , reported as PSI 0.13. The team review line sits at 0.10, and 0.13 clears it, so the job pages with the third bin named as the driver.
Code
The point is the distance decision with stdlib only: same shares as the worked example, same 0.13, same threshold check.
import math
expected = [0.30, 0.50, 0.20]actual = [0.20, 0.45, 0.35]
parts = [(a - e) * math.log(a / e) for a, e in zip(actual, expected)]psi = sum(parts)print([round(p, 4) for p in parts]) # -> [0.0405, 0.0053, 0.0839]print(round(psi, 4)) # -> 0.1298print(round(psi, 2) > 0.10) # -> True
train_counts = [int(e * 1000) for e in expected]live_counts = [int(a * 500) for a in actual]print(train_counts, live_counts) # -> [300, 500, 200] [100, 225, 175]Three contributions, one sum at 0.1298, and a boolean that pages. That boolean is the whole nightly job: distance, threshold, alert.
Watch Out For
Paging on seasons instead of shifts
A January baseline compared against July traffic screams drift every summer: swimwear rises, heating falls, and the PSI clears 0.10 on schedule. The symptom is a quarterly false alarm the team learns to ignore, followed by a real shift ignored with it.
Compare against the same season last year or a rolling 14-day window, and pin at least one baseline per season. Seasonality is a calendar effect, not a model failure, and the baseline must say which one it holds.
Retraining on distance without harm
A weakly predictive feature swings hard and the PSI hits 0.20 while accuracy never moves. The symptom is expensive retrains that change nothing, because distance in alone never proves the relationship broke.
Route every alert through an accuracy or proxy check first: recent labels, human review, or prediction-mix movement on guarded slices. Retrain on confirmed harm, not on movement alone.
The Quick Version
- Drift detection compares live traffic to the training baseline on a schedule and pages past a threshold.
- Data drift moves inputs , concept drift moves the relationship , and prediction drift proxies the second.
- PSI sums over shared bins, with 0.10 as a common review line.
- Shares of 0.30, 0.50, 0.20 against 0.20, 0.45, 0.35 give 0.0405 plus 0.0053 plus 0.0839 for PSI 0.13.
- Seasonality needs same-season or rolling baselines, or every summer pages falsely.
- Distance without accuracy harm wastes retrains, so confirm before rebuilding.
- Fired alerts resolve through lineage, which names the exact training snapshot to beat.