vigil-drift 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,369 @@
1
+ Metadata-Version: 2.4
2
+ Name: vigil-drift
3
+ Version: 0.1.0
4
+ Summary: Vigil β€” unsupervised concept drift detection for network streams with feature-level attribution. Based on arXiv:2605.29834.
5
+ Author: Venkateswara Sahu
6
+ License: MIT
7
+ Project-URL: Homepage, https://venkateswara-sahu.github.io/OWADD/
8
+ Project-URL: Repository, https://github.com/Venkateswara-Sahu/OWADD
9
+ Project-URL: Documentation, https://venkateswara-sahu.github.io/OWADD/
10
+ Project-URL: Bug Tracker, https://github.com/Venkateswara-Sahu/OWADD/issues
11
+ Keywords: concept-drift,data-streams,autoencoder,novelty-detection,mlops,unsupervised
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: torch>=2.0.0
23
+ Requires-Dist: numpy>=1.24.0
24
+ Requires-Dist: scipy>=1.10.0
25
+ Requires-Dist: scikit-learn>=1.2.0
26
+ Requires-Dist: pandas>=2.0.0
27
+ Provides-Extra: api
28
+ Requires-Dist: fastapi>=0.100.0; extra == "api"
29
+ Requires-Dist: uvicorn>=0.22.0; extra == "api"
30
+ Requires-Dist: pydantic>=2.0.0; extra == "api"
31
+ Provides-Extra: dashboard
32
+ Requires-Dist: streamlit>=1.25.0; extra == "dashboard"
33
+ Requires-Dist: plotly>=5.15.0; extra == "dashboard"
34
+ Provides-Extra: mlflow
35
+ Requires-Dist: mlflow>=2.5.0; extra == "mlflow"
36
+ Provides-Extra: kafka
37
+ Requires-Dist: confluent-kafka>=2.2.0; extra == "kafka"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
41
+ Requires-Dist: black>=23.0.0; extra == "dev"
42
+ Requires-Dist: ruff>=0.0.285; extra == "dev"
43
+ Requires-Dist: jupyter>=1.0.0; extra == "dev"
44
+ Provides-Extra: all
45
+ Requires-Dist: vigil-drift[api,dashboard,dev,kafka,mlflow]; extra == "all"
46
+
47
+ <div align="center">
48
+
49
+ <img src="https://img.shields.io/badge/Python-3.11-blue?style=flat-square&logo=python&logoColor=white"/>
50
+ <img src="https://img.shields.io/badge/PyTorch-2.x-EE4C2C?style=flat-square&logo=pytorch&logoColor=white"/>
51
+ <img src="https://img.shields.io/github/actions/workflow/status/Venkateswara-Sahu/OWADD/ci.yml?branch=main&style=flat-square&label=CI&logo=github"/>
52
+ <img src="https://img.shields.io/badge/License-MIT-green?style=flat-square"/>
53
+ <img src="https://img.shields.io/badge/arXiv-2605.29834-b31b1b?style=flat-square&logo=arxiv"/>
54
+
55
+ <br/><br/>
56
+
57
+ # πŸ›‘οΈ Vigil
58
+
59
+ ### Production-ready unsupervised concept drift detection for tabular data streams
60
+
61
+ **No labels. No manual thresholds. Knows when your data changes β€” and tells you exactly which features changed.**
62
+
63
+ [Quick Start](#-quick-start) Β· [Dashboard](#-live-dashboard) Β· [API Docs](#-rest-api) Β· [Architecture](#-architecture) Β· [Paper](#-research-basis)
64
+
65
+ </div>
66
+
67
+ ---
68
+
69
+ ## The Problem
70
+
71
+ 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.
72
+
73
+ Most drift detectors either require labels (which you never have in real-time) or only tell you *that* drift happened, not *what* drifted.
74
+
75
+ **OWADD Sentinel solves both.**
76
+
77
+ ---
78
+
79
+ ## What It Does
80
+
81
+ | Capability | How |
82
+ |---|---|
83
+ | πŸ” **Detect concept drift** | Replicated T-Test on autoencoder reconstruction errors β€” no labels needed |
84
+ | πŸ†• **Identify novel classes** | KDE density estimation on a frozen mirror autoencoder (A_KC) |
85
+ | πŸ“Š **Explain the drift** | Feature-level attribution: ranks which input features caused the drift |
86
+ | ⚑ **Serve at scale** | FastAPI REST service with `/detect` endpoint |
87
+ | πŸ“ˆ **Track experiments** | MLflow logs every chunk's drift severity, novelty rate, and attribution report |
88
+ | πŸ–₯️ **Visualize live** | SOC-style Streamlit dashboard with real-time charts |
89
+
90
+ ---
91
+
92
+ ## Architecture
93
+
94
+ ```
95
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
96
+ β”‚ OWADD Sentinel System β”‚
97
+ β”‚ β”‚
98
+ Data Stream β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
99
+ ─────────────► β”‚ β”‚ Chunk │───►│ Autoencoder A β”‚ β”‚
100
+ (CSV / API / β”‚ β”‚ (200 rowsβ”‚ β”‚ (adapts on drift) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
101
+ Kafka topic) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ │────►│ MLflow β”‚
102
+ β”‚ β”‚ β”‚ errors_A β”‚ β”‚ Tracking β”‚
103
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
104
+ β”‚ β”‚ β”‚ Replicated T-Test β”‚ β”‚
105
+ β”‚ β”‚ β”‚ (15 tests, Ξ±=0.05) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
106
+ β”‚ β”‚ β”‚ Buffer size: 1000 β”‚ │────►│FastAPI β”‚
107
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ /detect β”‚
108
+ β”‚ β”‚ β”‚ DRIFT? β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
109
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
110
+ β”‚ β”‚ β”‚ DriftAttributor β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
111
+ β”‚ β”‚ β”‚ (novel contribution) β”‚ │────►│Streamlit β”‚
112
+ β”‚ β”‚ β”‚ Which features drifted? β”‚ β”‚ β”‚Dashboard β”‚
113
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
114
+ β”‚ β”‚ β”‚
115
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
116
+ β”‚ └────────►│ Autoencoder A_KC β”‚ β”‚
117
+ β”‚ β”‚ (frozen mirror) β”‚ β”‚
118
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
119
+ β”‚ β”‚ errors_AKC β”‚
120
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
121
+ β”‚ β”‚ KDE Novelty Detector β”‚ β”‚
122
+ β”‚ β”‚ Novel class proportion β”‚ β”‚
123
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
124
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
125
+ ```
126
+
127
+ **Key design decisions (from arXiv:2605.29834):**
128
+ - Uses 1-D reconstruction error proxy instead of full feature vectors β†’ memory complexity O(buffer_size) not O(n Γ— d)
129
+ - Dual autoencoders: **A** adapts on drift; **A_KC** stays frozen to distinguish drift from novelty
130
+ - Replicated T-tests (r=15) reduce variance vs. a single test
131
+
132
+ **Novel contribution (OWADD Sentinel, not in the paper):**
133
+ - `DriftAttributor` computes per-feature reconstruction error delta β†’ ranks features by drift contribution β†’ makes alerts *actionable*
134
+
135
+ ---
136
+
137
+ ## πŸ“¦ Quick Start
138
+
139
+ ### Install
140
+
141
+ ```bash
142
+ git clone https://github.com/Venkateswara-Sahu/OWADD.git
143
+ cd OWADD
144
+ python -m venv .venv
145
+ .venv\Scripts\activate # Windows
146
+ # source .venv/bin/activate # Linux/Mac
147
+ pip install -e ".[dev]"
148
+ ```
149
+
150
+ ### Run as a Python library
151
+
152
+ ```python
153
+ import pandas as pd
154
+ from owadd_sentinel import OWADDSentinel
155
+
156
+ # Load your data
157
+ df = pd.read_csv("your_data.csv")
158
+ feature_cols = [c for c in df.columns if c != "label"]
159
+ X = df[feature_cols].values.astype("float32")
160
+
161
+ # Initialise
162
+ sentinel = OWADDSentinel(
163
+ feature_names=feature_cols,
164
+ top_k_features=5,
165
+ )
166
+
167
+ # Phase 1: train on first batch (offline)
168
+ sentinel.fit(X[:200])
169
+
170
+ # Phase 2: process stream chunk by chunk
171
+ chunk_size = 200
172
+ for i in range(200, len(X), chunk_size):
173
+ result = sentinel.detect(X[i:i+chunk_size])
174
+
175
+ print(result)
176
+ # SentinelResult(chunk=3, status=⚠️ DRIFT, severity=0.93, novelty=0.0%)
177
+
178
+ if result.drift_detected:
179
+ for feat in result.attribution.top_features:
180
+ print(f" {feat['feature_name']}: {feat['contribution']:.1%} contribution")
181
+ ```
182
+
183
+ ---
184
+
185
+ ## πŸ–₯️ Live Dashboard
186
+
187
+ SOC-style real-time monitoring of the NSL-KDD network traffic stream:
188
+
189
+ ```bash
190
+ pip install -e ".[dashboard]"
191
+ streamlit run dashboard/app.py
192
+ ```
193
+
194
+ Open **http://localhost:8501**, press **β–Ά Start** and watch:
195
+ - Reconstruction error timeline update chunk by chunk
196
+ - Red ⚠ drift markers appear when the traffic distribution shifts
197
+ - Feature attribution chart reveals which network features drifted
198
+ - Novelty gauge tracks unknown attack class emergence
199
+
200
+ ---
201
+
202
+ ## 🌐 REST API
203
+
204
+ FastAPI microservice for production deployment:
205
+
206
+ ```bash
207
+ pip install -e ".[api]"
208
+ uvicorn api.app:app --host 0.0.0.0 --port 8000 --reload
209
+ ```
210
+
211
+ Open **http://localhost:8000/docs** for interactive Swagger UI.
212
+
213
+ ```bash
214
+ # Train on initial data
215
+ curl -X POST http://localhost:8000/fit \
216
+ -H "Content-Type: application/json" \
217
+ -d '{"data": [[0.1, 0.5, ...], ...]}'
218
+
219
+ # Detect drift in incoming batch
220
+ curl -X POST http://localhost:8000/detect \
221
+ -H "Content-Type: application/json" \
222
+ -d '{"data": [[0.3, 0.9, ...], ...]}'
223
+ ```
224
+
225
+ **Response:**
226
+ ```json
227
+ {
228
+ "drift_detected": true,
229
+ "drift_severity": 0.93,
230
+ "novelty_proportion": 0.0,
231
+ "top_drifted_features": [
232
+ {"feature_name": "diff_srv_rate", "contribution": 0.23, "error_delta": 0.041},
233
+ {"feature_name": "service_other", "contribution": 0.07, "error_delta": 0.012}
234
+ ],
235
+ "message": "⚠️ Concept drift detected (severity=0.93)"
236
+ }
237
+ ```
238
+
239
+ ---
240
+
241
+ ## πŸ“Š MLflow Experiment Tracking
242
+
243
+ ```bash
244
+ pip install -e ".[mlflow]"
245
+
246
+ # Run stream with experiment logging
247
+ python -c "
248
+ from data.nsl_kdd_loader import load_nsl_kdd
249
+ from data.stream_simulator import StreamSimulator
250
+ from owadd_sentinel import OWADDSentinel
251
+ from owadd_sentinel.logging.mlflow_logger import MLflowLogger
252
+
253
+ X, y, feat = load_nsl_kdd()
254
+ sim = StreamSimulator(X, y)
255
+ sentinel = OWADDSentinel(feature_names=feat)
256
+ logger = MLflowLogger(experiment_name='nsl-kdd-stream')
257
+
258
+ first = next(sim.stream(n_chunks=1))
259
+ sentinel.fit(first.X, verbose=False)
260
+
261
+ logger.start_run(params={'buffer_size': 1000, 'drift_threshold': 0.3})
262
+ for chunk in sim.stream(n_chunks=25):
263
+ result = sentinel.detect(chunk.X)
264
+ logger.log_chunk(result, ground_truth_label=chunk.dominant_class)
265
+ logger.end_run()
266
+ "
267
+
268
+ # View results
269
+ mlflow ui # β†’ http://localhost:5000
270
+ ```
271
+
272
+ ---
273
+
274
+ ## πŸ§ͺ Tests
275
+
276
+ ```bash
277
+ pytest tests/ -v --cov=owadd_sentinel
278
+ ```
279
+
280
+ ```
281
+ tests/test_autoencoder.py::test_autoencoder_forward_shape PASSED
282
+ tests/test_autoencoder.py::test_reconstruction_errors_non_negative PASSED
283
+ tests/test_autoencoder.py::test_mirrored_pair_identical_weights PASSED
284
+ tests/test_autoencoder.py::test_training_reduces_loss PASSED
285
+ tests/test_drift_detector.py::test_no_drift_during_warmup PASSED
286
+ tests/test_drift_detector.py::test_no_false_drift_on_stable_stream PASSED
287
+ tests/test_drift_detector.py::test_drift_detected_on_shifted_dist PASSED
288
+ tests/test_sentinel.py::test_sentinel_fit_and_detect PASSED
289
+ tests/test_sentinel.py::test_sentinel_attribution_on_drift PASSED
290
+ ... 14 passed in 15s, 81% coverage
291
+ ```
292
+
293
+ ---
294
+
295
+ ## πŸ—‚οΈ Project Structure
296
+
297
+ ```
298
+ OWADD/
299
+ β”œβ”€β”€ owadd_sentinel/ # Core pip package
300
+ β”‚ β”œβ”€β”€ core/
301
+ β”‚ β”‚ β”œβ”€β”€ autoencoder.py # Dual mirrored autoencoders (A and A_KC)
302
+ β”‚ β”‚ β”œβ”€β”€ drift_detector.py # Replicated T-Test drift detection
303
+ β”‚ β”‚ └── novelty_detector.py # KDE-based novel class recognition
304
+ β”‚ β”œβ”€β”€ logging/
305
+ β”‚ β”‚ └── mlflow_logger.py # MLflow experiment tracking
306
+ β”‚ β”œβ”€β”€ attribution.py # Feature-level drift attribution (novel)
307
+ β”‚ └── sentinel.py # Main OWADDSentinel public API
308
+ β”‚
309
+ β”œβ”€β”€ api/
310
+ β”‚ β”œβ”€β”€ app.py # FastAPI REST service
311
+ β”‚ └── schemas.py # Pydantic request/response models
312
+ β”‚
313
+ β”œβ”€β”€ data/
314
+ β”‚ β”œβ”€β”€ nsl_kdd_loader.py # NSL-KDD download + preprocessing
315
+ β”‚ └── stream_simulator.py # 3-phase non-stationary stream
316
+ β”‚
317
+ β”œβ”€β”€ dashboard/
318
+ β”‚ └── app.py # SOC-style Streamlit dashboard
319
+ β”‚
320
+ β”œβ”€β”€ tests/ # 14 unit + integration tests
321
+ β”œβ”€β”€ .github/workflows/ci.yml # GitHub Actions CI
322
+ └── pyproject.toml # pip install owadd-sentinel
323
+ ```
324
+
325
+ ---
326
+
327
+ ## πŸ”¬ Research Basis
328
+
329
+ This project implements and extends:
330
+
331
+ > **"Open World Autoencoding Drift Detection with Novel Class Recognition in Tabular Non-stationary Data Streams"**
332
+ > arXiv:2605.29834
333
+
334
+ **Extensions in Vigil (not in the paper):**
335
+ - `DriftAttributor`: per-feature reconstruction error delta analysis β€” makes drift detection *actionable* by identifying which features drifted and by how much
336
+ - FastAPI REST service for production deployment
337
+ - MLflow integration for experiment tracking and model versioning
338
+ - SOC-style real-time monitoring dashboard
339
+
340
+ ---
341
+
342
+ ## πŸ› οΈ Tech Stack
343
+
344
+ | Component | Technology |
345
+ |---|---|
346
+ | Core algorithm | Python, PyTorch, SciPy, scikit-learn |
347
+ | REST API | FastAPI, Pydantic, Uvicorn |
348
+ | Experiment tracking | MLflow |
349
+ | Dashboard | Streamlit, Plotly |
350
+ | Dataset | NSL-KDD (Canadian Institute for Cybersecurity) |
351
+ | Testing | pytest, pytest-cov |
352
+ | Linting | Ruff |
353
+ | CI/CD | GitHub Actions |
354
+
355
+ ---
356
+
357
+ ## πŸ“„ License
358
+
359
+ MIT Β© Venkateswara Sahu
360
+
361
+ ---
362
+
363
+ <div align="center">
364
+
365
+ **Built with the belief that ML systems should know when they're wrong.**
366
+
367
+ [⭐ Star this repo](https://github.com/Venkateswara-Sahu/OWADD) if you find it useful
368
+
369
+ </div>
@@ -0,0 +1,323 @@
1
+ <div align="center">
2
+
3
+ <img src="https://img.shields.io/badge/Python-3.11-blue?style=flat-square&logo=python&logoColor=white"/>
4
+ <img src="https://img.shields.io/badge/PyTorch-2.x-EE4C2C?style=flat-square&logo=pytorch&logoColor=white"/>
5
+ <img src="https://img.shields.io/github/actions/workflow/status/Venkateswara-Sahu/OWADD/ci.yml?branch=main&style=flat-square&label=CI&logo=github"/>
6
+ <img src="https://img.shields.io/badge/License-MIT-green?style=flat-square"/>
7
+ <img src="https://img.shields.io/badge/arXiv-2605.29834-b31b1b?style=flat-square&logo=arxiv"/>
8
+
9
+ <br/><br/>
10
+
11
+ # πŸ›‘οΈ Vigil
12
+
13
+ ### Production-ready unsupervised concept drift detection for tabular data streams
14
+
15
+ **No labels. No manual thresholds. Knows when your data changes β€” and tells you exactly which features changed.**
16
+
17
+ [Quick Start](#-quick-start) Β· [Dashboard](#-live-dashboard) Β· [API Docs](#-rest-api) Β· [Architecture](#-architecture) Β· [Paper](#-research-basis)
18
+
19
+ </div>
20
+
21
+ ---
22
+
23
+ ## The Problem
24
+
25
+ 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.
26
+
27
+ Most drift detectors either require labels (which you never have in real-time) or only tell you *that* drift happened, not *what* drifted.
28
+
29
+ **OWADD Sentinel solves both.**
30
+
31
+ ---
32
+
33
+ ## What It Does
34
+
35
+ | Capability | How |
36
+ |---|---|
37
+ | πŸ” **Detect concept drift** | Replicated T-Test on autoencoder reconstruction errors β€” no labels needed |
38
+ | πŸ†• **Identify novel classes** | KDE density estimation on a frozen mirror autoencoder (A_KC) |
39
+ | πŸ“Š **Explain the drift** | Feature-level attribution: ranks which input features caused the drift |
40
+ | ⚑ **Serve at scale** | FastAPI REST service with `/detect` endpoint |
41
+ | πŸ“ˆ **Track experiments** | MLflow logs every chunk's drift severity, novelty rate, and attribution report |
42
+ | πŸ–₯️ **Visualize live** | SOC-style Streamlit dashboard with real-time charts |
43
+
44
+ ---
45
+
46
+ ## Architecture
47
+
48
+ ```
49
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
50
+ β”‚ OWADD Sentinel System β”‚
51
+ β”‚ β”‚
52
+ Data Stream β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
53
+ ─────────────► β”‚ β”‚ Chunk │───►│ Autoencoder A β”‚ β”‚
54
+ (CSV / API / β”‚ β”‚ (200 rowsβ”‚ β”‚ (adapts on drift) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
55
+ Kafka topic) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ │────►│ MLflow β”‚
56
+ β”‚ β”‚ β”‚ errors_A β”‚ β”‚ Tracking β”‚
57
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
58
+ β”‚ β”‚ β”‚ Replicated T-Test β”‚ β”‚
59
+ β”‚ β”‚ β”‚ (15 tests, Ξ±=0.05) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
60
+ β”‚ β”‚ β”‚ Buffer size: 1000 β”‚ │────►│FastAPI β”‚
61
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ /detect β”‚
62
+ β”‚ β”‚ β”‚ DRIFT? β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
63
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
64
+ β”‚ β”‚ β”‚ DriftAttributor β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
65
+ β”‚ β”‚ β”‚ (novel contribution) β”‚ │────►│Streamlit β”‚
66
+ β”‚ β”‚ β”‚ Which features drifted? β”‚ β”‚ β”‚Dashboard β”‚
67
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
68
+ β”‚ β”‚ β”‚
69
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
70
+ β”‚ └────────►│ Autoencoder A_KC β”‚ β”‚
71
+ β”‚ β”‚ (frozen mirror) β”‚ β”‚
72
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
73
+ β”‚ β”‚ errors_AKC β”‚
74
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
75
+ β”‚ β”‚ KDE Novelty Detector β”‚ β”‚
76
+ β”‚ β”‚ Novel class proportion β”‚ β”‚
77
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
78
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
79
+ ```
80
+
81
+ **Key design decisions (from arXiv:2605.29834):**
82
+ - Uses 1-D reconstruction error proxy instead of full feature vectors β†’ memory complexity O(buffer_size) not O(n Γ— d)
83
+ - Dual autoencoders: **A** adapts on drift; **A_KC** stays frozen to distinguish drift from novelty
84
+ - Replicated T-tests (r=15) reduce variance vs. a single test
85
+
86
+ **Novel contribution (OWADD Sentinel, not in the paper):**
87
+ - `DriftAttributor` computes per-feature reconstruction error delta β†’ ranks features by drift contribution β†’ makes alerts *actionable*
88
+
89
+ ---
90
+
91
+ ## πŸ“¦ Quick Start
92
+
93
+ ### Install
94
+
95
+ ```bash
96
+ git clone https://github.com/Venkateswara-Sahu/OWADD.git
97
+ cd OWADD
98
+ python -m venv .venv
99
+ .venv\Scripts\activate # Windows
100
+ # source .venv/bin/activate # Linux/Mac
101
+ pip install -e ".[dev]"
102
+ ```
103
+
104
+ ### Run as a Python library
105
+
106
+ ```python
107
+ import pandas as pd
108
+ from owadd_sentinel import OWADDSentinel
109
+
110
+ # Load your data
111
+ df = pd.read_csv("your_data.csv")
112
+ feature_cols = [c for c in df.columns if c != "label"]
113
+ X = df[feature_cols].values.astype("float32")
114
+
115
+ # Initialise
116
+ sentinel = OWADDSentinel(
117
+ feature_names=feature_cols,
118
+ top_k_features=5,
119
+ )
120
+
121
+ # Phase 1: train on first batch (offline)
122
+ sentinel.fit(X[:200])
123
+
124
+ # Phase 2: process stream chunk by chunk
125
+ chunk_size = 200
126
+ for i in range(200, len(X), chunk_size):
127
+ result = sentinel.detect(X[i:i+chunk_size])
128
+
129
+ print(result)
130
+ # SentinelResult(chunk=3, status=⚠️ DRIFT, severity=0.93, novelty=0.0%)
131
+
132
+ if result.drift_detected:
133
+ for feat in result.attribution.top_features:
134
+ print(f" {feat['feature_name']}: {feat['contribution']:.1%} contribution")
135
+ ```
136
+
137
+ ---
138
+
139
+ ## πŸ–₯️ Live Dashboard
140
+
141
+ SOC-style real-time monitoring of the NSL-KDD network traffic stream:
142
+
143
+ ```bash
144
+ pip install -e ".[dashboard]"
145
+ streamlit run dashboard/app.py
146
+ ```
147
+
148
+ Open **http://localhost:8501**, press **β–Ά Start** and watch:
149
+ - Reconstruction error timeline update chunk by chunk
150
+ - Red ⚠ drift markers appear when the traffic distribution shifts
151
+ - Feature attribution chart reveals which network features drifted
152
+ - Novelty gauge tracks unknown attack class emergence
153
+
154
+ ---
155
+
156
+ ## 🌐 REST API
157
+
158
+ FastAPI microservice for production deployment:
159
+
160
+ ```bash
161
+ pip install -e ".[api]"
162
+ uvicorn api.app:app --host 0.0.0.0 --port 8000 --reload
163
+ ```
164
+
165
+ Open **http://localhost:8000/docs** for interactive Swagger UI.
166
+
167
+ ```bash
168
+ # Train on initial data
169
+ curl -X POST http://localhost:8000/fit \
170
+ -H "Content-Type: application/json" \
171
+ -d '{"data": [[0.1, 0.5, ...], ...]}'
172
+
173
+ # Detect drift in incoming batch
174
+ curl -X POST http://localhost:8000/detect \
175
+ -H "Content-Type: application/json" \
176
+ -d '{"data": [[0.3, 0.9, ...], ...]}'
177
+ ```
178
+
179
+ **Response:**
180
+ ```json
181
+ {
182
+ "drift_detected": true,
183
+ "drift_severity": 0.93,
184
+ "novelty_proportion": 0.0,
185
+ "top_drifted_features": [
186
+ {"feature_name": "diff_srv_rate", "contribution": 0.23, "error_delta": 0.041},
187
+ {"feature_name": "service_other", "contribution": 0.07, "error_delta": 0.012}
188
+ ],
189
+ "message": "⚠️ Concept drift detected (severity=0.93)"
190
+ }
191
+ ```
192
+
193
+ ---
194
+
195
+ ## πŸ“Š MLflow Experiment Tracking
196
+
197
+ ```bash
198
+ pip install -e ".[mlflow]"
199
+
200
+ # Run stream with experiment logging
201
+ python -c "
202
+ from data.nsl_kdd_loader import load_nsl_kdd
203
+ from data.stream_simulator import StreamSimulator
204
+ from owadd_sentinel import OWADDSentinel
205
+ from owadd_sentinel.logging.mlflow_logger import MLflowLogger
206
+
207
+ X, y, feat = load_nsl_kdd()
208
+ sim = StreamSimulator(X, y)
209
+ sentinel = OWADDSentinel(feature_names=feat)
210
+ logger = MLflowLogger(experiment_name='nsl-kdd-stream')
211
+
212
+ first = next(sim.stream(n_chunks=1))
213
+ sentinel.fit(first.X, verbose=False)
214
+
215
+ logger.start_run(params={'buffer_size': 1000, 'drift_threshold': 0.3})
216
+ for chunk in sim.stream(n_chunks=25):
217
+ result = sentinel.detect(chunk.X)
218
+ logger.log_chunk(result, ground_truth_label=chunk.dominant_class)
219
+ logger.end_run()
220
+ "
221
+
222
+ # View results
223
+ mlflow ui # β†’ http://localhost:5000
224
+ ```
225
+
226
+ ---
227
+
228
+ ## πŸ§ͺ Tests
229
+
230
+ ```bash
231
+ pytest tests/ -v --cov=owadd_sentinel
232
+ ```
233
+
234
+ ```
235
+ tests/test_autoencoder.py::test_autoencoder_forward_shape PASSED
236
+ tests/test_autoencoder.py::test_reconstruction_errors_non_negative PASSED
237
+ tests/test_autoencoder.py::test_mirrored_pair_identical_weights PASSED
238
+ tests/test_autoencoder.py::test_training_reduces_loss PASSED
239
+ tests/test_drift_detector.py::test_no_drift_during_warmup PASSED
240
+ tests/test_drift_detector.py::test_no_false_drift_on_stable_stream PASSED
241
+ tests/test_drift_detector.py::test_drift_detected_on_shifted_dist PASSED
242
+ tests/test_sentinel.py::test_sentinel_fit_and_detect PASSED
243
+ tests/test_sentinel.py::test_sentinel_attribution_on_drift PASSED
244
+ ... 14 passed in 15s, 81% coverage
245
+ ```
246
+
247
+ ---
248
+
249
+ ## πŸ—‚οΈ Project Structure
250
+
251
+ ```
252
+ OWADD/
253
+ β”œβ”€β”€ owadd_sentinel/ # Core pip package
254
+ β”‚ β”œβ”€β”€ core/
255
+ β”‚ β”‚ β”œβ”€β”€ autoencoder.py # Dual mirrored autoencoders (A and A_KC)
256
+ β”‚ β”‚ β”œβ”€β”€ drift_detector.py # Replicated T-Test drift detection
257
+ β”‚ β”‚ └── novelty_detector.py # KDE-based novel class recognition
258
+ β”‚ β”œβ”€β”€ logging/
259
+ β”‚ β”‚ └── mlflow_logger.py # MLflow experiment tracking
260
+ β”‚ β”œβ”€β”€ attribution.py # Feature-level drift attribution (novel)
261
+ β”‚ └── sentinel.py # Main OWADDSentinel public API
262
+ β”‚
263
+ β”œβ”€β”€ api/
264
+ β”‚ β”œβ”€β”€ app.py # FastAPI REST service
265
+ β”‚ └── schemas.py # Pydantic request/response models
266
+ β”‚
267
+ β”œβ”€β”€ data/
268
+ β”‚ β”œβ”€β”€ nsl_kdd_loader.py # NSL-KDD download + preprocessing
269
+ β”‚ └── stream_simulator.py # 3-phase non-stationary stream
270
+ β”‚
271
+ β”œβ”€β”€ dashboard/
272
+ β”‚ └── app.py # SOC-style Streamlit dashboard
273
+ β”‚
274
+ β”œβ”€β”€ tests/ # 14 unit + integration tests
275
+ β”œβ”€β”€ .github/workflows/ci.yml # GitHub Actions CI
276
+ └── pyproject.toml # pip install owadd-sentinel
277
+ ```
278
+
279
+ ---
280
+
281
+ ## πŸ”¬ Research Basis
282
+
283
+ This project implements and extends:
284
+
285
+ > **"Open World Autoencoding Drift Detection with Novel Class Recognition in Tabular Non-stationary Data Streams"**
286
+ > arXiv:2605.29834
287
+
288
+ **Extensions in Vigil (not in the paper):**
289
+ - `DriftAttributor`: per-feature reconstruction error delta analysis β€” makes drift detection *actionable* by identifying which features drifted and by how much
290
+ - FastAPI REST service for production deployment
291
+ - MLflow integration for experiment tracking and model versioning
292
+ - SOC-style real-time monitoring dashboard
293
+
294
+ ---
295
+
296
+ ## πŸ› οΈ Tech Stack
297
+
298
+ | Component | Technology |
299
+ |---|---|
300
+ | Core algorithm | Python, PyTorch, SciPy, scikit-learn |
301
+ | REST API | FastAPI, Pydantic, Uvicorn |
302
+ | Experiment tracking | MLflow |
303
+ | Dashboard | Streamlit, Plotly |
304
+ | Dataset | NSL-KDD (Canadian Institute for Cybersecurity) |
305
+ | Testing | pytest, pytest-cov |
306
+ | Linting | Ruff |
307
+ | CI/CD | GitHub Actions |
308
+
309
+ ---
310
+
311
+ ## πŸ“„ License
312
+
313
+ MIT Β© Venkateswara Sahu
314
+
315
+ ---
316
+
317
+ <div align="center">
318
+
319
+ **Built with the belief that ML systems should know when they're wrong.**
320
+
321
+ [⭐ Star this repo](https://github.com/Venkateswara-Sahu/OWADD) if you find it useful
322
+
323
+ </div>