End-to-end MLOps pipeline for heart disease classification, deployed on GKE with CI/CD, explainability, fairness testing, and drift detection.
- Model: Logistic Regression (baseline)
- Dataset: 14 clinical attributes (age, gender, cp, trestbps, chol, etc.)
- Accuracy: 83.05%
- Training: scikit-learn, MLflow (
http://136.111.167.244:8100/) - API: FastAPI + Uvicorn (Docker)
- Deployment: GKE (
oppe2final, us-central1), HPA max 3 pods - CI/CD: GitHub Actions → GCR → GKE
- Analysed false negatives using SHAP LinearExplainer
cp(chest pain type) had the strongest negative SHAP values — the model is least dependent on low cp values for predicting heart disease
- Sensitive attribute:
age - Demographic Parity Difference: 0.2105 ⚠ (bias detected)
- Equalized Odds Difference: 0.3889
- FastAPI app containerized and pushed to GCR
- Deployed on GKE with HPA (min 1, max 3 pods, CPU threshold 60%)
- CI/CD via GitHub Actions on every push to
main
- 100 random samples run through deployed API
- Each request logged with input, prediction, probability, latency
- Avg latency: ~0.27ms | P95: ~0.34ms
- Tool:
wrk -t10 -c2000 -d60s - 2000 concurrent connections, 60 seconds
- HPA scaled pods during load test
- All 13 features showed significant drift (p < 0.05)
- Synthetic data vs training data compared using Kolmogorov-Smirnov test
├── train.py # Training, explainability, fairness, drift
├── inference.py # 100-sample inference against deployed API
├── app/main.py # FastAPI application
├── Dockerfile # Container definition
├── requirements.txt # Python dependencies
├── k8s-deployment.yaml # GKE Deployment + Service + HPA
├── wrk_script.lua # wrk load test script
└── .github/workflows/ci-cd.yml # GitHub Actions CI/CD pipeline
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/predict |
POST | Single prediction |
/predict_batch |
POST | Batch predictions |
curl -X POST http://<EXTERNAL-IP>/predict \
-H "Content-Type: application/json" \
-d '{"age":55,"gender":1,"cp":2,"trestbps":130.0,"chol":250.0,"fbs":0,"restecg":1,"thalach":150.0,"exang":0,"oldpeak":1.5,"slope":1,"ca":0,"thal":2}'