Learning machine learning

Como yo tengo memoria de pez y un blog de tecnología he decidido ir subiendo los apuntes que voy tomando sobre los temas de machine learning sobre los que voy leyendo, junto con los desarrollos matemáticos que a mí me parecen esenciales para la comprensión de cada tema.

Pues nada, dejo por aquí mis apuntes sobre la regresión lineal.

La regresión como predicción

Desde el punto de vista del machine learning, la regresión lineal es una forma de aprendizaje supervisado: nosotros entrenamos a la computadora de una familia de inputs y outputs y luego la invitamos a predecir cuál será el output para cualquier input arbitrario.

Como en cualquier otro sistema de aprendizaje supervisado, proveemos a la computadora de la función de entrenamiento, es decir, la función cuya ejecución nos permite ir ajustando/mejorando el sistema predictivo.

Regresión lineal con una sola familia de inputs o regresión lineal unidimensional o mínimos cuadrados ordinarios o regresión lineal de toda la vida

Cuando tenemos una sola familia de inputs (datos de entrada o variable predictora) asociada a una sola familia de outputs (datos de salida o variable respuesta) es fácil ver que la regresión lineal consistirá en una recta. La mejor forma de “fácil verlo” es, por supuesto, mediante un ejemplo:

Supongamos que tenemos una serie de datos, por ejemplo metros cuadrados habitables de una lista de viviendas. Supongamos también que disponemos de los precios de cada una de las viviendas y que queremos realizar predicciones del precio basándonos en nuestros datos sobre los metros cuadrados habitables.

Nuestro input serán los metros cuadrados habitables y nuestro output o variable que queremos predecir será, por tanto, el precio de la vivienda.

A modo de ejemplo, dejo por aquí una tabla con nuestro input y output de ejemplo:

$m^2$ habitables precio (€)
92,7 259000
139,4 295000
109,2 279000
114,5 259000
104,1 299000
91,8 299000
115,2 309000
139,4 289000
317,7 849000
278,7 829000
113,8 359000
144,2 315000
90,6 310000
104,1 309000
94,8 300000
139,4 289000
154,6 369000
138,2 419000
127,8 405000
139,4 439000
116,7 375000
157,0 379000
169,1 445000
153,5 379000
165,1 389000
139,7 369000
170,1 458000
111,5 410000

Si ploteamos sobre ejes cartesianos nuestra magnífica tabla de ejemplo veremos algo parecido a lo siguiente:


A primera vista del gráfico, no parece descabellado pensar que existe una fuerte correlación positiva lineal entre los metros cuadrados habitables y el precio. Dicho de otro modo, si nos pidieran que predijéramos un precio para una vivienda de 50 metros cuadrados, seguramente lo situaríamos, a ojo, alrededor de los 150.000 €, ya que nuestro magnífico cerebro intuye que existe un patrón lineal, una recta imaginaria, que se ajusta a la nube de puntos y que, aparentemente, predice con un margen de error aceptable el precio en función de los metros. Nuestro objetivo es, por tanto, calcular esa recta imaginaria que mejor se ajusta a la nube de puntos.

Si los puntos de nuestra serie de datos se ajustasen perfectamente a una recta imaginaria, la distancia entre cada punto y dicha recta sería nula. Puesto que no se ajustará perfectamente, veremos que existe cierta distancia entre cada punto del gráfico y dicha recta imaginaria. Así pues, una forma matemática de expresar “la recta que mejor se ajusta a la nube de puntos” podría ser: “aquella recta que minimiza el sumatorio de distancias entre la recta imaginaria y cada uno de los puntos”.

El lector avispado ahora se preguntará: ¿qué distancia puntos-recta tomaremos? ¿la distancia recta-puntos horizontal? ¿la distancia recta-puntos vertical o la distancia al punto perpendicular a la recta (mínima distancia punto-recta)?


En la regresión lineal ordinaria se toman las distancias verticales. Primeramente porque nuestro objetivo es predecir el valor de la vertical (el precio, en el ejemplo) y su ajuste con respecto a la recta (la predicción) y en segundo lugar porque la posición horizontal de cada punto es de sobras conocida en cada caso, no introduce ningún posible error y, por tanto, no tiene mucho sentido ponderarla (y, de paso, complicar los cálculos).

Existen modelos distintos a la regresión lineal ordinaria en los que tiene sentido tomar las distancias perpendiculares: modelos basados en datos aproximados que introducen posibles errores en las dos direcciones, modelos no lineales, etc. (la regresión de Deming es un ejemplo de ajuste lineal tomando las distancias ortogonales).

Empecemos con las mates:

  • Sea una serie de inputs de entrada $x_i$
  • Sea una serie de outputs de salida $y_i$
  • Y sea una recta arbitraria definida como $y=\alpha+\beta x$

Para cada punto del plano definido como $(x_i, y_i)$, la distancia vertical $\epsilon_i$ del punto a la recta será:

\[\epsilon_i = y_i - (\alpha+\beta x_i)\]

Como queremos minimizar la distancia de todos los puntos con respecto a la recta, una buena primera idea sería tratar de minimizar el sumatorio de los puntos:

\[\sum\epsilon_i = \sum(y_i - (\alpha+\beta x_i))\]

Sin embargo, hay que tener en cuenta que algunos puntos caerán por encima de la recta (distancia positiva) y otros puntos caerán por debajo de la recta (distancia negativa), con lo cual la suma total tendrá un resultado distorsionado de cara a valorar el ajuste de la recta a la nube de puntos.

Existen dos formas habituales de resolver este problemilla: hacer el sumatorio de valores absolutos de las distancias o hacer el sumatorio de cuadrados de la distancia. Las dos opciones son viables pero, como ya hicimos antes, optaremos por la opción que se comporta mejor analítica y algebraicamente que, en este caso, será el cuadrado de las distancias (para los curiosos, aquí hay información sobre el modelo de mínimas desviaciones absolutas).

La desventaja de utilizar el cuadrado de las distancias en lugar del valor absoluto es que sobrevaloraremos aquellos puntos que estén muy desviados respecto a la recta. De hecho, existen modelos de ajuste lineal más refinados que, por ejemplo, tratan de compensar la desviación ocasionada por el uso del cuadrado mediante la adición de sumandos “compensadores” o “regularizadores” (más info sobre el tema en Tikhonov regularization).

Nosotros, por ahora, nos conformaremos con el modelo ordinario y, por tanto, intentaremos minimizar la expresión:

\[\sum\epsilon_i^2 = \sum[y_i - (\alpha+\beta x_i)]^2\]

Minimizar esta función matemática equivale a encontrar los valores $\alpha$ y $\beta$ para los cuales el resultado del sumatorio de distancias al cuadrado es menor. Nótese que $\alpha$ y $\beta$ son, precisamente, las variables que definen nuestra recta mejor ajustada.

Como se trata de encontrar el mínimo de una función de dos variables, debemos realizar la derivada parcial de las distancias al cuadrado en función de cada una de las variables, igualar a cero y buscarnos la vida para despejar $\alpha$ y $\beta$ a partir de las dos expresiones resultantes.

Vayamos al lío. Derivando respecto a $\alpha$:

\[\dfrac{\partial (\sum\epsilon_i^2)}{\partial \alpha} = -2 \sum[y_i - (\alpha+\beta x_i)] = 0\]

Ordenando un poquito los términos obtenemos:

\[\sum y_i - \sum \alpha - \sum x_i \beta = 0\] \[n \alpha + \sum x_i \beta = \sum y_i \tag{1}\]

Análogamente, realizando la derivada parcial respecto a $\beta$:

\[\dfrac{\partial (\sum\epsilon_i^2)}{\partial \beta} = -2\sum[x_i(y_i - (\alpha+\beta x_i))] = 0\]

Y ordenando los términos:

\[\sum x_i y_i - \sum x_i \alpha - \sum x_i^2 \beta = 0\] \[\sum x_i \alpha + \sum x_i^2 \beta = \sum x_i y_i \tag{2}\]

Si ahora expresamos matricialmente el sistema de dos ecuaciones lineales (1) y (2), podemos despejar $\alpha$ y $\beta$ con operaciones matriciales muy sencillas:

\[\begin{bmatrix} n & \sum x_i \\ \sum x_i & \sum x_i^2 \end{bmatrix} \begin{bmatrix} \alpha \\ \beta \end{bmatrix} = \begin{bmatrix} \sum y_i \\ \sum x_i y_i \end{bmatrix}\] \[\begin{bmatrix} \alpha \\ \beta \end{bmatrix} = \begin{bmatrix} n & \sum x_i \\ \sum x_i & \sum x_i^2 \end{bmatrix}^{-1} \begin{bmatrix} \sum y_i \\ \sum x_i y_i \end{bmatrix} \tag{3}\] \[\begin{bmatrix} \alpha \\ \beta \end{bmatrix} = \frac{1}{n \sum x_i^2 - (\sum x_i)^2 } \begin{bmatrix} \sum x_i^2 & -\sum x_i \\ -\sum x_i & n \end{bmatrix} \begin{bmatrix} \sum y_i \\ \sum x_i y_i \end{bmatrix}\] \[\begin{bmatrix} \alpha \\ \beta \end{bmatrix} = \frac{1}{n \sum x_i^2 - (\sum x_i)^2 } \begin{bmatrix} \sum x_i^2 \sum y_i - \sum x_i \sum x_i y_i \\ -\sum x_i \sum y_i + n \sum x_i y_i \end{bmatrix}\] \[\alpha = \frac{\sum x_i^2 \sum y_i - \sum x_i \sum x_i y_i}{n \sum x_i^2 - (\sum x_i)^2 } \tag{4}\] \[\beta = \frac{-\sum x_i \sum y_i + n \sum x_i y_i}{n \sum x_i^2 - (\sum x_i)^2 } \tag{5}\]

Aunque las operaciones (4) y (5) parecen auténticos mastodontes a primera vista, se pueden simplificar enormemente si les aplicamos las definiciones de promedio, varianza y covarianza y expresamos $\alpha$ en función de $\beta$:

\[\beta = \frac{ss_{xy}}{ss_{xx} ss_{yy}}\] \[\alpha = \bar{y} - \beta \bar{x}\]

Nuestro objetivo, sin embargo, no es tanto expresar elegantemente nuestro modelo como computarlo eficientemente. Para ello utilizaremos una magnífica librería para python llamada numpy, ideal para mates discretas y computación científica en general.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import numpy as np


class SimpleLinearRegression(object):
    def __init__(self):
        self.w = np.zeros(2)

    def train(self, x_data, y_data):
        n = len(x_data)
        sum_x = np.sum(x_data)
        sum_x_squared = np.sum(np.power(x_data, 2))
        sum_y = np.sum(y_data)
        sum_xy = np.sum(x_data.dot(y_data))

        a = np.array([[n, sum_x], [sum_x, sum_x_squared]])
        b = np.transpose([sum_y, sum_xy])

        self.w = np.linalg.inv(a).dot(b)

    def predict(self, x):
        return np.array([1, x]).dot(self.w)

    def get_intercept(self):
        return self.w[0]

    def get_slope(self):
        return self.w[1]


X = np.array([92.7, 139.4, 109.2, 114.5, 104.1, 91.8, 115.2, 139.4, 317.7, 278.7, 113.8, 144.2, 90.6, 104.1, 94.8,
              139.4, 154.6, 138.2, 127.8, 139.4, 116.7, 157.0, 169.1, 153.5, 165.1, 139.7, 170.1, 111.5])
Y = np.array([259000, 295000, 279000, 259000, 299000, 299000, 309000, 289000, 849000, 829000, 359000, 315000, 310000,
              309000, 300000, 289000, 369000, 419000, 405000, 439000, 375000, 379000, 445000, 379000, 389000, 369000,
              458000, 410000])

slr = SimpleLinearRegression()
slr.train(X, Y)

print "intercept (alpha) =", slr.get_intercept()  # intercept (alpha) = 21502.4801962
print "slope (beta) =", slr.get_slope()  # slope (beta) = 2563.87624406
print "prize prediction for 50 m2 =", slr.predict(50)  # prize prediction for 50 m2 = 149696.292399

Ya podemos plotear nuestra línea de mejor ajuste y hacer predicciones en función de cualquier input de metros cuadrados habitables:


Ahora bien, ¿y si quisiéramos hacer predicciones de los precios de las vivienda no sólo en función de los metros cuadrados sino también, por ejemplo, de su antiguedad en años? ¿y si queremos agregar muchas más variables predictoras?

En el próximo post trataré de explicar las mates que hay detrás de la generalización a múltiples dimensiones a través del ejemplo más sencillo posible: dos inputs predictores en lugar de uno y su generalización para n inputs. Una vez entendida la aproximación al problema general, trataremos de computar la regresión lineal con un sencillo script de python, que podremos “alimentar” con cualquier set de datos de entrada y salida.

Machine learning desde cero (II) - Regresión lineal multidimensional