Haskell Consulting
Philemon

Themis

live

Looks at a loan applicant's payment history and decides whether to approve them, with the cutoff tuned so the bank loses the least money overall rather than just being right most often.

View source on GitHub

Calibrated XGBoost tuned on an asymmetric cost matrix, with SHAP explainability and a Fairlearn fairness audit. Full MLOps stack: MLflow experiment tracking and a Dockerized FastAPI service, deployed and verified end to end.

The demo calls the live FastAPI backend, not a browser model. The free-tier host sleeps when idle, so the first prediction after a quiet spell can take up to a minute.

themis.demo/predict
loading demo…

Results

Tuned on an asymmetric cost matrix where a missed defaulter costs ~14x a wrongly rejected applicant. Every number below is on the held-out test set (5,993 accounts).

−59%
Expected lending cost vs. a naive 0.5 threshold
NT$290K
Saved per 1,000 loans (~US$9.4K), held-out test set
0.773
ROC-AUC, calibrated XGBoost (0.755 baseline)
Live
FastAPI + Docker, verified end to end
ModelROC-AUCCost @ tuned thresholdSaved vs. naive
Logistic regression (baseline)0.755NT$40.7MNT$50.0M
XGBoost (deployed)0.773NT$37.9MNT$54.0M
Expected lending cost across decision thresholds, minimized near 0.065
The whole thesis in one chart: total lending cost bottoms out around a 6.7% threshold, not the default 50%. The dashed line is the closed-form optimum, derived rather than grid-searched.
SHAP global feature importance, PAY_0 dominant
What drives a decision: the most recent month's repayment status (PAY_0) outweighs everything else combined, then credit limit and prior months.
Approval rate by sex at the cost-optimal threshold
Honest fairness audit: a 2.4-point approval gap by sex, traced to a real difference in default rates in the data. Documented, not hidden, including a mitigation attempt that failed.
Calibration curves for logistic regression and XGBoost
Calibrated probabilities: after isotonic calibration, a predicted 15% risk really means ~15% observed default. That is what makes a dollar-cost threshold trustworthy.

Architecture

Data to a served decision: MLflow tracks training runs, the API serves a static exported model, and an Evidently drift report can trigger a retrain.

Architecture flowchart: UCI dataset through cleaning, training with MLflow tracking, calibration, model export, FastAPI serving, and Docker, with an Evidently drift report feeding back into retraining
End to end: raw data to a Dockerized FastAPI endpoint, with MLflow experiment tracking and drift monitoring feeding back into retraining.