💳 South German Credit — Cost-Aware Risk & a Fairness Audit
💳 South German Credit — Riesgo Sensible al Costo y Auditoría de Equidad
A CRISP-ML(Q) pipeline on the South German Credit dataset (UCI). Headline finding:
changing only the decision threshold (cost-aware, ratio 5:1) recall jumped
33%→82% and total cost dropped 47% — same model, no retraining.
And a subgroup fairness audit revealed an equalized-odds gap of 0.62
and a disparate impact of 0.41 by personal_status_sex.
Pipeline CRISP-ML(Q) sobre el dataset South German Credit (UCI). Hallazgo principal:
cambiando solo el umbral de decisión (cost-aware, ratio 5:1) el recall
subió 33%→82% y el costo bajó 47% — mismo modelo, sin
reentrenar. Y una auditoría de equidad por subgrupo reveló un
equalized-odds gap de 0.62 y un disparate impact de 0.41
por personal_status_sex.
🎯 Business Understanding
The task: flag bad-credit applicants before approving a loan. This is a classification problem with an asymmetric cost structure built into the dataset documentation: approving a bad customer costs ~5× more than rejecting a good one (ratio 5:1). Detecting the minority class (bad) is what matters — not overall accuracy.
Standard accuracy-maximizing models systematically fail here: when ~70% of applicants are good, a model that says "approve everyone" already gets 70% accuracy. The real question is not "how often is the model right?" but "how much does it cost when it is wrong, and which direction?"
Project goals:
- Expose the accuracy trap on an imbalanced dataset (the baseline looks fine until you check recall).
- Benchmark resampling strategies systematically, choosing by stability, not raw metric.
- Apply cost-aware threshold tuning and quantify the business-cost improvement.
- Run a fairness audit by sensitive group (
personal_status_sex) and surface the disparities honestly. - Propose Responsible AI remediation actions as mandatory gates, not afterthoughts.
La tarea: identificar solicitantes de mal crédito antes de aprobar un préstamo. Es un problema de clasificación con una estructura de costos asimétrica documentada en el dataset: aprobar a un mal cliente cuesta ~5× más que rechazar a uno bueno (ratio 5:1). Detectar la clase minoritaria (malos) es lo que importa — no la accuracy global.
Los modelos que maximizan accuracy fracasan aquí de forma sistemática: cuando ~70% de los solicitantes son buenos, un modelo que dice "aprobar a todos" ya obtiene 70% de accuracy. La pregunta real no es "¿con qué frecuencia acierta el modelo?" sino "¿cuánto cuesta cuando se equivoca, y en qué dirección?"
Objetivos del proyecto:
- Exponer la trampa de la accuracy en un dataset desbalanceado (el baseline parece bien hasta que se chequea el recall).
- Comparar estrategias de resampling de forma sistemática, eligiendo por estabilidad, no por métrica cruda.
- Aplicar threshold tuning sensible al costo y cuantificar la mejora en costo de negocio.
- Correr una auditoría de equidad por grupo sensible (
personal_status_sex) y reportar las disparidades con honestidad. - Proponer acciones de Responsible AI como gates obligatorios, no como ideas tardías.
credit_risk (0 = goodbueno, 1 = badmalo)📊 Data Understanding
The South German Credit dataset (UCI) contains 1,000 loan applicants from a German bank, described by 20 features covering checking account status, credit history, loan purpose, savings, employment, personal status, and more. The class distribution is ~70% good / 30% bad (2.33:1).
The accuracy trap: without resampling, a Random Forest achieves accuracy 0.71 but recall 0.08 — it detects only 8% of bad applicants. The accuracy looks fine because the majority class is "good." That model is effectively useless for the actual business purpose.
Sensitive attribute: the feature personal_status_sex encodes
gender and marital status jointly. This is the variable used for the fairness audit in
Phase 5. Its presence in the dataset — from credit decisions made in 1990s Germany —
creates a direct pathway for historical lending bias to be learned and amplified.
El dataset South German Credit (UCI) contiene 1,000 solicitantes de préstamos de un banco alemán, descritos por 20 features que cubren estado de cuenta corriente, historial crediticio, propósito del préstamo, ahorros, empleo, estado personal y más. La distribución de clases es ~70% bueno / 30% malo (2.33:1).
La trampa de la accuracy: sin resampling, un Random Forest alcanza accuracy 0.71 pero recall 0.08 — detecta solo el 8% de los malos solicitantes. La accuracy parece bien porque la clase mayoritaria es "bueno". Ese modelo es efectivamente inútil para el propósito real del negocio.
Atributo sensible: la feature personal_status_sex codifica
género y estado civil de forma conjunta. Es la variable usada en la auditoría de equidad
de la Fase 5. Su presencia en el dataset — proveniente de decisiones crediticias de la
Alemania de los 90 — crea un canal directo para que el sesgo histórico de préstamos sea
aprendido y amplificado.
| Characteristic | Característica | Value | Valor |
|---|---|---|---|
| Total applicantsTotal solicitantes | 1,000 | ||
| FeaturesFeatures | 20 | ||
| Class imbalanceDesbalance de clases | ~70% good / 30% bad (2.33:1) | ||
| Baseline RF accuracy (no resampling)Accuracy RF baseline (sin resampling) | 0.71 | ||
| Baseline RF recall bad class (no resampling)Recall clase mala RF baseline (sin resampling) | 0.08 — detects 8% of bad applicants | ||
| Sensitive attributeAtributo sensible | personal_status_sex |
||
| Cost structure (per dataset docs)Estructura de costos (según docs del dataset) | FN costs 5× FP (ratio 5:1)FN cuesta 5× FP (ratio 5:1) |
🔧 Data Preparation — Resampling Benchmark
The core data preparation challenge: handling the 2.33:1 class imbalance without overfitting. Four resampling strategies were benchmarked with XGBoost (GridSearch-tuned, 15-fold repeated cross-validation). The selection criterion was stability — lowest train/test gap — not best raw recall.
El desafío central de preparación: manejar el desbalance 2.33:1 sin sobreajuste. Cuatro estrategias de resampling fueron comparadas con XGBoost (tuneado por GridSearch, 15 folds de CV repetido). El criterio de selección fue la estabilidad — menor gap train/test — no la mejor métrica cruda.
| Resampler | Resampler | Accuracy | Recall | F1 |
|---|---|---|---|---|
| None | 0.74 | 0.50 | 0.54 | |
| RandomUnder | 0.67 | 0.70 | 0.56 | |
| SMOTE | 0.73 | 0.49 | 0.52 | |
| KMeansSMOTE | NaN | NaN | NaN | |
| SMOTETomek (selected)(seleccionado) | 0.74 | 0.48 | 0.53 |
Three key findings from the resampling benchmark
-
KMeansSMOTE failed silently. It returned NaN metrics without raising
an exception. Root cause: minority clusters with n≈850, k_neighbors=5 — clusters too
small for the default configuration. Fixed with
k_neighbors=3,cluster_balance_threshold=0.1. Silent failures in resampling pipelines are a real production hazard. - RandomUnder won on raw recall (0.70) but discards ~60% of training data. With only 1,000 rows, throwing away 600 is brutal — the model trains on a fragment of already-limited data.
- SMOTETomek won by stability. It had the smallest train/test gap (<3 points), indicating the model generalizes rather than memorizing the resampled distribution. That's the correct criterion for a small dataset.
Tres hallazgos clave del benchmark de resampling
-
KMeansSMOTE falló en silencio. Devolvía métricas NaN sin lanzar
ninguna excepción. Causa raíz: clusters minoritarios con n≈850, k_neighbors=5 —
clusters demasiado pequeños para la configuración por defecto. Se corrigió con
k_neighbors=3,cluster_balance_threshold=0.1. Los fallos silenciosos en pipelines de resampling son un riesgo real en producción. - RandomUnder ganó en recall crudo (0.70) pero descarta ~60% de los datos de entrenamiento. Con solo 1,000 filas, descartar 600 es brutal — el modelo entrena sobre un fragmento de datos ya limitados.
- SMOTETomek ganó por estabilidad. Tuvo el menor gap train/test (<3 puntos), indicando que el modelo generaliza en lugar de memorizar la distribución resampleada. Ese es el criterio correcto para un dataset pequeño.
🧠 Modeling
Final model: XGBoost tuned by GridSearch (256 hyperparameter combinations × 15-fold repeated cross-validation) combined with SMOTETomek resampling. The search space covered learning rate, max depth, subsample, colsample_bytree, and regularization parameters.
At the default threshold of 0.5, the tuned model achieves recall 0.33 — only 33% of bad applicants flagged. That is the starting point for the evaluation phase, where threshold tuning delivers the real result.
Modelo final: XGBoost tuneado por GridSearch (256 combinaciones de hiperparámetros × 15 folds de CV repetido) combinado con resampling SMOTETomek. El espacio de búsqueda cubrió learning rate, max depth, subsample, colsample_bytree y parámetros de regularización.
Con el umbral por defecto de 0.5, el modelo tuneado alcanza recall 0.33 — solo el 33% de los malos solicitantes detectados. Ese es el punto de partida para la fase de evaluación, donde el threshold tuning entrega el resultado real.
✅ Evaluation
Threshold tuning — the most important result
At threshold 0.5, the model has recall 0.33: 30 false negatives out of 45 bad
applicants. Minimizing the business cost function cost = 5×FN + 1×FP
yields the optimal threshold at 0.235.
Threshold tuning — el resultado más importante
Con umbral 0.5, el modelo tiene recall 0.33: 30 falsos negativos de 45 malos
solicitantes. Minimizando la función de costo de negocio
costo = 5×FN + 1×FP se obtiene el umbral óptimo en 0.235.
| Threshold | Umbral | Recall | FN | FP | Cost (5×FN + FP) | Costo (5×FN + FP) |
|---|---|---|---|---|---|---|
| 0.5 (default)(defecto) | 0.33 | 30 | 8 | 158 | ||
| 0.235 (cost-optimal)(costo-óptimo) | 0.82 | 8 | 44 | 84 (−47%) |
At the cost-optimal threshold: accuracy 0.65, ROC AUC 0.77, F1 0.59, precision 0.46. Precision drops from 0.65 to 0.46 — that trade-off is correct. More good applicants are rejected (FP goes 8→44), but 22 fewer bad applicants slip through (FN goes 30→8). The business cost drops 47% with the same model, simply by choosing the right threshold.
Con el umbral costo-óptimo: accuracy 0.65, ROC AUC 0.77, F1 0.59, precision 0.46. La precision baja de 0.65 a 0.46 — ese trade-off es correcto. Más buenos solicitantes son rechazados (FP sube 8→44), pero 22 malos menos pasan desapercibidos (FN baja 30→8). El costo de negocio baja 47% con el mismo modelo, simplemente eligiendo el umbral correcto.
Fairness audit — subgroup analysis by personal_status_sex
TPR (true positive rate = recall for bad class) by subgroup:
Auditoría de equidad — análisis por subgrupo en personal_status_sex
TPR (tasa de verdaderos positivos = recall para la clase mala) por subgrupo:
| Subgroup | Subgrupo | TPR | Notes | Notas |
|---|---|---|---|---|
| Women | Mujeres | 0.71 | highest TPR · n=42 in testmayor TPR · n=42 en test | |
| Married men | Hombres casados | 0.56 | ||
| Single men | Hombres solteros | 0.38 | lowest TPR · n=84 in testmenor TPR · n=84 en test | |
| Equalized-odds gapEqualized-odds gap | 0.62 | TPR women − TPR single menTPR mujeres − TPR hombres solteros | ||
| Disparate impactDisparate impact | 0.41 | EEOC 80% rule threshold is 0.80 — we are at halfRegla del 80% EEOC: umbral 0.80 — estamos en la mitad | ||
Three readings of the fairness gap — all can be true simultaneously
- (a) Historical bias in the data. The dataset reflects credit approval decisions from 1990s Germany. If lenders systematically treated subgroups differently at origination, the model learns and amplifies those patterns. The training signal is already contaminated.
- (b) Small subgroups make metrics noisy. n=84 single men vs n=42 women in the test set. With subgroups this small, a difference of 3–4 individuals flips the metric. The gap is real enough to flag — but not stable enough to act on without more data.
-
(c) Feature proxy:
telephone.telephoneranks #2 in permutation importance. In 1990s Germany, having a registered phone at home correlated with income, stable employment, and fixed residence — all socioeconomic proxies that correlate with both creditworthiness and subgroup membership. The model uses a "neutral" feature as a proxy for the sensitive attribute.
Tres lecturas del gap de equidad — todas pueden ser ciertas simultáneamente
- (a) Sesgo histórico en los datos. El dataset refleja decisiones de aprobación crediticia de la Alemania de los 90. Si los prestamistas trataron a los subgrupos de forma diferente en la originación, el modelo aprende y amplifica esos patrones. La señal de entrenamiento ya está contaminada.
- (b) Los subgrupos pequeños hacen las métricas ruidosas. n=84 hombres solteros vs n=42 mujeres en el test set. Con subgrupos tan pequeños, una diferencia de 3–4 individuos voltea la métrica. El gap es real suficiente para señalarlo — pero no lo suficientemente estable para actuar sin más datos.
-
(c) Feature proxy:
telephone.telephoneaparece #2 en permutation importance. En la Alemania de los 90, tener un teléfono registrado en casa correlacionaba con ingresos, empleo estable y residencia fija — todos proxies socioeconómicos que correlacionan tanto con la solvencia como con la pertenencia a un subgrupo. El modelo usa una feature "neutral" como proxy del atributo sensible.
⚠️ Honest caveats: dataset of only 1,000 applicants; subgroups in the test set are small and noisy (n=42–84); the data reflects 1990s Germany lending decisions, not a modern credit portfolio. The fairness numbers are real enough to warrant investigation — but small enough that they should be replicated on a larger, more recent dataset before drawing operational conclusions.
⚠️ Caveats honestos: dataset de solo 1,000 solicitantes; los subgrupos en el test set son pequeños y ruidosos (n=42–84); los datos reflejan decisiones de préstamos de la Alemania de los 90, no un portafolio crediticio moderno. Los números de equidad son reales como para merecer investigación — pero pequeños como para que deban replicarse en un dataset más grande y reciente antes de sacar conclusiones operativas.
🚀 Deployment
🖥️ Credit Risk Predictor — Live DemoPredictor de Riesgo Crediticio — Demo en Vivo
● LiveThe model is deployed as a live Streamlit app — a real-time credit-risk predictor you can try above. The full pipeline, notebook, and app code are at github.com/oscarinho/south-german-credit.
Responsible AI actions — mandatory gates, not afterthoughts
-
Remove
telephoneand socioeconomic proxies.telephone(#2 in permutation importance) acts as a proxy for income, employment stability, and residence — all features that correlate with subgroup membership. Remove it and re-run the audit. -
Apply
fairlearn.postprocessing.ThresholdOptimizerto equalize TPR across subgroups. This post-processing approach adjusts group-specific thresholds to satisfy equalized-odds constraints without retraining the underlying model. - Re-run the fairness audit on every retrain as a mandatory gate. Fairness is not a one-time check at development time. It must be measured in each deployment cycle — if the gap exceeds a defined threshold, the model should not go to production.
El modelo está desplegado como app Streamlit en vivo — un predictor de riesgo crediticio en tiempo real que puedes probar arriba. El pipeline completo, notebook y código están en github.com/oscarinho/south-german-credit.
Acciones de Responsible AI — gates obligatorios, no ideas tardías
-
Remover
telephoney proxies socioeconómicos.telephone(#2 en permutation importance) actúa como proxy de ingresos, estabilidad laboral y residencia — todas features que correlacionan con la pertenencia a un subgrupo. Removerla y volver a correr la auditoría. -
Aplicar
fairlearn.postprocessing.ThresholdOptimizerpara igualar el TPR entre subgrupos. Este enfoque de post-procesamiento ajusta umbrales específicos por grupo para satisfacer restricciones de equalized-odds sin reentrenar el modelo subyacente. - Re-correr la auditoría de equidad en cada retraining como gate obligatorio. La equidad no es un chequeo de una sola vez en el desarrollo. Debe medirse en cada ciclo de despliegue — si el gap supera un umbral definido, el modelo no debe ir a producción.
Business Impact
Impacto de Negocio
The headline result: a 47% reduction in total business cost — with the same model, no retraining — by moving the decision threshold from 0.5 to 0.235. Recall for bad applicants goes from 33% to 82%. The most valuable model is not the one with the best AUC — it is the one that documents its limits and applies cost-aware decision boundaries.
El resultado principal: una reducción del 47% en el costo de negocio total — con el mismo modelo, sin reentrenar — moviendo el umbral de decisión de 0.5 a 0.235. El recall para malos solicitantes pasa del 33% al 82%. El modelo más valioso no es el de mejor AUC — es el que documenta sus límites y aplica fronteras de decisión sensibles al costo.
- Cost-aware threshold (5:1 ratio): FN drops from 30 to 8. Each avoided false negative saves the equivalent of 5 false positives in the bank's cost model. Total cost: 158 → 84 (−47%).
- Fairness audit as value signal: a disparate impact of 0.41 is not just an ethical problem — it is a legal exposure and a signal that the model is using proxies that a regulator will flag. Auditing it before deployment is cheaper than defending it afterward.
- The silent-failure debug (KMeansSMOTE): demonstrates that production ML pipelines need output validation, not just code that runs without errors. A model returning NaN metrics without exception is a real production failure mode.
- Umbral sensible al costo (ratio 5:1): los FN bajan de 30 a 8. Cada falso negativo evitado ahorra el equivalente a 5 falsos positivos en el modelo de costos del banco. Costo total: 158 → 84 (−47%).
- La auditoría de equidad como señal de valor: un disparate impact de 0.41 no es solo un problema ético — es una exposición legal y una señal de que el modelo usa proxies que un regulador va a marcar. Auditarlo antes del despliegue es más barato que defenderlo después.
- El debug de fallo silencioso (KMeansSMOTE): demuestra que los pipelines de ML en producción necesitan validación de outputs, no solo código que corre sin errores. Un modelo que devuelve métricas NaN sin excepción es un modo de fallo real en producción.
Sources
Fuentes
Quantitative claims use only metrics computed on the South German Credit dataset with the pipeline described above.
Las afirmaciones cuantitativas usan únicamente métricas calculadas sobre el dataset South German Credit con el pipeline descrito arriba.
Dataset
Dataset
UCI South German Credit Dataset. 1,000 loan applicants from a German bank. 20 features. Cost matrix (5:1 FN:FP) documented in the dataset description.
CRISP-ML(Q) methodology
Metodología CRISP-ML(Q)
Studer S. et al. (2020). Towards CRISP-ML(Q). arXiv:2003.05155
SMOTE
SMOTE
Chawla N. V. et al. (2002). SMOTE: Synthetic Minority Over-sampling Technique. Journal of Artificial Intelligence Research, 16, 321–357.
Disparate impact / EEOC 80% rule
Disparate impact / Regla del 80% EEOC
U.S. Equal Employment Opportunity Commission (EEOC). Uniform Guidelines on Employee Selection Procedures (1978). The 80% (four-fifths) rule: a selection rate for any group less than 4/5 of the highest rate constitutes evidence of adverse impact.
fairlearn
fairlearn.org — open-source toolkit for assessing and mitigating fairness issues in machine learning models. fairlearn.postprocessing.ThresholdOptimizer used for equalized-odds post-processing.