<jupyter_start><jupyter_text>Aprendizaje Automático Diplodatos 2021 Integrantes de Grupo* <NAME>* <NAME>* <NAME>* <NAME>  Laboratorio 1: Regresión en BostonEn este laboratorio deben hacer experimentos de regresión con el conjunto de datos "Boston house prices dataset".Estudiarán el dataset, harán visualizaciones y seleccionarán atributos relevantes a mano.Luego, entrenarán y evaluarán diferentes tipos de regresiones, buscando las configuraciones que mejores resultados den.<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Carga del Conjunto de DatosCargamos el conjunto de datos y vemos su contenido.<jupyter_code>from sklearn.datasets import load_boston
boston = load_boston()
boston.keys()
# print(boston['DESCR'])   # descripción del dataset
# boston['data']           # matriz con los datos de entrada (atributos)
# boston['target']         # vector de valores a predecir
# boston['feature_names']  # nombres de los atributos para cada columna de 'data'
boston['data'].shape, boston['target'].shape<jupyter_output><empty_output><jupyter_text>División en Entrenamiento y EvaluaciónDividimos aleatoriamente los datos en 80% para entrenamiento y 20% para evaluación:<jupyter_code>from sklearn.model_selection import train_test_split
X, y = boston['data'], boston['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=0)
X_train.shape, X_test.shape, y_train.shape<jupyter_output><empty_output><jupyter_text>Ejercicio 1: Descripción de los Datos y la TareaResponda las siguientes preguntas:1. ¿De qué se trata el conjunto de datos?2. ¿Cuál es la variable objetivo que hay que predecir? ¿Qué significado tiene?3. ¿Qué información (atributos) hay disponibles para hacer la predicción?4. ¿Qué atributos imagina ud. que serán los más determinantes para la predicción?5. ¿Qué problemas observa a priori en el conjunto de datos? ¿Observa posibles sesgos, riesgos, dilemas éticos, etc? Piense que los datos pueden ser utilizados para hacer predicciones futuras.**No hace falta escribir código para responder estas preguntas.** **1.**  Nuestro conjunto de datos recopila los precios de viviendas de Boston (Estados Unidos)**2.**   En este caso la variable que vamos a predecir es el Precio de las casas. La idea del trabajo es poder predecir a partir de determinados atributos recopilados el precio de una vivienda. Asi, con dichos datos, podremos conocer si el precio que posee determinada vivienda se encuentra por encima o por debajo de la predicción que podemos obtener. **3.**   Los atributos que tenemos son:* `CRIM` Tasa de criminalidad per cápita por ciudad.* `ZN` Proporción de terreno residencial dividido en zonas para lotes de más de 25.000 pies cuadrados.* `INDUS` Proporción de acres comerciales no minoristas por ciudad.* `CHAS` Variable ficticia de Charles River (Es igual a 1 si el tramo limita con el río; 0 en caso contrario)* `NOX` Concentración de óxidos nítricos (partes por 10 millones)* `RM` Número medio de habitaciones por vivienda* `AGE` Proporción de unidades ocupadas por sus propietarios construidas antes de 1940* `DIS` Distancias ponderadas a cinco centros de empleo de Boston* `RAD` Índice de accesibilidad a carreteras radiales* `TAX` Tasa de impuesto a la propiedad de valor total por  \\$ 10.000 dolares* `PTRATIO` Proporción alumno-maestro por ciudad* `B` 1000 (Bk - 0.63) ^ 2 donde Bk es la proporción de negros por ciudad* `LSTAT` Porcentaje del status mas bajo de la poblacion* `MEDV` Valor medio de las viviendas ocupadas por sus propietarios en $ 1000 dolares**4.**   * `ZN` Proporción de terreno residencial dividido en zonas para lotes de más de 25.000 pies cuadrados.* `TAX` Tasa de impuesto a la propiedad de valor total por  \\$ 10.000 dolares**5.**   Observamos que determinados valores de atributos poseen una fuerte connotación cultural hacia ciertos sectores de la población, con fuerte sesgo social. Además de su composición, nos lleva a realizarnos la pregunta sobre cómo han sido obtenidos determinados índices poco objetivos, como el % de personas de color dentro de la población y el status de la población. Creemos que la recolección de estos índices ha llevado a un sesgo social de la persona que tomó los datos de la muestra. Entendemos que los atributos han sido recolectados hace algunos años; es dificil pensar que en la fecha se lleven a cabo dichos índices, por lo que no tendremos dichos datos actualizados para nuevas predicciones.   Ejercicio 2: Visualización de los Datos1. Para cada atributo de entrada, haga una gráfica que muestre su relación con la variable objetivo.2. Estudie las gráficas, identificando **a ojo** los atributos que a su criterio sean los más informativos para la predicción.3. Para ud., ¿cuáles son esos atributos? Lístelos en orden de importancia.<jupyter_code># 1. Resolver acá. Ayuda/ejemplo:
feature = 'CRIM'
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>En la variable de Crimenes observamos que los datos acumulados en valor 0 en la variable crímenes y distribuída en diferentes valores en la variable precio, hace que no podamos inferir una regresión en los datos.<jupyter_code>feature = 'ZN'
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>En esta variable no vemos que podamos trazar una regresión entre los valores del feature. Vemos valores dispersos sin una suficiente correlación.<jupyter_code>feature = "INDUS"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>Se observa una baja correlación entre la variable `INDUS` y el precio.<jupyter_code>feature = "CHAS"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>Estamos ante la presencia de una variable discreta - categórica, por lo que no podemos relacionarla al precio de las viviendas<jupyter_code>feature = "NOX"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>No vemos una correlación entre la variable `NOX` y la variable objetivo del estudio.<jupyter_code>feature = "RM"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>En este caso, observamos que encontramos relación entre la variable `RM` y la variable precio. Se puede inferir que habría una función que describa el comportamiento de la correlación.<jupyter_code>feature = "AGE"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>El feature `AGE` demuestra una determinada correlación, que se podrá observar con mayor detalle si quitamos los datos que se observan en la parte superior, en valores de y > 40<jupyter_code>feature = "DIS"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>Observamos una pequeña correlación, fuertemente concentrada en los valores iniciales de las variables `DIS` y el precio.<jupyter_code>feature = "RAD"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>Estamos ante la presencia de una variable categórica, que describe determinados niveles de accesibilidad. No se observa correlación.<jupyter_code>feature = "TAX"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>A pesar de ser una de las variables que intuíamos de que podría correlacionarse, no observamos una relación directa entre el feature `TAX` y el precio<jupyter_code>feature = "PTRATIO"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>En esta variable no se encuentra correlación con el precio.<jupyter_code>feature = "B"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>No encontramos correlación entre esta sorpresiva feature (por lo que representa) y el precio.<jupyter_code>feature = "LSTAT"
selector = (boston['feature_names'] == feature)
plt.scatter(X[:, selector], y, facecolor="dodgerblue", edgecolor="k", label="datos")
plt.title(feature)
plt.show()<jupyter_output><empty_output><jupyter_text>Se observa una correlación entre la variable `LSTAT` y el precio. La variable representa según determinadas distinciones de estatus socioeconómico, las clases sociales bajas.  2) Observamos que encontramos una correlación entre el precio y las variables `RM`, `CRIM` y `LSTAT`. 3) Si debemos colocar un orden a la relación de las variables, podemos decir que encontramos mayor correlación en la variable `LSTAT`, luego en la variable `RM` y por último en la variable `CRIM`. Veremos en los próximos puntos si a "prima facie" nos encontrábamos en lo cierto.   Ejercicio 3: Regresión Lineal1. Seleccione **un solo atributo** que considere puede ser el más apropiado.2. Instancie una regresión lineal de **scikit-learn**, y entrénela usando sólo el atributo seleccionado.3. Evalúe, calculando error cuadrático medio para los conjuntos de entrenamiento y evaluación.4. Grafique el modelo resultante, junto con los puntos de entrenamiento y evaluación.5. Interprete el resultado, haciendo algún comentario sobre las cualidades del modelo obtenido.**Observación:** Con algunos atributos se puede obtener un error en test menor a 50. ** RESOLUCION **Elegimos el atributo `LSTAT`, al cuál le encontramos una correlación visual con la variable precio. La variable elegida es la que representa el nivel de población de bajo status dentro de la zona de la vivienda.<jupyter_code># 1. Resolver acá. Ayuda:
feature = 'LSTAT'  # selecciono el atributo 'LSTAT'
selector = (boston['feature_names'] == feature)
X_train_f = X_train[:, selector]
X_test_f = X_test[:, selector]
X_train_f.shape, X_test_f.shape, y_train.shape
from sklearn.linear_model import LinearRegression

model = LinearRegression().fit(X_train_f, y_train)
print(f"El coeficiente de determinación R2 de la predicción es de {model.score(X_train_f, y_train):.3f}")
# Observamos los valores predichos de la variable X_test_f
model.predict(X_test_f)
#Observamos los valores reales de la variable test
y_test
from sklearn.metrics import mean_squared_error

y_train_pred = model.predict(X_train_f)
y_test_pred = model.predict(X_test_f)

train_error = mean_squared_error(y_train, y_train_pred)
test_error = mean_squared_error(y_test, y_test_pred)
print("Visualizamos los siguientes errores de la función de predicción:")
print(f'Train error: {train_error:0.4}')
print(f'Test error: {test_error:0.4}')
# 4. Graficar acá. 
x_start = min(np.min(X_train_f), np.min(X_test_f))
x_end = max(np.max(X_train_f), np.max(X_test_f))
x = np.linspace(x_start, x_end, 200).reshape(-1, 1)
#plt.plot(y_test, model.predict(X_test_f), color="tomato", label="modelo")

plt.plot(x, model.predict(x), color="tomato", label="modelo")

plt.scatter(X_train_f, y_train, facecolor="dodgerblue", edgecolor="k", label="train")
plt.scatter(X_test_f, y_test, facecolor="white", edgecolor="k", label="test")
plt.title(feature)
plt.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>En el gráfico anterior vemos los puntos que utilizamos para entrenamiento, los puntos para el test del modelo y la función de predicción encontrada.  Ejercicio 4: Regresión PolinomialEn este ejercicio deben entrenar regresiones polinomiales de diferente complejidad, siempre usando **scikit-learn**.Deben usar **el mismo atributo** seleccionado para el ejercicio anterior.1. Para varios grados de polinomio, haga lo siguiente:    1. Instancie y entrene una regresión polinomial.    2. Prediga y calcule error en entrenamiento y evaluación. Imprima los valores.    3. Guarde los errores en una lista.2. Grafique las curvas de error en términos del grado del polinomio.3. Interprete la curva, identificando el punto en que comienza a haber sobreajuste, si lo hay.4. Seleccione el modelo que mejor funcione, y grafique el modelo conjuntamente con los puntos.5. Interprete el resultado, haciendo algún comentario sobre las cualidades del modelo obtenido.**Observación:** Con algunos atributos se pueden obtener errores en test menores a 40 e incluso a 35. ** RESOLUCION ** 1) Modelamos la función de predicción de Regresión Polinomial con diferentes grados, para conocer de manera visual en que punto la función realiza overfitting<jupyter_code>from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

train_errors = []
test_errors = []
degrees = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14, 16, 18, 20, 22, 24, 26]
for degree in degrees:
    # train:
    pf = PolynomialFeatures(degree)
    lr = LinearRegression(fit_intercept=False)
    model = make_pipeline(pf, lr)
    model.fit(X_train_f, y_train)
    
    # predict:
    y_train_pred = model.predict(X_train_f)
    y_test_pred = model.predict(X_test_f)
    
    # evaluate:
    train_error = mean_squared_error(y_train, y_train_pred)
    test_error = mean_squared_error(y_test, y_test_pred)
    train_errors.append(train_error)
    test_errors.append(test_error)<jupyter_output><empty_output><jupyter_text>Realizamos los cálculos de los errores en entrenamiento y test y los guardamos en una lista (puntos 1 b y 1 c):<jupyter_code>listErrors = np.array([[degrees],[train_errors], [test_errors]])
listErrors = listErrors.tolist()
listErrors<jupyter_output><empty_output><jupyter_text>Se observa que el mejor grado de polinomio es 7, de acuerdo a registrar el menor error en test<jupyter_code># 2. Graficar curvas de error acá.
#Graficamos las curvas de error en términos del grado del polinomio
plt.title("Registro de error de acuerdo a diferentes Grados de Polinomio")
plt.plot(degrees, train_errors, color="blue", label="train")
plt.plot(degrees, test_errors, color="red", label="test")
plt.legend()
plt.xlabel("degree")
plt.ylabel("error")
plt.show()<jupyter_output><empty_output><jupyter_text>Se observa que el modelo comienza a realizar sobre ajuste en el conjunto de datos de test a partir de la aplicación del grado 10 de la función regresión lineal polinomial. A partir de dicho grado de polinomio el modelo se sobreentrena, por lo que ajusta a caracteristicas especificas que luego no sirve para realizar una adecuada generalización en los datos de test.  El siguiente gráfico se realiza con el grado de polinomio 7, que es el que mejor responder con los datos de test.<jupyter_code>degree = 7
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(X_train_f, y_train);

plt.scatter(X_train_f, y_train, color="blue", label="train")
plt.scatter(X_test_f, y_test, color="white", edgecolor="k", label="test")
plt.plot(x, model.predict(x), color="tomato", label="model")
plt.title("Gráfico con datos de Train y Test y Modelo bajo funcion Polinómica Lineal de Grado 7")
plt.legend()
plt.show()
y_train_pred = model.predict(X_train_f)
y_test_pred = model.predict(X_test_f)

train_error = mean_squared_error(y_train, y_train_pred)
test_error = mean_squared_error(y_test, y_test_pred)
print("Visualizamos los siguientes errores de la función de predicción:")
print(f'Train error: {train_error:0.4}')
print(f'Test error: {test_error:0.4}')<jupyter_output>Visualizamos los siguientes errores de la función de predicción:
Train error: 25.01
Test error: 34.26<jupyter_text>La funcion que arroja el modelo cuenta con un error en entrenamiento de 25.01 y en el dataset de test de 34.26.A los efectos de comparación, el modelo de regresión lineal arrojó valores en entrenamiento de 36.52 y en el conjunto de datos de test de 46.34. Es acertada la opción de utilizar el presente modelo de regresión polinomial de grado 7 por sobre el de regresión lineal.   Ejercicio 5: Regresión con más de un AtributoEn este ejercicio deben entrenar regresiones que toman más de un atributo de entrada.1. Seleccione **dos o tres atributos** entre los más relevantes encontrados en el ejercicio 2.2. Repita el ejercicio anterior, pero usando los atributos seleccionados. No hace falta graficar el modelo final.3. Interprete el resultado y compare con los ejercicios anteriores. ¿Se obtuvieron mejores modelos? ¿Porqué? ** RESOLUCION **Elegimos los features RM (número medio de habitaciones por vivienda) y LSTAT (% de población de personas de bajo status)<jupyter_code># 1. Resolver acá. Ayuda (con dos atributos):
selector = (boston['feature_names'] == 'RM') | (boston['feature_names'] == 'LSTAT')
X_train_fs = X_train[:, selector]
X_test_fs = X_test[:, selector]
X_train_fs.shape, X_test_fs.shape
from sklearn.linear_model import LinearRegression

model = LinearRegression().fit(X_train_fs, y_train)
model.score(X_train_fs, y_train)
model.coef_
from sklearn.metrics import mean_squared_error

y_train_pred = model.predict(X_train_fs)
y_test_pred = model.predict(X_test_fs)

train_error = mean_squared_error(y_train, y_train_pred)
test_error = mean_squared_error(y_test, y_test_pred)
print(f'Train error: {train_error:0.4}')
print(f'Test error: {test_error:0.4}')<jupyter_output>Train error: 28.79
Test error: 37.38<jupyter_text>Podemos ver que el modelo con más de un atributo arrojo unos niveles de error cuadrado medio en entrenamiento de 28.79 y en test 37.38. Si lo comparamos con el modelo de regresión polinomial de grado 7, arrojo en entrenamiento 25.01 y en test 34.26; en el modelo de regresión lineal con el feature LSTAT 36.52 y en el conjunto de datos de test de 46.34. Llegamos a la conclusión de que si tenemos la limitante de utilización del modelo de regresión lineal, funciona mejor si contamos con más de un atributo. Si no tenemos restricción en la elección de modelo y podemos utilizar solamente un feature, se prefiere la elección del modelo de regresión polinomial.   Más ejercicios (opcionales) Ejercicio 7: A Todo FeatureEntrene y evalúe regresiones pero utilizando todos los atributos de entrada (va a andar mucho más lento). Estudie los resultados. Ejercicio 8: RegularizaciónEntrene y evalúe regresiones con regularización "ridge". Deberá probar distintos valores de "alpha" (fuerza de la regularización). ¿Mejoran los resultados?<jupyter_code>#Ej 8
from sklearn.linear_model import Ridge
degree = 7
model = make_pipeline(PolynomialFeatures(degree),Ridge(alpha=1))
model.fit(X_train_f, y_train);
plt.scatter(X_train_f, y_train, color="blue", label="train")
plt.scatter(X_test_f, y_test, color="white", edgecolor="k", label="test")
plt.plot(x, model.predict(x), color="tomato", label="model")
#plt.plot(x, model.predict(x.reshape(-1, 1)), color="red", label="model")

plt.legend()
plt.show()
from sklearn.metrics import mean_squared_error

y_train_pred = model.predict(X_train_f)
y_test_pred = model.predict(X_test_f)

train_error = mean_squared_error(y_train, y_train_pred)
test_error = mean_squared_error(y_test, y_test_pred)
print("Visualizamos los siguientes errores de la función de predicción:")
print(f'Train error: {train_error:0.4}')
print(f'Test error: {test_error:0.4}')<jupyter_output>Visualizamos los siguientes errores de la función de predicción:
Train error: 25.12
Test error: 34.15
{'fraction_non_alphanumeric': 0.09132038357511679, 'fraction_numerical': 0.011507253503811163, 'mean_word_length': 6.173192239858906, 'pattern_counts': {'":': 0, '<': 94, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '532502', 'license': 'MIT', 'n_tokens_mistral': 7083, 'n_tokens_neox': 6879, 'n_words': 2302, 'path': 'Lab 1.ipynb', 'repo_name': 'DiploDatos-Grupo2/IntroduccionAprendizajeAutomatico', 'size': '20365'}