ML clásico · Media · 20 min

¿Te fías de tu clasificador?

Un modelo de bajas de clientes ya está entrenado. Tú pones el umbral: cada falso positivo es una llamada de más y cada falso negativo, un cliente que se va.

MODEL es una regresión logística ya ajustada sobre CUSTOMERS (80 clientes sintéticos: meses de antigüedad, tickets de soporte, gasto mensual y si se dieron de baja). Un porcentaje de acierto no te dice nada hasta que sabes qué errores comete. Escribe seis funciones:

sigmoid(z): 1 / (1 + e^-z).

probability(model, customer): z = b + Σ w[i] · (customer[feature] - mean[i]) / sd[i] sobre FEATURES, y devuelve sigmoid(z).

confusionMatrix(probs, labels, threshold): { tp, fp, fn, tn }. Se predice baja cuando p >= threshold; labels lleva 1 para las bajas reales.

metrics(cm): { precision, recall, accuracy }, con 0 cuando un denominador es 0.

cost(cm, costFP, costFN): fp · costFP + fn · costFN.

bestThreshold(probs, labels, costFP, costFN): el umbral de THRESHOLDS con menor coste; en empate, el más bajo.

La vista previa pinta la matriz y la curva de coste, y mueve el umbral y el precio relativo de cada error.

Retos 0/4

  • sigmoid y probability: la curva en S sobre las variables estandarizadas
  • confusionMatrix cuenta los cuatro casos desde el umbral (p >= threshold es baja)
  • metrics: precisión, recall y acierto, sin dividir entre cero
  • cost y bestThreshold: el umbral baja cuando perder un cliente sale caro

function sigmoid(z) {
  // 1 dividido entre (1 + e^-z), la curva en S
  return 0.5;
}

function probability(model, customer) {
  // z = b + suma de w[i] * (customer[variable] - mean[i]) / sd[i], y luego sigmoid
  return 0.5;
}

function confusionMatrix(probs, labels, threshold) {
  // se predice baja cuando p >= threshold; cuenta tp, fp, fn, tn
  return { tp: 0, fp: 0, fn: 0, tn: 0 };
}

function metrics(cm) {
  // precisión, recall y acierto; 0 cuando un denominador es 0
  return { precision: 0, recall: 0, accuracy: 0 };
}

function cost(cm, costFP, costFN) {
  // llamadas de más por costFP más bajas sin aviso por costFN
  return 0;
}

function bestThreshold(probs, labels, costFP, costFN) {
  // el umbral de THRESHOLDS con menor coste (en empate, el más bajo)
  return 0.5;
}

const probs = CUSTOMERS.map((c) => probability(MODEL, c));
console.log(confusionMatrix(probs, LABELS, 0.5), bestThreshold(probs, LABELS, 1, 8));
La consola aparece aquí (console.log).

La idea

Un clasificador no decide: da una probabilidad. La decisión la pone el umbral, y el umbral lo pones tú con los costes de tu negocio en la mano. La matriz de confusión separa los dos errores posibles: avisar a un cliente que no se iba a ir (una llamada de más) y no avisar a uno que se va (un cliente perdido). Casi nunca cuestan lo mismo.

Con el umbral a 0,5 el modelo de este ejercicio acierta mucho, pero se le escapan bajas. Si una baja sin aviso cuesta como veinte llamadas, bajar el umbral a 0,2 sale más barato aunque el «acierto» empeore. Esa cuenta es la que importa, y es la que el porcentaje de acierto esconde.

La precisión te dice cuántas de tus alarmas eran reales; el recall, cuántas bajas reales has cazado. Subir una suele bajar la otra, y el umbral es la palanca entre las dos.

Esto conecta con la transparencia que pide la Ley de IA de la UE para los sistemas que afectan a personas: poder decir qué error acepta el sistema, y por qué, es parte de la documentación.

Hazlo en tu máquina

Con Python y scikit-learn, sobre el CSV de este ejercicio (CC BY 4.0). Cambia el umbral y mira cómo se mueve la matriz:

curl -O https://vorluxai.com/data/labs/churn-clientes.csv
python3 -c "
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
df = pd.read_csv('churn-clientes.csv')
X, y = df[['meses', 'tickets', 'gasto_mensual_eur']], df.baja
p = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]
for t in (0.2, 0.5, 0.8): print(t, confusion_matrix(y, p >= t).ravel())
"

Comprueba lo que has entendido

1. Un modelo acierta el 95 % en un negocio donde solo el 5 % de los clientes se da de baja. ¿Qué sabes?
2. Perder un cliente cuesta mucho más que una llamada. ¿Hacia dónde mueves el umbral?
3. ¿Qué mide la precisión?
4. ¿Por qué la regresión logística estandariza las variables antes de entrenar?

Para profundizar: la referencia de Google Machine Learning Crash Course (en inglés) →

¿Esto en producción, con tus datos? Hablemos 15 minutos →