--- title: Real-Time Anomaly Detection emoji: 📡 colorFrom: blue colorTo: indigo sdk: docker app_port: 7860 pinned: false --- # Real-Time Anomaly Detection on Time-Series Streams **Live demo: [anomaly.zeetay.dev](https://anomaly.zeetay.dev)** Online anomaly detection on a multivariate sensor stream with concept drift handling. Each observation is scored once as it arrives. No batch retraining. ## Problem Sensor distributions shift over time. A static model will either miss anomalies in the new regime or start flagging normal post-drift readings as anomalous. This project fits a baseline during a stable warmup window, then scores every subsequent observation against it while monitoring for distribution shifts. ## Approach - **Synthetic stream**: Three-phase generator (normal baseline, gradual drift, injected anomalies) with configurable rates and ground truth labels. - **Online scorer**: Mahalanobis distance from a frozen Phase A baseline. Uses the full sensor covariance matrix, so it catches contextual anomalies (anti-correlated sensor readings) that per-feature z-score methods miss entirely. - **Drift detection**: ADWIN watches a normalized composite sensor signal. When the distribution shifts (Phase B), ADWIN fires. The anomaly baseline is kept since it was calibrated before drift started. - **Running metrics**: Precision, recall, and F1 are updated after every observation against ground truth labels. - **Real-time UI**: FastAPI + WebSocket streams each scored observation to a dashboard with rolling charts, anomaly/drift markers, and live counters. New visitors get a replay of the last 200 points. ## Results (offline benchmark) Evaluated on Phase C (300 obs, 10% anomaly rate, seed=42): | Method | Precision | Recall | F1 | Mode | |--------|-----------|--------|-----|------| | Mahalanobis + ADWIN | 0.833 | 1.000 | **0.909** | online | | Isolation Forest | 0.476 | 1.000 | 0.645 | batch | | Z-Score (max \|z\|>2.5) | 0.450 | 0.600 | 0.514 | batch | Z-Score's recall is limited by contextual anomalies where no single sensor exceeds the 2.5σ threshold. Mahalanobis catches these via the covariance term. ## Tech stack - **Python 3.10+** - **River** - online drift detection (ADWIN) - **numpy / scikit-learn** - Mahalanobis scoring, Isolation Forest baseline - **FastAPI** - REST + WebSocket, static dashboard - **asyncio** - async stream and pipeline - **pytest** - tests for stream, detector, pipeline, and server ## Setup and run ```bash python -m venv .venv .venv\Scripts\activate # Windows # source .venv/bin/activate # Linux/macOS pip install -r requirements.txt python run.py ``` Open **http://localhost:8000/**. The pipeline starts on first connection and loops automatically. ## Deploy - **Frontend**: [anomaly.zeetay.dev](https://anomaly.zeetay.dev) — Netlify, auto-deploys from `main` - **Backend**: [huggingface.co/spaces/zeetay/anomaly-detection](https://huggingface.co/spaces/zeetay/anomaly-detection) — HF Spaces (Docker) The frontend is static files served by Netlify. The backend is a FastAPI + WebSocket server running in a Docker container on HF Spaces. `dashboard.js` connects directly to the HF Space WebSocket from any non-HF host. **Docker locally:** `docker build -t anomaly . && docker run -p 7860:7860 anomaly` ## Tests ```bash pytest ``` All tests use the synthetic stream and need no external services. ## Project layout | Path | Role | |------|------| | `src/stream/` | Observation model, async synthetic generator (phases A/B/C) | | `src/detector/` | Anomaly scorer (Mahalanobis), drift detector (ADWIN), running metrics | | `src/pipeline/` | Async pipeline: stream, score, broadcast, replay buffer | | `src/server/` | FastAPI app, WebSocket, `/stats`, `/metrics` (Prometheus), static dashboard | | `tests/` | Pytest for stream, detector, pipeline, dashboard | ## Design notes - **Mahalanobis over Z-Score**: Z-Score tests each sensor independently. Mahalanobis uses the covariance matrix, so readings that look moderate in isolation but are unusual as a combination (e.g., temp up + pressure down when they normally move together) score high. - **ADWIN on sensor signal, not anomaly scores**: Watching the normalized sensor deviation catches sustained distribution shifts without false-firing on isolated anomaly spikes. - **Replay on connect**: New clients get the last 200 messages immediately, then live updates. - **Start on first connect**: The pipeline only runs while at least one client is connected. Counters reset each cycle.