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.svm import SVC
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)
Creamos una función que obtiene mediante grid search y K-Fold cross validation el parametro C y el kernel que maximiza la métrica de AUC_ROC para el modelo de SVM.
def obtenerMejoresParametros(datosPreprocesados):
mejor_valor = 0
mejor_c = None
mejor_kernel = None
y_array=np.array(y)
for c in [1,5,10,15,20,25,30,50,75,100,200,300]:
for kernel in ["poly", "rbf", "linear"]:
kf = StratifiedKFold(n_splits=8)
metricas = []
for fold_idx, (train_index, test_index) in enumerate(kf.split(datosPreprocesados, y_array)):
svm = SVC(C = c, kernel = kernel, probability = True)
svm.fit(datosPreprocesados[train_index], y_array[train_index].ravel())
predicciones = svm.predict_proba(datosPreprocesados[test_index])[:,1]
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_c = c
mejor_kernel = kernel
return mejor_valor, mejor_c, mejor_kernel
def obtenerMejorGamma(datosPreprocesados, mejor_c, mejor_kernel):
mejor_gamma = None
mejor_valor = 0
y_array=np.array(y)
for gamma in ['scale',0.00001,0.0001, 0.001,0.005, 0.01, 0.05, 0.1, 1, 10, 100, 1000]:
kf = StratifiedKFold(n_splits=8)
metricas = []
for fold_idx, (train_index, test_index) in enumerate(kf.split(datosPreprocesados, y_array)):
svm = SVC(C = mejor_c, kernel = mejor_kernel, gamma=gamma,probability = True)
svm.fit(datosPreprocesados[train_index], y_array[train_index].ravel())
predicciones = svm.predict_proba(datosPreprocesados[test_index])[:,1]
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_gamma = gamma
return mejor_valor, mejor_gamma
SVM es un método de aprendizaje supervisado que busca clasificar instancias a partir de un hiperplano separador de clases. Por lo tanto en principio las instancias deben ser linealmente separables.
Lo que se busca es encontrar un hiperplano que separe las clases y maximice la distancia entre las instancias mas cercanas al mismo, generando así un margen.
Se dice que el margen es suave o soft cuando se permite que el hiperplano encontrado no separe perfectamente a las clases y que se permitan clasificaciones erróneas en el set de entrenamiento con el objetivo de generalizar mejor. Suele ser útil tener un margen de este tipo cuando hay ruido o outliers.
Cuando las instancias no son linealmente separables se puede recurrir a una transformación de los datos a un espacio de dimensión mayor en el cual sí lo sean. Dado que esto puede ser muy costoso, se suelen utilizar kernels, funciones que permiten calcular las distancias entre instancias en un espacio de dimensión mayor sin realizar la transformación de los datos.
Los parámetros que consideraremos son C y el kernel:
C: Es un parámetro de regularización que indica la suavidad del margen generado por el clasificador SVM. Mientras mayor sea C se tiene un margen mas rígido y pequeño que puede ser bueno clasificando los puntos del set de entrenamiento pero generar overfitting. Mientras menor sea C el margen es mas suave en el sentido de que se permiten clasificaciones erróneas sobre el set de entrenamiento con el propósito de generalizar mejor.
Kernel: el kernel es la función que calcula el producto interno entre las observaciones en un espacio de dimensión mayor, sin tener que convertir los datos a ese espacio, logrando asi definir una distancia en dimensiones mayores. Consideraremos los kernels: lineal, radial y polinomial.
X_svm = conversionAVariablesNormalizadas(X)
mejor_valor, mejor_c, mejor_kernel = obtenerMejoresParametros(X_svm)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor,3)}")
print(f"El valor de C encontrado que maximiza el AUC fue: {mejor_c}")
print(f"El kernel encontrado que maximiza el AUC fue: {mejor_kernel}")
Dado que el mejor kernel encontrado fue rbf buscamos un optimizar un nuevo hiperparámetro:
$\gamma$, es un parámetro que se utiliza en este kernel y que indica cuánta influencia tiene cada instancia de entrenamiento. Este parámetro influencia en el cálculo de la distancia entre las instancias.
mejor_valor, mejor_gamma = obtenerMejorGamma(X_svm, mejor_c, mejor_kernel)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor,3)}")
print(f"El valor de gamma encontrado que maximiza el AUC fue: {mejor_gamma}")
Probamos ahora con el preprocesamiento que expande el set de datos.
X = expansionDelDataset(X)
X.head()
columnas_codificables_extra = ['pago_categorizado','edades_estratificadas','categoria_invitados']
columnas_numericas_extra = ['2_clusters','4_clusters','10_clusters','cantidad_total_invitados','total_pagado']
X_svm_exp = conversionAVariablesNormalizadas(X,columnas_codificables_extra,columnas_numericas_extra)
mejor_valor_exp, mejor_c_exp, mejor_kernel_exp = obtenerMejoresParametros(X_svm_exp)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor_exp,3)}")
print(f"El valor de C encontrado que maximiza el AUC fue: {mejor_c_exp}")
print(f"El kernel encontrado que maximiza el AUC fue: {mejor_kernel_exp}")
Devuelta como obtuvimos rbf buscamos si hay algun gamma mejor.
mejor_valor_exp, mejor_gamma_exp = obtenerMejorGamma(X_svm_exp, mejor_c_exp, mejor_kernel_exp)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor_exp,3)}")
print(f"El valor de gamma encontrado que maximiza el AUC fue: {mejor_gamma_exp}")
Vemos que empeoro algo el promedio de AUC encontrado respecto al primero probado sin las columnas nuevas. Probamos ahora sacandole algunas columnas de las nuevas creadas.
columnas_codificables_extra = ['pago_categorizado','edades_estratificadas']
columnas_numericas_extra = ['2_clusters','4_clusters','10_clusters']
X_svm_exp2 = conversionAVariablesNormalizadas(X,columnas_codificables_extra,columnas_numericas_extra)
mejor_valor_exp2, mejor_c_exp2, mejor_kernel_exp2 = obtenerMejoresParametros(X_svm_exp2)
print(f"El mejor valor fue de AUC fue: {round(mejor_valor_exp2,3)}")
print(f"El valor de C encontrado que maximiza el AUC fue: {mejor_c_exp2}")
print(f"El kernel encontrado que maximiza el AUC fue: {mejor_kernel_exp2}")
Mejoro ligeramente, pero no lo suficiente como para pasarlo.
X_train, X_test, y_train, y_test = train_test_split(X_svm, y, test_size=0.25, random_state=0)
svm = SVC(C=mejor_c, kernel=mejor_kernel,gamma = mejor_gamma, probability=True)
svm.fit(X_train, y_train)
y_pred = svm.predict(X_test)
print(classification_report(y_test, y_pred, target_names=['No vuelve','Vuelve']))
Observamos que tiene un 82% 79% de accuracy lo cual esta en el mismo orden de los otros modelos con esta métrica.
mostrarMatrizDeConfusion(y_pred,y_test)
Respecto de la matriz de confusión corroborar nuevamente que la clasificación tiene un buen accuracy, pues los valores en la diagonal principal son mucho mayores que los de la diagonal invertida.
Por otro lado pareciera tener una cantidad similar de falsos positivos y de falsos negativos
mostrarROCCurve(svm,"SVM",X_test, X_train, y_test, y_train)
mostrarAUCScore(svm,"SVM",X_test,y_test)
Obtenemos y preparamos el nuevo archivo realizando el mismo preprocesamiento realizado anteriormente.
holdout = obtenerHoldout()
ids_usuarios = np.array(holdout['id_usuario'])
holdout = prepararSetDeHoldout(holdout)
holdout_svm = conversionAVariablesNormalizadas(holdout)
Realizamos las predicciones y escribimos al archivo CSV.
predicciones_holdout = svm.predict(holdout_svm)
escribirPrediccionesAArchivo(predicciones_holdout,"SVM",ids_usuarios)