Ahmed Doghri Logo Image
Ahmed Doghri

driftfilter

Deployment data moves while frozen classifiers stand still. driftfilter updates class prototypes forward-only and recovers 22.1 points of accuracy.

driftfilter reproduced benchmark result

The World Moved. The Classifier Did Not

Most deployment drift is not one dramatic domain jump. It arrives a little at a time until yesterday's decision boundary is quietly wrong.

driftfilter streams two classes through a gradual temporal shift. A frozen nearest-prototype classifier and an adaptive one see the exact same unlabeled sequence.

Adapt Forward, Without Backprop

The filtered model assigns each observation, then updates the winning prototype with a conservative exponential step. No labels, batches, gradients, or architecture changes are required.

That makes the tradeoff easy to inspect: adaptation can follow a moving population, but bad pseudo-labels can also move the wrong prototype. The test keeps both visible.

Update: that "keeps both visible" turned out to be more literal than intended — the filter only tracks position, and it falls behind at faster drift. Fixed with a velocity-tracking version. Full writeup below.

The Number

The frozen classifier finishes at 77.9% accuracy. Filtered prototypes reach 100%, a 22.1-point recovery under seeded gradual drift.

This is a two-dimensional stress test, not a universal TTA result. The complete stream reproduces in one command.

A Perfect Score At One Drift Speed Is Not A General Result

The filter only tracks each prototype's position, nudging it 8% toward each new point with a plain exponential moving average. That's comfortably fast enough at the published drift rate, which is why filtered accuracy hits a seed-invariant 100% — a genuine result, not a tautology, but one characterized at exactly one point on a curve.

Push the same linear drift about 4.4x faster, still smooth and equally plausible as a real deployment scenario, and the plain-EMA filter falls behind: mean accuracy drops to 68.6% across 40 tuning seeds and a disjoint 30-seed holdout evaluated once, with some seeds as low as 61.7% — barely ahead of the frozen baseline it's supposed to beat by 22 points.

The fix adds a velocity state: it predicts each prototype's position forward before comparing against a new point, then updates both position and velocity from the observed displacement — the same idea a real Kalman filter uses, and closer to what the cited research actually does. At that same faster drift rate it holds ~100% mean accuracy on both sweeps, and it still reproduces the original 22.1-point number exactly at the published rate. One more honest result from testing this further: for classes drifting toward each other instead of together, neither version offers much advantage over frozen, since the frozen decision boundary already handles that symmetric case reasonably well.

Research Basis

Inspired by TMLR's 2026 STAD paper. The portfolio number above comes from this repository's own controlled benchmark, not from the paper.

Tools Used

Python
Test-Time Adaptation
Distribution Shift
Online Learning
Prototype Models
Kalman Filtering
unittest