<jupyter_start><jupyter_text>Laboratorio 2: Armado de un esquema de aprendizaje automáticoEn el laboratorio final se espera que puedan poner en práctica los conocimientos adquiridos en el curso, trabajando con un conjunto de datos de clasificación.El objetivo es que se introduzcan en el desarrollo de un esquema para hacer tareas de aprendizaje automático: selección de un modelo, ajuste de hiperparámetros y evaluación.El conjunto de datos a utilizar está en `./data/loan_data.csv`. Si abren el archivo verán que al principio (las líneas que empiezan con ``) describen el conjunto de datos y sus atributos (incluyendo el atributo de etiqueta o clase).Se espera que hagan uso de las herramientas vistas en el curso. Se espera que hagan uso especialmente de las herramientas brindadas por `scikit-learn`.<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# TODO: Agregar las librerías que hagan falta
from sklearn.model_selection import train_test_split
df = pd.read_csv("./data/loan_data.csv", comment="#")

# División entre instancias y etiquetas
X, y = df.iloc[:, 1:], df.TARGET

# división entre entrenamiento y evaluación
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)<jupyter_output><empty_output><jupyter_text>Documentación:- https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html  Ejercicio 1: Descripción de los Datos y la Tarea  1. ¿De qué se trata el conjunto de datos?El conjunto de datos describe distintas caracteristicas de personas que solicitaron un credito con garantia hipotecaria. Entre esos datos, se obtiene por ejemplo, el valor adeudado de la hipoteca, el valor de la propiedad,hace cuanto tiempo estan trabajando en el mismo lugar,etcAcerca de la compañíaLa empresa Dream Housing Finance se ocupa de todos los préstamos hipotecarios. Tienen presencia en todas las áreas urbanas, semiurbanas y rurales. El cliente primero solicita un préstamo hipotecario después de que la compañía valida la elegibilidad del cliente para el préstamo.ProblemaLa empresa desea automatizar el proceso de elegibilidad del préstamo (en tiempo real) en función de los detalles del cliente proporcionados al completar el formulario de solicitud en línea. Estos detalles son género, estado civil, educación, número de dependientes, ingresos, monto del préstamo, historial crediticio y otros. Para automatizar este proceso, han dado un problema para identificar los segmentos de clientes, los que son elegibles para el monto del préstamo para poder dirigirse específicamente a estos clientes. Aquí han proporcionado un conjunto de datos parcial.  2. ¿Cuál es la variable objetivo que hay que predecir? ¿Qué significado tiene?La variable objetivo que hay que predecir, es la variable BAD que hace referencia a si un cliente va a ser moroso a o no. Es un valor booleano que va a tener un 1 si el cliente es moroso y un 0 si el modelo predice que va a pagar el credito  3. ¿Qué información (atributos) hay disponible para hacer la predicción?* TARGET: BAD, 1 = impago del préstamo, 0 = préstamo reembolsado * LOAN: El monto del credito* MORTDUE: Monto adeudado de la hipoteca existente* VALUE: El valor de la propiedad* YOJ: Años en el trabajo actual* DEROG: Número de informes desfavorables importantes* DELINQ: Número de líneas comerciales morosas* CLAGE: Antigüedad de la línea comercial más antigua en meses* CLNO: Número de líneas comerciales* NINQ: Número de consultas crediticias recientes* DEBTINC: Relación deuda-ingresos<jupyter_code>df.columns<jupyter_output><empty_output><jupyter_text>4. ¿Qué atributos imagina ud. que son los más determinantes para la predicción?A priori, podríamos suponer que el monto del credito, el valor de la propiedad y si debe plata de la hipoteca serían buenos predictores. Pero también podría ser la variable de años en el trabajo actual.  Exploración de datos<jupyter_code>df.info()
df.describe()<jupyter_output><empty_output><jupyter_text>A priori no hay valores nan, ya que todas las columnas cuentan con 1854 observaciones.<jupyter_code>df['TARGET'] = df.TARGET.astype(str)
columns = ['LOAN', 'MORTDUE', 'VALUE', 'YOJ', 'DEROG', 'DELINQ', 'CLAGE',
       'NINQ', 'CLNO', 'DEBTINC']
print(df.TARGET.value_counts())
df["TARGET"].value_counts().plot(kind='barh')

fig = plt.figure(figsize=(10, 6))
fig.subplots_adjust(hspace=1, wspace=1)
ranges = range(10)
for column, i in zip(columns, ranges):
    ax = fig.add_subplot(4, 3, i+1)
    ax = df[column].plot.hist().set_title(column)<jupyter_output><empty_output><jupyter_text>En general las distribuciones presentan asimetría positiva y con rangos muy discímiles.<jupyter_code>import seaborn as sns

import matplotlib.pyplot as plt

# taking all rows but only 6 columns
correlation_mat = df.corr()
plt.figure(figsize=(10,10))

sns.heatmap(correlation_mat, annot = True)
plt.show()<jupyter_output><empty_output><jupyter_text>En general, los niveles de correlación entre las variables son bajos, con excepción de VALUE con MORTDUE y LOAN, por lo que podría eliminarse una de esas variables para evitar problemas luego.  Ejercicio 2: Predicción con Modelos LinealesEn este ejercicio se entrenarán modelos lineales de clasificación para predecir la variable objetivo.Para ello, deberán utilizar la clase SGDClassifier de scikit-learn.Documentación:- https://scikit-learn.org/stable/modules/sgd.html- https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html  Ejercicio 2.1: SGDClassifier con hiperparámetros por defectoEntrenar y evaluar el clasificador SGDClassifier usando los valores por omisión de scikit-learn para todos los parámetros. Únicamente **fijar la semilla aleatoria** para hacer repetible el experimento.Evaluar sobre el conjunto de **entrenamiento** y sobre el conjunto de **evaluación**, reportando:- Accuracy- Precision- Recall- F1- matriz de confusión<jupyter_code>from sklearn.linear_model import SGDClassifier
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

clf = Pipeline([('standardscaler', StandardScaler()), ('model',SGDClassifier(random_state=42))])


clf.fit(X_train,y_train)
test_predictions=clf.predict(X_test)
train_predictions=clf.predict(X_train)

print('Metricas de entrenamiento')

print(classification_report(y_train, train_predictions,zero_division=0))

print('Metricas de Test')

print(classification_report(y_test, test_predictions,zero_division=0))
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y_test, test_predictions)
disp = ConfusionMatrixDisplay(confusion_matrix=cm)
disp.plot()<jupyter_output><empty_output><jupyter_text>Ejercicio 2.2: Ajuste de HiperparámetrosSeleccionar valores para los hiperparámetros principales del SGDClassifier. Como mínimo, probar diferentes funciones de loss, tasas de entrenamiento y tasas de regularización.Para ello, usar grid-search y 5-fold cross-validation sobre el conjunto de entrenamiento para explorar muchas combinaciones posibles de valores.Reportar accuracy promedio y varianza para todas las configuraciones.Para la mejor configuración encontrada, evaluar sobre el conjunto de **entrenamiento** y sobre el conjunto de **evaluación**, reportando:- Accuracy- Precision- Recall- F1- matriz de confusiónDocumentación:- https://scikit-learn.org/stable/modules/grid_search.html- https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html<jupyter_code>from scipy import stats
param_dist = {
    'model__loss': [
        'hinge',        # SVM
        'log',          # logistic regression
    ],
    'model__learning_rate':['constant', 'optimal','adaptive'],
    'model__alpha': [0.1,0.5,0.6,0.7,0.8,0.9,1,5,10,50]}<jupyter_output><empty_output><jupyter_text>En esta sección usamos la funcionalidad GridSearch para encontrar la mejor combinación de hiperparámetros. En particular, para el parámetro alpha comenzamos con 6 valores, pero al notar que el mejor resultado estaba cerca de alpha 1 decidimos incorporar valores decimales entre 0.5 y 1, resultando alpha=0.6 como el mejor.<jupyter_code>from sklearn.model_selection import GridSearchCV

model = Pipeline([('standardscaler', StandardScaler()), ('model',SGDClassifier(random_state=42, eta0=0.1))])

cv = GridSearchCV(model, param_dist, scoring ='accuracy', refit=True, cv=5)
cv.fit(X, y);
results = cv.cv_results_

data_sdg_classifier=pd.DataFrame(results)
data_sdg_classifier.sort_values('rank_test_score')[:6]
data_sdg_classifier[['param_model__learning_rate','param_model__loss','param_model__alpha','mean_test_score','std_test_score','rank_test_score']]
cv.best_estimator_
print('Evaluacion de conjunto de entrenamiento con el mejor modelo')
predictions=cv.best_estimator_.predict(X_train)
print(classification_report(y_train, predictions,zero_division=0))

print('Evaluacion de conjunto de test con el mejor modelo')
predictions=cv.best_estimator_.predict(X_test)
print(classification_report(y_test, predictions,zero_division=0))
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y_test, test_predictions)
disp = ConfusionMatrixDisplay(confusion_matrix=cm,display_labels=cv.classes_)
disp.plot()<jupyter_output><empty_output><jupyter_text>Ejercicio 3: Árboles de DecisiónEn este ejercicio se entrenarán árboles de decisión para predecir la variable objetivo.Para ello, deberán utilizar la clase DecisionTreeClassifier de scikit-learn.Documentación:- https://scikit-learn.org/stable/modules/tree.html  - https://scikit-learn.org/stable/modules/tree.htmltips-on-practical-use- https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html- https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html  Ejercicio 3.1: DecisionTreeClassifier con hiperparámetros por defectoEntrenar y evaluar el clasificador DecisionTreeClassifier usando los valores por omisión de scikit-learn para todos los parámetros. Únicamente **fijar la semilla aleatoria** para hacer repetible el experimento.Evaluar sobre el conjunto de **entrenamiento** y sobre el conjunto de **evaluación**, reportando:- Accuracy- Precision- Recall- F1- matriz de confusión<jupyter_code>from sklearn.tree import DecisionTreeClassifier

model=DecisionTreeClassifier(random_state=0)
model = model.fit(X_train,y_train)

train_predictions_tree=model.predict(X_train)
predictions_tree=model.predict(X_test)


print('Evaluacion sobre conjunto de train')
print(classification_report(y_train, train_predictions_tree))

print('Evaluacion sobre conjunto de test')
print(classification_report(y_test, predictions_tree))
cm = confusion_matrix(y_test, predictions_tree)
disp = ConfusionMatrixDisplay(confusion_matrix=cm,display_labels=cv.classes_)
disp.plot()<jupyter_output><empty_output><jupyter_text>Sin ningún ajuste de los hiperparámetros 37/58 morosos se predicen correctamente, mientras que 23 no morosos se incluyen en esta categoría.<jupyter_code>from sklearn import tree
tree.plot_tree(model)
plt.show()<jupyter_output><empty_output><jupyter_text>Ejercicio 3.2: Ajuste de HiperparámetrosSeleccionar valores para los hiperparámetros principales del DecisionTreeClassifier. Como mínimo, probar diferentes criterios de partición (criterion), profundidad máxima del árbol (max_depth), y cantidad mínima de samples por hoja (min_samples_leaf).Para ello, usar grid-search y 5-fold cross-validation sobre el conjunto de entrenamiento para explorar muchas combinaciones posibles de valores.Reportar accuracy promedio y varianza para todas las configuraciones.Para la mejor configuración encontrada, evaluar sobre el conjunto de **entrenamiento** y sobre el conjunto de **evaluación**, reportando:- Accuracy- Precision- Recall- F1- matriz de confusiónDocumentación:- https://scikit-learn.org/stable/modules/grid_search.html- https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html<jupyter_code>param_dist_tree = {
    'criterion': [
        'entropy',      
        'gini',          
    ],
    'max_depth':[5,6,7,8,9,10,20,50,100,200],
    'min_samples_leaf': [1,5,10,50]}
model=DecisionTreeClassifier(random_state=0)
cv_tree = GridSearchCV(model, param_dist_tree, scoring ='accuracy', refit=True, cv=5)
cv_tree.fit(X, y);
tree_results = cv_tree.cv_results_

data_tree_classifier=pd.DataFrame(tree_results)
data_tree_classifier.sort_values('rank_test_score')[:6]
cv_tree.best_params_<jupyter_output><empty_output><jupyter_text>Nuevamente luego de obtener los mejores parámetros ajustamos los parámetros de entrada, dando mayores opciones cercanas a la mejor profundidad máxima y a al mínimo de hojas. Finalmente uno de los parámetros encontro un nuevo óptimo, mientras que el otro se mantuvo.<jupyter_code>data_tree_classifier[['param_criterion','param_max_depth','param_min_samples_leaf','mean_test_score','std_test_score','rank_test_score']]
print('Evaluacion de conjunto de entrenamiento con el mejor modelo')
predictions=cv_tree.best_estimator_.predict(X_train)
print(classification_report(y_train, predictions))

print('Evaluacion de conjunto de test con el mejor modelo')
predictions=cv_tree.best_estimator_.predict(X_test)
print(classification_report(y_test, predictions))
cm = confusion_matrix(y_test, predictions)
disp = ConfusionMatrixDisplay(confusion_matrix=cm,display_labels=cv.classes_)
disp.plot()<jupyter_output><empty_output><jupyter_text>Con el ajuste de hiperparámetros, solo 3 no morosos fueron predichos como morosos, mientras que 30 de estos últimos no fueron detectados por el árbol de decisión. Dado que se buscó optimizar accuracy, no es de extrañar la mejora sustancial tras la optimización de hiperparámetros disminuye notablemente los falsos positivos, a la vez que aumenta los falsos negativos.<jupyter_code>model=DecisionTreeClassifier(criterion='gini', max_depth= 6, min_samples_leaf=10, random_state=0)
clf = model.fit(X, y);
from sklearn import tree
plt.figure(figsize=(20,12))
tree.plot_tree(clf, fontsize=10,feature_names=df.columns[1:],impurity=False,class_names=['Cumplidor','Moroso'])
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08117638761548769, 'fraction_numerical': 0.009450595951759644, 'mean_word_length': 6.685636856368563, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 11, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '262833', 'license': 'MIT', 'n_tokens_mistral': 4736, 'n_tokens_neox': 4643, 'n_words': 1361, 'path': 'Trabajo-Practico-2 Parte.ipynb', 'repo_name': 'nicocarrion1/IntroduccionAprendizajeAutomatico', 'size': '14179'}