Phase 02 - Lesson 11
Métodos de conjunto
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Un grupo de alumnos débiles, combinados correctamente, se convierte en un alumno fuerte. Esto no es una metáfora. Es un teorema.
Tipo: Construcción Idioma: Python Requisitos previos: Fase 2, Lección 10 (Compensación entre sesgo y varianza) Tiempo: ~120 minutos
Objetivos de aprendizaje
- Implementar AdaBoost y el aumento de gradiente desde cero y explicar cómo el aumento secuencial reduce el sesgo
- Cree un conjunto de embolsado y demuestre cómo el promedio de modelos decorrelacionados reduce la varianza sin aumentar el sesgo.
- Compare el embolsado, el impulso y el apilamiento en términos de a qué componente de error apunta cada método
- Evaluar la diversidad del conjunto y explicar por qué la precisión de la votación mayoritaria mejora con alumnos débiles más independientes.
El problema
Un árbol de decisión único es rápido de entrenar y fácil de interpretar, pero se adapta demasiado. Un modelo lineal único no se adapta bien a límites complejos. Podrías pasar días diseñando la arquitectura del modelo perfecto. O podrías combinar un montón de modelos imperfectos y obtener algo mejor que cualquiera de ellos individualmente.
Los métodos de conjunto hacen exactamente esto. Son la técnica más confiable para ganar competencias Kaggle con datos tabulares, impulsan la mayoría de los sistemas de machine learning de producción e ilustran el equilibrio entre sesgo y variación en acción. Bagging reduce la variación. Boosting reduce el sesgo. El apilamiento aprende en qué modelos confiar y en qué entradas.
El concepto
Por qué funcionan los conjuntos
Suponga que tiene N clasificadores independientes, cada uno con una precisión p > 0,5. El voto mayoritario tiene exactitud:
P(majority correct) = sum over k > N/2 of C(N,k) * p^k * (1-p)^(N-k)
Para 21 clasificadores, cada uno con un 60% de precisión, la precisión del voto mayoritario es aproximadamente del 74%. Con 101 clasificadores, se eleva al 84%. Los errores se cancelan cuando los modelos cometen errores diferentes.
El requisito clave es la diversidad. Si todos los modelos cometen los mismos errores, combinarlos no ayuda en nada. Los ensambles funcionan porque producen diversos modelos a través de:
- Diferentes subconjuntos de entrenamiento (ensacado)
- Diferentes subconjuntos de características (bosques aleatorios)
- Corrección de errores secuencial (impulso)
- Diferentes familias de modelos (apilables)
Bagging (Agregación Bootstrap)
Bagging crea diversidad al entrenar cada modelo en una muestra de arranque diferente de los datos de entrenamiento.
flowchart TD
D[Training Data] --> B1[Bootstrap Sample 1]
D --> B2[Bootstrap Sample 2]
D --> B3[Bootstrap Sample 3]
D --> BN[Bootstrap Sample N]
B1 --> M1[Model 1]
B2 --> M2[Model 2]
B3 --> M3[Model 3]
BN --> MN[Model N]
M1 --> V[Average or Majority Vote]
M2 --> V
M3 --> V
MN --> V
V --> P[Final Prediction]
Se extrae una muestra de arranque con reemplazo de los datos originales, del mismo tamaño que el original. Aproximadamente el 63,2% de las muestras únicas aparecen en cada arranque. El 36,8% restante (muestras listas para usar) proporciona un conjunto de validación gratuito.
Bagging reduce la varianza sin aumentar mucho el sesgo. Cada árbol individual se sobreajusta a su muestra de arranque, pero el sobreajuste es diferente para cada árbol, por lo que el promedio cancela el ruido.
Los bosques aleatorios tienen un toque adicional: en cada división, solo se considera un subconjunto aleatorio de características. Esto obliga a una mayor diversidad entre los árboles. El número típico de características candidatas es sqrt(n_features) para clasificación y n_features / 3 para regresión.
Boosting (Corrección de errores secuenciales)
Boosting entrena modelos secuencialmente. Cada nuevo modelo se centra en los ejemplos en los que los modelos anteriores se equivocaron.
flowchart LR
D[Data with weights] --> M1[Model 1]
M1 --> E1[Find errors]
E1 --> W1[Increase weights on errors]
W1 --> M2[Model 2]
M2 --> E2[Find errors]
E2 --> W2[Increase weights on errors]
W2 --> M3[Model 3]
M3 --> F[Weighted sum of all models]
Boosting reduce el sesgo. Cada nuevo modelo corrige los errores sistemáticos del conjunto hasta el momento. La predicción final es una suma ponderada de todos los modelos, donde los mejores modelos obtienen ponderaciones más altas.
La compensación: el impulso puede sobreajustarse si ejecuta demasiadas rondas, porque sigue ajustándose a ejemplos más difíciles, algunos de los cuales pueden ser ruido.
AdaBoost
AdaBoost (Adaptable Boosting) fue el primer algoritmo de impulso práctico. Funciona con cualquier alumno base, normalmente muñones de decisión (árboles de profundidad 1).
El algoritmo:
1. Initialize sample weights: w_i = 1/N for all i
2. For t = 1 to T:
a. Train weak learner h_t on weighted data
b. Compute weighted error:
err_t = sum(w_i * I(h_t(x_i) != y_i)) / sum(w_i)
c. Compute model weight:
alpha_t = 0.5 * ln((1 - err_t) / err_t)
d. Update sample weights:
w_i = w_i * exp(-alpha_t * y_i * h_t(x_i))
e. Normalize weights to sum to 1
3. Final prediction: H(x) = sign(sum(alpha_t * h_t(x)))
Los modelos con menor error obtienen un alfa más alto. Las muestras mal clasificadas obtienen pesos más altos, por lo que el siguiente modelo se centra en ellas.
Gradient Boosting
El aumento de gradiente generaliza el aumento a funciones de pérdida arbitrarias. En lugar de volver a ponderar las muestras, ajusta cada nuevo modelo a los residuos (gradiente negativo de pérdida) del conjunto actual.
1. Initialize: F_0(x) = argmin_c sum(L(y_i, c))
2. For t = 1 to T:
a. Compute pseudo-residuals:
r_i = -dL(y_i, F_{t-1}(x_i)) / dF_{t-1}(x_i)
b. Fit a tree h_t to the residuals r_i
c. Find optimal step size:
gamma_t = argmin_gamma sum(L(y_i, F_{t-1}(x_i) + gamma * h_t(x_i)))
d. Update:
F_t(x) = F_{t-1}(x) + learning_rate * gamma_t * h_t(x)
3. Final prediction: F_T(x)
Para la pérdida por error al cuadrado, los pseudoresiduales son solo los residuos reales: r_i = y_i - F_{t-1}(x_i). Cada árbol se ajusta literalmente a los errores del conjunto anterior.
La tasa de aprendizaje (contracción) controla cuánto contribuye cada árbol. Las tasas de aprendizaje más pequeñas requieren más árboles pero se generalizan mejor. Valores típicos: 0,01 a 0,3.
XGBoost: Por qué domina los datos tabulares
XGBoost (eXtreme Gradient Boosting) aumenta el gradiente con optimizaciones de ingeniería que lo hacen rápido, preciso y resistente al sobreajuste:
- Objetivo regularizado: Las penalizaciones L1 y L2 en el peso de las hojas evitan que los árboles individuales tengan demasiada confianza
- Aproximación de segundo orden: Utiliza la primera y la segunda derivada de la pérdida, lo que proporciona mejores decisiones divididas.
- Divisiones que tienen en cuenta la escasez: Maneja los valores faltantes de forma nativa aprendiendo la mejor dirección para los datos faltantes en cada división
- Submuestreo de columnas: Al igual que los bosques aleatorios, muestra características en cada división para determinar la diversidad.
- Boceto cuantil ponderado: Encuentra eficientemente puntos de división para características continuas en datos distribuidos
- Estructura de bloques compatible con caché: Diseño de memoria optimizado para líneas de caché de CPU
Para datos tabulares, XGBoost (y su sucesor LightGBM) supera consistentemente a las redes neuronales. Esto no va a cambiar pronto. Si sus datos caben en una tabla con filas y columnas, comience aumentando el gradiente.
Apilamiento (Metaaprendizaje)
El apilamiento utiliza las predicciones de múltiples modelos base como características para un metaaprendiz.
flowchart TD
D[Training Data] --> M1[Model 1: Random Forest]
D --> M2[Model 2: SVM]
D --> M3[Model 3: Logistic Regression]
M1 --> P1[Predictions 1]
M2 --> P2[Predictions 2]
M3 --> P3[Predictions 3]
P1 --> META[Meta-Learner]
P2 --> META
P3 --> META
META --> F[Final Prediction]
El metaaprendiz aprende en qué modelo base confiar para qué entradas. Si el random forest es mejor en ciertas regiones y el SVM en otras, el metaaprendiz aprenderá a enrutar en consecuencia.
Para evitar la fuga de datos, las predicciones del modelo base deben generarse mediante validación cruzada en el conjunto de entrenamiento. Nunca entrenas modelos base y generas metafunciones con los mismos datos.
Votación
El conjunto más sencillo. Simplemente combine las predicciones directamente.
- Votación difícil: Voto mayoritario en etiquetas de clase.
- Votación suave: Probabilidades promedio previstas, elija la clase con la probabilidad promedio más alta. Generalmente es mejor porque utiliza inentrenamiento de confianza.
Constrúyelo
Paso 1: Punto de decisión (aprendiz base)
El código en code/ensembles.py implementa todo desde cero. Comenzamos con un muñón de decisión: un árbol con una única división.
class DecisionStump:
def __init__(self):
self.feature_idx = None
self.threshold = None
self.polarity = 1
self.alpha = None
def fit(self, X, y, weights):
n_samples, n_features = X.shape
best_error = float("inf")
for f in range(n_features):
thresholds = np.unique(X[:, f])
for thresh in thresholds:
for polarity in [1, -1]:
pred = np.ones(n_samples)
pred[polarity * X[:, f] < polarity * thresh] = -1
error = np.sum(weights[pred != y])
if error < best_error:
best_error = error
self.feature_idx = f
self.threshold = thresh
self.polarity = polarity
def predict(self, X):
n = X.shape[0]
pred = np.ones(n)
idx = self.polarity * X[:, self.feature_idx] < self.polarity * self.threshold
pred[idx] = -1
return pred
Paso 2: AdaBoost desde cero
class AdaBoostScratch:
def __init__(self, n_estimators=50):
self.n_estimators = n_estimators
self.stumps = []
self.alphas = []
def fit(self, X, y):
n = X.shape[0]
weights = np.full(n, 1 / n)
for _ in range(self.n_estimators):
stump = DecisionStump()
stump.fit(X, y, weights)
pred = stump.predict(X)
err = np.sum(weights[pred != y])
err = np.clip(err, 1e-10, 1 - 1e-10)
alpha = 0.5 * np.log((1 - err) / err)
weights *= np.exp(-alpha * y * pred)
weights /= weights.sum()
stump.alpha = alpha
self.stumps.append(stump)
self.alphas.append(alpha)
def predict(self, X):
total = sum(a * s.predict(X) for a, s in zip(self.alphas, self.stumps))
return np.sign(total)
Paso 3: Gradient Boosting desde cero
class GradientBoostingScratch:
def __init__(self, n_estimators=100, learning_rate=0.1, max_depth=3):
self.n_estimators = n_estimators
self.lr = learning_rate
self.max_depth = max_depth
self.trees = []
self.initial_pred = None
def fit(self, X, y):
self.initial_pred = np.mean(y)
current_pred = np.full(len(y), self.initial_pred)
for _ in range(self.n_estimators):
residuals = y - current_pred
tree = SimpleRegressionTree(max_depth=self.max_depth)
tree.fit(X, residuals)
update = tree.predict(X)
current_pred += self.lr * update
self.trees.append(tree)
def predict(self, X):
pred = np.full(X.shape[0], self.initial_pred)
for tree in self.trees:
pred += self.lr * tree.predict(X)
return pred
Paso 4: Comparar con sklearn
El código verifica que nuestras implementaciones desde cero producen una precisión similar a la de sklearn AdaBoostClassifier y GradientBoostingClassifier, y compara todos los métodos uno al lado del otro.
Úsalo
Cuándo utilizar cada método
| Método | Reduce | Lo mejor para | Cuidado con |
|---|---|---|---|
| Bagging / Random Forest | Variación | Datos ruidosos, muchas funciones | No ayuda con el sesgo |
| AdaBoost | Sesgo | Datos limpios, alumnos de base sencilla | Sensible a valores atípicos y ruido |
| Gradient Boosting | Sesgo | Datos tabulares, concursos | Lento de entrenar, fácil de sobreajustar sin afinar |
| XGBoost / LightGBM | Ambos | ML tabular de producción | Muchos hiperparámetros |
| Apilamiento | Ambos | Obteniendo el último 1-2% de precisión | Complejo, riesgo de sobreadaptación del metaaprendiz |
| Votación | Variación | Combinación rápida de diversos modelos | Sólo ayuda si los modelos son diversos |
La pila de producción para datos tabulares
Para la mayoría de los problemas de predicción tabular, este es el orden a intentar:
- LightGBM o XGBoost con parámetros predeterminados
- Ajuste n_estimators, learning_rate, max_profundidad, min_child_weight
- Si necesita el último 0,5%, construya un conjunto apilable con 3 a 5 modelos diferentes.
- Utilice validación cruzada en todo momento
Las redes neuronales en datos tabulares casi siempre son peores que el aumento de gradiente, a pesar de los continuos intentos de investigación. TabNet, NODE y arquitecturas similares ocasionalmente coinciden, pero rara vez superan, un XGBoost bien ajustado.
Envíalo
Esta lección produce outputs/prompt-ensemble-selector.md, un mensaje que le ayuda a elegir el método de conjunto correcto para un conjunto de datos determinado. Describe tus datos (tamaño, tipos de características, nivel de ruido, equilibrio de clases) y el problema que estás resolviendo. El mensaje recorre una lista de verificación de decisiones, recomienda un método, sugiere iniciar hiperparámetros y advierte sobre errores comunes de ese método. También produce outputs/skill-ensemble-builder.md con la guía de selección completa.
Ejercicios
Modifique la implementación AdaBoost para realizar un seguimiento de la precisión del entrenamiento después de cada ronda. Precisión de la gráfica frente al número de estimadores. ¿Cuándo converge?
Implemente un random forest desde cero agregando submuestreo de características aleatorias al árbol de regresión. Entrena 100 árboles con
max_features=sqrt(n_features)y predicciones promedio. Compare la reducción de la varianza con un solo árbol.En la implementación de aumento de gradiente, agregue una parada temprana: realice un seguimiento de la pérdida de validación después de cada ronda y deténgase cuando no haya mejorado durante 10 rondas consecutivas. ¿Cuántos árboles necesita realmente?
Construya un conjunto de apilamiento con tres modelos base (regresión logística, árbol de decisión, k vecinos más cercanos) y un metaaprendiz de regresión logística. Utilice validación cruzada quíntuple para generar metafunciones. Compare solo con cada modelo base.
Ejecute XGBoost en el mismo conjunto de datos con parámetros predeterminados. Compare su precisión con su aumento de gradiente desde cero. Tiempo ambos. ¿Qué tan grande es la diferencia de velocidad?
Términos clave
| Término | Lo que dice la gente | Lo que realmente significa |
|---|---|---|
| Bagging | "Entrena en subconjuntos aleatorios" | Agregación Bootstrap: entrene modelos en muestras bootstrap, predicciones promedio para reducir la varianza |
| Boosting | "Céntrese en ejemplos difíciles" | Entrene los modelos secuencialmente, cada uno corrigiendo los errores del conjunto hasta el momento, para reducir el sesgo |
| AdaBoost | "Reponderar los datos" | Boosting mediante actualizaciones del peso de las muestras; los puntos mal clasificados obtienen mayor peso para el siguiente alumno |
| Aumento de gradiente | "Ajustar los residuales" | Boosting ajustando cada nuevo modelo al gradiente negativo de la función de pérdida |
| XGBoost | "El arma Kaggle" | Aumento de gradiente con regularización, optimización de segundo orden y trucos de velocidad a nivel de sistemas |
| Apilamiento | "Modelos encima de modelos" | Utilice predicciones de modelos base como funciones de entrada para un metaaprendiz |
| Bosque aleatorio | "Muchos árboles aleatorios" | Bagging con árboles de decisión, agregando submuestreo de características aleatorias en cada división para lograr diversidad |
| Diversidad de conjuntos | "Comete diferentes errores" | Los modelos no deben estar correlacionados en sus errores para que el conjunto mejore con respecto a los individuos |
| Error de falta de bolsa | "Validación gratuita" | Las muestras que no están en un sorteo de arranque (~36,8%) sirven como conjunto de validación sin necesidad de reserva |
Lectura adicional
- Schapire & Freund: Boosting: Fundamentos y algoritmos -- el libro de los creadores de AdaBoost
- Friedman: Greedy Function Approximation: A Gradient Boosting Machine (2001) -- el documento original para aumentar el gradiente
- Chen & Guestrin: XGBoost (2016) -- el artículo XGBoost
- Wolpert: Stacked Generalization (1992) -- el documento apilado original
- scikit-learn Métodos de conjunto -- referencia práctica