¡Esta es una revisión vieja del documento!


06 - Sobreajuste y subajuste

En temas anteriores vimos la regresión polinomial. Vamos a construir datos con un polinomio de grado 2:

import numpy as np

import matplotlib.pyplot as plt

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import PolynomialFeatures

m = 100
X = 6 * np.random.rand(m, 1) -3
y = 0.5 * X**2 + X + 2 +np.random.rand(m, 1)

Mostramos los datos en una gráfica:

Separamos los datos en entrenamiento y test y mostramos los datos de entrenamiento en una gráfica:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

Creamos un modelo de regresión lineal simple:

model = LinearRegression()

model.fit(X_train, y_train)

y_predict = model.predict(X_train)

Como vemos, la recta no se ajusta de forma óptima a nuestros datos. Vamos a ver el MSE de entrenamiento y de test:

rmse = mean_squared_error(y_true = y_train, y_pred = y_predict)
print("MSE entrenamiento: %.2f" % rmse)

y_predict = model.predict(X_test)
rmse = mean_squared_error(y_true = y_test, y_pred = y_predict)
print("MSE test: %.2f" % rmse)

MSE entrenamiento: 1.56
MSE test: 1.49

Nuestro modelo no es capaz de adecuarse correctamente a los datos. Lo que está haciendo es subajustando los datos (underfitting).

Vamos a crear ahora un modelo de regresión polinomial de grado 2:

poly_features = PolynomialFeatures(degree = 2)
X_poly = poly_features.fit_transform(X_train)

model.fit(X_poly, y_train)

y_predict = model.predict(X_poly)

Ahora sí que nuestro modelo se ajusta a los datos. Si miramos el MSE de entrenamiento y de test vemos que mejora mucho el rendimiento:

rmse = mean_squared_error(y_true = y_train, y_pred = y_predict)
print("MSE entrenamiento: %.2f" % rmse)

X_poly = poly_features.transform(X_test)
y_predict = model.predict(X_poly)
rmse = mean_squared_error(y_true = y_test, y_pred = y_predict)
print("MSE test: %.2f" % rmse)

MSE entrenamiento: 0.08
MSE test: 0.07

¿Que pasaría si aumentáramos el grado de polinomio? Por ejemplo, a 35:

poly_features = PolynomialFeatures(degree = 35)
X_poly = poly_features.fit_transform(X_train)

model.fit(X_poly, y_train)

y_predict = model.predict(X_poly)

Parece que no ajusta mal del todo en la mayoría de datos. Vamos a ver el MSE de entrenamiento y error:

rmse = mean_squared_error(y_true = y_train, y_pred = y_predict)
print("MSE entrenamiento: %.2f" % rmse)

X_poly = poly_features.transform(X_test)
y_predict = model.predict(X_poly)
rmse = mean_squared_error(y_true = y_test, y_pred = y_predict)
print("MSE test: %.2f" % rmse)

MSE entrenamiento: 0.17
MSE test: 46545.50

¿Qué está pasando? Nuestro modelo funciona bastante bien con los datos de entrenamiento (mucho mejor que con una regresión lineal simple), pero no generaliza nada bien. Ésto ocurre porque el modelo está sobreajustando mucho los datos (overfitting).

Obviamente, en nuestro caso está claro que el modelo que mejor se ajusta a nuestros datos es el segundo (el de grado 2), ya que hemos creado nosotros los datos con un polinomio de grado 2. Pero, en general, no sabemos que función ha generado los datos, con lo que no tenemos modo de saber, a priori, como de complejo tiene que ser nuestro modelo. ¿Cómo puedo saber si el modelo está sobreajustando o subajustando los datos?

Una forma de saberlo es utilizando la validación cruzada. Si un modelo tiene un buen rendimiento con los datos de entrenamiento pero malo con los de validación, está sobreajustando. Si se tiene un mal rendimiento en ambos casos, está subajustando.

Otra forma de saberlo es fijarse en las curvas de aprendizaje. Por ejemplo, vamos a crear una función que muestre el RMSE en función del tamaño del conjunto de entrenamiento:

def plot_learning_curves(model, X, y):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    train_errors, test_errors = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_predict = model.predict(X_train[:m])
        y_test_predict = model.predict(X_test)
        train_errors.append(mean_squared_error(y_train[:m], y_train_predict, squared = False))
        test_errors.append(mean_squared_error(y_test, y_test_predict, squared = False))

    figure = plt.figure(figsize = (15, 10))
    axes = figure.add_subplot()
    _ = axes.plot(train_errors, "r-+", linewidth = 1, label = "train")
    _ = axes.plot(test_errors, "b-", linewidth = 1, label = "test")
    axes.set_ylim(ymin=0,ymax=3)
    axes.legend(fontsize=15,facecolor='#CDCDCD',labelcolor="#000000")
    axes.set_xlabel('Tamaño conjunto entrenamiento', fontsize=25,labelpad=20)
    axes.set_ylabel('RMSE', fontsize=25,labelpad=20)

Nuestra función recibe un modelo y unos datos (X, y). Separa en entrenamiento y test y va entrenando el modelo con un conjunto de entrenamiento cada vez más grande. Por último, muestra el gráfico.

Aplicamos la función al primer modelo (regresión lineal simple):

model  = LinearRegression()
plot_learning_curves(model, X, y)

Si nos fijamos en el rendimiento de los datos de entrenamiento, vemos que cuando hay una o dos instancias, el modelo ajusta perfectamente. A medida que vamos aumentando el tamaño del conjunto de entrenamiento, el error va aumentando, hasta llegar a una especie de meseta. En los datos de test, nuestro modelo es incapaz de ajustar cuando hay pocos datos. Si aumentamos el tamaño de los datos de entrenamiento, va mejorando el rendimiento hasta llegar a otra meseta cerca de la otra curva. Esta gráfica es típica de modelo subajustados. Ambas curvas están cerca, han llegado a una meseta y el error es bastante elevado.

Vamos ahora con el segundo modelo (polinomio de grado 2):

poly_features = PolynomialFeatures(degree = 2)
X_poly = poly_features.fit_transform(X)

plot_learning_curves(model, X_poly, y)

En este caso, las curvas están más cerca y el error es mucho menor. Nuestro modelo está funcionando de manera correcta.

Último modelo (polinomio de grado 35):

poly_features = PolynomialFeatures(degree = 35)
X_poly = poly_features.fit_transform(X)

plot_learning_curves(model, X_poly, y)

Aquí vemos que el error de entrenamiento va aumentando poco a poco, pero se mantiene bastante bajo. Sin embargo, el error de test se mantiene muy alto y las curvas están muy separadas. Obviamente, si aumentáramos el tamaño de los datos de entrenamiento las dos curvas se acercarían cada vez más.

Otra forma de intentar evitar el sobreajuste es, como veremos en el punto siguiente, la regularización.

  • clase/ia/saa/6_optimizacion_modelo/sobreajuste.1645091107.txt.gz
  • Última modificación: 2022/02/17 10:45
  • por cesguiro