Python & Data Science

Random Forests vs. Gradient Boosting: Por qué ganan los equipos de árboles

La última vez, Sam vio cómo un único árbol de decisión ordena datos desordenados en grupos limpios usando la impureza de Gini y la ganancia de información. Ahora quiere ver qué sucede cuando combinas un montón de ellos.

Digamos que estás decidiendo si comprar una casa. Pregúntale a un amigo y obtendrás consejos moldeados enteramente por su propia experiencia. Quizás compraron un lugar con un techo que goteaba, así que ahora le dicen a todos que eviten las casas antiguas. Un solo árbol de decisión actúa de la misma manera. Recuerda cada pequeño detalle de sus datos de entrenamiento, incluso las anomalías que no volverán a ocurrir.

Este es el tipo de decisión que Sam está automatizando en HomeMatch. Necesita predecir si un anuncio se vende en 30 días, y un solo árbol con sus propios sesgos no es lo suficientemente confiable para eso.

Eso es sobreajuste. El árbol no aprende los patrones: memoriza el ruido. ¿Pero qué pasaría si le preguntaras a 100 amigos? Sus sesgos individuales empezarían a cancelarse. Ese es el aprendizaje de conjuntos en pocas palabras. No un modelo perfecto, sino un equipo trabajando junto para encontrar la verdad.

1. La sabiduría de la multitud: Por qué un solo árbol no basta

Un solo árbol de decisión tiende a memorizar los valores atípicos. Si se le permite crecer lo suficiente, escribirá una regla específica para cada punto de datos en tu conjunto de entrenamiento.

Esto es lo que sucede cuando forzamos un solo árbol sobre un conjunto de datos ruidoso.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor

# Create a simple noisy dataset
np.random.seed(42)
X = np.sort(5 * np.random.rand(80, 1), axis=0)
y = np.sin(X).ravel() + np.random.normal(0, 0.2, X.shape[0])

# Train a very deep tree (no restrictions)
tree = DecisionTreeRegressor(max_depth=10)
tree.fit(X, y)

# Predict
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_pred = tree.predict(X_test)

# Plotting the 'jagged' result
plt.scatter(X, y, color='black', label='Data')
plt.plot(X_test, y_pred, color='red', label='Single Tree')
plt.title("A Single Tree Memorizing Noise")
plt.legend()
plt.show()
  • np.random.seed(42) fija el generador de números aleatorios para que el conjunto de datos ruidoso sea reproducible — sin esto, cada ejecución produciría diferentes puntos de datos.
  • X = np.sort(5 * np.random.rand(80, 1), axis=0) genera 80 valores aleatorios entre 0 y 5, ordenados a lo largo del primer eje — la ordenación asegura que el gráfico se lea de izquierda a derecha en lugar de esparcir los puntos aleatoriamente.
  • y = np.sin(X).ravel() + np.random.normal(0, 0.2, X.shape[0]) crea el objetivo como una onda senoidal más ruido gaussiano (media 0, desviación estándar 0.2) — el .ravel() aplana el arreglo 2D de vuelta a 1D para que coincida con la forma de etiqueta esperada por sklearn.
  • DecisionTreeRegressor(max_depth=10) permite que el árbol crezca hasta 10 niveles de profundidad — con solo 80 puntos de datos, esta es más que suficiente profundidad para permitir que el árbol cree una hoja única para casi cada punto, que es exactamente el comportamiento de sobreajuste que queremos demostrar.
  • X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis] crea una cuadrícula densa de 500 puntos de prueba de 0 a 5, redimensionada a 2D mediante [:, np.newaxis] — sklearn espera una entrada 2D, y np.newaxis agrega la dimensión de columna faltante.
  • plt.scatter y plt.plot superponen los datos crudos ruidosos (puntos negros) con las predicciones del árbol (línea roja) — la línea roja irregular demuestra visualmente cómo el árbol persigue los picos de ruido individuales en lugar de aprender la curva senoidal suave.

¿Qué está pasando aquí? La línea roja es irregular — salta hacia arriba y hacia abajo para alcanzar cada punto ruidoso. Dale a este modelo datos nuevos y probablemente fallará, porque trata esos saltos aleatorios como una señal real. Este árbol tiene una varianza alta. Cambia por completo en función de un puñado de puntos aleatorios.

2. Random Forests: El poder de la independencia

Si un árbol es demasiado sensible, ¿cómo lo arreglamos? Usamos Bagging — Bootstrap Aggregating. Piénsalo como un comité donde cada miembro trabaja con un conjunto ligeramente diferente de información, para que no todos cometan el mismo error.

En un Random Forest, aseguramos la independencia de dos maneras:

  1. Muestreo de filas: Cada árbol ve solo un subconjunto aleatorio de las filas de tus datos.
  2. Aleatoriedad de características: En cada división, el árbol solo puede considerar un puñado aleatorio de columnas (características).

Los árboles cometen errores diferentes porque se ven forzados a ser diferentes. Si promedias sus respuestas, los errores se cancelan mientras la señal se mantiene. La predicción se vuelve notablemente más suave.

from sklearn.ensemble import RandomForestRegressor

# Train a Random Forest
rf = RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42)
rf.fit(X, y)

y_rf_pred = rf.predict(X_test)

plt.scatter(X, y, color='black', label='Data')
plt.plot(X_test, y_rf_pred, color='blue', label='Random Forest')
plt.title("Random Forest: The Power of Averaging")
plt.legend()
plt.show()
  • from sklearn.ensemble import RandomForestRegressor importa la clase de regresión de Random Forest — internamente gestiona los 100 árboles, el bootstrapping y el submuestreo de características por ti.
  • n_estimators=100 le indica al bosque que construya 100 árboles independientes — más árboles generalmente reducen la varianza aún más, pero con rendimientos decrecientes y un mayor costo computacional.
  • max_depth=10 coincide con el límite de profundidad usado para el árbol único anterior — esto mantiene la comparación justa para que la mejora provenga del promedio, no de árboles menos profundos.
  • random_state=42 asegura que el bootstrapping y el muestreo de características sean reproducibles — la misma semilla produce los mismos subconjuntos de filas/columnas en cada ejecución.
  • rf.fit(X, y) entrena los 100 árboles en una sola llamada — cada árbol ve una muestra bootstrap diferente de las 80 filas, por lo que aprenden patrones ligeramente diferentes.
  • rf.predict(X_test) promedia las predicciones individuales de los 100 árboles para producir una única predicción suave — la línea azul debería ser notablemente menos irregular que la línea roja del árbol único.

Observa la línea azul. Mucho más suave que la roja. El Random Forest no persiguió cada valor atípico. Al promediar 100 árboles, reducimos la varianza. Este es el modelo de ‘configurar y olvidar’ de la ciencia de datos — difícil de romper.

3. Gradient Boosting: Aprendiendo de los errores

Mientras que los Random Forests construyen árboles en paralelo, Gradient Boosting los construye en secuencia — como un relevo donde cada corredor corrige al anterior.

Aquí está el detalle: el segundo árbol no predice el objetivo (YY). Predice el residuo — el error que dejó el primer árbol.

Construyamos un boost de 2 pasos manualmente para ver qué significa eso.

# Step 1: Train the first tree on the actual data
tree_1 = DecisionTreeRegressor(max_depth=2)
tree_1.fit(X, y)
prediction_1 = tree_1.predict(X)

# Step 2: Calculate the 'leftover' mistakes (residuals)
residuals = y - prediction_1

# Step 3: Train the second tree ONLY on the mistakes
tree_2 = DecisionTreeRegressor(max_depth=2)
tree_2.fit(X, residuals)

# Final Prediction = Tree 1 + Tree 2
final_pred = tree_1.predict(X_test) + tree_2.predict(X_test)

print(f"Average error of Tree 1: {np.mean(np.abs(residuals)):.4f}")
new_residuals = y - (tree_1.predict(X) + tree_2.predict(X))
print(f"Average error after Tree 2: {np.mean(np.abs(new_residuals)):.4f}")
  • tree_1 = DecisionTreeRegressor(max_depth=2) usa un árbol poco profundo (profundidad 2) — en boosting, cada árbol es intencionalmente débil para que capture solo una parte del patrón, dejando espacio para que el siguiente árbol contribuya.
  • tree_1.fit(X, y) entrena el primer árbol en el objetivo original, igual que un árbol normal — prediction_1 = tree_1.predict(X) obtiene las predicciones in-sample para los datos de entrenamiento.
  • residuals = y - prediction_1 calcula el error “sobrante” — este es el paso clave: en lugar de reentrenar con el mismo objetivo, cambiamos el objetivo para que sean los errores.
  • tree_2.fit(X, residuals) entrena el segundo árbol para predecir esos residuos — aprende “¿dónde se equivocó el Árbol 1, y en qué medida?”
  • final_pred = tree_1.predict(X_test) + tree_2.predict(X_test) combina ambos árboles sumando sus predicciones — el Árbol 1 proporciona el patrón base, el Árbol 2 proporciona la corrección.
  • new_residuals = y - (tree_1.predict(X) + tree_2.predict(X)) recalcula el error después de ambos árboles — el resultado impreso debería mostrar un error promedio menor, demostrando que la corrección del Árbol 2 realmente ayudó.

Actualización del residuo en Gradient Boosting — el proceso secuencial donde cada árbol corrige los errores del ensamble anterior:

ri(m)=yiFm1(xi)r_i^{(m)} = y_i - F_{m-1}(x_i)

Fm(x)=Fm1(x)+νhm(x)F_m(x) = F_{m-1}(x) + \nu \cdot h_m(x)

donde ri(m)r_i^{(m)} es el residuo en el paso mm, Fm(x)F_m(x) es la predicción del ensamble después de mm árboles, hm(x)h_m(x) es el mm-ésimo árbol ajustado a los residuos, y ν\nu es la tasa de aprendizaje.

Lenguaje sencilloSímbolo estadísticoEquivalente en Python
Residuo (error sobrante)ri(m)=yiFm1(xi)r_i^{(m)} = y_i - F_{m-1}(x_i)residuals = y - prediction_1
mm-ésimo árbol (ajustado a los residuos)hm(x)h_m(x)tree_2.fit(X, residuals)
Predicción del ensamble después de mm árbolesFm(x)F_m(x)tree_1.predict(X_test) + tree_2.predict(X_test)
Tasa de aprendizaje (tamaño del paso)ν\nulearning_rate=0.1 en GradientBoostingRegressor
Residuo actualizado después de la correcciónri(m+1)=yiFm(xi)r_i^{(m+1)} = y_i - F_m(x_i)new_residuals = y - (tree_1.predict(X) + tree_2.predict(X))

El error bajó. El Árbol 2 no se preocupó por el patrón completo. Solo se enfocó en dónde se equivocó el Árbol 1. Esa es la parte más difícil de entender del boosting: estamos modelando la brecha entre la realidad y nuestra suposición actual.

4. Frente a frente: ¿Cuándo usar cada uno?

Entonces, ¿cuál deberías usar?

Random Forests son como un SUV confiable. Son robustos, manejan bien los datos desordenados, y no tienes que ajustarlos mucho para obtener un buen resultado. Son excelentes cuando te preocupa el sobreajuste.

Gradient Boosting es como un coche de Fórmula 1. Es más rápido y puede lograr una precisión mucho mayor, pero es sensible. Si no lo ajustas correctamente, puede “sobreestudiar” los datos y volverse tan irregular como nuestro primer árbol individual.

CriterioRandom ForestGradient Boosting
Construcción de árbolesParalela (independiente)Secuencial (cada uno corrige al anterior)
Qué reduceVarianzaSesgo
Sensibilidad al ajusteBaja — funciona bien sin ajustesAlta — la tasa de aprendizaje y la profundidad importan
Riesgo de sobreajusteMenor (el promedio amortigua el ruido)Mayor (puede perseguir los residuales demasiado lejos)
Ideal paraLínea base rápida, datos ruidososMáxima precisión con datos limpios
AnalogíaSUV confiableCoche de Fórmula 1

Regla práctica: Comienza con un Random Forest para una línea base rápida y confiable. Recurre a Gradient Boosting cuando necesitas cada última gota de precisión y estás dispuesto a ajustar la tasa de aprendizaje y la profundidad de los árboles con cuidado.

Compáralos con una métrica real: Error Cuadrático Medio (MSE). Más bajo es mejor.

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error

# Setup models
gbr = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=2, random_state=42)
gbr.fit(X, y)

rf_mse = mean_squared_error(y, rf.predict(X))
gb_mse = mean_squared_error(y, gbr.predict(X))

print(f"Random Forest MSE: {rf_mse:.5f}")
print(f"Gradient Boosting MSE: {gb_mse:.5f}")
  • from sklearn.ensemble import GradientBoostingRegressor importa la clase de gradient boosting — internamente, encadena árboles de la misma manera que el ejemplo manual de 2 pasos, pero por 100 iteraciones.
  • n_estimators=100 construye 100 árboles secuenciales, cada uno corrigiendo el residual del ensemble hasta el momento — más árboles significa más correcciones pero también más riesgo de sobreajuste.
  • learning_rate=0.1 controla cuánto contribuye cada árbol a la predicción final — una tasa más pequeña significa que cada árbol empuja suavemente al ensemble, requiriendo más árboles para converger pero reduciendo el riesgo de sobreajuste.
  • max_depth=2 mantiene cada árbol superficial — los “weak learners” superficiales son el ingrediente secreto del boosting; un árbol profundo sobreajustaría los residuales y frustraría el propósito.
  • rf_mse = mean_squared_error(y, rf.predict(X)) calcula el MSE del Random Forest de la Sección 2 — nota que rf ya fue entrenado arriba, así que esto reutiliza ese modelo.
  • gb_mse = mean_squared_error(y, gbr.predict(X)) hace lo mismo para el Gradient Booster — comparar ambos en los mismos datos de entrenamiento nos permite ver qué enfoque se ajusta mejor (aunque en la práctica compararías en un conjunto de prueba separado).

El modelo de Gradient Boosting probablemente tiene un error menor aquí. Se enfocó en los puntos difíciles que el Random Forest simplemente promedió. Ten en cuenta el parámetro learning_rate en el código — ese es el ‘tamaño del paso.’ Pasos pequeños evitan que el modelo reaccione de manera exagerada a un solo error.

5. Resumen: Tu nuevo conjunto de herramientas

Hemos pasado de un solo árbol inestable a equipos de árboles. Esta es la conclusión:

  • Árboles individuales son propensos al sobreajuste porque memorizan el ruido.
  • Random Forests utilizan ‘Bagging’ para construir muchos árboles independientes a la vez. Reducen la varianza y se mantienen estables.
  • Gradient Boosting construye árboles uno tras otro, donde cada uno corrige los errores del anterior. Reduce el sesgo y suele ser el más preciso, pero requiere más cuidado.
  • Consejo profesional: Siempre empieza con un Random Forest. Te da una línea base sólida con casi cero esfuerzo. Si necesitas más velocidad o precisión, pasa a Gradient Boosting.

Así que ya sabes cómo funcionan estos equipos. Es hora de empezar a ajustarlos para tus propios conjuntos de datos. Los equipos de árboles que votan o se corrigen mutuamente funcionan bien cuando las categorías son claramente separables, pero Sam se pregunta por un enfoque totalmente diferente que traza la frontera más segura posible. Lo próximo en esta serie: Máquinas de Vectores de Soporte, una forma diferente de trazar la línea entre clases.

Comprueba tu comprensión

Las preguntas a continuación pasan de un simple recuerdo hasta el diseño abierto, siguiendo aproximadamente la Taxonomía de Bloom.

Recordar ¿Cuál es la diferencia estructural clave entre cómo Random Forests y Gradient Boosting construyen sus árboles: en paralelo vs. en secuencia?

Comprender Con tus propias palabras, explica qué es un “residuo” en Gradient Boosting y por qué el Árbol 2 se entrena con los residuos en lugar de con la variable objetivo original y.

Aplicar Usando el código manual de boosting de 2 pasos del artículo, si el error absoluto medio del Árbol 1 fue 0.15 y el Árbol 2 aprende con éxito la mitad del patrón residual restante, ¿aproximadamente qué esperarías que fuera el nuevo error absoluto medio después de añadir las predicciones del Árbol 2?

Analizar El artículo dice que Random Forests reduce la varianza mientras que Gradient Boosting reduce el sesgo. Explica paso a paso por qué el promedio de muchos árboles independientes (Random Forest) ataca la varianza específicamente, mientras que una cadena secuencial de árboles en la que cada uno corrige los errores del anterior (Boosting) ataca el sesgo específicamente.

Evaluar El “Pro Tip” del artículo dice que siempre se debe empezar con un Random Forest como línea base, y luego pasar a Gradient Boosting si se necesita más precisión. Critica este enfoque por defecto: ¿hay algún tipo de conjunto de datos o problema en el que esperarías que Gradient Boosting valiera el esfuerzo adicional de ajuste desde el principio, en lugar de tratarlo como una mejora en el segundo paso?

Crear Diseña un pequeño experimento para demostrar la fragilidad del “auto de Fórmula 1” de Gradient Boosting de la que el artículo advierte: describe una propiedad del conjunto de datos (como algunos valores atípicos extremos) y una configuración de learning_rate con la que esperarías que un modelo de Gradient Boosting presentara un sobreajuste dramáticamente peor que un Random Forest entrenado con los mismos datos.

Artículos relacionados

Referencias y lecturas adicionales

  • Breiman, L. (2001). “Random Forests.” Machine Learning, 45(1), 5–32. — el artículo fundamental que introdujo los Random Forests, formalizando el enfoque de agregación bootstrap (“bagging”) con aleatoriedad de características que hace que los árboles del ensemble sean lo suficientemente independientes como para que el promedio cancele su ruido individual.
  • Friedman, J. H. (2001). “Greedy Function Approximation: A Gradient Boosting Machine.” Annals of Statistics, 29(5), 1189–1232. — el artículo fundamental que formalizó el Gradient Boosting como un modelado aditivo por etapas codicioso, introduciendo el proceso de ajuste de residuos y la reducción de la tasa de aprendizaje que controla qué tan agresivamente cada árbol corrige al anterior.
  • Kaggle: House Prices — Advanced Regression Techniques — una competencia donde los ensembles de gradient boosting (XGBoost, LightGBM, CatBoost) dominan la tabla de clasificación para la predicción de viviendas con datos tabulares — el mismo dominio en el que trabaja Sam en HomeMatch.
  • scikit-learn: Documentación de métodos de ensemble — la documentación oficial que cubre tanto RandomForestRegressor como GradientBoostingRegressor, incluyendo guías de ajuste para n_estimators, learning_rate y max_depth.

Esta traducción fue generada automáticamente y puede contener errores. Si el idioma inglés es tu preferencia, puedes leer el artículo original en inglés .

«Aplica lo que aprendiste» es para suscriptores Supporter e Insider.

Suscríbete para desbloquear los ejercicios de este artículo.

Ver planes

¿Buscas otra cosa?

Busca en todos los artículos por título, resumen o tema.