¿Cómo implementar Gradient Boosting con scikit-learn en un dataset de enfermedades cardiacas?
La implementación de modelos de Machine Learning puede parecer intimidante al principio, pero con las herramientas correctas, se vuelve bastante manejable. Scikit-learn es una biblioteca en Python que facilita este proceso con sus modelos pre-construidos y métodos de ensamblado como el Gradient Boosting. En esta guía, aprenderás a aplicar Gradient Boosting para clasificar un dataset de enfermedades cardiacas, obteniendo resultados precisos y significativos.
¿Qué cambios de código son necesarios?
Para comenzar, es fundamental trabajar desde una base de código preexistente. Aquí, se parte de un código que ya procesa un dataset de enfermedades cardíacas. Sin embargo, dado que emplearemos Gradient Boosting, ciertas librerías utilizadas inicialmente ya no serán necesarias:
from sklearn.ensemble import GradientBoostingClassifier
Este es el único cambio de importación necesario. El clasificador Gradient Boosting, basado en árboles de decisión, prescindirá del clasificador K-Nearest Neighbors utilizado previamente.
¿Cómo definimos y entrenamos nuestro clasificador?
Definir el clasificador es simple. Usamos el método GradientBoostingClassifier para crear un modelo que ajustará los datos de entrenamiento. Aquí, establecemos un parámetro clave: el número de árboles en el ensamblado.
# Definimos el clasificadorclasificador = GradientBoostingClassifier(n_estimators=50)# Entrenamos con los datos de entrenamientoclasificador.fit(X_train, y_train)
Elegimos utilizar 50 estimadores, y aunque este número es inicialmente arbitrario, puedes ajustarlo según el rendimiento, usando técnicas como validación cruzada (cross-validation) para optimizar los hiperparámetros.
¿Cómo generamos predicciones y evaluamos el modelo?
Una vez tenemos el clasificador entrenado, el siguiente paso es generar predicciones sobre los datos de prueba y evaluar la precisión de nuestro modelo.
Este proceso nos permite medir qué tan bien nuestro modelo está clasificando las instancias del dataset de prueba. En este ejercicio particular, el modelo alcanza una impresionante precisión del 93%, lo que representa una mejora respecto al método previo, el K-Nearest Neighbors.
¿Por qué evaluar múltiples métodos de ensamble?
Si bien en este ejemplo observamos un impresionante aumento en la precisión del 93% con Gradient Boosting, es crucial recordar que los resultados pueden variar según el dataset. Cada modelo de Machine Learning tiene sus fortalezas y debilidades, y es por eso que te recomendamos probar diferentes métodos de ensamble y clasificadores para determinar cuál se adapta mejor a tus necesidades.
Esta práctica te permitirá establecer un enfoque más robusto y adaptado a tu problema específico, asegurando así que el modelo sea no solo preciso, sino también eficiente y relevante.
Cambios en los archivos y ejecución
Finalmente, para mantener la coherencia y la organización del proyecto, renombramos el archivo que contiene este proceso a boosting.py, garantizando que siempre estaremos trabajando con los contenidos correctos en los repositorios de código.
Con este entendimiento de cómo integrar Gradient Boosting en tus proyectos, estarás mejor preparado para enfrentar desafíos más complejos en tus exploraciones de Machine Learning. ¡Sigue aprendiendo y mejorando tus modelos!
Mi intento de cross validation, al final deje n_estimators en 3 con 81%.
un 0.93 de accuracy no seria overfitting?
No necesariamente, si revisas kaggle.com, puedes encontrar que en varias competencia los usuarios publican sus modelos y muchos pueden llegar hasta un 0.99 de accuracy (algunos hasta 1)
Las pruebas que realiza Kaggle en sus competencias son usando un 50% de los datos de test que suministran y otro 50% que no suministran. Por lo tanto obtienen un modelo muy acertado que no necesariamente se aprendió el dataset de memoria (overfitting)
No es overfitting, por que ese 0.93 esta basado sobre los datos de prueba y no de entrenamiento, eso significa que el modelo se comporto muy bien con datos que no conocía :)
👩🏼💻 En lo personal soy una persona bastante visual, y me gusta ver los resultados más haya de los números, así que investigue sobre como podía ver de forma gráfica los resultados del entrenamiento de los tres modelos que vimos en ésta y las dos clases anteriores, y este es el resultado, lo divido en dos partes:
Matriz de confusión: Nos muestra en un heatmap cuantos aciertos y cuantos errores tuvo el modelo en sus predicciones.
Frontera de decisión: Mediante un scatter plot nos muestra las áreas que definen las clases y los limites entre ellas. Básicamente podemos ver como es que un modelo de clasificación toma sus decisiones.
⚠️ Asumiendo que se siguió el código del profesor y que ya se cuenta con las variables X e y; datasets de entrenamiento y pruebas; así como con los modelos KNeighborsClassifier, BaggingClassifier y GradientBoostingClassifier entrenados. Entonces pueden utilizar el siguiente código en sus notebooks.
Librerías necesarias para el ejemplo:
import matplotlib.pyplot as plt # Gráficosimport seaborn as sns
import numpy as np # Manejo de matrices y vectorsfrom sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA # Modelo para reducir la dimensionalidadfrom sklearn.metrics import confusion_matrix # Visualizar cuantos aciertos y cuantos errores tuvo cada modelo
Visualizar la matriz de confusión con los resultados de las predicciones de cada uno de los modelos, podemos corroborar el porcentaje de accuracy de cada uno.
En este ejemplo el ganador definitivo fue GradientBoosting con el menor número de fallos.
Frontera de decisión
Este ejemplo es un poco más complejo, pues solo funciona para modelos que se entrenaron con dos features (2 dimensiones). En el caso de nuestro ejemplo de predecir si un paciente tiene o no una enfermedad cardíaca, utilizamos 13 features, por lo tanto visualizar un gráfico con esas dimensiones no es físicamente posible, así que una solución es hacer una reducción de dimensionalidad y pasar de 13 a 2 features.
👀 La reducción de dimensionalidad a del dataset a 2 features se realiza para visualizar una proyección aproximada de las predicciones realizadas por los modelos.
Pasos para crear visualizar la frontera de decisión:
Entrenar los modelos usando TODAS las features originales (13) ✅ (ya lo tenemos).
Estandarizar los datos (e.g. StandardScaler) en caso de que no se haya realizado este proceso previamente.
Pasamos los datos estandarizados a un algoritmo de reducción de dimensionalidad (e.g. PCA).
Entrenamos modelos secundarios idénticos a los anteriores con los datos reducidos a dos dimensiones.
Creamos la visualización para cada uno de los modelos.
Código de la función para crear la visualización:
defplot_decision_boundary_CM(clf, X, y, cm, title):# Limites del gráfico x_min, x_max = X[:,0].min()-1, X[:,0].max()+1 y_min, y_max = X[:,1].min()-1, X[:,1].max()+1# Ejes del grid (o malla) x_axis = np.arange(x_min, x_max,0.02) y_axis = np.arange(y_min, y_max,0.02)# Generamos la malla (meshgrid) xx, yy = np.meshgrid(x_axis, y_axis)# Aplanamos y combinamos las coordenadas para generar las predicciones Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])# Reorganizamos las predicciones a la misma forma de la matrix de las mallas xx y xx Z = Z.reshape(xx.shape)# Graficamos fig,(ax1, ax2)= plt.subplots(nrows=1, ncols=2, figsize=(16,6)) ax1.contourf(xx, yy, Z, alpha=0.4, cmap='viridis') ax1.scatter(X[:,0], X[:,1], c=y, s=20, edgecolor='k', cmap='viridis') ax1.set_title(title) ax1.set_xlabel('PC1') ax1.set_ylabel('PC2') sns.heatmap(cm, annot=True, cmap="viridis", ax=ax2) ax2.set_title("Matriz de Confusión") ax2.set_xlabel("Valores Reales") ax2.set_ylabel("Valores Predichos") plt.show()
Procedemos con los pasos que nos hacen falta:
# Estandatizamos los datos de entrenamiento y pruebasscaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)# Reducimos la dimensionalidad de los datos a dos dimensionespca = PCA(n_components=2)X_train_pca = pca.fit_transform(X_train_scaled)X_test_pca = pca.transform(X_test_scaled)# Entrenamos los modelos secundarios con los datos reducidos a dos dimensionesestimators ={"K-Neighbors": KNeighborsClassifier(),"Bagging": BaggingClassifier(estimator=KNeighborsClassifier(), n_estimators=50, random_state=42),"Boosting": GradientBoostingClassifier(n_estimators=50, random_state=42)}for name, estimator in estimators.items(): estimator.fit(X_train_pca, y_train) prediction = estimator.predict(X_test_pca) accuracy = accuracy_score(y_test, prediction) cm = confusion_matrix(y_test, prediction)# Visualizamos plot_decision_boundary_CM(estimator, X_train_pca, y_train, cm,F"{name} - Frontera de Decisión \n(Accuracy: {accuracy:.4%})")
Resultado:
Espero éste aporte les sea de utilidad 💜.
Les dejó mi notebook con los apuntes de las clases y el ejemplo completo, así como un enlace al notebook en Google Colab.
GitHub:
Google Colab:
Cualquier feedback es bienvenido!!
#NuncaParenDeAprender y no dejen de curiosear 🧠.
Entonces, ¿Cuándo podría ser mejor aplicar las técnicas de boosting y bagging?
Se suelen usar con problemas de clasificacion, el Bagging cuando tenes una alta varianza y el Boosting cuando tenes un alto bias.
investoge que es el cross validadtion para encontrar el n_estimators correcto, básicamente es dividir los datos en bloques y utilizar cada bloque como testing y el resto como training en cada iteration, con lo que se obtiene un valor de como se comporta el modelo con cualquier combinacion de los datos.
con este indicador se pueden compara modelos entre si o en este caso valores para un parametro, si quieren profundizar les recomiendo este video.
¿Cómo puedo implementar cross validation para encontrar el mejor número de estimadores?
Me gusta mucho el código que lo que hace es iterar para buscar una mejor respuesta. Por esto es que les dejo este mismo para que lo prueben:
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import accuracy_score
if __name__ =="__main__":# Cargar los datos data = pd.read_csv('./data/heart.csv')# Preparar las variables predictoras y la variable objetivo X = data.drop(['target'], axis=1) Y = data['target']# Dividir en conjunto de entrenamiento y prueba X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.35, random_state=42)# Definir el rango de valores a probar para los hiperparámetros param_grid ={'n_estimators':[50,100,200],'learning_rate':[0.01,0.1,0.2],'max_depth':[3,5,7]}# Crear un modelo de Gradient Boosting gb_model = GradientBoostingClassifier(random_state=42)# Crear GridSearchCV para buscar la mejor combinación de parámetros grid_search = GridSearchCV(estimator=gb_model, param_grid=param_grid, cv=5, scoring='accuracy')# Entrenar el GridSearchCV grid_search.fit(X_train, Y_train)# Imprimir todos los resultados de las combinaciones results = grid_search.cv_results_
print("Resultados para cada combinación de hiperparámetros de Gradient Boosting:")for mean_score, params inzip(results['mean_test_score'], results['params']):print(f"Parámetros: {params}, Precisión promedio: {mean_score}")# Mejor modelo encontrado best_params = grid_search.best_params_
print(f"\nMejores parámetros encontrados: {best_params}")# Evaluar el mejor modelo en los datos de prueba best_model = grid_search.best_estimator_
Y_pred = best_model.predict(X_test) accuracy = accuracy_score(Y_test, Y_pred)print(f"\nPrecisión del mejor modelo en los datos de prueba: {accuracy}")
Muy bueno. Gracias
Les adjunto mi version del codigo:
import pandas as pd
from sklearn.ensembleimportGradientBoostingClassifierfrom sklearn.model_selectionimporttrain_test_splitfrom sklearn.metricsimport accuracy_score
if __name__ =="__main__": path ='./Boosting/data/heart.csv' dataset = pd.read_csv(path)print(dataset.head(5))print('')print(dataset['target'].describe()) x = dataset.drop(['target'], axis=1, inplace=False) y = dataset['target'] x_train, x_test, y_train, y_test =train_test_split(x, y, test_size=0.35, random_state=42) boost =GradientBoostingClassifier(n_estimators=50).fit(x_train, y_train) boost_pred = boost.predict(x_test)print('')#print('Accuracy GBC:',accuracy_score(y_test, boost_pred)) # este esta bien.print('Accuracy GBC:',accuracy_score(boost_pred, y_test)) # este me parece que esta mal.
Donde el output es el siguiente:
Accuracy GBC: 0.935933147632312
qué tal👋 he encontrado cosas muy interesantes del dataset que hemos estado usando: "Heart Disease" – Cleveland database" No es el original.
El que hemos estado usando tiene 1025 filas, cuando en el original es de solo 303. El nuestro de 1025 (o el que al menos yo he estado usando) es una versión modificada/aumentada que circula mucho en Kaggle y otros sitios.
Por lo que alguien tomó el dataset original de 303 filas y lo combinó con datos sintéticos (probablemente generados artificialmente para parecerse al original). Importante hacer un df.drop_duplicates() y un df.duplicated().sum() antes para confirmar la cantidad de duplicados. Si no los borramos podría existir el mismo paciente varias veces, por lo que es probable que aparezca tanto en entrenamiento como en test, generando data leakage.
Esto quiere decir que hay 700 ocurrencias que deben sacarse. Y es la razón por la que hemos obtenido una precisión tan alta.
En adición, varias columnas 'ca', 'thal', etc. No son numéricas continuas, sino categóricas.
siendo:
ca (Vasos): Los valores 4 son errores de codificación (nulos)
thal (Talasemia): Los valores 0 son nulos
dada su naturaleza (columna categórica) lo mejor sería imputarlos por la moda. Encontré que son MCAR, por lo que no agregamos tanto ruido al imputar.
Importante escalar numéricas y hacer enconding a las categóricas después del split
Bagging: paraleliza, busca reducir la varianza.
Boosting: secuencial, busca reducir el sesgo (haciendo que el modelo pueda aprender relaciones más complejas).
Le agrego el parametro "loss='exponential' me hubiera gustado haber ahondado mas, quiza es la funcion de perdida????
<u>Bagging vs Boosting </u>
Tanto el bagging (agregación por bootstrap) como el boosting son técnicas de aprendizaje ensemble usadas en machine learning para mejorar el rendimiento de un modelo al combinar las predicciones de múltiples modelos más débiles (también llamados estimadores base). Si bien comparten el mismo objetivo, lo logran de formas diferentes:
Bagging (Agregación por Bootstrap):
Diversidad a través del muestreo aleatorio: El bagging se centra en crear diversidad entre los estimadores base. Logra esto entrenando a cada estimador base en una muestra bootstrap diferente de los datos de entrenamiento. El bootstrap implica crear un nuevo conjunto de datos mediante el muestreo aleatorio (con reemplazo) de los datos de entrenamiento originales. Esto significa que algunos puntos de datos pueden aparecer varias veces en una muestra bootstrap, mientras que otros pueden no incluirse en absoluto.
Entrenamiento independiente: Cada estimador base en el bagging se entrena independientemente de los demás. No se "comunican" ni aprenden de los errores de los otros.
Agregación de predicciones: Finalmente, el bagging agrega las predicciones de todos los estimadores base utilizando técnicas como la votación por mayoría para clasificación o el promedio para regresión.
Boosting (Boosting de gradiente):
Aprendizaje secuencial: El boosting toma un enfoque más secuencial. Entrena a los estimadores base uno tras otro, donde cada estimador posterior se enfoca en mejorar los errores cometidos por los anteriores.
Corrección de errores: El boosting utiliza una técnica llamada descenso del gradiente para ajustar los pesos de los estimadores base. Los pesos determinan la influencia de cada estimador en la predicción final. A los estimadores que cometen más errores se les asignan pesos más bajos, mientras que a los que cometen menos errores se les aumentan los pesos.
Aprendizaje adaptativo: Este proceso de enfocarse en corregir errores pasados permite que el boosting cree un ensemble más poderoso que pueda aprender patrones más complejos en los datos.
Diferencias clave y cuándo elegir cuál:
Aquí algunas pautas generales para elegir entre bagging y boosting:
Usa bagging: Si te preocupa principalmente reducir la varianza y tus estimadores base ya son relativamente buenos, el bagging es una opción más simple y rápida. También es más fácil interpretar los modelos individuales en un ensemble de bagging.
Usa boosting: Si deseas un modelo potencialmente más poderoso que pueda reducir tanto la varianza como el sesgo, especialmente para problemas complejos, el boosting es una buena opción. Sin embargo, los algoritmos de boosting pueden ser más costosos computacionalmente y menos interpretables.
En última instancia, la mejor opción depende de las características específicas de tus datos y tu problema. Es posible que desees experimentar con bagging y boosting para ver cuál funciona mejor en tu conjunto de datos.
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import BaggingClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
if __name__ =='__main__':# Load the dataset df = pd.read_csv('./data/heart.csv')print(df['target'].describe())# Split the dataset into features (X) and target (y) X = df.drop(['target'], axis=1) y = df['target']# Split the dataset into training and testing sets X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.35, random_state=42)# Fit a Gradient Boosting classifier to the training data and evaluate its performance on the testing data boost = GradientBoostingClassifier(n_estimators=50).fit(X_train, y_train) boost_pred = boost.predict(X_test)print('='*64)print('Gradient Boosting Accuracy: ', accuracy_score(y_test, boost_pred))
Epero este aporte les sirva. Estan implementados tanto sin metodo ensamble como con bagging y boosting como resultado
import pandas as pd
from sklearn.neighborsimportKNeighborsClassifierfrom sklearn.ensembleimportBaggingClassifierfrom sklearn.ensembleimportGradientBoostingClassifierfrom sklearn.linear_modelimportLogisticRegressionfrom sklearn.svmimportSVCfrom sklearn.svmimportLinearSVCfrom sklearn.linear_modelimportSGDClassifierfrom sklearn.treeimportDecisionTreeClassifierfrom sklearn.ensembleimportRandomForestClassifierfrom sklearn.model_selectionimporttrain_test_splitfrom sklearn.metricsimport accuracy_score
import warnings
warnings.filterwarnings("ignore")if __name__ =='__main__': #dt_heart = pd.read_csv('./datasets/heart.csv') dt_heart = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/platzi/datas/heart.csv')#print(dt_heart['target'].describe()) x = dt_heart.drop(['target'], axis=1) y = dt_heart['target'] x_train, x_test, y_train, y_test =train_test_split(x, y, test_size=0.35, random_state=1) estimators ={'LogisticRegression':LogisticRegression(),'SVC':SVC(),'LinearSVC':LinearSVC(),'SGD':SGDClassifier(loss="hinge", penalty="l2", max_iter=5),'KNN':KNeighborsClassifier(),'DecisionTreeClf':DecisionTreeClassifier(),'RandomTreeForest':RandomForestClassifier(random_state=0)}for name, estimator in estimators.items(): estimator_class = estimator.fit(x_train, y_train) estimator_prediction = estimator.predict(x_test)print('='*64)print('SCORE no bagging {} : {}'.format(name,accuracy_score(knn_prediction, y_test))) bag_class =BaggingClassifier(base_estimator=estimator, n_estimators=50).fit(x_train, y_train) bag_predict = bag_class.predict(x_test)print('SCORE Bagging with {} : {}'.format(name,accuracy_score(bag_predict, y_test))) boost =GradientBoostingClassifier(n_estimators=50).fit(x_train, y_train) boost_pred = boost.predict(x_test)print('SCORE Boosting with {} : {}'.format(name,accuracy_score(boost_pred, y_test)))
resultado :
===============================================SCORE no bagging LogisticRegression:0.7270194986072424SCOREBaggingwithLogisticRegression:0.8133704735376045SCOREBoostingwithLogisticRegression:0.924791086350975===============================================SCORE no bagging SVC:0.7270194986072424SCOREBaggingwithSVC:0.6629526462395543SCOREBoostingwithSVC:0.924791086350975===============================================SCORE no bagging LinearSVC:0.7270194986072424SCOREBaggingwithLinearSVC:0.8245125348189415SCOREBoostingwithLinearSVC:0.924791086350975===============================================SCORE no bagging SGD:0.7270194986072424SCOREBaggingwithSGD:0.6685236768802229SCOREBoostingwithSGD:0.924791086350975===============================================SCORE no bagging KNN:0.7270194986072424SCOREBaggingwithKNN:0.766016713091922SCOREBoostingwithKNN:0.924791086350975===============================================SCORE no bagging DecisionTreeClf:0.7270194986072424SCOREBaggingwithDecisionTreeClf:0.9721448467966574SCOREBoostingwithDecisionTreeClf:0.924791086350975===============================================SCORE no bagging RandomTreeForest:0.7270194986072424SCOREBaggingwithRandomTreeForest:0.9805013927576601SCOREBoostingwithRandomTreeForest:0.924791086350975