Módulo 3 · Aprendizaje supervisado

Lección 1 de 9

Baseline y métricas

En esta páginaEl problema

El problema

Un compañero entrenó un modelo que clasifica piezas de una línea de producción como buenas (clase 0) o defectuosas (clase 1). Cada pieza tiene seis mediciones sintéticas. Evaluó el modelo en 250 piezas de prueba que no usó para entrenar:

clase realpiezas de prueba
buena (0)235
defectuosa (1)15
total250

El modelo acierta en 238 de las 250 piezas. Su reporte dice: «exactitud 0.952».

Exactitud y baseline

La exactitud es la proporción de predicciones correctas. Un número aislado no dice si el modelo aprendió algo. Hace falta un punto de comparación: un baseline, un predictor trivial que no mira las mediciones.

DummyClassifier(strategy="most_frequent") aprende solo cuál es la clase más frecuente en entrenamiento. Después predice esa clase para cada fila, sin importar X.

Las piezas se generan con make_classification: 1000 filas de seis mediciones, con un 94 % de piezas buenas. train_test_split aparta 250 para prueba, y stratify=y conserva en ambas partes la proporción de clases.

Mostrar códigoOcultar códigoPython
Python 3 · ejemplo reproducible
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(
    n_samples=1000,
    n_features=6,
    n_informative=3,
    weights=[0.94],
    class_sep=0.8,
    flip_y=0,
    random_state=3,
)
X_entrena, X_prueba, y_entrena, y_prueba = train_test_split(
    X, y, test_size=250, stratify=y, random_state=0
)

X_entrena tiene forma (750, 6) y X_prueba, (250, 6): una fila por pieza y una columna por medición. np.bincount cuenta cuántas veces aparece cada clase en las etiquetas de prueba.

Python 3 · ejemplo reproducible
print(np.bincount(y_prueba))
Salida esperada

La salida esperada está oculta.

LogisticRegression es el modelo del compañero. Cada predictor se ajusta con fit sobre los datos de entrenamiento, y predict devuelve una clase por pieza de prueba.

Python 3 · ejemplo reproducible
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression

modelo = LogisticRegression().fit(X_entrena, y_entrena)
baseline = DummyClassifier(strategy="most_frequent").fit(X_entrena, y_entrena)

pred_modelo = modelo.predict(X_prueba)
pred_baseline = baseline.predict(X_prueba)

accuracy_score compara las clases reales con las predichas y devuelve la proporción de coincidencias. np.unique lista las clases distintas que aparecen en las predicciones.

Pregunta. Con la tabla del problema, calcula la exactitud del baseline en las 250 piezas de prueba. Decide qué clase predice para todas.

Python 3 · ejemplo reproducible
from sklearn.metrics import accuracy_score

print(np.unique(pred_baseline))
print(f"modelo:   {accuracy_score(y_prueba, pred_modelo):.3f}")
print(f"baseline: {accuracy_score(y_prueba, pred_baseline):.3f}")
Salida esperada

La salida esperada está oculta.

Matriz de confusión

La exactitud mezcla dos tipos de acierto y dos tipos de error. La matriz de confusión los separa. En scikit-learn, cada fila es la clase real y cada columna es la clase predicha, en el orden 0, 1:

predicha buenapredicha defectuosa
real buenaVNFP
real defectuosaFNVP

VN son verdaderos negativos, FP falsos positivos, FN falsos negativos y VP verdaderos positivos. La clase positiva es la que se busca detectar: aquí, defectuosa.

confusion_matrix recibe las clases reales y las predichas y devuelve esa tabla como un arreglo de 2 × 2.

Pregunta. Escribe la matriz completa del baseline. Para el modelo, decide qué casillas deben sumar 238 y cuáles 12.

Python 3 · ejemplo reproducible
from sklearn.metrics import confusion_matrix

print(confusion_matrix(y_prueba, pred_baseline))
print(confusion_matrix(y_prueba, pred_modelo))
Salida esperada

La salida esperada está oculta.

Precisión, recall y F1

Tres métricas resumen la clase positiva a partir de la matriz:

  • precisión = VP / (VP + FP): de las piezas marcadas como defectuosas, qué fracción lo era
  • recall = VP / (VP + FN): de las piezas defectuosas, qué fracción se detectó
  • F1 = 2 · precisión · recall / (precisión + recall): la media armónica de ambas

Ninguna de las tres usa VN. En scikit-learn, precision_score, recall_score y f1_score reciben las clases reales y las predichas y toman 1 como clase positiva.

Pregunta. Con la matriz del modelo, calcula a mano su precisión, su recall y su F1. Predice también los tres valores del baseline.

Python 3 · ejemplo reproducible
from sklearn.metrics import f1_score, precision_score, recall_score

for nombre, pred in [("modelo", pred_modelo), ("baseline", pred_baseline)]:
    precision = precision_score(y_prueba, pred, zero_division=0)
    recall = recall_score(y_prueba, pred)
    f1 = f1_score(y_prueba, pred)
    print(f"{nombre:8}  precisión={precision:.3f}  recall={recall:.3f}  F1={f1:.3f}")
Salida esperada

La salida esperada está oculta.

ROC-AUC y ordenamiento

predict entrega una decisión por pieza. predict_proba entrega, en su segunda columna, una probabilidad de que la pieza sea defectuosa. ROC-AUC evalúa esas probabilidades sin convertirlas en decisiones: es la proporción de pares (defectuosa, buena) en los que la defectuosa recibe una probabilidad mayor. Un empate cuenta como medio par. El nombre viene del área bajo la curva ROC, y esa área coincide con la proporción de pares bien ordenados.

En este ejemplo pequeño hay 3 piezas defectuosas y 5 buenas, así que hay 3 × 5 = 15 pares. La máscara p_mini[y_mini == 1] separa las probabilidades de las defectuosas, y roc_auc_score recibe las clases reales y las probabilidades.

Pregunta. Cuenta cuántos de los 15 pares tienen a la defectuosa por encima de la buena. Calcula el ROC-AUC.

Python 3 · ejemplo reproducible
from sklearn.metrics import roc_auc_score

y_mini = np.array([1, 1, 1, 0, 0, 0, 0, 0])
p_mini = np.array([0.90, 0.60, 0.30, 0.80, 0.40, 0.20, 0.10, 0.05])

defectuosas = p_mini[y_mini == 1]
buenas = p_mini[y_mini == 0]
# recorre los 15 pares (defectuosa, buena)
bien_ordenados = sum(int(d > b) for d in defectuosas for b in buenas)

print(bien_ordenados, defectuosas.size * buenas.size)
print(round(bien_ordenados / (defectuosas.size * buenas.size), 3))
print(round(roc_auc_score(y_mini, p_mini), 3))
Salida esperada

La salida esperada está oculta.

Pregunta. El baseline asigna la misma probabilidad a todas las piezas. Predice su ROC-AUC. Predice también si el modelo, que solo detectó 5 de 15 con predict, queda cerca o lejos de 1.

Python 3 · ejemplo reproducible
# [:, 1] toma la columna de la clase 1 (defectuosa)
auc_modelo = roc_auc_score(y_prueba, modelo.predict_proba(X_prueba)[:, 1])
auc_baseline = roc_auc_score(y_prueba, baseline.predict_proba(X_prueba)[:, 1])

print(f"modelo:   {auc_modelo:.3f}")
print(f"baseline: {auc_baseline:.3f}")
Salida esperada

La salida esperada está oculta.

Explorador

Umbral y curva ROC

Mueve el umbral y observa cómo cambian la matriz de confusión, las métricas y el punto sobre la curva ROC.

Qué probar

Con 10 % de defectuosas, compara la exactitud con la del baseline que marca todas como buenas.

Curva ROCAUC 0.83

Una pieza se marca como defectuosa si su probabilidad es mayor o igual que el umbral. Cada umbral da un punto de la curva ROC: tasa de FP = FP / (FP + VN) en el eje horizontal y recall en el vertical. También puedes arrastrar la línea sobre la franja o moverla con las flechas.

Parámetros
Proporción de defectuosas
Umbral 0.50: VP 75, FP 29, FN 25, VN 71. Precisión 0.72, recall 0.75, F1 0.74. Exactitud 0.73; el baseline (todas buenas) da 0.50. AUC 0.83.

MAE y RMSE

La misma línea estima cuántos minutos tarda la reparación de cada pieza defectuosa. En regresión, el error de una fila es la predicción menos el valor real, en minutos:

  • MAE es el promedio de los errores en valor absoluto
  • RMSE es la raíz cuadrada del promedio de los errores al cuadrado

DummyRegressor(strategy="mean") predice para cada fila el promedio de los tiempos de entrenamiento. Igual que DummyClassifier, ignora los valores de X, aunque fit y predict necesitan una tabla con una fila por observación.

Hay dos modelos. A se equivoca por 4 minutos en cada pieza. B acierta cinco piezas y se equivoca por 24 minutos en la última. mean_absolute_error y root_mean_squared_error calculan cada métrica a partir de los valores reales y los predichos.

Pregunta. Calcula el valor que predice el baseline. Después, ordena baseline, A y B por MAE y por RMSE.

Python 3 · ejemplo reproducible
from sklearn.dummy import DummyRegressor
from sklearn.metrics import mean_absolute_error, root_mean_squared_error

tiempos_entrena = np.array([18, 22, 25, 27, 30, 33, 36, 49], dtype=float)
tiempos_prueba = np.array([20, 24, 28, 32, 36, 40], dtype=float)
pred_a = np.array([24, 20, 32, 28, 40, 36], dtype=float)
pred_b = np.array([20, 24, 28, 32, 36, 64], dtype=float)

# X de ceros: solo fija una fila por pieza
baseline_reg = DummyRegressor(strategy="mean").fit(np.zeros((8, 1)), tiempos_entrena)
pred_base_reg = baseline_reg.predict(np.zeros((6, 1)))
print(pred_base_reg)

for nombre, pred in [("baseline", pred_base_reg), ("A", pred_a), ("B", pred_b)]:
    mae = mean_absolute_error(tiempos_prueba, pred)
    rmse = root_mean_squared_error(tiempos_prueba, pred)
    print(f"{nombre:8}  MAE={mae:.2f}  RMSE={rmse:.2f}")
Salida esperada

La salida esperada está oculta.

Práctica

Traza guiada

Antes de procesar, escribe VP, FP, FN y VN para estas diez piezas. Después calcula exactitud, precisión y recall.

Python 3 · ejemplo reproducible
y_real = np.array([1, 0, 0, 1, 0, 0, 1, 0, 0, 0])
y_pred = np.array([1, 0, 1, 0, 0, 0, 1, 0, 0, 1])

Comprueba con confusion_matrix, accuracy_score, precision_score y recall_score. Si una casilla no coincide, recorre las diez posiciones y clasifica cada una como VP, FP, FN o VN.

Baseline estratificado

Cambia la estrategia del baseline del problema a DummyClassifier(strategy="stratified", random_state=0). Esta estrategia predice al azar con las proporciones de clase de entrenamiento. Antes de procesar, predice si su exactitud sube o baja frente a most_frequent, si aparecen falsos positivos y si su ROC-AUC queda cerca de 0.5 o de 1. Comprueba con la matriz de confusión y roc_auc_score.

Tabla de comparación

Genera un problema nuevo con make_classification(n_samples=1000, n_features=6, n_informative=3, weights=[0.8], random_state=1). Divídelo con estratificación, ajusta un baseline most_frequent y un LogisticRegression, y construye un DataFrame con una fila por predictor y estas columnas:

  1. exactitud
  2. precisión
  3. recall
  4. F1
  5. ROC-AUC

Supón que dejar pasar un positivo cuesta diez veces más que una falsa alarma. Escribe qué métrica reportarías primero y qué casilla de la matriz de confusión quieres reducir.

Reto opcional

Calcula el ROC-AUC del modelo del problema contando pares: recorre las 15 probabilidades de piezas defectuosas y las 235 de piezas buenas, suma 1 por cada par bien ordenado y 0.5 por cada empate, y divide entre 15 × 235. Comprueba que coincide con roc_auc_score.

Errores que revisar

  • Si la exactitud de un modelo parece alta, calcula primero la del baseline most_frequent con el conteo de clases de prueba.
  • Si precision_score emite UndefinedMetricWarning, el predictor no marcó ninguna fila como positiva; revisa la columna de la clase positiva en la matriz de confusión.
  • Si la matriz parece invertida, recuerda que las filas son la clase real y las columnas la predicha; con etiquetas 0 y 1, VP está en la esquina inferior derecha.
  • Si pasas la salida de predict a roc_auc_score, solo evalúas dos valores posibles y el orden se pierde; usa la columna de la clase positiva de predict_proba.
  • Si el RMSE es mucho mayor que el MAE, busca las filas con los errores más grandes antes de comparar modelos.

Cierre

Escribe una respuesta de tres líneas al reporte «exactitud 0.952»: qué baseline agregarías, qué dos métricas de la clase defectuosa pondrías a su lado y cuál reportarías primero si una pieza defectuosa entregada al cliente es el error más caro.

Lista de comprobación

  • ¿Puedes calcular la exactitud de most_frequent con solo el conteo de clases de prueba?
  • ¿Sabes qué casilla de la matriz cuenta las piezas defectuosas que pasaron como buenas?
  • ¿Distingues lo que mide ROC-AUC (orden de probabilidades) de lo que miden precisión y recall (decisiones)?
  • ¿Puedes construir dos conjuntos de predicciones con el mismo MAE y distinto RMSE?
  • ¿Puedes justificar una métrica a partir del error que cuesta más?

Documentación oficial

Cargando progreso…