Project Detail

Credit Risk Scoring API

A production-grade credit risk scoring API trained on the German Credit dataset (1,000 applicants, 20 features). Three classifiers are compared — Logistic Regression, Random Forest, and XGBoost — with every training run tracked including hyperparameters, metrics, and model artefacts. Model selection uses a cost-weighted score that reflects the asymmetric business cost of credit lending: approving a bad loan costs 5x more than rejecting a good one. Logistic Regression wins with a cost-weighted score of 0.75, outperforming XGBoost (0.62) and Random Forest (0.55) despite lower raw accuracy — demonstrating why domain-appropriate metrics matter.

The winning model is served via FastAPI — chosen over Flask for its API-first design, Pydantic input validation, and auto-generated OpenAPI documentation at /docs. Every prediction returns a risk score (0-100), risk band (Low/Medium/High/Very High), business decision (Approve/Review/Decline), and plain-English feature contribution explanations of the factors driving the score — legally relevant in jurisdictions requiring adverse action notices.

The project runs locally via Docker Compose with two services: the scoring API on port 8000 and the experiment tracking server on port 5000. A GitHub Actions CI/CD pipeline runs pytest on every push and automatically retrains and deploys to Azure App Service on every merge to main.

Data machine-learning classification logistic-regression shap explainability mlflow web-app deployment azure docker CI-CD tabular-data feature-engineering benchmarking jupyter REST-API

Quick Facts

Tech:
Python scikit-learn SHAP MLflow FastAPI Uvicorn Docker Docker Compose Pandas NumPy Matplotlib Seaborn Gunicorn Azure App Service GitHub Actions pytest

Overview

Problem

Demonstrating a complete ML API lifecycle — experiment tracking, model selection by business metric, API-first serving with input validation, multi-container local development, and automated CI/CD deployment — in a finance domain where explainability is a legal requirement, not just a nice-to-have.

Solution

Logistic Regression pipeline (ColumnTransformer preprocessor + classifier) selected by cost-weighted score across three tracked training runs. FastAPI serves POST /score and POST /score/batch with Pydantic validation and plain-language feature contribution explanations via risk_explainer.py. Docker Compose runs API + experiment tracking server locally. GitHub Actions retrains the model and deploys to Azure App Service on every push to main.

Challenges

Class imbalance (70/30 good/bad) requires explicit handling via class_weight — accuracy alone is misleading, and the highest-accuracy model (Random Forest at 76%) ranks last on the cost-weighted metric. The cost-weighted evaluation score must be defined and implemented manually as sklearn has no built-in support for asymmetric error costs. FastAPI's ASGI architecture requires gunicorn with UvicornWorker on Azure App Service rather than the standard gunicorn workers used for Flask/Django.

Results / Metrics

Logistic Regression: cost-weighted score 0.75, ROC-AUC 0.80, recall 0.87 on the bad credit class (the high-cost class). Random Forest: cost-weighted 0.55, ROC-AUC 0.79, recall 0.37 — highest accuracy but worst business outcome due to missed defaults. XGBoost: cost-weighted 0.62, ROC-AUC 0.76, recall 0.53. The result illustrates that optimising for accuracy on an imbalanced dataset actively harms the business objective.

Screenshots

Click to enlarge.

Click to enlarge.

Videos