21. Detección de sobreajuste y subajuste

21.1 Dos errores opuestos y muy comunes

Cuando entrenamos un modelo, no alcanza con que funcione bien sobre los datos vistos. Lo importante es que también funcione bien sobre datos nuevos.

En ese camino aparecen dos problemas clásicos:

  • subajuste;
  • sobreajuste.

Entenderlos es fundamental, porque gran parte del trabajo práctico en Machine Learning consiste en encontrar un equilibrio entre ambos.

21.2 Qué es el subajuste

Hay subajuste cuando el modelo es demasiado simple para capturar el patrón real de los datos.

En ese caso:

  • rinde mal en entrenamiento;
  • también rinde mal en prueba;
  • parece “no haber aprendido lo suficiente”.

Es como intentar explicar una situación compleja con una regla demasiado pobre.

21.3 Qué es el sobreajuste

Hay sobreajuste cuando el modelo aprende demasiado los detalles del conjunto de entrenamiento, incluso el ruido o particularidades que no se repiten fuera de esa muestra.

En ese caso:

  • rinde muy bien en entrenamiento;
  • pero baja claramente en prueba;
  • parece haber “memorizado” más que aprendido a generalizar.

21.4 La señal más importante

Una forma simple de detectar estos problemas es comparar el rendimiento en entrenamiento y en prueba.

  • Ambos bajos: probable subajuste.
  • Entrenamiento alto y prueba baja: probable sobreajuste.
  • Ambos razonablemente buenos y cercanos: mejor equilibrio.

21.5 Ejemplo intuitivo

Imaginemos que un estudiante prepara un examen:

  • si estudia solo definiciones muy básicas, puede no entender bien el tema: eso se parece al subajuste;
  • si memoriza preguntas exactas de un simulacro, puede rendir bien ahí, pero fallar ante preguntas nuevas: eso se parece al sobreajuste.

El objetivo real no es memorizar ni quedarse corto, sino comprender lo suficiente para responder bien en casos nuevos.

21.6 Ejemplo muy claro: árboles demasiado simples y demasiado complejos

Vamos a usar un problema de aprobación de estudiantes. Compararemos dos árboles:

  • uno muy simple, con profundidad 1;
  • otro mucho más libre, sin límite de profundidad.

Luego observaremos la exactitud en entrenamiento y en prueba para cada caso.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

datos = pd.DataFrame({
    "horas_estudio": [1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 3, 5, 7, 9],
    "practicas": [0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 2, 3, 4, 6],
    "aprobo": [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
})

X = datos[["horas_estudio", "practicas"]]
y = datos["aprobo"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

modelo_simple = DecisionTreeClassifier(max_depth=1, random_state=42)
modelo_complejo = DecisionTreeClassifier(random_state=42)

modelo_simple.fit(X_train, y_train)
modelo_complejo.fit(X_train, y_train)

pred_train_simple = modelo_simple.predict(X_train)
pred_test_simple = modelo_simple.predict(X_test)
pred_train_complejo = modelo_complejo.predict(X_train)
pred_test_complejo = modelo_complejo.predict(X_test)

print("Modelo simple")
print("Exactitud en entrenamiento:", accuracy_score(y_train, pred_train_simple))
print("Exactitud en prueba:", accuracy_score(y_test, pred_test_simple))

print("Modelo complejo")
print("Exactitud en entrenamiento:", accuracy_score(y_train, pred_train_complejo))
print("Exactitud en prueba:", accuracy_score(y_test, pred_test_complejo))

nuevo_estudiante = pd.DataFrame({
    "horas_estudio": [7],
    "practicas": [4]
})

prediccion = modelo_complejo.predict(nuevo_estudiante)[0]
print("Predicción para el nuevo estudiante:", prediccion)

Salida resumida esperada:

Modelo simple
Exactitud en entrenamiento: ...
Exactitud en prueba: ...
Modelo complejo
Exactitud en entrenamiento: ...
Exactitud en prueba: ...
Predicción para el nuevo estudiante: ...

21.7 Qué está mostrando el ejemplo

El modelo simple, con profundidad 1, tiene muy poca capacidad. Si los datos requieren una lógica más rica, tenderá a subajustar.

El modelo complejo, en cambio, puede adaptarse mucho mejor al entrenamiento. Pero si se vuelve demasiado específico, puede sobreajustar y perder rendimiento en prueba.

21.8 Cómo leer las métricas en este contexto

  • si el árbol simple da resultados bajos en ambos conjuntos, hay señal de subajuste;
  • si el árbol complejo logra una exactitud muy alta en entrenamiento pero cae en prueba, hay señal de sobreajuste;
  • si ambos valores quedan razonablemente cercanos, el modelo está generalizando mejor.

La diferencia entre entrenamiento y prueba es, muchas veces, más informativa que un único número aislado.

21.9 Qué decisiones ayudan a corregir estos problemas

Si hay subajuste, suele ayudar:

  • usar un modelo más expresivo;
  • agregar variables útiles;
  • permitir más complejidad.

Si hay sobreajuste, suele ayudar:

  • reducir la complejidad del modelo;
  • regularizar;
  • usar más datos si es posible;
  • validar mejor los hiperparámetros.

21.10 Explicación detallada del código

  • DecisionTreeClassifier(max_depth=1): crea un árbol muy simple, útil para mostrar subajuste.
  • DecisionTreeClassifier(): crea un árbol sin límite explícito de profundidad, más propenso a sobreajustar.
  • accuracy_score(y_train, ...): mide qué tan bien rinde en entrenamiento.
  • accuracy_score(y_test, ...): mide qué tan bien generaliza a datos nuevos.

21.11 Errores frecuentes

  • Mirar solo el resultado en entrenamiento: puede dar una falsa sensación de éxito.
  • Asumir que un modelo más complejo siempre es mejor: no necesariamente generaliza mejor.
  • Confundir baja exactitud con mala implementación: a veces el problema es la complejidad elegida.
  • No reservar un conjunto de prueba: sin esa comparación es mucho más difícil detectar sobreajuste.

21.12 Qué deberías retener

  • Subajuste significa que el modelo se queda corto.
  • Sobreajuste significa que el modelo se adapta demasiado al entrenamiento.
  • Comparar entrenamiento y prueba ayuda a detectar ambos problemas.
  • La complejidad del modelo debe ser suficiente, pero no excesiva.
  • Gran parte del trabajo en Machine Learning consiste en encontrar ese equilibrio.