An end-to-end MLOps demonstrator: a FastAPI service serving transaction-risk scores from models trained on the Synthetic Credit Card Fraud (Interpretable 1M) dataset — 1M rows, 27 features, ~1.9% fraud rate, CC0-licensed.
Built to be reviewed the way a hiring manager would review it: contract-first API, calibrated probabilities, explicit threshold economics, reproducible packaging, and a pipeline that verifies all of it on every push.
What’s inside
| Piece | What It Does |
|---|---|
| Versioned API | /v1/risk-score (stable 8-field contract) and /v2/risk-score (all 27 features) behind one FastAPI app |
| Calibrated model | HistGradientBoosting + Platt sigmoid: Brier 0.130 → 0.017, ECE 0.278 → 0.001, ROC-AUC 0.83 preserved |
| Cost-based thresholds | Operating point chosen by minimizing expected cost (FN weighted 20× FP), with a high-recall alternative preserved side by side |
| Provenance | Dataset SHA-256 recorded in model metadata and exposed via /v1/model-info |
| Drift reporting | Evidently comparison of earliest vs latest data windows |
| CI/CD | GitHub Actions: lint, train, 11 tests, container smoke test; releases push a Trivy-scanned image to GHCR |
The honest numbers
At the default operating point: test recall 40.6%, precision 15.3% (~6.5 alerts per confirmed fraud). The high-recall alternative (81.3% recall, 4.6% precision) ships in the same report — same calibrated probabilities, one config value to switch. The data is synthetic and the system targets a review queue, not approve/decline automation; the model card says so plainly.
What it demonstrates
- Contract-first API design that survived a dataset swap without breaking clients
- Probability calibration as a product decision, with the trade-offs written down
- Artifact/runtime version pinning (sklearn 1.8.0) after a real unpickling failure
- Docker images that train deterministic models at build time, verified by pulling the published image anonymously
- Serving hardening: request IDs, structured JSON logs that never contain payloads, per-client rate limiting