Ha entrenado su primer modelo de aprendizaje automático. Funciona de manera brillante con tus datos de entrenamiento: ¡98% de precisión! Lo pruebas con datos nuevos y se desmorona: 61% de precisión. ¿Qué salió mal?
Es casi seguro que su modelo tiene sobreajuste. Este es uno de los dos modos de falla más comunes en el aprendizaje automático, y comprenderlo (junto con su opuesto, el desajuste) es esencial para construir modelos que realmente funcionen en el mundo real.
Antes de profundizar en los ejemplos, es útil comprender el marco teórico detrás de estos conceptos: el compromiso sesgo-varianza.
Todo modelo comete errores de predicción. Esos errores se pueden descomponer en tres partes:
Total Error = Bias² + Variance + Irreducible Noise
Sesgo es el error resultante de suposiciones erróneas en el modelo. Un modelo de alto sesgo es demasiado simple: sistemáticamente omite el verdadero patrón de los datos.
Varianza es el error de la sensibilidad a pequeñas fluctuaciones en los datos de entrenamiento. Un modelo de alta varianza es demasiado complejo: memoriza los datos de entrenamiento, incluido su ruido, en lugar de aprender el patrón subyacente.
Ruido irreducible es la aleatoriedad natural de los datos que ningún modelo puede eliminar.
La compensación: reducir el sesgo tiende a aumentar la varianza y viceversa. Su trabajo como profesional del aprendizaje automático es encontrar el punto óptimo.
El ajuste insuficiente ocurre cuando su modelo es demasiado simple para capturar el patrón real en los datos. Tiene un rendimiento deficiente tanto con datos de entrenamiento como con datos nuevos.
Imagine que tiene datos que muestran los precios de la vivienda según el tamaño. La verdadera relación es más o menos una curva suave: los precios aumentan con el tamaño, pero con cierto estancamiento en el extremo superior.
Si ajustas una línea recta horizontal a estos datos:
# Underfitting: overly simple model
from sklearn.linear_model import LinearRegression
import numpy as np
# True relationship is quadratic, but we're fitting a simple mean
model = DummyRegressor(strategy='mean')
model.fit(X_train, y_train)
# Training accuracy: 55%
# Test accuracy: 54%
# Both are bad — classic underfitting
Iniciar sesión unirse a la discusión
El modelo ignora la relación real entre el tamaño de la casa y el precio. No importa si le muestra datos de entrenamiento o datos nuevos: de cualquier manera está mal.
El sobreajuste ocurre cuando su modelo aprende los datos de entrenamiento demasiado bien (incluido su ruido y variación aleatoria) y no logra generalizar a nuevos ejemplos.
Usando un polinomio de 15 grados para ajustar los mismos datos del precio de la vivienda:
# Overfitting: overly complex model
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
# Degree-15 polynomial — wildly complex for this problem
model = make_pipeline(PolynomialFeatures(15), LinearRegression())
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train) # 0.99 — looks amazing!
test_score = model.score(X_test, y_test) # 0.43 — terrible on new data
El polinomio se ha retorcido en nudos para pasar por cada punto de entrenamiento, incluidos los ruidosos valores atípicos. Ha memorizado el conjunto de entrenamiento en lugar de aprender el patrón subyacente. Con datos invisibles, es inútil.
Una herramienta fundamental para detectar el sobreajuste es dividir los datos en tres conjuntos:
from sklearn.model_selection import train_test_split
# First split: hold out 20% as the final test set
X_train_val, X_test, y_train_val, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Second split: hold out 20% of remaining as validation set
X_train, X_val, y_train, y_val = train_test_split(
X_train_val, y_train_val, test_size=0.25, random_state=42
)
# Result: 60% train / 20% validation / 20% test
El conjunto de validación es su sistema de alerta temprana. Si la precisión del entrenamiento sigue mejorando pero la precisión de la validación se estabiliza o disminuye, estás sobreajustando.
La regularización añade una penalización a la función de pérdida que disuade al modelo de aprender patrones demasiado complejos.
Regularización L2 (Ridge) penaliza pesos grandes:
from sklearn.linear_model import Ridge
model = Ridge(alpha=1.0) # alpha controls regularisation strength
model.fit(X_train, y_train)
Regularización L1 (Lazo) puede llevar algunos pesos hasta cero, realizando la selección de características:
from sklearn.linear_model import Lasso
model = Lasso(alpha=0.1)
model.fit(X_train, y_train)
Durante el entrenamiento, "abandona" aleatoriamente (se pone a cero) una proporción de neuronas:
import torch.nn as nn
model = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Dropout(p=0.5), # 50% of neurons randomly deactivated during training
nn.Linear(64, 1)
)
Esto evita que las neuronas se coadapten y obliga a la red a aprender representaciones distribuidas más sólidas.
Monitoree la pérdida de validación durante el entrenamiento y deténgase cuando comience a aumentar:
from tensorflow.keras.callbacks import EarlyStopping
early_stop = EarlyStopping(
monitor='val_loss',
patience=5, # stop after 5 epochs without improvement
restore_best_weights=True
)
model.fit(X_train, y_train,
validation_data=(X_val, y_val),
callbacks=[early_stop],
epochs=1000)
Más datos hacen que al modelo le resulte más difícil memorizar el ruido: simplemente hay demasiados para ajustarlos exactamente. Cuando la recopilación de datos es costosa, el aumento de datos (crear copias modificadas de ejemplos existentes) puede ayudar.
A veces, la solución correcta es simplemente elegir un modelo menos complejo para el problema.
Con conjuntos de datos pequeños, una única división tren/val puede ser engañosa debido a la aleatoriedad. La validación cruzada de K veces proporciona una estimación más confiable:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100)
# 5-fold cross-validation
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} ± {scores.std():.3f}")
# Mean accuracy: 0.847 ± 0.023
Los datos se dividen en 5 pliegues; el modelo entrena en 4 y valida en 1, rotando cada vez. La puntuación final es el promedio de los 5, mucho más confiable que una sola división.
Un modelo logra una precisión del 99 % en los datos de entrenamiento, pero solo del 62 % en los datos de prueba. ¿Qué indica esto?