Telco Churn MLOps
Plataforma MLOps completa para predecir el abandono de clientes de telecomunicaciones (7,043 clientes, dataset Telco Customer Churn). No es un notebook de clasificación: es el ciclo de vida completo de un modelo en producción — comparación de 4 familias de modelos, tuning bayesiano con Optuna, tracking en MLflow, versionado de datos con DVC, detección de drift con Evidently AI, promoción champion/challenger con criterio de negocio explícito, serving vía FastAPI con explicabilidad SHAP, dashboard interactivo en Streamlit, y una capa de valor de negocio que traduce cada predicción en un número accionable: cuánto se pierde si ese cliente se va, y si conviene una campaña de retención.
Retener un cliente existente cuesta significativamente menos que adquirir uno nuevo, pero solo si la retención se dirige a los clientes correctos y con margen suficiente para justificar el costo de la campaña. El proyecto parte de un dataset real de telecomunicaciones (Telco Customer Churn, 7,043 clientes, 50 columnas: datos demográficos, contractuales, de servicios contratados y facturación) con un objetivo doble:
- Estimar la probabilidad de churn de cada cliente con suficiente confianza para priorizar intervención.
- Sostener esa confianza en el tiempo, detectando cuándo el modelo se degrada frente a datos nuevos y decidiendo de forma objetiva cuándo reemplazarlo — no un modelo que se entrena una vez y se abandona.
El sistema se diseñó como capas con dependencia en una sola dirección (Datos → Features → Training → Registry/Serving → Monitoreo → Presentación), cada una desacoplada de la siguiente para que, por ejemplo, servir predicciones nunca dependa de que el monitoreo esté arriba. Cada decisión de arquitectura o tooling no obvia quedó documentada como ADR antes de implementarse (14 ADRs a lo largo de 8 fases) — el objetivo explícito del proyecto es demostrar el ciclo de vida completo de MLOps con las mismas prácticas de un equipo real, no solo entregar un clasificador con buena métrica.
El EDA identificó, entre otros hallazgos, cuatro columnas con data leakage y columnas de bajo valor de señal, documentadas y excluidas explícitamente en ADR 0006:
- Leakage:
Churn Score,Customer Status,Churn Category,Churn Reason— todas calculadas a partir del churn o solo disponibles después de que ya ocurrió. - Bajo valor de señal:
Latitude,Longitude,Zip Code,CLTV.
Antes de llegar al modelo, cada batch de datos pasa por un contrato de schema con Pandera (tipos, nulabilidad, valores categóricos válidos) verificado con tests automatizados — el objetivo es que un problema de calidad de datos falle ruidoso en el pipeline, no silenciosamente en una predicción de producción.
De las 50 columnas originales quedan 37 features crudas después de excluir leakage, IDs y columnas de bajo valor, más 2 variables derivadas (is_new_customer, num_extra_services) diseñadas a partir de los hallazgos del EDA, no de forma arbitraria. El encoding y el escalado viven deliberadamente en la capa de entrenamiento, no en features — features produce datos limpios y comprensibles para un humano; el training decide cómo codificarlos según lo que cada familia de modelo necesita, una separación de responsabilidades documentada en ADR 0007.
Se compararon 4 familias de modelos bajo el mismo protocolo de evaluación — Regresión Logística (baseline), XGBoost, LightGBM y CatBoost — con foco explícito en cómo cada una maneja las categóricas de alta cardinalidad del dataset (ADR 0009). LightGBM ganó con F1=0.929 en la comparación inicial; ese resultado se llevó a un pipeline definitivo con tuning bayesiano de hiperparámetros vía Optuna (50 trials, Stratified K-Fold para no sobreajustar la selección al split), que subió el F1 del champion final a 0.931 con ROC-AUC de 0.992.
Cada experimento (parámetros, métricas, artefactos) queda registrado en MLflow, con el Model Registry gestionando qué versión sirve en producción vía aliases (champion), no el sistema de stages ya deprecado. El código se versiona con Git y los datos/modelos con DVC contra un remoto en DagsHub — la combinación permite reproducir cualquier resultado pasado sabiendo exactamente qué código, qué datos y qué configuración lo generaron, sin depender de que un notebook local siga intacto.
El sistema simula la llegada de datos nuevos con perturbaciones de intensidad creciente y los evalúa con Evidently AI para detectar drift de forma cuantitativa (dataset_drift_share). Un umbral de reentrenamiento (10%, calibrado contra los propios resultados del proyecto, no un número arbitrario) decide si amerita reentrenar; el challenger resultante se compara contra el champion actual con un criterio de negocio explícito — no solo F1 agregado. Para ser promovido, el challenger debe:
- Ganar por un margen mínimo de 1 punto.
- No retroceder más de una tolerancia definida en ningún segmento de negocio relevante: tipo de contrato, método de pago, tipo de internet.
Reentrenar y promover son pasos deliberadamente manuales, con un humano en el loop, no un cron job automático.
El modelo se sirve vía una API REST (FastAPI, validación de requests con Pydantic) con dos endpoints de negocio: predicción y explicación por SHAP (TreeExplainer, valores exactos para modelos de árboles, sin necesitar un dataset de background). Un dashboard interactivo en Streamlit consume esa API y agrega una capa de valor de negocio: para cada predicción, estima cuánto ingreso queda en riesgo si ese cliente específico hace churn (valor restante de su contrato actual) y compara ese número contra el costo de una campaña de retención para decidir, con un criterio explícito, si vale la pena intervenir — la parte del proyecto que conecta la predicción técnica con la decisión de negocio real.
149 tests automatizados (unitarios, de validación de datos, y de integración) corren en cada push y pull request contra main vía GitHub Actions, junto con lint (Ruff), formato y type-checking (MyPy) como gate obligatorio antes de mergear. Los tests de integración van más allá de mockear dependencias externas: ejercitan el pipeline de producción real (sin mocks) y levantan el stack completo con Docker Compose para detectar en CI la clase de bug que un test unitario, por diseño, no puede ver — un contenedor que construye pero no arranca, o un artefacto que nunca llega a la imagen final.
La API y el dashboard están empaquetados como imágenes Docker independientes (multi-stage builds, con uv para instalar dependencias exactas vía lockfile) y orquestables localmente con Docker Compose. El destino final de hosting quedó deliberadamente abierto durante el desarrollo: dos plataformas de free-tier (Render, luego Hugging Face Spaces) cambiaron de política de precios entre que se diseñó el despliegue y que se intentó ejecutar — una decisión de infraestructura real y su trade-off, no una limitación técnica del proyecto.
El proyecto documentó y resolvió tres clases de problema real, no hipotético:
- Un bug del artifact storage de MLflow en DagsHub que bloqueaba descargar el pipeline desde el Registry — diagnosticado con evidencia (reproducido, aislado de causas como cuota o credenciales) y resuelto rediseñando para depender menos de esa descarga, versionando el champion con DVC en vez de MLflow.
- Una imagen Docker que construía sin error pero entraba en crash loop en producción por una librería del sistema operativo (
libgomp1) perdida entre etapas de un multi-stage build. - Dos plataformas de hosting gratuito que cambiaron su política de precios a mitad de la implementación.
Cada episodio quedó documentado con el diagnóstico completo, no solo el fix, como parte del criterio de ingeniería del proyecto.
El resultado no es solo un clasificador con ROC-AUC de 0.99 y F1 de 0.93 sobre el champion en producción — es un sistema donde cada componente (qué datos entraron, qué modelo se entrenó con qué hiperparámetros, por qué se promovió o no una versión nueva, qué tan confiable sigue siendo frente a datos que cambian) es trazable y auditable. El proyecto demuestra que la parte más difícil de un sistema de ML en producción rara vez es el modelo: es todo lo que lo rodea — validación, reproducibilidad, gobierno de versiones, monitoreo continuo y la traducción final de una probabilidad a una decisión de negocio con un costo real.