LightGBM — Detección Temprana de Riesgo de Diabetes
Modelo de clasificación binaria para detección temprana de riesgo de diabetes desarrollado como Trabajo de Fin de Máster (TFM) en el Máster en Data Science de KSchool (2025–2026).
Pipeline sklearn completo: preprocesamiento determinista + ColumnTransformer + clasificador LightGBM, entrenado sobre 257.709 observaciones clínicas autorreportadas con 22 variables del cuestionario BRFSS 2015 (CDC).
Repositorio: TFM — GitHub
Demo interactiva: diabetes-risk-demo — HF Spaces
Métricas
Resultados de validación cruzada estratificada (5 folds) sobre el conjunto de entrenamiento:
| Modelo | ROC-AUC (CV media) | PRC-AUC (CV media) | ROC-AUC (CV std) |
|---|---|---|---|
| Random Forest | 0.836 | 0.378 | 0.0018 |
| XGBoost | 0.837 | 0.384 | 0.0022 |
| LightGBM ✓ | 0.839 | 0.391 | 0.0017 |
Resultados sobre el conjunto de test (threshold=0.4733, F2-score óptimo en validación):
| Métrica | Valor |
|---|---|
| ROC-AUC | 0.839 |
| PRC-AUC | 0.391 |
| Recall | 0.813 |
| Precision | 0.256 |
LightGBM se seleccionó como modelo final por ofrecer el mejor equilibrio entre ROC-AUC, PRC-AUC y recall en un contexto clínico desbalanceado. El threshold de 0.4733 maximiza el F2-score en validación (β=2), priorizando el recall sobre la precisión — en cribado clínico un falso negativo tiene un coste sanitario mucho mayor que un falso positivo.
Matriz de confusión (test, threshold=0.4733)
| Predicho: No riesgo | Predicho: Riesgo | |
|---|---|---|
| Real: No riesgo | 32.444 (TN) | 13.420 (FP) |
| Real: Riesgo | 1.063 (FN) | 4.615 (TP) |
Uso
import sys
import types
import numpy as np
import pandas as pd
import joblib
from huggingface_hub import hf_hub_download
# 1. Registrar el módulo src — necesario para deserializar el pipeline
# El pkl fue serializado con src.preprocessing.preprocessing_pipeline
BINARY_VARS = [
"BPMEDS", "BLOODCHO", "HAVARTH3", "QLACTLM2", "USEEQUIP",
"BLIND", "DECIDE", "DIFFWALK", "DIFFALON", "DIFFDRES",
"SMOKE100", "ADDEPEV2", "SEX",
]
CATEGORICAL_NOMINAL = ["BPHIGH4", "_RACE"]
def boosting_deterministic_preproc(X_input):
X = X_input.copy()
X = X.replace(-1, np.nan)
for col in BINARY_VARS:
if col in X.columns:
X[col] = (X[col] == 1).astype(int)
for col in CATEGORICAL_NOMINAL:
if col in X.columns:
X[col] = X[col].astype("category")
return X
def cap_outliers_numeric(X_input, numeric_vars):
X = X_input.copy()
for col in numeric_vars:
low = np.nanpercentile(X[col], 1)
high = np.nanpercentile(X[col], 99)
X[col] = X[col].clip(low, high)
return X
src_mod = types.ModuleType("src")
preprocessing_mod = types.ModuleType("src.preprocessing")
pipeline_mod = types.ModuleType("src.preprocessing.preprocessing_pipeline")
pipeline_mod.boosting_deterministic_preproc = boosting_deterministic_preproc
pipeline_mod.cap_outliers_numeric = cap_outliers_numeric
src_mod.preprocessing = preprocessing_mod
preprocessing_mod.preprocessing_pipeline = pipeline_mod
sys.modules.setdefault("src", src_mod)
sys.modules.setdefault("src.preprocessing", preprocessing_mod)
sys.modules.setdefault("src.preprocessing.preprocessing_pipeline", pipeline_mod)
# 2. Descargar y cargar el pipeline
ruta = hf_hub_download(
repo_id="Jesusrodriguezf90/lgbm-diabetes-early-detection",
filename="lgbm_diabetes_pipeline.pkl"
)
pipeline = joblib.load(ruta)
# 3. Preparar datos de entrada
# X debe ser un DataFrame con las 22 variables del BRFSS 2015
# en su codificación original (valores enteros según el codebook del CDC)
X = pd.DataFrame([{
"GENHLTH": 3, "BPHIGH4": 3, "BPMEDS": 2, "BLOODCHO": 1,
"HAVARTH3": 2, "ADDEPEV2": 2, "SEX": 1, "QLACTLM2": 2,
"USEEQUIP": 2, "BLIND": 2, "DECIDE": 2, "DIFFWALK": 2,
"DIFFDRES": 2, "DIFFALON": 2, "SMOKE100": 2, "EXEROFT1": 3.0,
"_RACE": 1, "_AGEG5YR": 7, "_BMI5CAT": 2,
"_FRUTSUM": 1.0, "_VEGESUM": 2.0, "_PACAT1": 2,
}])
# 4. Inferencia con threshold dinámico
proba = pipeline.predict_proba(X)[0][1]
threshold = 0.4733 # threshold por defecto — ajustable según contexto asistencial
decision = "Realizar prueba HbA1c" if proba >= threshold else "No realizar prueba HbA1c"
print(f"Probabilidad estimada de riesgo: {proba * 100:.1f}%")
print(f"Decisión (threshold={threshold}): {decision}")
Arquitectura y entrenamiento
Pipeline sklearn
El pipeline encapsula todas las transformaciones para garantizar ausencia de data leakage:
| Step | Componente | Descripción |
|---|---|---|
| 1 | FunctionTransformer |
Preprocesamiento determinista: reemplaza -1 por NaN, normaliza binarias a 0/1, fuerza tipo category en nominales |
| 2 | ColumnTransformer |
Imputación (moda / mediana) + capping de outliers (P1-P99) + encoding nativo para boosting |
| 3 | LGBMClassifier |
Clasificador final con codificación nativa de categóricas |
Selección de threshold
El threshold óptimo se determina maximizando el F2-score (β=2) sobre la curva Precision–Recall del conjunto de validación. Con β=2, el recall tiene cuatro veces más peso que la precisión — refleja el coste clínico asimétrico donde un falso negativo (diabetes no detectada) genera un coste estimado de ~2.817€/paciente/año, frente a ~30€ de una prueba HbA1c innecesaria.
El pipeline soporta threshold dinámico (rango 0.10–0.90) para que el profesional sanitario pueda ajustar la sensibilidad según la capacidad asistencial disponible.
Parámetros de entrenamiento
| Parámetro | Valor |
|---|---|
| Dataset | BRFSS 2015 (CDC) |
| Observaciones | 257.709 |
| Variables de entrada | 22 (clínicas y demográficas) |
| Split | 60% train / 20% val / 20% test — estratificado |
| Tamaño conjunto test | 51.542 individuos |
| Validación cruzada | 5 folds estratificados sobre train |
| Desbalance clase positiva | Tratado con class_weight en LightGBM |
| Threshold por defecto | 0.4733 (F2-score óptimo en validación) |
| random_state | 42 |
| Hardware | Google Colab (CPU) |
Interpretabilidad
El modelo incluye análisis de interpretabilidad con Feature Importance y SHAP (SHapley Additive exPlanations), que permite explicar tanto el comportamiento global del modelo como predicciones individuales.
Las variables con mayor influencia sobre el riesgo de diabetes identificadas por el modelo:
| Variable | Importancia | Interpretación clínica |
|---|---|---|
_VEGESUM |
1143 | Total porciones de vegetales diarias |
_FRUTSUM |
917 | Total porciones de frutas diarias |
_AGEG5YR |
712 | Edad en grupos de 5 años |
EXEROFT1 |
617 | Frecuencia de ejercicio semanal |
GENHLTH |
477 | Estado general de salud autorreportado |
_BMI5CAT |
358 | Categoría de IMC |
La demo interactiva incluye un gráfico SHAP waterfall individual que explica en tiempo real qué variables aumentaron o redujeron el riesgo para cada predicción concreta.
Limitaciones
- Entrenado sobre datos del BRFSS 2015 (población estadounidense) — puede no generalizar directamente a otras poblaciones sin recalibración
- Precision baja (0.256) con threshold=0.4733 — diseño deliberado para maximizar recall en cribado; genera falsos positivos asumibles dado el bajo coste de la prueba HbA1c confirmatoria
- Las 22 variables son autorreportadas — sujetas a sesgo de respuesta del encuestado
- El modelo es un apoyo al cribado y no reemplaza el diagnóstico médico; la decisión clínica final corresponde siempre al profesional sanitario
- Sin datos de seguimiento longitudinal — no predice progresión ni complicaciones
Dataset
BRFSS 2015 — Behavioral Risk Factor Surveillance System
Centers for Disease Control and Prevention (CDC), Estados Unidos
🔗 CDC BRFSS 2015
- 257.709 observaciones clínicas autorreportadas
- 22 variables de entrada (hábitos de vida, condiciones médicas, datos demográficos)
- Variable objetivo:
DIABETE3— riesgo de diabetes (binaria: 1=sí, 3=no en codificación original CDC) - Dataset procesado disponible en: Jesusrodriguezf90/brfss2015-diabetes-detection
Autor
Jesús Rodríguez Fernández — Data Scientist
🔗 LinkedIn · GitHub · HF Profile
Licencia
Uso no comercial. Dataset BRFSS 2015 de dominio público (CDC). Ver términos de uso del CDC para redistribución de datos derivados.