Saltar al contenido

lección 4

Miércoles: el primer modelo (y la euforia)

Partir los datos, entrenar una regresión logística con scikit-learn y medir el acierto: sale un 99%, y Raúl te dice que no mandes nada todavía.

⏱ 50 min

## Miércoles, 10:00. Por fin, un modelo

Dos días mirando y limpiando datos, y hoy por fin tocas scikit-learn. Es la librería estándar de Python para machine learning, y conquistó el mundo académico y profesional con una decisión de diseño sencilla y genial: todos sus modelos se usan igual. Creas el modelo, lo entrenas con `.fit(X, y)` pasándole las features y el target, y predices con `.predict(X)`. Da igual que por dentro sea una regresión logística, un árbol o un bosque: la forma de llamarlo es la misma. Esa uniformidad es la razón de que puedas cambiar de algoritmo en una línea, y de que se enseñe scikit-learn antes que cualquier otra cosa. Lo de hoy va al tercer fichero del proyecto, `src/03_modelo.py`.

Merece la pena detenerse un segundo en esa decisión de diseño, porque explica media historia del machine learning práctico. Antes de scikit-learn, cada algoritmo venía en su propia librería, con su propia forma de llamarlo: cambiar de un modelo a otro significaba reescribir medio programa. Un grupo de investigadores decidió, a finales de los 2000, que todos los modelos compartieran la misma interfaz —`fit` para aprender, `predict` para predecir— y eso, que suena a detalle técnico, cambió cómo trabaja todo el mundo. De repente probar cinco algoritmos sobre el mismo problema era cambiar una línea. No ganó por tener los mejores algoritmos, ganó por una decisión de diseño que le ahorró tiempo a millones de personas. Es un patrón que se repite en la tecnología: muchas veces no gana lo más potente, sino lo que mejor encaja en la forma de trabajar de la gente.

Antes de entrenar hay que preparar la tabla de modelado: una fila por cliente, con sus features en unas columnas y el target `se_fue` en otra. El trabajo de agregación —resumir los seis meses de consumo en una media por cliente, contar los tickets de cada uno— ya está hecho en una tabla limpia llamada `tabla_modelo_full`, para que te centres en el modelo. Trae estas columnas: `antiguedad_meses`, `cuota_mensual`, `consumo_gb_medio`, `n_tickets`, `contrato_mensual` (un 1/0 que marca si el contrato es mensual, la sospecha de Lola convertida en número), `gestion_retencion` (sí, la columna rara que apuntaste el lunes y no quitaste) y `se_fue` (el target).

Fíjate en la forma que tiene que tener esa tabla, porque es la forma que espera scikit-learn y la vas a montar mil veces: una matriz donde cada fila es un ejemplo (un cliente), unas columnas son las features que el modelo mira (la X), y una columna aparte es el target que intenta adivinar (la y). El modelo aprende la relación entre la X y la y mirando muchas filas; luego, dado un cliente nuevo con su X pero sin su y, estima la y. Toda la agregación del lunes y la limpieza del martes existían para llegar a esta tabla rectangular y limpia. Míra su anatomía:

La forma que espera scikit-learn: una fila por cliente, las features en la X y el target en la y. Toda la limpieza de la semana servía para llegar aquí.

## Partir los datos: entrenar en un sitio, examinar en otro

Aquí hay una regla que es el corazón de todo el machine learning honesto, y es tan importante que si solo te llevas una cosa de esta semana, que sea esta. Un modelo no se evalúa con los mismos datos con los que aprendió. Si le enseñas a un estudiante exactamente las preguntas del examen y luego le pones ese mismo examen, sacará un diez y no habrás medido nada: solo ha memorizado. Para saber si de verdad ha aprendido, le pones preguntas que no ha visto. Con un modelo es igual: se parten los datos en dos trozos, uno para entrenar (que el modelo ve) y otro para probar (que se guarda y el modelo no toca hasta el final). El acierto que vale es el del trozo de prueba, el de las preguntas nuevas.

En scikit-learn esa partición se hace con `train_test_split`, que reparte las filas al azar entre entrenamiento y prueba. Le pasamos `test_size=0.2` (el 20% de los clientes se guarda para probar; con 12.000 son 2.400) y `random_state=42` (una semilla fija para que el reparto al azar sea siempre el mismo y los resultados se puedan reproducir). Esa semilla no es un capricho: sin ella, cada ejecución repartiría distinto y tu acierto bailaría un poco cada vez, y entonces no podrías comparar dos modelos de forma limpia. Fijar la semilla es parte de hacer ciencia reproducible.

train_test_split reparte al azar: el modelo aprende con el 80% y se mide con el 20% que no vio. Ese 20% son 2.400 clientes.

## La regresión logística, en una frase

El modelo que vas a usar es una regresión logística. No te asustes con el nombre: la idea es sencilla. Dadas las features de un cliente, calcula un número entre 0 y 1 que se interpreta como la probabilidad de que ese cliente se vaya. Un cliente con 0,8 es más sospechoso que uno con 0,2. Para dar una respuesta de sí/no, se pone un umbral (por defecto 0,5: por encima, «se va»; por debajo, «se queda»). La regresión logística lleva más de cuarenta años en uso y sigue viva en banca y en seguros por una razón que no es nostalgia: se puede explicar. Puedes decir qué peso le da a cada feature, y eso importa cuando tienes que justificar ante un regulador —o ante un cliente enfadado— por qué el modelo lo marcó. Para un primer modelo honesto, es la elección por defecto.

Merece la pena ver, sin una sola fórmula, de dónde sale ese número entre 0 y 1, porque es lo que hace que el modelo sirva para ordenar una lista. La regresión logística combina las features de un cliente en una puntuación y la pasa por una curva en forma de ese alargada —se llama la curva logística, de ahí el nombre— que aplasta cualquier valor dentro del rango de 0 a 1. Clientes con poca pinta de irse caen a la izquierda, cerca de 0; los muy sospechosos caen a la derecha, cerca de 1; y en medio hay una zona de duda donde un empujón de cualquier feature mueve mucho la probabilidad. Esa probabilidad continua es, en el fondo, más útil que el sí/no, porque permite ORDENAR a los clientes de más a menos riesgo, que es exactamente lo que le pediste al modelo para la lista de Lola.

El modelo no dice «sí/no», dice una probabilidad entre 0 y 1. Eso permite ORDENAR la lista de riesgo, que es lo que necesita retención. El umbral de 0,5 solo convierte esa probabilidad en una decisión.

La entrenas con `max_iter=1000` (le damos hasta mil pasos para que el ajuste interno termine bien) y `random_state=42` (de nuevo, para que sea reproducible). Y aquí viene el momento del miércoles: entrenas el modelo con TODAS las columnas disponibles —incluida `gestion_retencion`, que no quitaste— y mides el acierto sobre la prueba. Sale un 99%. Un 99% a la primera, en tu primer modelo. La euforia es inevitable, y por un momento parece que el trabajo de la semana está hecho.

Lo cuentas en el canal del equipo, y la reacción es inmediata y de dos tipos muy distintos:

1Tú 12:48
2Primer modelo entrenado! 99% de acierto sobre la prueba 🎉
3
4Lola 12:49
5BIEN!!! lo sabia. me pasas la lista hoy? el equipo esta esperando
6
7Raúl 12:53
8Enhorabuena por el primer modelo. Pero no mandes nada todavia.
9Manana a primera hora miramos ese 99% juntos. Tengo una pregunta
10para ti, y creo que no te va a gustar la respuesta.
11
12Lola 13:10
13raul porfa que me corre, me vale aunque sea provisional
14
15Tú 13:14
16Lola, te la paso manana a primera hora revisada. Prefiero darte
17una lista en la que puedas confiar que una rapida que falle.

Dos reacciones opuestas al 99%: Lola quiere la lista ya; Raúl desconfía y pide un día.

Fíjate en que has vuelto a sostener la presión de ayer, ahora con el modelo encima de la mesa. Le has dicho a Lola que la tendrá mañana «revisada», que es la forma educada de decir «no me fío todavía de este número». Y haces bien, aunque te cueste, porque entregar una lista basada en un 99% que no has auditado sería técnicamente rápido y profesionalmente temerario. Raúl no te ha dicho qué falla; te ha dicho que mañana lo miréis juntos. Esa pausa de un día, incómoda frente a la prisa de Lola, es exactamente lo que separa un modelo que cambia algo de uno que hace daño con convicción.

Y aquí conviene recordar algo que Raúl repite y que el día de hoy pone a prueba: el modelo es el 10% del trabajo. Mira todo lo que ha hecho falta para llegar a esta línea de `.fit()`: entender el negocio y la economía de la fuga (lunes), explorar cinco extractos y ver sus trampas (lunes), limpiar la suciedad del core y construir el target (martes), decidir qué columnas son features legítimas y cuáles no. Entrenar el modelo ha sido una tarde y tres líneas de código. Un científico de datos que sale de aquí sabiendo llamar a `.fit()` pero sin entender si su validación es honesta no ha aprendido lo que importa. Lo importante empieza mañana, cuando ese 99% se enfrente a la pregunta que debería haberte hecho tú solo.

Un 99% de acierto en un primer modelo de churn, hecho en una tarde, debería ponerte en guardia en vez de contento. Los problemas de verdad no suelen ser tan fáciles. Cuando un resultado es demasiado bueno para ser verdad, casi siempre es que no es verdad: o estás midiendo mal, o el modelo está viendo algo que no debería ver. Guarda esa incomodidad para mañana.

## Qué pasa de verdad cuando llamas a .fit()

Vale la pena entender, sin fórmulas, qué hace el modelo en ese `.fit(X_train, y_train)` que parece una línea mágica. El modelo tiene un peso por cada feature —un número que dice cuánto empuja esa columna hacia «se va»—, y al principio esos pesos no significan nada. Entrenar es ir ajustándolos poco a poco: el modelo mira un cliente del que sabe el desenlace, hace su predicción con los pesos actuales, compara con la verdad, y corrige los pesos un pelín en la dirección que reduce el error. Repite eso miles de veces sobre los 9.600 clientes de entrenamiento, y al final los pesos han «aprendido» qué combinación de antigüedad, cuota, consumo y tickets suele venir antes de una baja. El `max_iter=1000` es, precisamente, el número máximo de veces que le dejamos repetir ese ajuste. No hay magia: hay un bucle que afina números hasta que dejan de mejorar.

Y aquí está la clave de por qué el modelo solo puede ver el entrenamiento: si lo dejaras ajustar sus pesos mirando también la prueba, estaría afinándose para acertar justo esos clientes, y el acierto que midieras después sería mentira, porque ya habría «estudiado» el examen. Por eso el conjunto de prueba se aparta antes de entrenar y no se toca hasta el final. Es la misma lógica del examen con preguntas nuevas, pero dicha con pesos: el modelo aprende con unos datos y se mide con otros, y confundir los dos es la forma más común de engañarse a uno mismo con un número bonito.

Consejo de senior: la semilla aleatoria (random_state) no es un detalle para aprobar el ejercicio. En un proyecto real, fijarla es lo que permite que un compañero reproduzca tu resultado exacto, que compares dos modelos sin que el azar del reparto meta ruido, y que dentro de seis meses tú mismo puedas volver a tu análisis y obtener lo mismo. Un resultado que no se puede reproducir no es un resultado, es una anécdota.

Cierras el miércoles con un modelo entrenado, un 99% en la mano y a Lola celebrando en Slack. Sobre el papel, la semana está hecha: tienes un modelo que acierta casi siempre. Pero le has pedido a Lola un día, y Raúl te ha dejado caer que mañana tiene una pregunta para ti que no te va a gustar. Esa incomodidad de fondo —el presentimiento de que un 99% a la primera es demasiado fácil— es la que separa a quien va a crecer en este oficio de quien se queda celebrando el número. Mañana, en dos golpes encadenados, ese 99% se cae, y entiendes de verdad en qué consiste ser científico de datos.

Regístrate para guardar tu progreso.