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.
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.
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.
Á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## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...
Regístrate para guardar tu progreso.