Skip to main content



🛡️ Vigil

Production-ready unsupervised concept drift detection for tabular data streams

No labels. No manual thresholds. Knows when your data changes — and tells you exactly which features changed.

Quick Start · Dashboard · API Docs · Architecture · Paper


The Problem

In production ML systems, the data your model was trained on eventually stops looking like the data it receives — a phenomenon called concept drift. For network security, this means attacks evolve. For fraud detection, patterns shift. For predictive maintenance, machinery ages.

Most drift detectors either require labels (which you never have in real-time) or only tell you that drift happened, not what drifted.

OWADD Sentinel solves both.


What It Does

Capability How
🔍 Detect concept drift Replicated T-Test on autoencoder reconstruction errors — no labels needed
🆕 Identify novel classes KDE density estimation on a frozen mirror autoencoder (A_KC)
📊 Explain the drift Feature-level attribution: ranks which input features caused the drift
Serve at scale FastAPI REST service with /detect endpoint
📈 Track experiments MLflow logs every chunk's drift severity, novelty rate, and attribution report
🖥️ Visualize live SOC-style Streamlit dashboard with real-time charts

Architecture

                    ┌─────────────────────────────────────────────────┐
                    │              OWADD Sentinel System               │
                    │                                                  │
  Data Stream       │  ┌──────────┐    ┌─────────────────────────┐   │
 ─────────────►     │  │  Chunk   │───►│    Autoencoder A         │   │
  (CSV / API /      │  │ (200 rows│    │   (adapts on drift)      │   │     ┌──────────┐
   Kafka topic)     │  └──────────┘    └──────────┬──────────────┘   │────►│  MLflow  │
                    │        │                    │ errors_A          │     │ Tracking │
                    │        │         ┌──────────▼──────────────┐   │     └──────────┘
                    │        │         │  Replicated T-Test       │   │
                    │        │         │  (15 tests, α=0.05)      │   │     ┌──────────┐
                    │        │         │  Buffer size: 1000       │   │────►│FastAPI   │
                    │        │         └──────────┬──────────────┘   │     │ /detect  │
                    │        │                    │ DRIFT?            │     └──────────┘
                    │        │         ┌──────────▼──────────────┐   │
                    │        │         │  DriftAttributor         │   │     ┌──────────┐
                    │        │         │  (novel contribution)    │   │────►│Streamlit │
                    │        │         │  Which features drifted? │   │     │Dashboard │
                    │        │         └─────────────────────────┘   │     └──────────┘
                    │        │                                        │
                    │        │         ┌─────────────────────────┐   │
                    │        └────────►│  Autoencoder A_KC        │   │
                    │                 │  (frozen mirror)         │   │
                    │                 └──────────┬──────────────┘   │
                    │                            │ errors_AKC        │
                    │                 ┌──────────▼──────────────┐   │
                    │                 │  KDE Novelty Detector    │   │
                    │                 │  Novel class proportion  │   │
                    │                 └─────────────────────────┘   │
                    └─────────────────────────────────────────────────┘

Key design decisions (from arXiv:2605.29834):

  • Uses 1-D reconstruction error proxy instead of full feature vectors → memory complexity O(buffer_size) not O(n × d)
  • Dual autoencoders: A adapts on drift; A_KC stays frozen to distinguish drift from novelty
  • Replicated T-tests (r=15) reduce variance vs. a single test

Novel contribution (OWADD Sentinel, not in the paper):

  • DriftAttributor computes per-feature reconstruction error delta → ranks features by drift contribution → makes alerts actionable

📦 Quick Start

Install

git clone https://github.com/Venkateswara-Sahu/OWADD.git
cd OWADD
python -m venv .venv
.venv\Scripts\activate        # Windows
# source .venv/bin/activate   # Linux/Mac
pip install -e ".[dev]"

Run as a Python library

import pandas as pd
from owadd_sentinel import OWADDSentinel

# Load your data
df = pd.read_csv("your_data.csv")
feature_cols = [c for c in df.columns if c != "label"]
X = df[feature_cols].values.astype("float32")

# Initialise
sentinel = OWADDSentinel(
    feature_names=feature_cols,
    top_k_features=5,
)

# Phase 1: train on first batch (offline)
sentinel.fit(X[:200])

# Phase 2: process stream chunk by chunk
chunk_size = 200
for i in range(200, len(X), chunk_size):
    result = sentinel.detect(X[i:i+chunk_size])

    print(result)
    # SentinelResult(chunk=3, status=⚠️  DRIFT, severity=0.93, novelty=0.0%)

    if result.drift_detected:
        for feat in result.attribution.top_features:
            print(f"  {feat['feature_name']}: {feat['contribution']:.1%} contribution")

🖥️ Live Dashboard

SOC-style real-time monitoring of the NSL-KDD network traffic stream:

pip install -e ".[dashboard]"
streamlit run dashboard/app.py

Open http://localhost:8501, press ▶ Start and watch:

  • Reconstruction error timeline update chunk by chunk
  • Red ⚠ drift markers appear when the traffic distribution shifts
  • Feature attribution chart reveals which network features drifted
  • Novelty gauge tracks unknown attack class emergence

🌐 REST API

FastAPI microservice for production deployment:

pip install -e ".[api]"
uvicorn api.app:app --host 0.0.0.0 --port 8000 --reload

Open http://localhost:8000/docs for interactive Swagger UI.

# Train on initial data
curl -X POST http://localhost:8000/fit \
  -H "Content-Type: application/json" \
  -d '{"data": [[0.1, 0.5, ...], ...]}'

# Detect drift in incoming batch
curl -X POST http://localhost:8000/detect \
  -H "Content-Type: application/json" \
  -d '{"data": [[0.3, 0.9, ...], ...]}'

Response:

{
  "drift_detected": true,
  "drift_severity": 0.93,
  "novelty_proportion": 0.0,
  "top_drifted_features": [
    {"feature_name": "diff_srv_rate", "contribution": 0.23, "error_delta": 0.041},
    {"feature_name": "service_other", "contribution": 0.07, "error_delta": 0.012}
  ],
  "message": "⚠️ Concept drift detected (severity=0.93)"
}

📊 MLflow Experiment Tracking

pip install -e ".[mlflow]"

# Run stream with experiment logging
python -c "
from data.nsl_kdd_loader import load_nsl_kdd
from data.stream_simulator import StreamSimulator
from owadd_sentinel import OWADDSentinel
from owadd_sentinel.logging.mlflow_logger import MLflowLogger

X, y, feat = load_nsl_kdd()
sim = StreamSimulator(X, y)
sentinel = OWADDSentinel(feature_names=feat)
logger = MLflowLogger(experiment_name='nsl-kdd-stream')

first = next(sim.stream(n_chunks=1))
sentinel.fit(first.X, verbose=False)

logger.start_run(params={'buffer_size': 1000, 'drift_threshold': 0.3})
for chunk in sim.stream(n_chunks=25):
    result = sentinel.detect(chunk.X)
    logger.log_chunk(result, ground_truth_label=chunk.dominant_class)
logger.end_run()
"

# View results
mlflow ui   # → http://localhost:5000

🧪 Tests

pytest tests/ -v --cov=owadd_sentinel
tests/test_autoencoder.py::test_autoencoder_forward_shape          PASSED
tests/test_autoencoder.py::test_reconstruction_errors_non_negative PASSED
tests/test_autoencoder.py::test_mirrored_pair_identical_weights    PASSED
tests/test_autoencoder.py::test_training_reduces_loss              PASSED
tests/test_drift_detector.py::test_no_drift_during_warmup          PASSED
tests/test_drift_detector.py::test_no_false_drift_on_stable_stream PASSED
tests/test_drift_detector.py::test_drift_detected_on_shifted_dist  PASSED
tests/test_sentinel.py::test_sentinel_fit_and_detect               PASSED
tests/test_sentinel.py::test_sentinel_attribution_on_drift         PASSED
... 14 passed in 15s, 81% coverage

🗂️ Project Structure

OWADD/
├── owadd_sentinel/              # Core pip package
│   ├── core/
│   │   ├── autoencoder.py       # Dual mirrored autoencoders (A and A_KC)
│   │   ├── drift_detector.py    # Replicated T-Test drift detection
│   │   └── novelty_detector.py  # KDE-based novel class recognition
│   ├── logging/
│   │   └── mlflow_logger.py     # MLflow experiment tracking
│   ├── attribution.py           # Feature-level drift attribution (novel)
│   └── sentinel.py              # Main OWADDSentinel public API
│
├── api/
│   ├── app.py                   # FastAPI REST service
│   └── schemas.py               # Pydantic request/response models
│
├── data/
│   ├── nsl_kdd_loader.py        # NSL-KDD download + preprocessing
│   └── stream_simulator.py      # 3-phase non-stationary stream
│
├── dashboard/
│   └── app.py                   # SOC-style Streamlit dashboard
│
├── tests/                       # 14 unit + integration tests
├── .github/workflows/ci.yml     # GitHub Actions CI
└── pyproject.toml               # pip install owadd-sentinel

🔬 Research Basis

This project implements and extends:

"Open World Autoencoding Drift Detection with Novel Class Recognition in Tabular Non-stationary Data Streams"
arXiv:2605.29834

Extensions in Vigil (not in the paper):

  • DriftAttributor: per-feature reconstruction error delta analysis — makes drift detection actionable by identifying which features drifted and by how much
  • FastAPI REST service for production deployment
  • MLflow integration for experiment tracking and model versioning
  • SOC-style real-time monitoring dashboard

🛠️ Tech Stack

Component Technology
Core algorithm Python, PyTorch, SciPy, scikit-learn
REST API FastAPI, Pydantic, Uvicorn
Experiment tracking MLflow
Dashboard Streamlit, Plotly
Dataset NSL-KDD (Canadian Institute for Cybersecurity)
Testing pytest, pytest-cov
Linting Ruff
CI/CD GitHub Actions

📄 License

MIT © Venkateswara Sahu


Built with the belief that ML systems should know when they're wrong.

⭐ Star this repo if you find it useful

Release files for vigil-drift 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vigil-drift 0.1.0
File Size Uploaded
vigil_drift-0.1.0.tar.gz 25.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vigil-drift 0.1.0
File Interpreter ABI Platform
vigil_drift-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.6 kB

Release files / vigil_drift-0.1.0.tar.gz

Download URL vigil_drift-0.1.0.tar.gz
Size 25.6 kB
Tags Source
SHA-256 checksum
How to use checksums
0d161780cfccbee38ff7c179011c43dfa226a9801275ceaa4255a185be05bd9a
BLAKE2b-256 checksum
How to use checksums
09a6d5d14adef7a8fba728d1170003b7ae672f9a51a5adc283ed84ed386e5f15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / vigil_drift-0.1.0-py3-none-any.whl

Download URL vigil_drift-0.1.0-py3-none-any.whl
Size 22.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
76984df5013c4f69fc4e002fc32470ed8e235f6bbe8c0aff987642cc92d821a1
BLAKE2b-256 checksum
How to use checksums
d57846f261055a9259fb08881720c742e7ecd3321a8756c84e99355626c612b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page