script

miércoles, 28 de mayo de 2014

Regresión lineal: Primeras noción


La regresión lineal es un algoritmo que trata de predecir un valor real por medio de una función lineal de una o mas variables reales de entrada. Es uno de los algoritmos de aprendizaje estadístico mas utilizado y con el cuidado adecuado puede llegar a funcionar verdaderamente bien. Ademas, su simpleza y claridad permiten analizar muchas características útiles para otros modelos. Esto lo convierte en el punto de partida ideal para entender otros algoritmos de aprendizaje estadístico.


Antes de poner manos a la obra, aclaremos algunos puntos. Por si todavía no quedo claro, la regresión lineal es un algoritmo de aprendizaje supervisado y como ya dijimos en la entrada anterior (les dije que iba a ser útil toda esa introducción) se necesita de un conjunto de "datos y respuestas correctas" para realizar el entrenamiento. Siendo \(m\) un natural que representa el tamaño de nuestro conjunto definamos entonces la siguiente notación: \(X^i\) para referirnos a nuestros datos de entrada (empecemos primero por el caso mas sencillo, una sola variable, mas adelante extenderemos la noción a varias) y \(Y^i\) para mencionar a las salidas esperadas siendo el super indice \(i\) la i-esima posición en nuestro conjunto de datos. Juntando toda esta nueva notación, tenemos entonces que los datos con los que contamos para caracterizar a nuestro problema (osea, entrenar a nuestro algoritmo), podrían listarse en tuplas de la forma (dato de entrada, salida esperada) de la siguiente manera \[ (X^1, Y^1) \\ (X^2, Y^2) \\ ... \\ (X^i, Y^i) \\ ... \\ (X^m, Y^m) \].
Bien, supongamos ahora entonces que contamos con el siguiente conjunto de entrenamiento \( (1, 1), (3 , 3), (4, 4) \) representado en el siguiente gráfico donde el eje horizontal representa a mis valores de \(X\) y el vertical o sus respectivas respuestas, osea \(Y\):

Y se nos pide, por alguna razón, calcular cual debería ser la salida en el caso de que nuestro próximo valor de entrada sea un 2. No es para nada complicado darse cuenta que, siguiendo el crecimiento que llevan nuestros valores, la respuesta debería ser la siguiente:
Básicamente, lo que nuestro cerebro esta haciendo (y que trataremos de llevar a nuestro algoritmo) es trazar una linea (osea, generar una función, asumo que si están leyendo esto tienen nociones matemáticas básicas) que pase por la mayor cantidad posible de puntos, cosa que no siempre es posible:

Esta función, que notaremos \(h(x)\) es nuestra hipótesis, aquella que debería representar con la mayor precisión posible a nuestro conjunto de datos. Veamos entonces como gracias a \(h(x)\) también podemos saber los valores de \(h(2,5)\), \(h(3,7)\) e incluso podríamos extenderla hacia adelante obteniendo valores para, por ejemplo \(h(6)\). Tenemos así una relación entre nuestras variables de entrada y nuestra variables de salida. 
Siendo \(h(x)\) una funcional lineal que depende de una sola variable, podemos asumir entonces que tiene la siguiente forma: \(h(x)=X^i.\theta_1\), donde \(\theta\) varia la pendiente o inclinación de la linea que estamos trazando. Pensemos ademas que si queremos que no pase solamente por el origen debemos darle la altura inicial indicada con un valor que en particular, va a estar acompañado siempre de un \(X_0 = 1\). Luego, nuestra función hipótesis queda de la siguiente manera:
\[h(x) = \theta_0 + X^i\theta_1\]

Ahora, supongamos que nuestro conjunto de entrenamiento es un poco mas complicado y se esta representado por el siguiente gráfico:

Se ve a simple vista que, a pesar de poder hacernos una mínima idea de por donde debería ir nuestra hipótesis, es claro que no podríamos definirla con exactitud a simple vista. Volvamos, entonces, a detenernos un segundo y pensar en lo que estamos tratando de buscar. Queremos que para los valores que ya conocemos, la diferencia entre nuestra función y el valor real que debemos obtener (que desde ahora llamaremos costo), sea el menor posible. Si lo pensamos un segundo, lo que buscamos es que \(h(X^i) - Y^i\) sea lo mas cercano posible a cero para todos los valores de entrenamiento. O dicho de otra manera, queremos que al sumar los costos para cada variable, este lo mas cercana a cero. Definamos una función J que en base a los valores de \(\theta\) genere esta idea a la cual llamaremos, como es de esperarse, función de costo:
\[ J(\theta_0, \theta_1) = \frac{1}{2m} \sum\limits_{i=1}^m (h(X^i) - Y^i)^2 \]

Esta función que acabamos de presentar, se conoce como Error Cuadrático Medio (o MSE por sus siglas en ingles). Difiere levemente de la idea que estábamos manejando (cada termino se eleva al cuadrado y al final todo se multiplica por \(\frac{1}{2m}\) porque es un estimador que esta relacionado con la varianza y otras ideas estadísticas) pero la idea que representa es la misma.

Ya tenemos dos funciones fundamentales: Una que representa la idea que queremos construir (\(h(x)\)), y otra que nos sirve para saber cuan lejos estamos del objetivo (\(J(\theta_0, \theta_1)\)). Nuestro objetivo entonces, es tratar de minimizar \(J(\theta_0, \theta_1)\) para acercarla lo mas posible a cero. Para esto, vamos a utilizar la noción de derivada y de descenso por gradiente. Para todo aquel que no tenga nociones de calculo, la derivada de una función en un punto me da la pendiente en ese punto, que vendría a ser cuan inclinada y en que dirección esta la función en el punto donde me encuentro parado. El descenso por gradiente entonces no es mas que esta noción aplicada a buscar un mínimo y consiste en actualizar los valores de \(\theta\) en la dirección donde se produce un descenso. Ahora, si derivamos \(J(\theta_0, \theta_1)\) respecto de \(\theta_0\) y \(\theta_1\) obtenemos lo siguiente:
\[\theta_0' = \frac{1}{m} \sum\limits_{i=1}^m (h(X^i) - Y^i)^2\]
\[\theta_1' = \frac{1}{m} \sum\limits_{i=1}^m (h(X^i) - Y^i)^2 * X^i\]

Y para actualizar los valores de \(\theta\) deberíamos actualizar estos valores hasta alcanzar un error lo suficientemente pequeño o un punto donde ya no se realizan mas actualizaciones (también podria usarse como criterio de parada una cantidad p de actualizaciones):

                                mientras no converga { 
                                      \(\theta_0 = \theta_0 - a * \frac{1}{m} \sum\limits_{i=1}^m (h(X^i) - Y^i)^2\)
                                      \(\theta_1 = \theta_1 - a * \frac{1}{m} \sum\limits_{i=1}^m (h(X^i) - Y^i)^2 * X^i\)
                                }


Con todo esto entonces ya tenemos una noción básica de como, a partir de un conjunto de datos de entrenamiento podemos aproximar una función con la intención de representarla lo mas fielmente posible y así poder conseguir valores fuera de nuestro conjunto con cierta precisión. En la próxima entrega vamos a extender esta noción a funciones de varias variables (en particular utilizando matrices, para facilitar los cálculos y poder computar de forma mas eficiente) y mas adelante vamos a llevar todo esto a código.
Espero haber sido claro, aunque de todas formas, cualquier sugerencia es bienvenida.



No hay comentarios:

Publicar un comentario