Contenido
- El contexto de negocio: por qué predecir el churn importa
- El dataset:
clientes_churn.csv - El notebook inicial, completo y explicado
- Diagnóstico: qué está mal en este notebook
- El stack del curso: etapa → herramienta → módulo
- Hoja de ruta del resto del curso
El contexto de negocio: por qué predecir el churn importa
CineClick es una plataforma de streaming por suscripción con planes mensuales (básico, estándar y premium). Como todo negocio de suscripción, su salud depende de una aritmética sencilla y despiadada:
- Adquirir un cliente nuevo es caro: campañas, descuentos de bienvenida, meses gratis. En el sector se asume que captar un suscriptor cuesta varias veces más que retener a uno existente (una regla habitual dice que entre 5 y 7 veces más, aunque el múltiplo exacto depende del negocio).
- Retener es barato... si sabes a quién retener: una llamada, un descuento puntual o una recomendación de contenido pueden bastar. Pero ofrecer descuentos a todos los clientes destruye margen; la clave es dirigir la retención a quienes realmente están en riesgo.
- El churn compone: un 5% de bajas mensuales parece poco, pero sostenido significa perder cerca de la mitad de la base de clientes en un año si no se compensa con adquisición.
De ahí el proyecto: un modelo que, para cada suscriptor activo, estime la probabilidad de que se dé de baja próximamente, para que el equipo de retención concentre sus recursos en los clientes de mayor riesgo. La métrica de negocio es clara (bajas evitadas e ingresos retenidos frente al coste de las campañas), y la de ML la discutiremos críticamente más abajo — adelanto: el notebook actual usa la equivocada.
Todos los datos del proyecto son ficticios y generados sintéticamente: representan patrones plausibles de un negocio de streaming, pero ningún cliente real. Es la práctica correcta para formación y para desarrollo: nunca uses datos reales de personas donde no toca.
El dataset: clientes_churn.csv
El data engineer de CineClick genera periódicamente un extracto con una fila por suscriptor y estas columnas:
| Columna | Tipo | Descripción | Valores de ejemplo |
|---|---|---|---|
id_cliente |
entero | Identificador único del suscriptor (sin significado predictivo) | 10482 |
antiguedad_meses |
entero | Meses desde el alta | 1, 8, 36 |
horas_semana |
decimal | Media de horas de visualización semanales del último mes | 0.5, 6.2, 21.0 |
tickets_soporte |
entero | Tickets de soporte abiertos en los últimos 3 meses | 0, 1, 4 |
plan |
categórica | Tipo de plan contratado | basico, estandar, premium |
metodo_pago |
categórica | Método de pago de la suscripción | tarjeta, domiciliacion, paypal |
descuento_activo |
booleano (0/1) | Si tiene actualmente algún descuento promocional | 0, 1 |
abandono |
booleano (0/1) | Etiqueta: 1 si se dio de baja en los 30 días siguientes al extracto | 0, 1 |
Dos rasgos del dataset que serán importantes durante todo el curso:
- Las clases están desbalanceadas: solo en torno al 15% de las filas tienen
abandono = 1. Es lo normal en churn (por suerte para CineClick, la mayoría de los clientes se queda), y tiene consecuencias directas sobre qué métricas sirven y cuáles engañan. - Mezcla de tipos: numéricas (
antiguedad_meses,horas_semana,tickets_soporte), categóricas (plan,metodo_pago) y binarias (descuento_activo). Las categóricas necesitan codificación antes de entrenar.
El notebook inicial, completo y explicado
Este es el estado del arte en CineClick hoy: un único notebook, churn_final_v3_DEFINITIVO.ipynb (el nombre ya cuenta una historia), en el portátil de la data scientist. Su contenido, volcado como un solo bloque, es este:
# churn_final_v3_DEFINITIVO.ipynb
import pandas as pd
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# Cargar los datos
df = pd.read_csv("C:/Users/laura/Desktop/datos/clientes_churn.csv")
# Limpieza: fuera nulos y algún negativo raro que venia en horas
df = df.dropna()
df = df[df["horas_semana"] >= 0]
# Quitar el id, que no aporta
df = df.drop(columns=["id_cliente"])
# Pasar las categoricas a columnas 0/1
df = pd.get_dummies(df, columns=["plan", "metodo_pago"])
# Separar features y etiqueta
X = df.drop(columns=["abandono"])
y = df["abandono"]
# Partir en train y test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Entrenar el modelo
modelo = RandomForestClassifier(n_estimators=100)
modelo.fit(X_train, y_train)
# Evaluar
predicciones = modelo.predict(X_test)
print("Accuracy:", accuracy_score(y_test, predicciones))
# Salió 0.87, ¡mejor que la v2!
# Guardar el modelo
with open("C:/Users/laura/Desktop/modelos/modelo_churn.pkl", "wb") as f:
pickle.dump(modelo, f)Expliquémoslo paso a paso, porque como código de exploración es perfectamente razonable y conviene entender qué hace cada parte:
- Carga (
pd.read_csv): lee el CSV desde una ruta del escritorio de Laura a un DataFrame de pandas — la estructura tabular con la que trabajaremos siempre. - Limpieza (
dropna, filtro de negativos): elimina las filas con valores nulos y las que tienen horas de visualización negativas (un error conocido del sistema de logs). Decisiones defendibles, pero tomadas sin registrar cuántas filas se pierden ni por qué. - Eliminación del id (
drop): correcto —id_clientees un identificador arbitrario; dejarlo permitiría al modelo "memorizar" clientes en lugar de aprender patrones. - One-hot encoding (
pd.get_dummies): convierte cada categórica en columnas binarias.planse convierte enplan_basico,plan_estandar,plan_premiumcon valores 0/1, y análogamentemetodo_pago. Es necesario porque los algoritmos de scikit-learn requieren entradas numéricas. - Separación X/y:
Xson las features (todas las columnas menos la etiqueta) eyla etiquetaabandono. train_test_split(X, y, test_size=0.2): reserva el 20% de las filas para evaluar el modelo con datos que no ha visto durante el entrenamiento. Fíjate en lo que falta entre los paréntesis; volveremos sobre ello enseguida.RandomForestClassifier(n_estimators=100): un bosque aleatorio de 100 árboles de decisión. Elección razonable para tabulares: robusto, admite mezcla de tipos de features y funciona decentemente sin apenas ajuste.modelo.fit(...): el entrenamiento propiamente dicho.- Evaluación con
accuracy_score: porcentaje de predicciones correctas sobre el conjunto de test. El comentario# Salió 0.87es literalmente todo el registro experimental que existe del proyecto. pickle.dump(...): serializa el modelo entrenado a un fichero binario.pklen el escritorio, que es lo que Laura enviaría por chat si alguien le pidiera "el modelo".
Insistamos: este notebook no es obra de una mala profesional. Es el resultado natural de la fase de exploración, y como exploración cumplió su misión: demostró que hay señal en los datos. El problema es que CineClick quiere usarlo en producción tal cual, y para eso acumula todos los pecados que ahora vamos a nombrar.
Diagnóstico: qué está mal en este notebook
Pasemos la lista con la mirada MLOps de las lecciones anteriores. Cada problema es concreto y tiene consecuencia concreta:
| # | Problema | Dónde se ve | Consecuencia |
|---|---|---|---|
| 1 | Rutas absolutas y personales | C:/Users/laura/Desktop/... en carga y guardado |
El código solo funciona en el portátil de Laura. En cualquier otra máquina (o servidor) falla en la primera línea útil. |
| 2 | Sin semilla aleatoria | train_test_split sin random_state; RandomForestClassifier sin random_state |
Cada ejecución parte los datos de forma distinta y entrena árboles distintos: el "0.87" es irrepetible. Imposible saber si un cambio mejoró el modelo o fue azar. |
| 3 | Datos sin versionar | El CSV del escritorio se sobrescribe con cada extracto | No se puede reproducir el modelo actual ni comparar entrenamientos: el dataset de la "v3" ya no existe. |
| 4 | Modelo sin versionar ni metadatos | modelo_churn.pkl guardado a mano, pisando el anterior |
Nadie sabe qué código, datos ni parámetros produjeron el .pkl que circula. Las "versiones" viven en el nombre del notebook. |
| 5 | Métrica única y engañosa | Solo accuracy con ~15% de positivos |
Un modelo inútil que prediga siempre "no abandona" ya obtiene ~0.85 de accuracy. El 0.87 celebrado podría estar detectando poquísimos abandonos reales — que es lo único que le importa a retención. |
| 6 | Sin tests de ningún tipo | Todo el notebook | Nada comprueba que la limpieza hace lo esperado, que el esquema del CSV es el previsto o que el modelo supera un mínimo. Cualquier cambio se valida "a ojo". |
| 7 | Sin registro de experimentos | El comentario # Salió 0.87, ¡mejor que la v2! |
La historia del proyecto vive en comentarios y memoria. ¿Qué era la v2? ¿Con qué datos? Nadie puede responder. |
| 8 | Dependencias implícitas | Ningún fichero declara versiones de pandas/scikit-learn | Otro compañero con otras versiones puede obtener resultados distintos o errores. "En mi máquina funciona" garantizado. |
| 9 | Preprocesado no reutilizable | get_dummies y limpieza inline sobre el DataFrame |
Para predecir sobre clientes nuevos habrá que reimplementar la misma transformación en el servicio; cualquier divergencia (una categoría ausente, columnas en otro orden) degradará el modelo en silencio. |
| 10 | Proceso 100% manual | Todo | Reentrenar = localizar a Laura, su portátil y su memoria. Nivel 0 de manual. |
Merece la pena detenerse en el problema 5, porque es el único que engaña incluso con el notebook delante. Con clases desbalanceadas (85% "no abandona"), la accuracy es una métrica casi vacía: mide sobre todo lo fácil (acertar los que se quedan) e ignora lo valioso (encontrar los que se van). Las métricas adecuadas para este problema —precision y recall sobre la clase positiva, y sus derivadas— aparecerán cuando montemos la evaluación seria del pipeline en el módulo 2 y las comparaciones de experimentos en el módulo 3. De momento, quédate con la sospecha: ese 0.87 no dice lo que Laura cree que dice.
El stack del curso: etapa → herramienta → módulo
Cada problema del diagnóstico tiene su respuesta en una etapa del ciclo de vida, y cada etapa su herramienta en este curso. Todas son de código abierto o tienen capa gratuita, y todas son estándares de facto o candidatas serias a serlo — pero recuerda la lección 01-01: lo importante es la práctica que encarnan, no la marca.
| Etapa del ciclo | Herramienta | Qué resuelve del diagnóstico | Módulo |
|---|---|---|---|
| Versionado de código | Git | Base de todo; problemas 1 y 10 empiezan a resolverse aquí | Transversal (se asume conocido) |
| Estructura y entorno reproducible | Empaquetado Python + gestión de dependencias | Problemas 1, 8 y 9 | Módulo 2 |
| Versionado de datos | DVC | Problema 3 (y enlaza datos con código) | Módulo 2 |
| Pipeline de entrenamiento | DVC pipelines | Problemas 2, 9 y 10 (proceso repetible) | Módulo 2 |
| Tracking de experimentos | MLflow | Problemas 5 (métricas múltiples) y 7 | Módulo 3 |
| Registro de modelos | MLflow Model Registry | Problema 4 | Módulo 3 |
| Servicio de predicción | FastAPI | Llevar el modelo a quien lo necesita | Módulo 4 |
| Empaquetado y despliegue | Docker (+ Kubernetes/serverless) | Problemas 1 y 8 en producción | Módulo 4 |
| CI/CD | GitHub Actions | Problema 6 (tests) y automatización del despliegue | Módulo 5 |
| Orquestación de pipelines | Prefect | Ejecutar y programar los pipelines de forma fiable | Módulo 5 |
| Monitorización y deriva | Evidently (+ métricas de servicio) | Vigilar el modelo vivo; cerrar el ciclo con el reentrenamiento | Módulo 6 |
Un aviso sobre expectativas: este stack es una combinación coherente y muy usada, no la verdad revelada. En tu empresa quizá se use otro orquestador u otra plataforma de tracking; los conceptos que aprenderás se transfieren casi uno a uno, porque todas las herramientas de cada categoría resuelven el mismo problema del ciclo de vida.
Hoja de ruta del resto del curso
Así se desarrollará la transformación de CineClick, módulo a módulo:
flowchart TD
M1["Módulo 1 (aquí)<br/>Fundamentos: el notebook<br/>y sus problemas"] --> M2["Módulo 2<br/>Código reproducible:<br/>proyecto estructurado, entornos,<br/>DVC, pipeline de entrenamiento"]
M2 --> M3["Módulo 3<br/>Experimentos y registro:<br/>MLflow tracking y model registry,<br/>feature stores"]
M3 --> M4["Módulo 4<br/>Servir en producción:<br/>FastAPI, Docker,<br/>Kubernetes, optimización"]
M4 --> M5["Módulo 5<br/>Automatización:<br/>CI/CD con GitHub Actions,<br/>Prefect, estrategias de release"]
M5 --> M6["Módulo 6<br/>Operación:<br/>monitorización, deriva, reentrenamiento,<br/>gobernanza y proyecto final"]- Módulo 2 — Del notebook al código reproducible: refactorizaremos este notebook en un proyecto Python estructurado, con entorno reproducible, el dataset bajo control de DVC y un pipeline de entrenamiento que cualquiera puede ejecutar. Al terminarlo, los problemas 1, 2, 3, 8, 9 y 10 estarán resueltos o encarrilados.
- Módulo 3 — Experimentos y registro: cada entrenamiento quedará registrado en MLflow con sus parámetros y métricas (las correctas, no solo accuracy), y los modelos se versionarán y promocionarán en el model registry. Adiós a los problemas 4, 5 y 7.
- Módulo 4 — Servir en producción: el modelo dejará el
.pkldel escritorio para convertirse en un servicio FastAPI empaquetado con Docker y desplegado de forma escalable. - Módulo 5 — Automatización: tests de código, datos y modelo en CI (problema 6), despliegue automatizado y orquestación de los pipelines — el salto del nivel 1 al nivel 2 de madurez.
- Módulo 6 — Operación: monitorizaremos el servicio y el modelo, detectaremos deriva, automatizaremos el reentrenamiento con cabeza (recordando los bucles de realimentación de la lección 01-02) y cerraremos con gobernanza, documentación y el proyecto final integrador.
Errores Comunes y Consejos
- Error: despreciar el notebook de Laura. El notebook demostró que el problema tiene solución con los datos disponibles — eso es la parte difícil e irreemplazable. MLOps no sustituye la exploración: la industrializa. Trata el código exploratorio con respeto y a su autora como aliada, no como culpable.
- Error: fiarse de la accuracy con clases desbalanceadas. Es el error de modelado más común en churn, fraude o detección de fallos. Antes de celebrar una métrica, pregúntate siempre: ¿qué obtendría un modelo trivial que predice siempre la clase mayoritaria?
- Error: "arreglar" el notebook añadiéndole celdas. La tentación es parchear: una celda que fija la semilla, otra que copia el CSV con fecha en el nombre... El notebook seguiría siendo un notebook, con su estado oculto y su ejecución fuera de orden. La solución del módulo 2 es estructural, no cosmética.
- Error: querer aplicar las once herramientas de la tabla la semana que viene. El stack se adopta en el orden del curso por una razón: cada pieza se apoya en la anterior (no puedes hacer CI de un pipeline que no existe). En tu trabajo real, sigue el mismo orden incremental.
- Consejo: guarda una copia de este notebook tal cual está. Al final del curso, compararlo con la plataforma final será la mejor medida de lo aprendido — y un ejercicio de humildad útil: casi todos tenemos un
_final_v3_DEFINITIVOen algún cajón.
Ejercicios
Ejercicio 1
Sin ejecutar nada, razona numéricamente: el dataset tiene un 15% de clientes con abandono = 1. (1) ¿Qué accuracy obtendría un "modelo" que predice siempre abandono = 0? (2) ¿Cuántos de los clientes que van a abandonar detectaría ese modelo? (3) ¿Qué te dice esto sobre el 0.87 del notebook y sobre qué debería preguntarle el equipo de retención a Laura en lugar de "¿qué accuracy tiene?"?
Ejercicio 2
Ejecutas el notebook de Laura dos veces seguidas, sin cambiar ni una línea ni el CSV, y obtienes accuracy 0.87 la primera vez y 0.86 la segunda. (1) Señala las dos líneas exactas del código responsables de esta variabilidad y explica el mecanismo de cada una. (2) Explica por qué esto es grave más allá de la molestia: ¿qué pregunta fundamental del trabajo experimental resulta imposible de responder?
Ejercicio 3
Relaciona cada problema del diagnóstico con su remedio: para los problemas 3, 5, 6 y 9 de la tabla de diagnóstico, indica qué herramienta del stack (y qué módulo del curso) lo aborda principalmente, y escribe en una frase qué práctica (más allá de la herramienta) es la que realmente lo resuelve.
Soluciones
Solución 1:
- Prediciendo siempre "no abandona" aciertas en todos los negativos (85%) y fallas en todos los positivos (15%): accuracy = 0.85.
- Cero. Ese modelo no detecta ni un solo abandono: es perfectamente inútil para retención pese a su 0.85.
- El 0.87 del notebook está solo dos puntos por encima del modelo trivial, así que la mayor parte de esa accuracy es "acertar lo fácil". Podría estar detectando muchos abandonos o casi ninguno: la accuracy no lo distingue. Retención debería preguntar: "de los clientes que realmente abandonan, ¿qué fracción detecta el modelo?" (recall de la clase positiva) y "de los que el modelo marca como riesgo, ¿cuántos abandonan de verdad?" (precision) — porque esas dos cifras determinan, respectivamente, cuántas bajas puede evitar la campaña y cuánto dinero se malgasta en falsos positivos.
Solución 2:
- Las dos líneas:
train_test_split(X, y, test_size=0.2)— sinrandom_state, cada ejecución baraja los datos con una semilla distinta, así que train y test contienen filas diferentes cada vez; yRandomForestClassifier(n_estimators=100)— sinrandom_state, el muestreo bootstrap de cada árbol y la selección aleatoria de features en cada división cambian entre ejecuciones, produciendo bosques distintos incluso con los mismos datos de entrenamiento. - Lo grave: resulta imposible responder a la pregunta "¿este cambio mejoró el modelo?". Si mañana Laura añade una feature nueva y la accuracy pasa de 0.86 a 0.875, no hay forma de saber si la mejora se debe a la feature o al mismo ruido aleatorio que ya produce oscilaciones de ±0.01 sin tocar nada. Sin reproducibilidad no hay experimentación válida: todo el proceso de mejora del modelo queda edificado sobre comparaciones sin valor.
Solución 3:
- Problema 3 (datos sin versionar) → DVC, módulo 2. La práctica real: cada dataset de entrenamiento queda inmutablemente identificado y enlazado a la versión del código que lo usó, de modo que cualquier modelo pasado pueda reconstruirse.
- Problema 5 (métrica única engañosa) → MLflow, módulo 3 (con las métricas correctas introducidas ya en el pipeline del módulo 2). La práctica real: definir antes de entrenar un conjunto de métricas alineado con el negocio y registrarlas todas en cada experimento, para comparar modelos sobre lo que importa.
- Problema 6 (sin tests) → GitHub Actions, módulo 5. La práctica real: codificar las expectativas sobre código, datos y modelo como comprobaciones automáticas que se ejecutan en cada cambio — el CI es solo el motor que las ejecuta.
- Problema 9 (preprocesado no reutilizable) → estructura de proyecto Python, módulo 2 (y su reutilización en el servicio, módulo 4). La práctica real: un único módulo de preprocesado, testeado y versionado, compartido por entrenamiento e inferencia, para que sea imposible por construcción que producción transforme los datos de otra manera.
Conclusión
Ya tenemos el punto de partida completo y sin maquillaje: un negocio con un problema real y cuantificable (el churn compone, y retener es mucho más barato que adquirir), un dataset ficticio pero verosímil con sus features numéricas y categóricas y su etiqueta desbalanceada, y un notebook monolítico que demuestra que hay señal en los datos pero que acumula diez problemas concretos —rutas personales, azar sin control, datos y modelo sin versionar, una accuracy que no dice lo que parece, cero tests y un proceso que vive en el portátil de una persona—. También tenemos el plan: un stack de herramientas (Git, DVC, MLflow, FastAPI, Docker, GitHub Actions, Prefect, Evidently) mapeado etapa a etapa sobre el ciclo de vida, y una hoja de ruta que recorre ese mapa módulo a módulo. Con esto cerramos los fundamentos. En el módulo 2 nos remangamos: tomaremos este notebook exactamente como está y lo convertiremos, paso a paso, en un proyecto Python estructurado y reproducible — la primera piedra, y la más importante, del camino de CineClick hacia la producción.
Curso de MLOps
Módulo 1: Fundamentos de MLOps
- Qué es MLOps y por qué los modelos mueren en el notebook
- El ciclo de vida de un modelo de ML en producción
- Niveles de madurez MLOps y roles del equipo
- El proyecto del curso: del notebook a producción
Módulo 2: Del notebook al código reproducible
- Estructura de un proyecto de ML: del notebook al paquete
- Entornos reproducibles y gestión de dependencias
- Versionado de datos con DVC
- Pipelines de entrenamiento reproducibles
Módulo 3: Experimentos y registro de modelos
- Tracking de experimentos con MLflow
- Model registry: versionar y promocionar modelos
- Feature stores: cuándo y para qué
Módulo 4: Servir modelos en producción
- Patrones de despliegue: batch, online y streaming
- Un servicio de predicción con FastAPI
- Empaquetado con Docker
- Escalado y despliegue: Kubernetes y serverless
- Optimización de la inferencia: latencia y coste
Módulo 5: Automatización: CI/CD y orquestación
- CI para ML: tests de código, datos y modelos
- CD: automatizar el despliegue del modelo
- Orquestación de pipelines de ML
- Estrategias de release: shadow, canary y A/B
