File size: 7,115 Bytes
1e3caac
2e552e9
 
 
1e3caac
 
2e552e9
 
 
 
1e3caac
 
2e552e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c966ef1
2e552e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
---
title: PneumoOps
emoji: 🫁
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: true
license: mit
short_description: MLOps A/B testing & drift monitoring
---

# 🫁 PneumoOps

**Continuous MLOps Pipeline with A/B Testing & Data Drift Monitoring for 14-Class Thoracic Disease Detection**

[![CI](https://github.com/Prakhar54-byte/PneumoOps/actions/workflows/deploy.yml/badge.svg)](https://github.com/Prakhar54-byte/PneumoOps/actions)
[![Python 3.11](https://img.shields.io/badge/python-3.11-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](#)

---

## What This Is

PneumoOps is a production-style MLOps system for **multi-label chest X-ray classification**. It demonstrates real-world deployment challenges:

- **A/B Testing** β€” every inference request is randomly routed to either *Model A (PyTorch)* or *Model B (ONNX)*, letting you measure real-world latency differences between serving backends.
- **Data Drift Monitoring** β€” statistical pixel-distribution comparison (KS-test) against the training baseline. When distribution shifts, the system flags `DRIFT_DETECTED` β€” the trigger for automated retraining in production.
- **Prometheus Observability** β€” request counters, latency histograms, per-disease prediction rates, and drift alert counters are all scraped at `/metrics`.
- **Dockerized Deployment** β€” the entire stack runs in containers, deployable to Hugging Face Spaces via a single `git push`.

---

## Architecture

```
         Train (ChestMNIST + MobileNetV3-small)
                    β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    Model A (.pth)       Model B (.onnx)
    PyTorch serving      ONNX Runtime serving
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚  FastAPI Backend       β”‚
        β”‚  β”œβ”€ A/B Router (60/40) β”‚
        β”‚  β”œβ”€ Drift Monitor (KS) β”‚
        β”‚  β”œβ”€ Prometheus /metricsβ”‚
        β”‚  └─ /health /history   β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚   Gradio UI         β”‚
          β”‚  Top-3 predictions  β”‚
          β”‚  Model arm used     β”‚
          β”‚  Latency (ms)       β”‚
          β”‚  Drift alert badge  β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## Dataset & Model

| Property | Value |
|---|---|
| Dataset | [ChestMNIST](https://medmnist.com/) β€” 14-class multi-label chest X-ray |
| Classes | Atelectasis, Cardiomegaly, Effusion, Infiltration, Mass, Nodule, Pneumonia, Pneumothorax, Consolidation, Edema, Emphysema, Fibrosis, Pleural Thickening, Hernia |
| Model A | MobileNetV3-small (PyTorch `.pth`) |
| Model B | MobileNetV3-small (ONNX Runtime `.onnx`) |
| Training | 5 epochs, AdamW, BCEWithLogitsLoss, per-class threshold tuning |
| Macro AUROC | 0.686 (5-epoch, 5k samples β€” improves with full dataset) |

---

## Project Structure

```
pneumo_ops/
β”œβ”€β”€ backend/
β”‚   └── main.py              # FastAPI: A/B routing, drift monitor, Prometheus
β”œβ”€β”€ frontend/
β”‚   └── app.py               # Gradio UI: top-3 chart, drift badge, latency
β”œβ”€β”€ scripts/
β”‚   └── train_chestmnist.py  # Training: ChestMNIST β†’ MobileNetV3 β†’ ONNX export
β”œβ”€β”€ models/
β”‚   └── chestmnist_mobilenetv3/
β”‚       β”œβ”€β”€ mobilenetv3_chestmnist.pth    # Model A (PyTorch)
β”‚       β”œβ”€β”€ mobilenetv3_chestmnist.onnx   # Model B (ONNX)
β”‚       β”œβ”€β”€ training_metrics.json
β”‚       └── baseline_stats.json           # Pixel stats for drift reference
β”œβ”€β”€ model_utils.py           # CalibratedModel + temperature scaling util
β”œβ”€β”€ Dockerfile               # Single-container build
β”œβ”€β”€ docker-compose.yml       # backend + frontend services
β”œβ”€β”€ requirements.txt
└── .github/workflows/
    └── deploy.yml           # CI (lint/import check) + HF Spaces deploy
```

---

## Quick Start

### 1. Install

```bash
git clone https://github.com/Prakhar54-byte/PneumoOps
cd pneumo_ops
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
```

### 2. Train the model

```bash
# Quick run (5k samples, ~1 min on GPU)
python3 scripts/train_chestmnist.py --epochs 5 --batch-size 32 --max-train-samples 5000

# Full dataset
python3 scripts/train_chestmnist.py --epochs 15 --batch-size 64
```

Outputs saved to `models/chestmnist_mobilenetv3/`:
- `mobilenetv3_chestmnist.pth` β€” PyTorch checkpoint
- `mobilenetv3_chestmnist.onnx` β€” ONNX export
- `training_metrics.json` β€” AUROC, AUPRC, F1, thresholds
- `baseline_stats.json` β€” pixel reference for drift detection

### 3. Run the backend

```bash
PNEUMOOPS_PROFILE=chestmnist python3 -m uvicorn backend.main:app --port 7860
```

Key endpoints:

| Endpoint | Description |
|---|---|
| `POST /predict` | Run inference (A/B routed) |
| `GET /health` | System status + model metadata |
| `GET /metrics` | Prometheus scrape endpoint |
| `GET /history` | Last 20 requests |
| `GET /metrics/class-rates` | Per-class prediction rates |
| `GET /metrics/calibration` | AUROC / AUPRC / Brier per class |

### 4. Run the UI

```bash
BACKEND_PREDICT_URL=http://127.0.0.1:7860/predict python3 frontend/app.py

```

### 5. Docker (full stack)

```bash
docker compose up --build
# Backend β†’ http://localhost:7860
# Frontend β†’ http://localhost:7861
```

---

## Deployment β€” Hugging Face Spaces

### Manual push

```bash
# Add HF remote
git remote add space https://huggingface.co/spaces/Prakhar54-byte/PneumoOps

# Push (Spaces will build the Docker image automatically)
git push space main
```

### Automated (GitHub Actions)

Set these repository secrets on GitHub:

| Secret | Description |
|---|---|
| `HF_TOKEN` | Hugging Face access token (write permission) |
| `HF_SPACE_REPO` | e.g. `your-username/pneumoops` |
| `HF_MODEL_REPO` | *(optional)* e.g. `your-username/pneumoops-models` |

Every push to `main` triggers CI checks then deploys to your Space automatically.

---

## Real-World MLOps Challenges Addressed

| Challenge | Solution |
|---|---|
| Model degradation over time | Drift Monitor (KS-test on pixel distribution) |
| Serving latency variance | A/B routing between PyTorch and ONNX, latency tracked per arm |
| Class imbalance (rare diseases) | Per-class threshold tuning on val set + AUPRC tracking |
| Missed diagnoses | Per-class recall monitored at `/metrics/class-rates` |
| Production observability | Prometheus metrics β€” latency histograms, per-disease counters, drift alerts |
| Automated retraining signals | `DRIFT_DETECTED` flag logged + exposed via Prometheus counter |

---

## Libraries

`PyTorch` Β· `ONNX Runtime` Β· `FastAPI` Β· `Gradio` Β· `Docker` Β· `Hugging Face Hub/Spaces` Β· `scikit-learn` Β· `Prometheus` Β· `MedMNIST` Β· `SciPy`

---

## License

MIT