Random Forest es uno de los modelos de Machine Learning más potentes para resolver problemas basados en datos tabulares, está basado en técnicas de ensemble de árboles de decisión y además se entiende muy bien.
En este tutorial construimos el algoritmo paso a paso en Python para desvelar todos sus secretos, lo aplicamos en un ejemplo y obtenemos una precisión mayor que con el Random Forest de Scikit-learn 😎.
Las técnicas de ensemble consisten en combinar las predicciones de un conjunto de modelos para obtener un resultado más preciso que cualquier modelo por sí solo.
Si escuchas el podcast, sabrás que todos los modelos de Machine Learning asumen cosas sobre los datos que modelan. En cierta manera, es como si se fijaran en aspectos distintos de los datos en función de sus asunciones.
La idea es que combinando las predicciones de distintos modelos aprovechamos la diversidad de los modelos que hemos elegido y explotamos las fortalezas de cada uno.
Tenemos tres modalidades:
🎧 Si quieres saber más sobre las técnicas de ensemble en Machine Learning puedes escuchar este episodio del podcast.
Random Forest es posiblemente la implementación más conocida de la opción de Bagging dentro de las técnicas de ensemble en Machine Learning.
¿Recuerdas el árbol de decisión que implementamos desde cero en Python en este tutorial?
Pues, la idea es combinar un único tipo de modelo: nuestro simpático árbol de decisión, e introducir la diversidad en el ensemble entrenando cada árbol con un subconjunto del dataset de entrenamiento.
Es decir, entrenamos múltiples árboles de decisión, cada uno con un subconjunto de filas del dataset de entrenamiento y también con un subconjunto de las características (columnas).
Al utilizar sólo un subconjunto de características y un subconjunto de muestras conseguimos que los árboles de decisión sean distintos y estén menos correlados por lo que hay más diversidad en el ensemble.
Cuando hemos entrenado todos los árboles de decisión lo único que tenemos que hacer es combinar sus predicciones.
En el caso de un problema de clasificación el resultado será la clase que haya predicho la mayoría.
Para entender al 100% cómo funciona el algoritmo Random Forest vamos a implementarlo desde cero en Python.
Son solamente tres pasos:
Seguiremos cada paso por separado y luego los combinaremos todos en una clase que podríamos usar igual que hacemos con los modelos de las librerías top (como Scikit learn).
Ahora que hemos desarrollado todos los pasos necesarios para implementar nuestro modelo Random Forest, es momento de integrarlo todo en una clase que nos permita reutilizar el código de forma eficiente siempre que lo necesitemos.
Vamos a entrenar el modelo de Random Forest que hemos creado desde cero para predecir enfermedades cardiacas utilizando el dataset Heart Disease, disponible en el repositorio UCI Machine Learning.
Este dataset contiene información clínica sobre pacientes utilizada principalmente para la investigación y experimentación en machine learning para predecir enfermedades cardíacas. De un total de 76 atributos disponibles originalmente, utilizaremos un subconjunto de 14 características.
La variable objetivo (num
) es un valor entero que va de 0 a 4, donde:
El dataset incluye 14 variables divididas en características predictoras (features) y la variable objetivo (target).
Primero instalamos la librería ucimlrepo
para poder importar el dataset directamente desde el repositorio de Machine Learning de UC Irvine.
!pip install ucimlrepo
Importamos todas las librerías que vamos a usar hoy...
¡Y cargamos los datos!
import pandas as pd import numpy as np from ucimlrepo import fetch_ucirepo # fetch dataset heart_disease = fetch_ucirepo(id=45) # data (as pandas dataframes) X = heart_disease.data.features y = heart_disease.data.targets
Ahora iría la fase de Análisis Exploratorio de Datos pero para no hacer el tutorial demasiado largo (aún más… 🫢) voy a pasar directamente a inspeccionar los datos y entrenar el modelo.
Lo primero que necesitamos es convertir nuestra variable objetivo y
a una variable que tenga los valores:
1
: Existe una enfermedad cardiaca. Si te fijas, los valores originales de y
nos indican el tipo de enfermedad tomando los valores de 1
a 4
así que vamos a transformar esos valores en un 1
, que señale la presencia de enfermedad.0
: No existe enfermedad cardiaca.# La variable objetivo indica el tipo de enfermedad cardiaca pero sólo nos interesa saber si hay enfermedad (1-4) o no (0) y = y['num'].apply(lambda x: 1 if x > 0 else 0)
Recuerda que nuestro modelo Random Forest tenía tres hiperparámetros así que lo primero que vamos a hacer es definirlos y dividir los datos en entrenamiento y test (para luego poder medir la precisión del modelo).
Lo que acabamos de hacer es una versión muy básica de lo que hay detrás del modelo RandomForestClassifier
de la librería Scikit-learn.
Se utilizaría exactamente igual.
Si suponemos que tenemos nuestro dataset de entrenamiento en la variable X_train
con sus correspondientes etiquetas en y_train
, crear, entrenar y utilizar un bosque aleatorio en Scikit-learn es tan sencillo como:
from sklearn.ensemble import RandomForestClassifier rf_sk = RandomForestClassifier(n_estimators=n_trees, max_depth=max_depth, max_features=max_features, random_state=99) # Entrenamos el modelo rf_sk.fit(X_train, y_train) # Calculamos las predicciones sobre el conjunto de test rf_sk.predict(X_test) # Medimos la precisión score = accuracy_score(y_test, rf_sk.predict(X_test)) print(f"Precisión: {score:.2f}")
Precisión: 0.80
¡Incluso nuestra sencilla implementación ha superado la de Scikit learn! 😎
Pero bueno, no nos emocionemos... Con conjuntos de datos pequeños, pequeñas variaciones en los árboles pueden tener un impacto muy grande en la precisión...
⚠️ Recuerda que aunque los árboles de decisión pueden manejar variables categóricos, la implementación de Scikit-learn no lo soporta de forma directa. Esto significa que:
Una de las diferencias fundamentales entre nuestra implementación del algoritmo Random Forest paso a paso y el de Scikit learn son los hiperparámetros.
Nosotros sólo hemos tenido en cuenta los indispensables:
n_trees
: El número de árboles del bosque aleatorio (en Scikit-learn se corresponde con n_estimators
).max_features
: El número de características que queremos para entrenar cada árbol.max_depth
: La profundidad de los árboles de decisión, es decir, el número de veces que los vamos a dividir.Otros hiperarámetros que podemos ajustar para mejorar el rendimiento de nuestro bosque aleatorio son:
oob_score
: Si es True
, calcula el rendimiento del modelo usando las observaciones que no se incluyeron en las muestras de entrenamiento de cada árbol (acuérdate que no las usamos todas, sino que las seleccionamos aletoriamente).warm_start
: Si es True
, permite añadir más árboles al modelo previamente entrenado sin reiniciarlo, reutilizando lo que ya ha aprendido.bootstrap
:True
, utiliza muestreo con reemplazo para crear los subconjuntos de entrenamiento para cada árbol (bagging);False
, usa todas las observaciones (pasting).verbose
: Controla la cantidad de mensajes de progreso que se muestran durante el entrenamiento; valores más altos producen más detalles.Y, además, también están todos los hiperparámetros que traen los propios árboles de decisión de casa y que veíamos en este tutorial.
En este tutorial me he sacado los hiperparámetros de la manga (😬) porque no era el objetivo aprender a ajustarlos pero si quieres ver un ejemplo de métodos de ensemble con ajuste de hiperparámetros puedes echarle un vistazo al tutorial de XGBoost.
En este tutorial, hemos explorado a fondo cómo funciona Random Forest, desde los conceptos fundamentales de las técnicas de ensemble hasta la implementación paso a paso de este poderoso modelo en Python.
Al construir nuestra propia clase RandomForestNinja
, no solo hemos entendido mejor cada etapa del algoritmo, sino que también "hemos logrado" superar la precisión del modelo de Scikit-learn en nuestro ejemplo.