Alumnos y Padrón
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
from sklearn.model_selection import KFold, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from preprocessing import prepararSetDeDatos
from preprocessing import prepararSetDeHoldout
from preprocessing import prepararSetDeValidacion
from preprocessing import expansionDelDataset
from preprocessing import conversionAVariablesNormalizadas
from funcionesAuxiliares import mostrarAUCScore
from funcionesAuxiliares import mostrarROCCurve
from funcionesAuxiliares import mostrarMatrizDeConfusion
from funcionesAuxiliares import escribirPrediccionesAArchivo
from funcionesAuxiliares import obtenerDatasets
from funcionesAuxiliares import obtenerHoldout
X, y = obtenerDatasets()
X = prepararSetDeDatos(X)
y = prepararSetDeValidacion(y)
X_rf = conversionAVariablesNormalizadas(X)
def obtenerMejoresParametros(datosPreprocesados):
mejor_valor = 0
mejor_profundidad = None
mejor_cantidad_estimadores = None
mejor_criterio = None
y_array=np.array(y)
for profundidad in [1,2,3,4,5,6,7,9,10]:
for criterio in ["gini", "entropy"]:
for cantidad_estimadores in [1,10,50,100,200]:
kf = StratifiedKFold(n_splits=7)
metricas = []
for fold_idx, (train_index, test_index) in enumerate(kf.split(datosPreprocesados, y_array)):
rf = RandomForestClassifier(criterion=criterio, max_depth=profundidad, n_estimators=cantidad_estimadores)
rf.fit(datosPreprocesados[train_index], y_array[train_index].ravel())
predicciones = rf.predict(datosPreprocesados[test_index])
score_obtenida = roc_auc_score(y_array[test_index],predicciones)
metricas.append(score_obtenida)
if np.mean(metricas) >= mejor_valor:
mejor_valor = np.mean(metricas)
mejor_profundidad = profundidad
mejor_criterio = criterio
mejor_cantidad_estimadores = cantidad_estimadores
return mejor_valor, mejor_profundidad, mejor_criterio, mejor_cantidad_estimadores
X_train, X_test, y_train, y_test = train_test_split(X_rf, y, test_size=0.25, random_state=0)
X_expandido = expansionDelDataset(X)
columnas_codificables_extra = ['pago_categorizado','edades_estratificadas','categoria_invitados']
columnas_numericas_extra = ['4_clusters','10_clusters','total_pagado']
X_exp = conversionAVariablesNormalizadas(X_expandido,columnas_codificables_extra,columnas_numericas_extra)
X_exp_train, X_exp_test, y_exp_train, y_exp_test = train_test_split(X_exp, y, test_size=0.25, random_state=0)
Random forest es un ensamble que consiste en el entrenamiento de varios clasificadores de árbol de decisión.
Cada árbol del ensamble es construido a partir de una muestra simple con reposición del set de entrenamiento. Además se intenta forzar heterogeneidad entre los árboles al escoger el nodo principal de cada árbol a partir de un subconjunto aleatorio de los features.
En la implementación de sci-kit learn, cada árbol indica la probabilidad de un feature de pertenecer a una clase. Para la predicción final se promedian estas probabilidades.
Los parámetros que se deben definir son:
Profundidad máxima: le impone un limite a la profundidad de máxima de cada árbol, los árboles muy profundos tienden a overfittear.
Criterio: es el criterio con el que se escoge el "mejor feature" para ocupar un nodo. Decimos que un feature es mejor que otro si maximiza la ganancia de información o si tiene baja impureza segun el criterio de impureza Gini.
Cantidad de estimadores: es la cantidad de árboles que se entrenarán.
Buscaremos dos modelos, uno utilizando dataset expandido y otro sin expandir.
mejor_valor, mejor_profundidad, mejor_criterio, mejor_cantidad_estimadores = obtenerMejoresParametros(X_rf)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor,3)}")
print(f"La profundidad encontrada que maximiza el AUC fue: {mejor_profundidad}")
print(f"El criterio encontrado que maximiza el AUC fue: {mejor_criterio}")
print(f"La cantidad de estimadores que maximizan el AUC fue: {mejor_cantidad_estimadores}")
mejor_valor_exp, mejor_profundidad_exp, mejor_criterio_exp, mejor_cantidad_estimadores_exp = obtenerMejoresParametros(X_exp)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor_exp,3)}")
print(f"La profundidad encontrada que maximiza el AUC fue: {mejor_profundidad_exp}")
print(f"El criterio encontrado que maximiza el AUC fue: {mejor_criterio_exp}")
print(f"La cantidad de estimadores que maximizan el AUC fue: {mejor_cantidad_estimadores_exp}")
rf = RandomForestClassifier(max_depth=mejor_profundidad, random_state=0, n_estimators = mejor_cantidad_estimadores, criterion=mejor_criterio)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
print(classification_report(y_test, y_pred, target_names=['No vuelve','Vuelve']))
rf_exp = RandomForestClassifier(max_depth=mejor_profundidad_exp, random_state=0, n_estimators = mejor_cantidad_estimadores_exp, criterion=mejor_criterio_exp)
rf_exp.fit(X_exp_train, y_exp_train)
y_exp_pred = rf_exp.predict(X_exp_test)
print(classification_report(y_exp_test, y_exp_pred, target_names=['No vuelve','Vuelve']))
mostrarMatrizDeConfusion(y_pred,y_test)
Se puede ver que el random forest entrenado sin el dataset expandido tiene un muy buen accuracy pues lo valores en la diagonal principal son mucho mayores que los de la diagonal invertida.
Se puede observar que tenemos un muy buen precision también: de los que predecimos que volverian (55 + 6), efectivamente 55 vuelven, un 90% de precision. Por otro lado el recall no es tan bueno, de los 55 + 25 que volverian pudimos detectar que 55 volverían. Es decir que casi dos tercios de los que volverían los clasificamos correctamente.
mostrarMatrizDeConfusion(y_exp_pred,y_exp_test)
mostrarROCCurve(rf,"Random Forest",X_test, X_train, y_test, y_train)
Como era de esperarse, la curva ROC sobre el set de entrenamiento es más cercana a la ideal que aquella sobre el set de test, sin embargo esta diferencia no es tan grande, ambas curvas son bastante similares y además muy buenas, su área bajo la curva debe ser cercana a 1.
mostrarAUCScore(rf,"Random Forest",X_test, y_test)
mostrarROCCurve(rf_exp,"Random Forest",X_exp_test, X_exp_train, y_exp_test, y_exp_train)
mostrarAUCScore(rf_exp,"Random Forest",X_exp_test, y_exp_test)
Como se puede ver, obtuvimos significativamente peores resultados con la expansión del dataset. Por lo tanto realizaremos las predicciones con el modelo que no fue entrenado con el dataset expandido.
Realizamos ahora las predicciones del nuevo archivo entregado.
holdout = obtenerHoldout()
ids_usuarios = np.array(holdout['id_usuario'])
holdout = prepararSetDeHoldout(holdout)
holdout_rf = conversionAVariablesNormalizadas(holdout)
predicciones_holdout = rf.predict(holdout_rf)
escribirPrediccionesAArchivo(predicciones_holdout,"Random Forest",ids_usuarios)