Métricas: clasificación, regresión, qué elegir

r
machine-learning
tidymodels
yardstick como API unificada. Accuracy, precision, recall, F1, ROC AUC vs PR AUC para clases desbalanceadas, log-loss y calibración. RMSE vs MAE vs MAPE en regresión. metric_set() para combinarlas.

yardstick: la API unificada

Antes de yardstick, calcular métricas en R era un mosaico: ROCR para curvas ROC, pROC para AUC, fórmulas a mano para RMSE, caret::confusionMatrix para clasificación. Cada uno con su sintaxis.

yardstick unifica todo bajo un patrón consistente: la métrica recibe un data.frame con las columnas de verdad y predicción, y devuelve un valor.

library(tidymodels)

# Patrón base (regresión)
predicciones |>
  rmse(truth = real, estimate = .pred)

# Patrón base (clasificación con probabilidades)
predicciones |>
  roc_auc(truth = clase, .pred_positivo)

Funciona idénticamente sea cual sea la métrica. Si dominas el patrón, todo el catálogo es accesible.

Métricas de clasificación: el zoo

Para clasificación binaria con clase positiva = “Sí”:

Métrica Qué mide Función
Accuracy Predicciones correctas / total accuracy()
Precision Verdaderos positivos / predichos positivos precision()
Recall (Sensitivity) Verdaderos positivos / reales positivos recall()
Specificity Verdaderos negativos / reales negativos specificity()
F1 Media armónica de precision y recall f_meas()
ROC AUC Área bajo la curva ROC roc_auc()
PR AUC Área bajo la curva precision-recall pr_auc()
Log-loss Penalización logarítmica de probabilidades mn_log_loss()

Más de 30 funciones en total. El catálogo cubre prácticamente cualquier situación.

Por qué accuracy no basta con clases desbalanceadas

Imagina un detector de fraude con 1 % de positivos. Un modelo que dice “no fraude” siempre tiene:

  • Accuracy = 99 %: parece excelente.
  • Recall = 0 %: no detecta ningún fraude.
  • Precision = NaN (no hay positivos predichos).
  • F1 = 0.

Accuracy es engañosa cuando hay clases desbalanceadas. La métrica útil depende del coste de cada tipo de error:

  • Si falsos negativos son caros (perder un fraude, no detectar una enfermedad grave): prioriza recall.
  • Si falsos positivos son caros (mandar a investigación a un cliente legítimo): prioriza precision.
  • Si ambos importan por igual: F1 (la media armónica los balancea).
  • Si quieres una vista global: ROC AUC o PR AUC.

ROC AUC vs PR AUC: cuándo cada una

Las dos curvas más usadas para clasificación binaria con probabilidades:

ROC AUC representa true positive rate vs false positive rate al variar el umbral. AUC = 1 es perfecto, AUC = 0.5 es azar.

  • Bueno: invariante al desbalanceo de clases en muchos casos.
  • Trampa: con clases muy desbalanceadas, ROC AUC puede dar valores engañosamente altos porque el espacio de “negativos correctos” es enorme y dominan la curva.

PR AUC representa precision vs recall al variar el umbral.

  • Mejor para clases desbalanceadas porque ignora los verdaderos negativos.
  • Trampa: depende de la proporción de la clase positiva, no es comparable directamente entre datasets con distinto balance.

Regla práctica:

  • Balance ~50/50: ROC AUC.
  • Desbalanceo notable (≤ 10 % de positivos): PR AUC, o reporta ambas.
  • Para detectar minoría: PR AUC casi siempre.
predicciones |>
  roc_auc(truth = clase, .pred_positivo)
predicciones |>
  pr_auc(truth = clase, .pred_positivo)

Visualizar las curvas

library(ggplot2)

predicciones |>
  roc_curve(truth = clase, .pred_positivo) |>
  autoplot()

predicciones |>
  pr_curve(truth = clase, .pred_positivo) |>
  autoplot()

autoplot() da una curva con la diagonal de referencia. Si el modelo está cerca de la diagonal en ROC, predice al azar.

Log-loss y calibración

Log-loss (también llamado cross-entropy) penaliza la distancia entre probabilidad predicha y realidad. Si tu modelo dice “85 % probabilidad de cáncer” y resulta positivo, log-loss penaliza poco. Si dice “5 %” y resulta positivo, penaliza mucho.

predicciones |>
  mn_log_loss(truth = clase, .pred_positivo)

Cuándo importa: cuando las probabilidades predichas se usan para decidir (no solo la clase). En medicina, un “75 % de probabilidad de enfermedad” debería ocurrir el 75 % de las veces, si tu modelo dice 75 % y solo el 30 % de esos casos son positivos, está mal calibrado aunque clasifique bien.

Para evaluar calibración visualmente:

library(probably)
predicciones |>
  cal_plot_breaks(truth = clase, estimate = .pred_positivo)

La curva debería estar cerca de la diagonal. Modelos sobreconfiados (gradient boosting, random forest sin calibración) suelen necesitar recalibración (Platt scaling, isotonic regression) si el caso de uso depende de probabilidades.

Métricas de regresión: las tres clásicas

Métrica Qué mide Función Penaliza
RMSE Raíz del error cuadrático medio rmse() Errores grandes desproporcionadamente
MAE Error absoluto medio mae() Errores proporcionalmente
MAPE Error absoluto porcentual medio mape() En proporción relativa
Proporción de varianza explicada rsq() (Bondad de ajuste, no error)

Diferencias críticas:

  • RMSE > MAE siempre cuando hay errores grandes (RMSE eleva al cuadrado). Si los outliers en error te preocupan más que los típicos, RMSE es la métrica.
  • MAE trata todos los errores por igual. Si un error de 100 es exactamente 2× peor que uno de 50 en tu problema, MAE es lo correcto.
  • MAPE expresa en porcentaje. Útil cuando las escalas varían: predecir el precio de viviendas con MAPE permite comparar un error en una casa de 100k€ con uno en 1M€. Trampa: indefinida cuando la real es 0. Explota cuando es pequeña.
  • es la métrica que entiende cualquier audiencia no técnica. Para reporting general, suma R² al RMSE/MAE.
predicciones |>
  metrics(truth = real, estimate = .pred)
#> # A tibble: 3 × 3
#>   .metric .estimator .estimate
#>   <chr>   <chr>          <dbl>
#> 1 rmse    standard       2.45
#> 2 rsq     standard       0.85
#> 3 mae     standard       1.82

metrics() aplica un conjunto sensato por defecto (RMSE, RSQ, MAE para regresión. Accuracy y AUC para clasificación).

metric_set(): combinar varias en una llamada

Cuando entrenas con fit_resamples() o tune_grid(), especificas qué métricas calcular:

mis_metricas <- metric_set(roc_auc, pr_auc, f_meas, accuracy)

res <- fit_resamples(
  wf,
  resamples = folds,
  metrics   = mis_metricas
)

metric_set() agrupa varias en un objeto único. Todas se calculan sobre los mismos folds, así que la comparación entre modelos es directa.

Trampas habituales

  • Optimizar para accuracy con clases desbalanceadas. El modelo se desplazará hacia predecir siempre la clase mayoritaria. Si te importan los positivos, usa F1, recall o PR AUC.
  • Comparar AUC entre datasets distintos. Una AUC de 0.85 en un dataset desbalanceado y 0.85 en uno balanceado no son lo mismo. La AUC depende del problema.
  • Reportar solo el punto sin IC. Cualquier métrica que sale de CV viene con std_err. Reportar “AUC = 0.82” sin “IC ≈ ±0.04” esconde la incertidumbre real.
  • Olvidar argumento event_level. En clasificación binaria, yardstick asume por defecto que el primer nivel del factor es el evento positivo. Si tu factor está al revés (No primero, segundo), precision() y recall() miden la clase incorrecta. event_level = "second" lo corrige.

En la siguiente entrega

Has aprendido a evaluar correctamente. La siguiente pieza es comparar múltiples modelos en paralelo sin escribir cada uno a mano. workflowsets automatiza el cruce recipes × modelos y devuelve un ranking honesto. Lo siguiente.