Saltar al contenido

lección 6

Jueves: el problema era el corte, no el modelo

La tentación de reequilibrar con class_weight, por qué se descarta (ordena igual y estropea las probabilidades), y la solución: ordenar por riesgo y llamar a tantos como quepan.

⏱ 55 min

## Jueves, 11:35. La receta que sale en todos los tutoriales

Ya sabes que, con el corte en 0,5, tu modelo caza a 3 de 329. Buscas «clases desbalanceadas» y casi todas las recetas dicen lo mismo: reequilibra. En scikit-learn se hace con `class_weight='balanced'`, que le dice al modelo que un error con la clase rara pesa más, en proporción a lo rara que es. Se lo cuentas a Álvaro. «Pruébalo», te dice. «Pero luego compáralo bien: con el mismo número de llamadas. Si no, comparas peras con manzanas.»

Reentrenas y los números dan un vuelco: con el corte en 0,5, el reequilibrado caza a 234 de 329 (un recall de 0,711). Parece magia. Pero mira cuántos socios marca: 1.141, frente a los 7 de antes. Claro que caza más: llama a muchos más. La comparación justa es con las mismas llamadas. Si cada modelo llama a sus 450 socios de más riesgo, el normal caza 157 y el reequilibrado 158. Y si al normal le dejas llamar a los mismos 1.141, caza 235, uno más que el reequilibrado. Los dos ordenan a los socios casi igual (PR-AUC de 0,351 y 0,348). Reequilibrar no le ha enseñado nada nuevo al modelo: ha subido todas sus probabilidades, y con ellas el número de socios que pasan del 0,5.

La comparación justa. Con el corte en 0,5 el reequilibrado parece mucho mejor, pero es porque llama a 1.141 socios. Con las mismas 450 llamadas cazan lo mismo, y el reequilibrado ya no dice la verdad sobre el riesgo.

Y ese vuelco tiene un precio que la matriz de confusión no enseña. El modelo normal predice un riesgo medio de 0,109, casi la tasa real (0,11): sus probabilidades significan lo que dicen. Eso se llama estar calibrado. El reequilibrado predice un riesgo medio de 0,436, cuatro veces la realidad: está descalibrado, y un 0,5 suyo no es un 50 % de riesgo. Por eso bajar el corte a 0,3 en el modelo reequilibrado marca al 74 % de la prueba (2.207 de 3.000): no es que tres de cada cuatro socios tengan un 30 % de riesgo, es que las probabilidades están infladas. Y con probabilidades infladas ya no puedes hacer cuentas con ellas: ni cuántos socios esperas perder, ni si compensa una llamada.

## La otra tentación: remuestrear a mano

La segunda receta de los tutoriales es remuestrear: en vez de cambiar los pesos, cambias los datos. Te quedas con todos los socios del entrenamiento que no renuevan (1.314) y con una muestra igual de grande de los que sí, usando `df.sample()`. El modelo ve las dos clases a partes iguales y, con el corte en 0,5, el recall sube a 0,723. Pero es la misma trampa con otro disfraz: el modelo cree que la mitad de los socios se va, así que infla el riesgo (riesgo medio previsto 0,439), y además has tirado 9.372 socios que renovaban y que le enseñaban algo. Su PR-AUC queda en 0,347, un pelo por debajo del normal: ordena igual o un poco peor.

Las dos recetas para reequilibrar: cambiar los pesos (class_weight) o cambiar los datos (df.sample). Las dos hacen lo mismo: inflan las probabilidades para que más socios pasen del 0,5. Ninguna ordena mejor que el modelo normal.

Si te encuentras SMOTE o la librería imbalanced-learn (imblearn) en algún tutorial: no funcionan en el navegador donde corre tu código. Y por lo que acabas de ver, para ordenar una lista tampoco hacen falta. Aquí el reequilibrado solo aparece con class_weight='balanced' y df.sample(), y solo para entender por qué no lo usamos.

## La solución de verdad: ordenar y llamar a tantos como quepan

Álvaro te lo resume: «Fidelización no te ha pedido una casilla de sí o no. Te ha pedido a quién llamar primero. Tienen manos para unas 2.000 llamadas entre los 13.500 socios de hoy, un 15 %. Pues ordena por riesgo y corta donde se acaben las llamadas.» Eso es un umbral por capacidad: el corte no es 0,5, es el riesgo del socio que ocupa el puesto 2.000 de la lista. En la prueba, ese 15 % son 450 socios. Con esas 450 llamadas, el modelo normal, sin tocarlo, caza a 157 de los 329 que se van. Llamando a 450 socios al azar cazarías unos 49. Más de tres veces más con las mismas llamadas.

Hay otra forma de elegir el corte: por coste. Si un día sabéis cuánto cuesta una llamada y cuánto vale un socio que se queda, compensa llamar a un socio cuando su riesgo, multiplicado por lo que se recupera si la llamada funciona, supera lo que cuesta llamar. Esa cuenta solo vale con probabilidades calibradas, las del modelo normal: otra razón para no reequilibrar. Y tiene un dato que estos datos no tienen: cuántos de los llamados se quedan gracias a la llamada. Eso no se deduce de quién renovó; se mide con una prueba: de la lista, llamar a una mitad elegida al azar, no llamar a la otra, y comparar cuántos renuevan en cada mitad.

La curva precision-recall: cada corte de la lista es un punto. Pocas llamadas dan una lista corta y precisa; muchas, una lista larga que se deja a menos. El punto lo elige la capacidad del equipo, no el 0,5.

Álvaro cierra el día: «Resumen de lo que has descubierto hoy, y vale más que el modelo entero. El 90% era un espejismo, sí. Pero el modelo no estaba mal: estaba mal el corte. Reequilibrar parecía arreglarlo porque subía el número de marcados, pero ordena igual y rompe las probabilidades.» Y tú lo rematas: «Entonces no hay que cambiar el modelo, hay que cambiar cómo se usa: ordenar por riesgo y llamar a tantos como quepan.» Eso es lo que lleváis el viernes.

Sigues con datos/tabla_modelo.csv y el reparto de siempre (test_size=0.2, random_state=42, stratify=y). Hoy cierras src/04_evaluar.py (las dos tentaciones de reequilibrar, y por qué se descartan) y abres src/05_prediccion.py con el umbral por capacidad. En la prueba hay 3.000 socios: la capacidad equivalente a 2.000 llamadas entre 13.500 son 450.

volver a la visión del caso y descargar los datasets

Regístrate para guardar tu progreso.