02 — Case study
CardioML
A heart-risk model that has to explain itself.

The problem
A risk score on its own is useless. If a model says High, the next question is always why. So I picked a model I could explain and added SHAP, so every prediction lists what pushed it up and what held it down.
This is an academic project. It is not a diagnostic tool and it is not medical advice.
What it does
Eight inputs, checked twice
The form validates in the browser and the server validates again against medical limits.
The reasons, in plain words
SHAP values become risk factors, protective factors and recommendations a non-specialist can read.
History
Every assessment is saved per user and shown in a sortable table.
Admin view
Risk distribution and predictions over time, behind a separate admin login.
How it works
The React app posts the form to FastAPI. The backend validates the numbers, scales them, runs the model, computes SHAP values and turns them into readable text before anything is saved to MongoDB.
- 01React appAssess · History · Admin
- 02FastAPIValidate → scale → model → SHAP → explain
- 03MongoDBusers · predictions · model logs
The model is a logistic regression trained on 5,500 clinical records, saved together with its scaler.
Engineering decisions
- 01
A model I can defend
I tried several approaches and kept logistic regression. I can explain it end to end, and for health data that matters more than a point of accuracy.
- 02
Validation lives outside the model
A separate, tested module rejects impossible values, so the model never sees a blood pressure that cannot exist.
- 03
Explanations are a feature, not a chart
A dedicated layer converts SHAP output into sentences. That layer is where most of the product value is.
- 04
Admin is its own surface
A separate login route and admin-only endpoints keep analytics away from ordinary users.
Stack
- React 19
- Vite
- Tailwind CSS
- FastAPI
- Motor (MongoDB)
- scikit-learn
- SHAP
- Pydantic
- JWT
- pytest
Worth knowing
Educational project. Not a diagnostic tool.