anomaly-detection / README.md
zeetay's picture
add favicon
1cd4171 verified
|
Raw
History Blame Contribute Delete
4.53 kB
metadata
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

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

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

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

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.