En la lección anterior aprendimos con qué datos evaluar un modelo; ahora toca decidir con qué número. Hasta el momento hemos usado el accuracy (clasificación) y el MSE (regresión) de forma instrumental, sin cuestionarlos. Eso se acaba aquí: veremos que el accuracy puede ser una trampa descarada con clases desbalanceadas —el churn de MercaFresh ronda el 20 %—, que la matriz de confusión distingue tipos de error con costes de negocio muy distintos (¿qué es peor: llamar a un cliente fiel o perder a uno que se iba a marchar?), y que en regresión conviven varias métricas (MAE, MSE, RMSE, R²) con interpretaciones y unidades diferentes. Cerraremos con los modelos dummy, la materialización en scikit-learn del concepto de baseline que introdujimos en la lección 01-04: ninguna métrica significa nada en el vacío; solo significa algo comparada con una referencia.

Contenido

  1. La matriz de confusión: los cuatro destinos de una predicción
  2. Accuracy y su trampa con el desbalanceo
  3. Precision, recall y F1: fórmulas e interpretación de negocio
  4. classification_report y elección de métrica según el problema
  5. Métricas de regresión: MAE, MSE, RMSE y R²
  6. El baseline: DummyClassifier y DummyRegressor

La matriz de confusión: los cuatro destinos de una predicción

En un problema binario como el churn (1 = el cliente se da de baja, 0 = se queda), cada predicción puede acabar en uno de cuatro destinos, según lo que predijo el modelo y lo que ocurrió en realidad:

Predicho: churn (1) Predicho: se queda (0)
Real: churn (1) TP (verdadero positivo) FN (falso negativo)
Real: se queda (0) FP (falso positivo) TN (verdadero negativo)
  • TP (true positive): predijimos churn y el cliente efectivamente se fue. Acierto.
  • TN (true negative): predijimos que se quedaba y se quedó. Acierto.
  • FP (falso positivo): predijimos churn, pero el cliente era fiel. Es la "falsa alarma": MercaFresh gasta una llamada y quizá un descuento en alguien que no se iba a marchar.
  • FN (falso negativo): predijimos que se quedaba y se fue. El error más caro aquí: perdemos al cliente sin haber intentado retenerlo.

Esto no es nuevo para ti: en la lección 02-05, con el teorema de Bayes, analizamos un detector con falsos positivos y vimos que el coste de cada tipo de error es distinto y que con eventos poco frecuentes las falsas alarmas pueden dominar. La matriz de confusión es exactamente ese análisis, aplicado a un clasificador.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y  # como en 06-01
)

modelo = LogisticRegression(max_iter=1000)
modelo.fit(X_train, y_train)
y_pred = modelo.predict(X_test)

cm = confusion_matrix(y_test, y_pred)
print(cm)
# [[TN  FP]
#  [FN  TP]]   ← ojo: sklearn ordena las clases 0, 1 (filas = real, columnas = predicho)

ConfusionMatrixDisplay(cm, display_labels=["Se queda", "Churn"]).plot()

Supongamos que sobre 2.000 clientes de test obtenemos:

Pred: churn Pred: se queda
Real: churn (400) TP = 240 FN = 160
Real: se queda (1.600) FP = 120 TN = 1.480

Toda métrica de clasificación binaria se calcula a partir de estos cuatro números.

Accuracy y su trampa con el desbalanceo

El accuracy es la fracción de aciertos totales:

$$\text{accuracy} = \frac{TP + TN}{TP + TN + FP + FN} = \frac{240 + 1480}{2000} = 0.86$$

Un 86 % suena bien... hasta que recordamos el desbalanceo. Con un churn del 20 %, un "modelo" absurdo que prediga siempre "se queda" acierta el 80 % de las veces:

import numpy as np
y_pred_vago = np.zeros_like(y_test)          # siempre predice "se queda"
print((y_pred_vago == y_test).mean())        # ≈ 0.80

Ese modelo tiene un 80 % de accuracy y es completamente inútil: no detecta ni un solo cliente en fuga (TP = 0). La trampa se agrava cuanto mayor es el desbalanceo: en detección de fraude con un 1 % de positivos, "nunca hay fraude" alcanza un 99 % de accuracy. Moraleja: con clases desbalanceadas, el accuracy por sí solo engaña; necesitamos métricas que miren cada clase por separado.

Precision, recall y F1: fórmulas e interpretación de negocio

Precision: cuando el modelo da la alarma, ¿acierta?

$$\text{precision} = \frac{TP}{TP + FP} = \frac{240}{240 + 120} = 0.67$$

De todos los clientes que el modelo señaló como churn, ¿qué fracción se iba de verdad? En MercaFresh: si el equipo de retención llama a los señalados, la precision mide qué porcentaje de llamadas no se desperdicia. Una precision baja significa muchas falsas alarmas: coste de campaña tirado en clientes fieles (y el riesgo de molestarlos).

Recall: de los que se van, ¿a cuántos cazamos?

$$\text{recall} = \frac{TP}{TP + FN} = \frac{240}{240 + 160} = 0.60$$

De todos los clientes que realmente se dieron de baja, ¿qué fracción detectó el modelo? Un recall del 60 % significa que el 40 % de las fugas reales pasan desapercibidas: clientes que se pierden sin que nadie intentara retenerlos. También se llama sensibilidad o tasa de verdaderos positivos (con ese nombre reaparecerá en la curva ROC de 06-04).

El tira y afloja, y la pregunta de negocio

Precision y recall están en tensión: para subir el recall hay que señalar a más clientes (alarma más sensible), lo que genera más falsos positivos y baja la precision, y viceversa. ¿Cuál priorizar? Depende de los costes:

  • Coste de un FP: una llamada del equipo de retención más, quizá, un vale de 10 €. Barato.
  • Coste de un FN: perder un cliente cuyo valor anual medio en MercaFresh puede ser de cientos de euros. Caro.

Con esta estructura de costes, en el churn suele interesar priorizar el recall: mejor unas cuantas llamadas de más que clientes valiosos escapándose en silencio. En otros problemas es al revés (un filtro de spam con precision baja entierra correos legítimos). En 06-04 veremos que el umbral de decisión de la regresión logística (04-02) es precisamente la palanca para mover este equilibrio.

F1: el resumen en un número

Cuando se necesita una sola cifra que combine ambas, se usa la media armónica de precision y recall:

$$F_1 = 2 \cdot \frac{\text{precision} \cdot \text{recall}}{\text{precision} + \text{recall}} = 2 \cdot \frac{0.67 \cdot 0.60}{0.67 + 0.60} \approx 0.63$$

La media armónica castiga los desequilibrios: si una de las dos es próxima a 0, el F1 se hunde aunque la otra sea perfecta (a diferencia de la media aritmética). Un modelo solo tiene buen F1 si es razonable en ambas.

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

print("Accuracy :", accuracy_score(y_test, y_pred))
print("Precision:", precision_score(y_test, y_pred))
print("Recall   :", recall_score(y_test, y_pred))
print("F1       :", f1_score(y_test, y_pred))

classification_report y elección de métrica según el problema

scikit-learn resume todo lo anterior, por clase, en una sola llamada:

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred, target_names=["Se queda", "Churn"]))
              precision    recall  f1-score   support

    Se queda       0.90      0.93      0.91      1600
       Churn       0.67      0.60      0.63       400

    accuracy                           0.86      2000
   macro avg       0.78      0.76      0.77      2000
weighted avg       0.85      0.86      0.86      2000

Cómo leerlo:

  • Cada fila da precision/recall/F1 tratando esa clase como "positiva". support es cuántos ejemplos reales hay de esa clase en el test.
  • macro avg: media simple de las clases (todas pesan igual, útil con desbalanceo para no ocultar la clase minoritaria).
  • weighted avg: media ponderada por support (con desbalanceo se parece al accuracy y puede maquillar una clase minoritaria mala).
  • Fíjate en que la clase mayoritaria luce un 0.91 de F1 mientras el churn se queda en 0.63: el informe por clases destapa lo que el accuracy global escondía.

¿Qué métrica elegir? Una guía práctica:

Situación Métrica principal Por qué
Clases equilibradas, errores de coste similar Accuracy Simple y suficiente
El coste alto es la falsa alarma (spam, bloquear compras legítimas) Precision Minimizar FP
El coste alto es el positivo no detectado (churn, diagnóstico, fraude) Recall Minimizar FN
Desbalanceo y se quiere un solo número equilibrado F1 Combina P y R
Comparar modelos independientemente del umbral ROC-AUC Lección 06-04

Métricas de regresión: MAE, MSE, RMSE y R²

Cambiamos de problema: la predicción del gasto mensual de cada cliente de MercaFresh, la regresión que planteamos en 04-01. Aquí no hay aciertos y fallos, sino distancias entre lo predicho ($\hat{y}_i$) y lo real ($y_i$).

  • MAE (error absoluto medio): $\frac{1}{n}\sum |y_i - \hat{y}_i|$. Se lee directamente en las unidades del target: "nos equivocamos, de media, en 18 € al mes". Trata todos los errores por igual.
  • MSE (error cuadrático medio): $\frac{1}{n}\sum (y_i - \hat{y}_i)^2$. Al elevar al cuadrado, castiga mucho más los errores grandes (un error de 100 € pesa como 100 errores de 10 €) y sus unidades son €², poco interpretables. Es la métrica que la regresión lineal minimiza internamente (04-01).
  • RMSE: $\sqrt{MSE}$. Recupera las unidades originales (€) manteniendo la sensibilidad a errores grandes. Siempre RMSE ≥ MAE; una brecha grande entre ambos delata la existencia de errores puntuales enormes (outliers).
  • (coeficiente de determinación): fracción de la varianza del target que el modelo explica. 1.0 = predicción perfecta; 0.0 = tan bueno como predecir siempre la media; puede ser negativo en test si el modelo es peor que la media. Es adimensional, lo que permite comparar problemas con escalas distintas.
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

reg = LinearRegression().fit(X_train_g, y_train_g)   # gasto mensual (€)
y_pred_g = reg.predict(X_test_g)

mae  = mean_absolute_error(y_test_g, y_pred_g)
mse  = mean_squared_error(y_test_g, y_pred_g)
rmse = np.sqrt(mse)
r2   = r2_score(y_test_g, y_pred_g)

print(f"MAE : {mae:.2f} €      ← error medio 'típico'")
print(f"MSE : {mse:.2f} €²     ← difícil de interpretar directamente")
print(f"RMSE: {rmse:.2f} €      ← como MAE pero penaliza errores grandes")
print(f"R²  : {r2:.3f}          ← fracción de varianza explicada")
Métrica Unidades Sensible a outliers Cuándo preferirla
MAE Las del target (€) No especialmente Interpretación directa; errores grandes no son "peores por unidad"
MSE Target² (€²) Mucho Optimización interna; comparación técnica
RMSE Las del target (€) Mucho Reportar cuando los errores grandes son especialmente costosos
Sin unidades Moderada Comunicar "cuánto explica" el modelo; comparar datasets

En MercaFresh, un MAE de 18 € sobre un gasto medio de 120 € mensuales (~15 % de error relativo) es un mensaje que el negocio entiende de inmediato; un MSE de 850 €² no.

El baseline: DummyClassifier y DummyRegressor

En la lección 01-04 introdujimos el concepto de baseline: antes de celebrar cualquier métrica, hay que preguntarse "¿mejor que qué?". scikit-learn lo materializa con modelos deliberadamente tontos:

from sklearn.dummy import DummyClassifier, DummyRegressor
from sklearn.metrics import f1_score, mean_absolute_error

# Clasificación: predecir siempre la clase mayoritaria ("se queda")
dummy_clf = DummyClassifier(strategy="most_frequent")
dummy_clf.fit(X_train, y_train)
print("Accuracy dummy:", dummy_clf.score(X_test, y_test))          # ≈ 0.80
print("F1 dummy      :", f1_score(y_test, dummy_clf.predict(X_test)))  # 0.0 !

# Regresión: predecir siempre el gasto medio
dummy_reg = DummyRegressor(strategy="mean")
dummy_reg.fit(X_train_g, y_train_g)
print("MAE dummy:", mean_absolute_error(y_test_g, dummy_reg.predict(X_test_g)))

La lectura es reveladora: el dummy alcanza un 80 % de accuracy (¡la trampa del desbalanceo, cuantificada!) pero un F1 de 0 en la clase churn. Nuestro modelo real, con 86 % de accuracy, solo mejora al dummy en 6 puntos de accuracy... pero pasa de 0.0 a 0.63 de F1, que es donde está el valor real. Reglas prácticas:

  • Calcula siempre el baseline dummy antes de evaluar tu primer modelo.
  • Reporta las métricas de tu modelo junto a las del baseline: "MAE de 18 € frente a 41 € del baseline de la media" es un resultado; "MAE de 18 €" a secas, no.
  • Si tu modelo no supera claramente al dummy, algo falla: features sin señal, fuga en dirección contraria, un bug... o un problema que simplemente no se puede predecir con esos datos.

Errores Comunes y Consejos

  • Reportar solo accuracy con clases desbalanceadas. Es el error estrella. Acompáñalo siempre de la matriz de confusión y del F1 (o precision/recall) de la clase minoritaria.
  • Confundir precision con recall. Truco mnemotécnico: precision = de mis predicciones positivas, ¿cuántas eran correctas?; recall = de los positivos reales, ¿cuántos recuperé?
  • Elegir la métrica después de ver los resultados. Decidir "mi métrica es la que me sale mejor" es autoengaño. Fija la métrica (con el negocio) antes de comparar modelos.
  • Interpretar el MSE en unidades del target. El MSE está en unidades al cuadrado; para comunicar usa MAE o RMSE.
  • Asumir que R² alto = modelo útil. Un R² de 0.95 con fuga de datos (06-01) no vale nada; y un R² de 0.3 puede ser valiosísimo en un problema muy ruidoso, si supera con claridad al baseline.
  • Consejo: la matriz de confusión es tu radiografía por defecto: casi cualquier duda sobre un clasificador binario ("¿dónde falla?, ¿a quién confunde?") se responde mirándola antes que a ninguna métrica agregada.

Ejercicios

Ejercicio 1

Un modelo de churn evaluado sobre 1.000 clientes (200 churn reales) produce: TP = 150, FN = 50, FP = 100, TN = 700. Calcula a mano accuracy, precision, recall y F1. ¿Qué accuracy tendría el dummy de clase mayoritaria? ¿Merece la pena el modelo?

Ejercicio 2

MercaFresh evalúa dos modelos de gasto mensual. Modelo A: MAE = 15 €, RMSE = 45 €. Modelo B: MAE = 18 €, RMSE = 22 €. Explica qué revela la diferencia entre MAE y RMSE de cada modelo y razona cuál elegirías si los errores grandes de predicción (p. ej., infraestimar en 200 € el gasto de un gran cliente) son especialmente costosos.

Ejercicio 3

Escribe el código que entrena una LogisticRegression y un DummyClassifier(strategy="most_frequent") sobre X_train, y_train (churn), y para ambos imprime el classification_report sobre test. Indica qué dos números del informe compararías para decidir si el modelo aporta valor.

Soluciones

Solución 1.

  • Accuracy = (150 + 700) / 1000 = 0.85
  • Precision = 150 / (150 + 100) = 0.60
  • Recall = 150 / (150 + 50) = 0.75
  • F1 = 2 · (0.60 · 0.75) / (0.60 + 0.75) = 0.9 / 1.35 ≈ 0.67
  • Dummy mayoritario: acierta los 800 "se queda" → accuracy = 0.80.

El modelo solo gana 5 puntos de accuracy al dummy, pero eso es engañoso a favor del dummy: el modelo detecta el 75 % de las fugas reales (el dummy, el 0 %). Con el coste asimétrico del churn (perder un cliente ≫ una llamada de más), sí merece la pena, y el recall de 0.75 con precision 0.60 es un compromiso razonable para una campaña de retención.

Solución 2. La brecha RMSE − MAE mide cuánto pesan los errores grandes. El modelo A tiene mejor error típico (MAE 15 < 18) pero un RMSE triple que su MAE (45 vs. 15): comete errores puntuales muy grandes. El modelo B es algo peor "en el día a día" pero sus errores son homogéneos (22 vs. 18: sin catástrofes). Si los errores grandes son especialmente costosos, se elige el modelo B: sacrifica 3 € de error medio a cambio de eliminar los fallos extremos.

Solución 3.

from sklearn.linear_model import LogisticRegression
from sklearn.dummy import DummyClassifier
from sklearn.metrics import classification_report

modelo = LogisticRegression(max_iter=1000).fit(X_train, y_train)
dummy  = DummyClassifier(strategy="most_frequent").fit(X_train, y_train)

for nombre, m in [("Regresión logística", modelo), ("Dummy", dummy)]:
    print(f"===== {nombre} =====")
    print(classification_report(y_test, m.predict(X_test),
                                target_names=["Se queda", "Churn"],
                                zero_division=0))

Compararía sobre todo el recall y el F1 de la clase "Churn": en el dummy son 0.0 (no señala a nadie), así que cualquier valor claramente positivo del modelo demuestra que aporta capacidad real de detección, cosa que el accuracy (0.80 vs. ~0.86) apenas deja ver.

Conclusión

Ya sabemos medir: la matriz de confusión separa los cuatro destinos de una predicción y conecta con el análisis de costes de errores que hicimos con Bayes en 02-05; el accuracy engaña con el desbalanceo del churn; precision y recall traducen los errores a lenguaje de negocio (llamadas desperdiciadas frente a clientes perdidos) y el F1 los resume; en regresión, MAE/RMSE hablan en euros y el R² en varianza explicada; y el DummyClassifier/DummyRegressor convierte el baseline de 01-04 en código, dando contexto a cualquier cifra. Pero todas estas métricas las hemos calculado sobre una única división train/test, y esa división depende del azar de una semilla: con otro random_state, otros números. ¿Cómo de fiable es entonces una métrica calculada una sola vez? Esa inquietud tiene nombre y solución —la validación cruzada— y es exactamente el tema de la próxima lección.

Curso de Machine Learning

Módulo 1: Introducción al Machine Learning

Módulo 2: Fundamentos de Estadística y Probabilidad

Módulo 3: Preprocesamiento de Datos

Módulo 4: Algoritmos de Machine Learning Supervisado

Módulo 5: Algoritmos de Machine Learning No Supervisado

Módulo 6: Evaluación y Validación de Modelos

Módulo 7: Técnicas Avanzadas y Optimización

Módulo 8: Implementación y Despliegue de Modelos

Módulo 9: Proyectos Prácticos

Módulo 10: Recursos Adicionales

© Copyright 2026. Todos los derechos reservados