10

Generalizar y evaluar

¿Cómo sabes si un modelo funciona de verdad?

9 minRuta ejecutiva

Generalizar y evaluar

Con voz. Puedes pausar y saltar a cualquier paso.

Lección completa

Subtítulos

Un modelo que acierta todos sus ejemplos de entrenamiento puede fallar con los casos nuevos. Medir bien es medir con datos que el modelo nunca vio.

Voz

Sin voz: la lección avanza sola con subtítulos.

La voz de esta lección está en preparación: por ahora avanza sola con subtítulos.

0:00 / 0:00

Qué lograrás

  • Distinguir subajuste de sobreajuste y detectarlos comparando el error de entrenamiento con el de validación.
  • Explicar para qué sirven los conjuntos de entrenamiento, validación y prueba, y qué es una fuga de datos.
  • Calcular e interpretar precisión, exhaustividad y la matriz de confusión, incluido el efecto de la tasa base.

Ideas clave

  1. Bajar el error de entrenamiento no basta: lo que importa es el error con datos que el modelo no vio.
  2. Subajuste = modelo demasiado simple; sobreajuste = aprende el ruido. Una brecha grande entre error de entrenamiento y de validación es la señal.
  3. Entrenamiento para aprender, validación para elegir, prueba para informar (una sola vez). Si el examen se filtra al entrenamiento, hay fuga de datos.
  4. Se frena el sobreajuste con más datos, modelos más simples, regularización y parada temprana.
  5. La exactitud engaña con clases desbalanceadas: mira precisión, exhaustividad y la matriz de confusión, y recuerda que la tasa base manda.

Lo que mucha gente cree

Creencia común «El modelo acierta el 99 % en las pruebas, así que funciona muy bien».

Lo correcto Depende de con qué datos se midió y de la tasa base. Con datos que el modelo ya vio, la cifra no dice nada de lo nuevo; y con eventos raros, un 99 % puede ser inútil.

Compruébalo

Cuatro preguntas. Sin nota ni cuenta: sirven para ver si la idea quedó.

ConceptoUn modelo tiene error de entrenamiento muy bajo y error de validación alto. ¿Qué indica?
Concepto¿Para qué sirve el conjunto de prueba?
CálculoUna enfermedad afecta al 1 % de la población. Una prueba tiene 90 % de sensibilidad y 90 % de especificidad. Entre quienes dan positivo, ¿qué fracción está realmente enferma?
AplicaciónUn proveedor presume «98 % de exactitud» en detección de fraude, donde el fraude es el 1 % de las transacciones. ¿Qué haces?

Por dentroPara quien quiere las fórmulas y el código. No hace falta para seguir el curso.

Sesgo, varianza y ruido

Para un modelo f^\hat f entrenado con un conjunto aleatorio, el error esperado en un punto nuevo se descompone en

E[(y−f^(x))2]=(E[f^(x)]−f(x))2⏟sesgo2+Var(f^(x))⏟varianza+σ2⏟ruido irreducible.\mathbb{E}\big[(y-\hat f(x))^2\big] = \underbrace{\big(\mathbb{E}[\hat f(x)]-f(x)\big)^2}_{\text{sesgo}^2} + \underbrace{\mathrm{Var}\big(\hat f(x)\big)}_{\text{varianza}} + \underbrace{\sigma^2}_{\text{ruido irreducible}}.

Es la forma estándar con error cuadrático (Hastie, Tibshirani y Friedman, ec. 7.9). El subajuste es sesgo alto (modelo demasiado rígido); el sobreajuste es varianza alta (el modelo cambia mucho con cada muestra). En la lección el ruido es σ=0.2\sigma = 0.2: en promedio, ningún modelo puede bajar de ese error en datos nuevos (en una muestra concreta de 80 puntos puede quedar un poco por encima o por debajo), y el mejor grado (4) llega a ≈0.22\approx 0.22 en la validación de la escena.

Esto se comprobó simulando 4 000 conjuntos de entrenamiento con la misma receta de datos (verify-data.ts): la suma σ2+sesgo2+varianza\sigma^2+\text{sesgo}^2+\text{varianza} coincide con el error simulado en puntos nuevos. Con grado 1: sesgo² ≈ 0.066 y varianza ≈ 0.007 (el error es de sesgo). Con grado 4: 0.001 y 0.017. Con grado 10: sesgo² ≈ 0 y varianza ≈ 1.6, sobre todo hacia los extremos del intervalo (el error es de varianza). Con grado 10 y λ=0.03\lambda = 0.03: 0.005 y 0.018; la regularización cambia varianza por un poco de sesgo.

Regularización ridge

El ajuste polinómico con castigo λ\lambda a los pesos (regresión ridge, o decaimiento de pesos, weight decay) minimiza ∥y−Xw∥2+λ∥w∥2\lVert y - Xw\rVert^2 + \lambda \lVert w\rVert^2, con solución cerrada

w^=(X⊤X+λI)−1X⊤y.\hat w = (X^\top X + \lambda I)^{-1} X^\top y.

Con λ=0\lambda = 0 se recupera el ajuste libre; al subir λ\lambda los pesos se encogen y la curva se suaviza (en la escena, la norma de los pesos de grado 10 baja de ≈ 327 con λ=0\lambda = 0 a ≈ 0.19 con λ=10\lambda = 10). Dos precisiones. El término constante no se penaliza (como en la escena y en scikit-learn; Hastie et al., §3.4.1), y las entradas conviene normalizarlas antes (la escena pasa x∈[0,1]x\in[0,1] a [−1,1][-1,1]). Y con λ\lambda demasiado grande el modelo vuelve a subajustar: con grado 10, el error de validación de la escena baja hasta λ≈0.02\lambda \approx 0.02 (≈ 0.24) y luego sube (≈ 0.44 con λ=10\lambda = 10, peor que la recta).

La parada temprana del descenso del gradiente actúa de forma parecida a una regularización: detener antes limita cuánto crecen los pesos. Para un modelo lineal con error cuadrático y descenso del gradiente simple equivale, con una aproximación cuadrática, a una regularización L2L^2 (Goodfellow, Bengio y Courville, §7.8). Otras defensas: más datos (reales o aumentados, §7.4) y, en redes neuronales, el dropout: apagar unidades al azar durante el entrenamiento (Srivastava et al., 2014).

Métricas de clasificación

Con verdaderos positivos TPTP, falsos positivos FPFP, falsos negativos FNFN y verdaderos negativos TNTN:

exactitud=TP+TNTP+FP+FN+TN,precisioˊn=TPTP+FP,recall=TPTP+FN,F1=2 P RP+R.\text{exactitud}=\frac{TP+TN}{TP+FP+FN+TN},\quad \text{precisión}=\frac{TP}{TP+FP},\quad \text{recall}=\frac{TP}{TP+FN},\quad F_1=\frac{2\,P\,R}{P+R}.

Si no se marca a nadie (TP+FP=0TP+FP=0) la precisión es 0/00/0: por convención (la de la escena) se toma 1, de modo que un detector que dice «sano» siempre tiene precisión perfecta y recall 0, y por eso hay que mirar las dos (Goodfellow et al., §11.1). El F1F_1 es la media armónica de precisión y recall.

El umbral y los costos

Si un falso negativo cuesta cFNc_{FN} y un falso positivo cFPc_{FP} (los aciertos no cuestan), la decisión de menor costo esperado es marcar «positivo» cuando

P(enfermo∣puntuacioˊn)≥cFPcFP+cFN(Elkan, 2001).P(\text{enfermo}\mid \text{puntuación}) \ge \frac{c_{FP}}{c_{FP}+c_{FN}}\qquad\text{(Elkan, 2001)}.

Con costos iguales el corte de probabilidad es 0.5; si perder un caso cuesta 10 veces más, baja a 1/11≈0.091/11\approx 0.09. En la escena (dos campanas y 10 % de enfermas) eso da los umbrales de puntuación óptimos 2.23 (costos iguales), 1.46 (perder un caso cuesta 10) y 3.00 (la falsa alarma cuesta 10).

La tasa base (teorema de Bayes)

Con prevalencia pp, sensibilidad ss (= recall) y especificidad cc, la probabilidad de estar enfermo dado un positivo es

P(enfermo∣+)=s ps p+(1−c)(1−p).P(\text{enfermo}\mid +)=\frac{s\,p}{s\,p+(1-c)(1-p)}.

Con p=0.01p=0.01 y s=c=0.95s=c=0.95: 0.00950.0095+0.0495≈0.16\dfrac{0.0095}{0.0095+0.0495}\approx 0.16, el «uno de cada seis» de la lección. Con s=c=0.95s=c=0.95 hay más falsas alarmas que aciertos mientras la prevalencia sea menor que 5 %.

Presentar el cálculo en frecuencias («de 2 000 personas, 20 enfermas, 19 detectadas…») en lugar de probabilidades ayuda a razonar: Gigerenzer y Hoffrage (1995) muestran que los algoritmos bayesianos son computacionalmente más simples en ese formato y que, con él, personas sin formación estadística siguieron algoritmos bayesianos hasta en el 50 % de sus inferencias. Es el formato que usa la lección.

En código

import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Ridge

f = lambda x: 0.6 * np.sin(2 * np.pi * x) + 0.2          # la señal verdadera
rng = np.random.default_rng(0)
x_tr = ((np.arange(12) + 0.5) / 12)[:, None]              # 12 mediciones repartidas en [0, 1]
y_tr = f(x_tr[:, 0]) + 0.2 * rng.normal(size=12)
x_va = rng.uniform(0, 1, 80)[:, None]                     # 80 mediciones nuevas
y_va = f(x_va[:, 0]) + 0.2 * rng.normal(size=80)

for grado in (1, 4, 10):
    for lam in (1e-9, 3e-2):                              # 1e-9 hace de λ = 0
        m = make_pipeline(PolynomialFeatures(grado, include_bias=False), Ridge(alpha=lam)).fit(2 * x_tr - 1, y_tr)
        rmse = lambda a, b: float(np.sqrt(np.mean((m.predict(2 * a - 1) - b) ** 2)))
        print(grado, lam, round(rmse(x_tr, y_tr), 3), round(rmse(x_va, y_va), 3))   # entrenamiento y validación

# Con esa semilla: grado 1 → 0.302 y 0.339 (subajuste); grado 4 → 0.093 y 0.221 (mejor validación);
# grado 10 → 0.006 y 0.251 con λ ≈ 0 (sobreajuste) y 0.103 y 0.237 con λ = 0.03.
# Son datos nuevos con la misma receta que la escena: no repiten sus números exactos.

def ppv(prev, sens, spec):          # valor predictivo positivo
    tp, fp = prev * sens, (1 - prev) * (1 - spec)
    return tp / (tp + fp)

print(round(ppv(0.01, 0.95, 0.95), 3))   # 0.161

Lo que viene

Para imágenes, una red totalmente conectada no escala. La lección 11 muestra la idea de las convoluciones. La lección 34 retoma la evaluación para sistemas de IA generativa, donde medir bien es más difícil.

Lente de decisión

Qué significa esta idea si tu trabajo es decidir, no programar.

  • Pide siempre que el rendimiento se reporte sobre datos que el modelo no vio, y que el equipo explique cómo evitó fugas de datos.
  • Una sola cifra de exactitud no basta: pide precisión y exhaustividad, y la matriz de confusión con los costos reales de cada tipo de error.
  • Con eventos raros (fraude, fallas, enfermedades) manda la tasa base: calcula cuántas falsas alarmas puede absorber tu operación antes de aprobar un sistema.
  • El umbral de decisión es una palanca de negocio: ajústalo según el costo de un falso negativo frente a un falso positivo y revísalo cuando cambie el entorno.

Términos de la lección

Transcripción completa
ComentariosConversación pública de esta lección: dudas, datos por revisar, ideas.

Los comentarios viven en GitHub Discussions, en el repositorio público del curso; para escribir se necesita una cuenta de GitHub. Se cargan desde giscus.app al abrir esta sección. Las personas que revisan el curso los leen, y si un cambio se acepta, se aplica en los dos idiomas. Abrir el hilo en GitHub

Fuentes