Dense-Armor vs a Kalman filter¶
A comparison between Dense-Armor and a Kalman filter. They solve different problems: the Kalman filter needs a model of how the system moves, while Dense-Armor's tools check each reading against its recent neighbours and a reference. Neither is always better.
Dense-Armor does not replace a Kalman filter. They solve different problems: the Kalman filter reconstructs a signal through a known dynamic model, the shields reconstruct a signal through local coherence and a reference. Neither is strictly better; they lose in different regimes.
1. The two settings¶
A Kalman filter knows the dynamics of the system: how the state evolves, how the sensors see it, how much noise each has. Given that model, it produces the minimum-variance estimate — the best possible, in the statistical sense.
Dense-Armor knows nothing about the dynamics. It knows the shape the signal is supposed to have: smooth in time, coherent in space, close to a reference. It builds a robust blind estimate from that shape alone.
The two answers agree when the dynamics are linear and the noise is Gaussian. They diverge the moment the signal carries a spike, a NaN, or a missing sample that the Kalman model has not been told about.
2. A measurement¶
A random walk, 15% missing samples, 3% huge spikes. Both filters see
the same corrupted stream; the mean squared error against the clean signal:
| method | MSE |
|---|---|
| no protection | ~21000 |
| Kalman, no anti-outlier gating (the common case) | ~7300 |
| Kalman, with anti-outlier gating and known dynamics | ~0.12 |
| Dense-Armor, blind mode | ~0.23 |
import numpy as np
from dense_armor.utility.anomaly.mahalanobis import OnlineRobustMahalanobis
rng = np.random.default_rng(0)
est = OnlineRobustMahalanobis()
for _ in range(150):
est.learn_one({"x": float(1.0 + 0.05 * rng.normal())})
print(round(float(est.score_one({"x": 1e6})), 2))
A single spike throws off the Kalman gain — the filter trusts the
measurement and drags the state behind itself. Dense-Armor scores the
spike as astronomically far from the centre it has learned (25 million
against a training stream around 1.0), which a downstream detector
flags as an anomaly instead of feeding it into the reconstruction. In
blind mode it does not know the true dynamics, so it cannot beat the
correctly configured Kalman filter (0.23 vs 0.12 MSE) — but it does
not need to be configured either.
3. When to use which¶
Use a Kalman filter when:
- you know the dynamics (a robot model, a physics simulation)
- the noise is Gaussian, or close
- you can spend the time to calibrate
QandR - you want the minimum-variance estimate for that specific model
Use Dense-Armor when:
- you do not know the dynamics (a new sensor, an LLM embedding, an image)
- the signal carries spikes, NaN, missing data, adversarial perturbations
- you need something that works as-is on any tensor, temporal or not
- you want the same interface (
learn_one,transform_one) as the rest of the library
The two compose: a Kalman filter for the clean stream, a shield in front of it for the corrupted one.
Details¶
References¶
The comparison is measured by test/experiments/testKalman.py and the
benchmarks/anomaly_streaming.py script on the same random walk.
The Kalman formulation is Kalman, R. E. (1960). A new approach to linear
filtering and prediction problems. Journal of Basic Engineering, 82(1),
35-45. The robust blind estimator is OnlineRobustMahalanobis of the anomaly
section, see anomaly/mahalanobis.md.
Choices stated explicitly¶
- The comparison is on a random walk, not on a robot joint. The point is to have a signal where the Kalman model is exactly known, so the comparison is favorable to the Kalman filter. On a robot joint the dynamic model is itself an approximation.
- The Kalman "no gating" case is the common case. Most Kalman deployments in practice do not add anti-outlier gating; the number ~7300 is the one a user is likely to see without extra work.
- "Blind mode" means no reference. With
reference=historical_baselinethe shield can also track slow drift, see drift/detectors.md.
Measured cost¶
OnlineRobustMahalanobis runs in < 1 ms per sample (budget_s = 1e-3 s),
memory O(n^2) with n the number of features. The Kalman filter in the
comparison is a standard Kalman filter from a general-purpose filtering library, no JIT.
Development diary¶
The comparison was added to the README after a user asked whether Dense-Armor is "a Kalman replacement". It is not: it is a shield, it works without a process model, it does not give the minimum-variance estimate. The table is kept honest by showing the case where a well-calibrated Kalman wins (0.12 vs 0.23), not only the case where Dense-Armor wins.