Objetivos:
1.- Utilizar conceptos que ya conocen, e.g., el de una recta, para introducirnos en Python.
2.- Visualizar lo que significa miminimizar una función de riesgo, argumento que está detrás de ML:
Ahora vamos a traducir lo que usted acaba de hacer a Python
Para ello vamos a importar
dos modulos
o paquetes
que nos permitan graficar y crear datos
Lo que en R
sería library(paquete)
, por ejemplo, en Python es import paquete
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# Vamos a planear el marco donde queremos dibuarj
fig, ax = plt.subplots(figsize = (10,4))
plt.show()
type(ax)
fig, ax = plt.subplots(nrows = 2, ncols =1 , figsize = (10,2))
plt.show()
type(ax)
ax[0]
# Hide the right and top spines
# Donde dibujamos la recta
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
plt.show()
fig, ax = plt.subplots(2,1, figsize = (10,4))
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax[0].spines["right"].set_visible(False)
ax[0].spines["top"].set_visible(False)
plt.show()
# Agregarle un cuadriculado
# Hide the right and top spines
# Donde dibujamos la recta
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.grid()
plt.show()
# Vamos a ver que podemos agregar al gráfico lo que querramos.
# Vamos a dibujar la recta en este gráfico
# Hacer tal cual hicimos en el dibujo
# Vamos a escribir la ecuación de la recta
b = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.plot(x,y)
ax.grid()
plt.show()
# Vamos a incluir los nombres de los ejes
# Vamos a escribir la ecuación de la recta
b = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# Nombres de los ejes
ax.set_xlabel("Tamaño en $mt^2$")
ax.set_ylabel("Dólares/$mt^2$")
ax.grid()
ax.plot(x,y)
plt.show()
# Vamos a incluir dos lineas en el valor 0
# Vamos a escribir la ecuación de la recta
bb = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.grid()
#Grafico
ax.plot(x,y)
# Nombres de los ejes
ax.set_xlabel("Tamaño en $mt^2$")
ax.set_ylabel("Dólares/$mt^2$")
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
plt.show()
# Que los ejes corten en cero
bb = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
#Grafico
ax.plot(x,y)
# Nombres de los ejes
ax.set_xlabel("Tamaño en $mt^2$")
ax.set_ylabel("Dólares/$mt^2$")
ax.grid()
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
plt.show()
# Vamos a escribir la ecuación de la recta
# Que los ejes corten en cero
b = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
#Grafico
ax.plot(x,y)
# Nombres de los ejes
ax.set_xlabel("Tamaño en $mt^2$")
ax.set_ylabel("Dólares/$mt^2$")
ax.grid()
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
# Ejes se intersectan en 0
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
# Texto de la recta
# Agregams un texto a la funcion y un titulo al grpafico
ax.text(200, 265, f"$Y =$ {b} + {w}$\\times X$", size = 10 )
plt.title("Recta de regresión $Y =b + w X$", size = 15)
plt.show()
¿Qué es esa recta -modelo- que hemos dibujado?
Es un regla que relaciona el tamaño de un inmueble con el precio: si me das el tamaño, te doy el precio.
Es decir, vincula un input con un output
¿Por qué es bueno tener un modelo; una regla que relacione los inputs o las variables de control con los outputs o variables dependientes?
Si conozco este modelo puedo:
Extrapolar: predecir fuera del rango de valores observados
Interpolar: predecir dentro del rango de valores de la variable
Analizar cómo cambios en la $X$ provocan cambios en la $Y$
¿Es posible expresar esta regla en lenguaje matemático, i.e., con formulas?
¿Que representa cada alemento en esta función?
En la realidad ¿qué elementos de este modelo, de esta regla, son observables?
Podemos obtener información de $Y$: precio por metro cuadrado; $X$: tamaño del piso
Lo que en la realidad no conocemos, son $b$ y $w$:
En economía se llaman parámetros: $b$ la ordenada en el origen; $w$, la pendiente
En ML: $b$ es el sesgo; $w$, son los pesos de cada variable.
Antes, veamos como distintos parámetros -rectas- pueden hacernos tomar distintas decisiones
# Veamos cómo afectan cada uno de estos parámetros
b = 100
w = 1.5
x = np.linspace(30,250,100)
y = b + w*x
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# Agregams un texto a la funcion y un titulo al grpafico
ax.text(200, 275, f"$Y =$ {b} + {w}$\\times X$", size = 10 )
plt.title("Com cambia la recta $Y =b + w X$ modificando la pendiente de -1 a 2", size = 15)
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
# Ejes se intersectan en 0
#ax.spines['left'].set_position('zero')
#ax.spines['bottom'].set_position('zero')
ax.plot(x,y)
ax.grid(True, linestyle = ":", color = "blue", alpha = 0.5 )
# Vemos com afectan los parámetros
w_i = [0, -2]
for p in w_i:
ax.plot(x, b + p*x)
ax.text(200, b + (p+.1)*175, f"$Y =$ {b} + {p} $\\times X$", size = 10 )
plt.show()
Distintos parametros: distintas reglas
Cambio los parámetros y me cambia la relacion entre las variables
Lo importante a observar en el desarrollo anterior es que pudimos hacer el gráfico de la recta por que conociamos los parámetros
Le hemos dado valores a los párametros de la recta $Y = b + w \times X$
Problema: cuando analizamos datos de precios de viviendas y de tamaño de inmuebles: no conocemos los parámetros
Cuando uno analiza datos de precios y tamaño, deberá buscar alguna herramienta para obtener un valor aproximado de dichos parámetors.
¿Cómo obtenemos los parámetros?
Según lo que hemos aprendido en primero de escuela, bastarían dos puntos para graficar una recta.
Bueno, utilicemos este prodecimiendo: agarremos el periódico y saquemos dos apartamentos, tamaño y precios
Con ello, hagamos el gráfico.
x_c = np.array([100,200])
y_c = np.array([150,300])
print(x_c)
print(y_c)
#%matplotlib qt # pone el gráfico fuera del jn
#%matplotlib inline
# Hacemos el grafico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# Agregams un texto a la funcion y un titulo al grpafico
#ax.text(5, 10, f"Y = {b} + {w}X", size = 10 )
#plt.title("Com cambia la recta $Y =b + w X$ modificando la pendiente de -1 a 2", size = 15)
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.grid(True, linestyle = ":", color = "blue", alpha = 0.2 )
# vamos a fijar los limites del gráfico
ax.set_xlim(-1,300)
ax.set_ylim(-1,400)
ax.scatter(x_c,y_c,s=100)
plt.show()
Dado mi modelo lineal, debería buscar algun algoritmo para poder conocer misa parámetors, i.e., alguna sucesion de pasos que vincule lo que observo y lo que no observo.
Algoritmo:
La función: $Y = f(X; b,w) = b + w \times X$ tiene que satisfacer: $Y_0 = f(X_0; b,w) = b + w X_0$
La función, el modelo, tiene que pasar por el punto.
Si tengo dos puntos:
$$ Y_0 = b + w X_0 \\ Y_1 = b + w X_1 $$Tengo 2 ecuaciones con dos incógnitas: despejo para encontrar los parámetros
De lo que observo, puedo identificar los parámetros que no conozco.
$$ w = \frac{Y_1 - Y_0}{X_1-X_0} \\ $$entonces $$ b = Y_0 - w X_0 $$
En otras palabras, me valen solo dos observaciones para encontrar los dos parámetros de la recta.
Entonces, vamos a construirnos en Python este algoritmo como una función
Pseudo-codigo
1.- Escribir una función
2.- Inputs: coordenadas de los puntos.
2.- Utilizar las ecuaciones para obtener los parámetros
3.- Output: los parámetros de la ecuación de la recta.
# Algoritmo para encontrar los parámetros dados los dos puntos
def algoritmo_recta(coords_x,coords_y):
'''Obtener intercept y pendiente de una recta dados dos puntos'''
x_0 = coords_x[0]
x_1 = coords_x[1]
y_0 = coords_y[0]
y_1 = coords_y[1]
w = (y_1 - y_0) / (x_1 - x_0)
b = y_0 - w*x_0
return (b,w)
Veamos si el algoritmo funciona
x_c = np.array([100,200])
y_c = np.array([150,300])
b, w = algoritmo_recta(x_c,y_c)
x = np.linspace(30,250,100)
y = b + w*x
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# Agregams un texto a la funcion y un titulo al grpafico
#ax.text(5, 10, f"Y = {b} + {w}X", size = 10 )
#plt.title("Com cambia la recta $Y =b + w X$ modificando la pendiente de -1 a 2", size = 15)
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.grid(True, linestyle = ":", color = "blue", alpha = 0.2 )
# vamos a fijar los limites del gráfico
ax.set_xlim(-1,300)
ax.set_ylim(-1,400)
ax.scatter(x_c,y_c,s=100)
ax.plot(x,y, color = "green")
plt.show()
algoritmo_recta(x_c,y_c)
Una inmobiliaria: ¿aceptaría que le vendieramos esta regla para predecir el valor del precio de un piso dado su tamaño?
Objetivo:
El objetivo en el análisis de datos es buscar patrones, i.e., funciones que relaciones inputs con outputs
Que las predicciones sean lo más acertadas posibles.
# Ahora vamos a crear una función grafica para no tener que repetir todas las cosas
def graph(xcoord, ycoord):
# Ahora vamos a insertar un tercer punto en el gráfico
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# Lineas en el nivel 0
ax.axvline(x=0, ymin=0, ymax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.axhline(y=0, xmin=0, xmax=1, color = "k", linewidth = 1, alpha = 0.25)
ax.grid(True, linestyle = ":", color = "blue", alpha = 0.2 )
ax.scatter(xcoord,ycoord,s=100)
Bueno, si con dos puntos no parece razonable, busquemos 3 apartamentos.
# Que pasa si tenemos tres puntos en la gráfica
# Ahora vamos a insertar un tercer punto en el gráfico
x_c = np.array([50,100,150])
y_c = np.array([100,300,225])
graph(x_c,y_c)
Cuantas combinaciones de dos puntos puedo hacer con tres puntos.
Veamos una forma de resolver
from itertools import combinations
puntos = [0,1,2]
comb_indices = combinations(puntos,2)
comb_indices
type(comb_indices)
comb_indices.__next__()
comb_indices.__next__()
Itertools
es una fábrica de lazy iterators
: vermos los conceptos de iterables, iteradores y generadores.
comb_puntos = list(combinations(puntos,2))
comb_puntos
Vamos a utilizar esta combinación de indices para realizar todos los gráficos que pasan por los puntos
Como tenemos que hace lo mismo varias veces, vamos a utilizar un loop
x_c = np.array([50,100,150])
y_c = np.array([100,300,225])
fig, ax = plt.subplots(figsize = (10,4))
# Elimnamos las lines superiores y derecha
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
for p in comb_puntos:
p_1, p_2 = p
xc_1 = np.array([x_c[p_1],x_c[p_2]])
yc_1 = np.array([y_c[p_1],y_c[p_2]])
b, w = algoritmo_recta(xc_1,yc_1)
#Regresion de la recta
x = np.linspace(30,250,100)
y = b + w*x
plt.plot(x,y)
plt.scatter(x_c,y_c,s=100)
plt.show()
En otras palabras, ¿cuál es la mejor predicción a partir de estos tres puntos que le daremos al gerente de la empresa inmobiliaria?
Objetivo: encontrar la recta que mejor ajusta a estos tres puntos
¿En qué sentido: mejor ajusta?
Vamos a elegir una media: minimizar el error de predicción (cuadrático)
El error cuadrático se define como la distancia entre lo que quiero predecir y cómo lo predigo: entre el precio por $mt^2$ y la recta que utilizo para predecirlo, $b + w \times X$: $$ (Y_i - (b + w \times X_i))^2 $$
Quiero minimizar esta distancia para las 3 observaciones. En otras palablras, lo que busco es mimnimizar el error cuadratico medio:
$$ min_{b,w} \frac{1}{3}\sum_{i=1}^{3} (Y_i - (b + w \times X_i))^2 $$Veamos gráficamente lo que estoy buscando
from mpl_toolkits.mplot3d import Axes3D
import collections
Ver el modulo collection, que es muy util.
Aquí veremos namedtupled
def error(w, b, points):
'''
ECM en w: los pesos y b: el bias
Diseñada para tabajar con namedtuples con dos coordenadas, x e y
'''
ECM = 0
for i in range(0, len(points)):
ECM += (points[i].y - (w * points[i].x + b)) ** 2
return ECM / float(len(points))
print(error.__doc__)
# Puntos observados
x_c = np.array([50,100,150])
y_c = np.array([100,300,225])
# Valores iniciales
ym = y_c.mean()
xm = x_c.mean()
w = np.dot((y_c-ym), (x_c-xm))/np.dot((x_c-xm),(x_c-xm))
b = ym - w*xm
# Creo un namedtupled para guardar la información
Point = collections.namedtuple('Point', ['x', 'y'])
points = [Point(xp, yp) for xp,yp in zip(x_c, y_c)]
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(111, projection='3d')
ms = np.linspace(w - 5, w + 5, 100)
bs = np.linspace(b - 50, b + 50, 100)
M, B = np.meshgrid(ms, bs)
zs = np.array([error(mp, bp, points) for mp, bp in zip(np.ravel(M), np.ravel(B))])
Z = zs.reshape(M.shape)
ax.plot_surface(M, B, Z, rstride=1, cstride=1, color='b', alpha=0.5)
ax.set_xlabel('w')
ax.set_ylabel('b')
ax.set_zlabel('error')
plt.show()
Ejercicio
1.- Ir a la documentación de collections
y ver qué namedtupled
2.- Obtener la coordenada de x
del primer elemento de points
3.- ¿Que hace np.ravel
?
4.- ¿Para que hacemos el np.meshgrid
?
5.- ¿Qué se está haciendo en:
````
zs = np.array([error(mp, bp, points)for mp, bp in zip(np.ravel(M), np.ravel(B))])
````
# Si hacemos el gráfico en este mínimo tenemos
fig, ax = plt.subplots(figsize = (10,4))
x = np.linspace(30,250,100)
y = b + w*x
ax.plot(x,y, color = "green")
plt.scatter(x_c,y_c,s=100)
plt.show()
Lo que hemos hecho arriba tenia como objetivo repazar algunos elementos de Python y de regresión.
En la realidad, observo
Los datos que observo de la variable dependiente, $Y$, se desvían de la relación lineal $\beta_0 + \beta_1 \times X$: por cuestiones inobservables, $U$
$$ Y = \beta_0 + \beta_1 \times X + U $$Dado esto, lo que me interesa es predecir $Y$ lo mejor posible:
$$ Y = \underbrace{\beta_0 + \beta_1X}_{\hat{Y} = \hat{\beta_0} + \hat{\beta_1} \times X } + U $$Necesito entonces un algoritmo para buscar los estimadores de lo parámetros $ \hat{\beta_0},\hat{\beta_1}$.
# Construimos un modelo simulado
n = 100
x = np.random.normal(0,1,n)
x_ones = np.ones(n)
a = 1
b = 5
ϵ = np.random.normal(0,.5,n)
y = a * x_ones + b*x + ϵ
fig, ax = plt.subplots(figsize = (10,5))
ax.axvline(color = "k", linewidth = 1)
ax.axhline(color = "k", linewidth = 1)
ax.grid(True, ls = ":")
ax.set_xlim(-2,2)
ax.set_ylim(-5,5)
ax.set_xlabel("Valores de la variable independiente $X$")
ax.set_ylabel("Valores de la variable dependiente $Y$")
#ax.set_title("Recta de regresión: mejor ajuste entre los datos observados")
#ax.text(0.5, 1.5, "$\\hatY = \\hat\\beta_0 + \\hat\\beta_1 X $", size = 20)
#ax.plot(x,yp, color = "red")
ax.scatter(x,y,s=10)
plt.show()
def ecm(y,yp):
e = y - yp
ec = np.mean(e**2)
return ec
# Valor inicial de los parámetros
a_hat = 0
b_hat = 0
learning_rate = 0.01 # Tasa de aprendizaje
n_iters = 1000 # Numero de iteraciones para hacer el gradient descent
# Gradient descent
for epoch in range(n_iters):
y_pred = a_hat + b_hat*x # el valor predicho de Y
D_a = (-2/n) * sum(y - y_pred) # Derivada con respecto a a
D_b = (-2/n) * sum(x* (y - y_pred)) # Derivada con respecto a b
a_hat = a_hat - learning_rate * D_a # update a
b_hat = b_hat - learning_rate * D_b # update b
if epoch %2 == 0:
print(f'epoch {epoch + 1}: a = {a_hat:.3f}, b = {b_hat:.3f}, ecm = {ecm(y,y_pred):3f}' )
fig, ax = plt.subplots(figsize = (10,5))
ax.axvline(color = "k", linewidth = 1)
ax.axhline(color = "k", linewidth = 1)
ax.grid(True, ls = ":")
ax.set_xlim(-2,2)
ax.set_ylim(-5,5)
ax.set_xlabel("Valores de la variable independiente $X$")
ax.set_ylabel("Valores de la variable dependiente $Y$")
#ax.set_title("Recta de regresión: mejor ajuste entre los datos observados")
#ax.text(0.5, 1.5, "$\\hatY = \\hat\\beta_0 + \\hat\\beta_1 X $", size = 20)
ax.plot(x,y_pred, color = "red")
ax.scatter(x,y,s=10)
plt.show()
Ejercicio:
Modifique el algoritmo de gradient descent para que itere siempre que la tolerancia, definida como la variación del ECM entre una iteración y otra, sea mayor que un nivel.
Analice cómo depende el resulado de la tolerancia que usted asigna
Asigne dos tolerancias:
1.- 0.5
2.- 0.01
a_hat = 0
b_hat = 0
tol = 0.5
epoch = 1
ecm_b = 1
while True:
y_pred = a_hat + b_hat*x # el valor predicho de Y
D_a = (-2/n) * sum(y - y_pred) # Derivada con respecto a a
D_b = (-2/n) * sum(x* (y - y_pred)) # Derivada con respecto a b
a_hat = a_hat - learning_rate * D_a # update a
b_hat = b_hat - learning_rate * D_b # update b
if epoch %2 == 0:
print(f'epoch {epoch}: a = {a_hat:.3f}, b = {b_hat:.3f}, ecm = {ecm(y,y_pred):3f}' )
epoch +=1
if abs(ecm_b - ecm(y,y_pred)) < tol:
break
ecm_b = ecm(y,y_pred)
fig, ax = plt.subplots(figsize = (10,5))
ax.axvline(color = "k", linewidth = 1)
ax.axhline(color = "k", linewidth = 1)
ax.grid(True, ls = ":")
ax.set_xlim(-2,2)
ax.set_ylim(-5,5)
ax.set_xlabel("Valores de la variable independiente $X$")
ax.set_ylabel("Valores de la variable dependiente $Y$")
#ax.set_title("Recta de regresión: mejor ajuste entre los datos observados")
#ax.text(0.5, 1.5, "$\\hatY = \\hat\\beta_0 + \\hat\\beta_1 X $", size = 20)
ax.plot(x,y_pred, color = "red")
ax.scatter(x,y,s=10)
plt.show()