10 Distribution Shift
Every earlier chapter assumed that the target distribution \(p\) stands still and that the only gap between training and deployment is how \(q\) was drawn from it. Deployment does not stand still: users change, sensors drift, prices move. When the world itself moves, the training data describes a distribution that no longer exists. This chapter names the kinds of shift by which part of \(p\) changes, shows which kinds can be detected without labels, and says which of the levers can repair each kind.
10.1 The problem
A model is trained on data from one period and used in the next. Has the distribution changed? If so, in a way that matters to the model, in a way that can be corrected, or only in a way that new labels can reveal?
10.2 Assumptions
- Shift happens between periods, not within them. Data within a period is i.i.d.; gradual drift is approximated by comparing a recent window with a reference window.
- One kind of shift at a time. Real shifts mix kinds, and the taxonomy below describes the pure cases.
- Input-only tests see only inputs. A test that compares the inputs of two periods can detect a change in \(p(x)\) and nothing else. Detecting anything else requires labels.
10.3 Derivation or demonstration
A taxonomy by which factor changes. Moreno-Torres et al. unify the terms used across the literature (Moreno-Torres et al. 2012, Definitions 1–4). Dataset shift is any difference between the joint training and test distributions. Three special cases are distinguished by which factor of the joint changes:
- covariate shift “appears only in X→Y problems”, where inputs cause the label: \(p(y \mid x)\) is unchanged and \(p(x)\) changes;
- prior probability shift “appears only in Y→X problems”, where the label causes the inputs: \(p(x \mid y)\) is unchanged and \(p(y)\) changes;
- concept shift: the conditional changes (\(p(y \mid x)\) in X→Y problems, \(p(x \mid y)\) in Y→X problems) while the other factor does not.
The distinction is causal: which factor stays fixed depends on the direction of cause. It decides the remedy. Under covariate shift, importance weighting by \(p(x)/q(x)\) re-targets the risk (Sampling and Weighting). Under prior shift, the prior-shift correction re-targets the posterior, and EM estimates the new prior from unlabeled data. Under concept shift, no reweighting of old examples can help: the old labels answer a question that is no longer asked.
Detecting shift. A classifier two-sample test “pair[s] the n examples in \(S_P\) with a positive label, and … the m examples in \(S_Q\) with a negative label”; if the two samples come from the same distribution, “the classification accuracy of a binary classifier on a held-out subset … should remain near chance-level” (Lopez-Paz and Oquab 2016, abstract). The testbed’s version fits a logistic regression, on the features and their squares, to half of each sample. It then tests whether the classifier’s scores on the held-out halves separate the two samples, using a one-sided Mann–Whitney U test. That test is valid when there is no shift, because the held-out scores are then exchangeable. Over 400 no-shift seeds its p-values were uniform (tests/test_shift_detection.py::test_classifier_two_sample_test_is_calibrated_under_no_shift). For comparison, a simpler test runs a Kolmogorov–Smirnov test on each feature and corrects for the three tests.
On T1, with 500 examples in each sample and 200 seeds:
- no shift: the two tests rejected at rates 0.025 and 0.030, at a level of 0.05;
- covariate shift: moving the mean of \(z_1\) by 0.1 standard deviations was detected 0.10 of the time by the classifier test; by 0.3 standard deviations, 0.90 and 0.96;
- prior shift from a class share of 0.5 to 0.3 changes \(p(x)\), because \(p(x)\) is a mixture of the classes: detected 0.54 and 0.67 of the time;
- concept shift, the same inputs labeled by a different rule: 0.045 and 0.055, no more than with no shift. A labeled check, the deployed model’s accuracy on 500 labeled new examples, detected the concept shift 0.78 of the time, and fired 0.045 of the time with no shift (
tests/test_shift_detection.py::test_input_tests_detect_covariate_and_prior_shift_not_concept_shift).
A test that compares inputs can detect any change in \(p(x)\): covariate shift, and prior shift whenever the classes differ in their inputs. Its power grows with the size of the shift and the size of the samples. Concept shift leaves \(p(x)\) unchanged, so no input-only test can see it; only labels from the new period can.
| Changes | Evaluation only: compares new inputs with reference inputs (and, with labels, new accuracy with reference accuracy) |
| Assumption | Samples within each period are i.i.d.; for input-only tests, the shift that matters shows up in \(p(x)\) |
| What it does to the learned function | None on the model; an alarm that the training distribution no longer matches deployment |
| Which models care | All |
| Fit on | A reference sample from training time; for the classifier test, half of each sample (the other half is held out) |
| Failure modes | Concept shift leaves the inputs unchanged and is invisible to input-only tests; small shifts need large samples; a detected shift may not hurt the model, and an undetected one may |
| Alternatives | Monitoring the deployed model’s accuracy on a stream of labeled cases; monitoring its predicted-class distribution (prior shift) |
| Checked by | tests/test_shift_detection.py::test_input_tests_detect_covariate_and_prior_shift_not_concept_shift |
Evidence that shift is common. Recht et al. built new test sets for CIFAR-10 and ImageNet following the original collection procedures. The paper reports “accuracy drops of 3% – 15% on CIFAR-10 and 11% – 14% on ImageNet” across a broad range of models. It attributes them not to test-set reuse (“adaptivity”) but to “the models’ inability to generalize to slightly ‘harder’ images than those found in the original test sets” (Recht et al. 2019, abstract). This is one study, of two image benchmarks, as of its 2019 publication. It shows that even a careful replication of a data-generating process produces a shift large enough to matter.
scipy.stats.ks_2samp compares one feature between two samples; with many features, correct for the number of tests. A classifier two-sample test needs only a classifier and a held-out half. Monitor labeled performance wherever labels arrive, however late: input monitoring cannot see concept shift.
10.4 What it does to the learned model
Shift does not change the model. It changes what the model’s fixed decisions are worth. Under covariate shift the model is right where it was right, but it is now asked more often where it saw few examples. Under prior shift its probabilities are miscalibrated by a factor that can be estimated and removed. Under concept shift it is confidently wrong in a way that its inputs do not reveal.
10.5 Failure modes
Figure 10.1 shows the detection results. The failures:
- Watching inputs, missing concepts. A dashboard of feature distributions stays green while the rule linking features to outcomes changes (panel B).
- Small shifts, small samples. At 0.1 standard deviations, 500 examples per window detected the shift in about one seed in ten (panel A).
- Detected but harmless, or undetected but harmful. A test answers whether \(p(x)\) changed, not whether the model got worse. A shift in a feature the model ignores raises an alarm; a shift concentrated where the model is weak may not.
- The wrong correction. Applying a prior-shift correction to a covariate shift, or importance weights to a concept shift, corrects a factor that did not change.
10.6 What the toy cannot show
T1’s shifts are clean: one factor changes, by a known amount, at a known time. Real shifts mix kinds, drift gradually, and arrive with labels that are late, sparse or biased. The demonstrations show what each test can see in principle, not what it will catch in a given monitoring setup.
10.7 Connections
- Sampling and Weighting: the corrections for covariate shift (importance weights) and prior shift (Saerens et al.’s correction and EM).
- The Data-Generating Process: selection on \(x\) and selection on \(y\) are the collection-time versions of covariate and concept-like shift.
- Data Types and Their Structure: time-ordered evaluation, the minimum that a world that drifts requires.
- modern-ai-systems-and-methods, ML Systems and MLOps: drift monitoring in production systems.