In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

Aprendizaje supervisado parte 2 -- Regresión

En regresión intentamos predecir una variable continua de salida -- al contrario que las variables nominales que predecíamos en los ejemplos anteriores de clasificación.

Vamos a empezar con un ejemplo simple de juguete con una sola dimensión de entrada (variable explicativa) y una variable objetivo. Crearemos un dataset mediante una curva sinusoidal con algo de ruido:


In [ ]:
x = np.linspace(-3, 3, 100)
print(x)

In [ ]:
rng = np.random.RandomState(42)
y = np.sin(4 * x) + x + rng.uniform(size=len(x))

In [ ]:
plt.plot(x, y, 'o');

Regresión lineal

El primer modelo que vamos a introducir es el llamado regresión lineal simple, que simplemente predice que los datos caen en una línea recta. Una forma de ajustar este modelo es usar LinearRegression (también conocido como regresión Ordinary Least Squares (OLS)). La interfaz para LinearRegression es exactamente la misma que para los clasificadores explorados anteriormente, con la salvedad de que ahora $y$ contiene valores reales, en lugar de valores categóricos.

Como recordaremos, el API de scikit-learn requiere que le pasemos la variable objetivo ($y$) como un array de 1 dimensión; el API de scikit-learn espera que los ejemplos ($\mathbf{X}$) vengan en forma de un array de dos dimensiones (aunque solo haya una columna o característica). Por tanto, debemos convertir el array NumPy $x$ de una dimensión a una matriz $\mathbf{X}$ con dos ejes:


In [ ]:
print('Antes: ', x.shape)
X = x[:, np.newaxis]
print('Después: ', X.shape)

De nuevo dividimos nuestro dataset en entrenamiento (75%) y test (25%):


In [ ]:
from sklearn.model_selection import train_test_split

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

Ahora usamos el algoritmo LinearRegression para ajustar un modelo de regresión a los datos de entrenamiento:


In [ ]:
from sklearn.linear_model import LinearRegression

regressor = LinearRegression(n_jobs=2)
regressor.fit(X_train, y_train)

Después del ajuste, podemos observar los parámetros obtenidos.


In [ ]:
print('Pesos: ', regressor.coef_)
print('Ordenada en el origen: ', regressor.intercept_)

Ya que nuestro modelo de regresión es lineal, la relación entre la variable objetivo ($y$) y la variable independiente ($x$) se define como:

$$y = peso \times x + \text{ordenadaOrigen}$$

Para representar el modelo, introducimos los valores máximo y mínimo de $x$ en esta ecuación.


In [ ]:
min_pt = X.min() * regressor.coef_[0] + regressor.intercept_
max_pt = X.max() * regressor.coef_[0] + regressor.intercept_

plt.plot([X.min(), X.max()], [min_pt, max_pt])
plt.plot(X_train, y_train, 'o');

De la misma forma que con los estimadores de clasificación, utilizamos el método predict para obtener valores estimados de la variable objetivo. Y esperamos que esos valores caigan en la línea anterior:


In [ ]:
y_pred_train = regressor.predict(X_train)

In [ ]:
plt.plot(X_train, y_train, 'o', label="datos")
plt.plot(X_train, y_pred_train, 'o', label="predicciones")
plt.plot([X.min(), X.max()], [min_pt, max_pt], label='ajuste')
plt.legend(loc='best')

Como podemos ver en el gráfico, la línea es capaz de capturar la pendiente general de los datos, pero no muchos detalles.

Ahora vamos a probar con el conjunto de test:


In [ ]:
y_pred_test = regressor.predict(X_test)

In [ ]:
plt.plot(X_test, y_test, 'o', label="datos")
plt.plot(X_test, y_pred_test, 'o', label="predicción")
plt.plot([X.min(), X.max()], [min_pt, max_pt], label='ajuste')
plt.legend(loc='best');

De nuevo, scikit-learn proporciona un método fácil para evaluar cuantitativamente los estimadores de regresión a través de la función score. Para tareas de regresión, este sería por defecto el R2. Otra forma popular de evaluación sería el Mean Squared Error (MSE). Como indica su nombre, el MSE consiste simplemente en la diferencia al cuadrado promediada entre los valores predichos y los valores reales:

$$MSE = \frac{1}{n} \sum_{i=1}^{n} (\text{predicted}_i - \text{true}_i)^2$$

In [ ]:
regressor.score(X_test, y_test)
EJERCICIO:
  • Añade una característica que consista en `sin(4x)` a la matriz `X` y vuelve a ajustar el modelo. Visualiza las predicciones con este modelo más complejo, aunque aún lineal.

In [ ]:

KNeighborsRegression

Del mismo modo que para clasificación, podemos utilizar un método basado en vecinos para regresión. Podemos predecir directamente el valor de la variable objetivo del punto más cercano o podemos promediar varios de los puntos más cercanos. Los métodos basados en vecinos son más populares en clasificación que en regresión, pese a lo cual es un método sencillo que puede servir como baseline.


In [ ]:
from sklearn.neighbors import KNeighborsRegressor
kneighbor_regression = KNeighborsRegressor(n_neighbors=1)
kneighbor_regression.fit(X_train, y_train)

De nuevo, vamos a comprobar como se comporta en entrenamiento y en test:


In [ ]:
y_pred_train = kneighbor_regression.predict(X_train)

plt.plot(X_train, y_train, 'o', label="datos", markersize=10)
plt.plot(X_train, y_pred_train, 's', label="predicción", markersize=4)
plt.legend(loc='best');

En entrenamiento, la predicción es perfecta, ya que cada ejemplo es su vecino más cercano.


In [ ]:
y_pred_test = kneighbor_regression.predict(X_test)

plt.plot(X_test, y_test, 'o', label="datos", markersize=8)
plt.plot(X_test, y_pred_test, 's', label="predicción", markersize=4)
plt.legend(loc='best');

En el conjunto de test, capturamos mejor la variación de los datos que con el modelo lineal, aunque nuestro estimador haga algunas cosas un poco raras. Evaluemos la puntuación R2:


In [ ]:
kneighbor_regression.score(X_test, y_test)

Es mucho mejor que antes. El problema era que un modelo lineal no era un ajuste adecuado para el problema, ya que no tenía la suficiente complejidad e infra-entrenaba los datos (error alto por sesgo).

EJERCICIO:
  • Compara KNeighborsRegressor y LinearRegression en el dataset boston housing. Puedes cargar el dataset utilizando ``sklearn.datasets.load_boston``. Puedes saber más de este dataset observando el atributo ``DESCR`` del objeto resultante.

In [ ]: