O bagging combina modelos treinados em paralelo e independentes, atacando a variância. Boosting faz o oposto: treina modelos em sequência, cada um corrigindo os erros do anterior, atacando o viés. É a família por trás do XGBoost, LightGBM e CatBoost — os modelos que ganham a maioria das competições de dados tabulares e sustentam sistemas de produção no mundo inteiro. Fechamos com o stacking, a forma mais sofisticada de combinar modelos.
Entender o mecanismo do boosting (do AdaBoost ao gradient boosting), dominar na prática XGBoost, LightGBM e CatBoost com suas técnicas de regularização e early stopping, e construir ensembles de stacking sem vazamento.
A ideia do boosting: aprender com os erros
Boosting constrói um modelo forte somando muitos modelos fracos (tipicamente árvores rasas), sequencialmente. Cada novo modelo foca nos exemplos que os anteriores erraram. O resultado é um modelo de baixo viés — capaz de capturar padrões complexos que um único modelo fraco jamais aprenderia.
Contraste-chave com bagging:
| Bagging | Boosting | |
|---|---|---|
| Treino | Paralelo, independente | Sequencial, dependente |
| Ataca | Variância | Viés |
| Modelo base | Forte (árvore profunda) | Fraco (árvore rasa) |
| Risco | Baixo overfitting | Pode overfittar sem regularização |
AdaBoost — o primeiro boosting
AdaBoost (Adaptive Boosting) reponderam os exemplos: após cada modelo, aumenta o peso dos exemplos classificados errado, forçando o próximo a focar neles. Os modelos são combinados com pesos proporcionais à sua acurácia.
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=3000, n_features=20, random_state=42)
ada = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1), # "stump" = árvore de 1 nível
n_estimators=200, learning_rate=0.5, random_state=42,
)
print("AdaBoost:", cross_val_score(ada, X, y, cv=5, scoring="roc_auc").mean())
AdaBoost é sensível a ruído e outliers (dá peso crescente a exemplos difíceis, que podem ser justamente os ruidosos). Foi superado pelo gradient boosting, mas é a raiz conceitual da família.
Gradient Boosting — boosting como descida de gradiente
A generalização decisiva: em vez de reponderar exemplos, cada novo modelo é treinado para prever os resíduos (o gradiente da função de perda) do ensemble atual. É, literalmente, descida de gradiente no espaço de funções.
F₀ = predição inicial (ex.: média)
Para m = 1 até M:
resíduo = gradiente negativo da perda em F_{m-1}
treina árvore h_m para prever esse resíduo
F_m = F_{m-1} + learning_rate · h_m
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(
n_estimators=300, learning_rate=0.05, max_depth=3,
subsample=0.8, random_state=42,
)
print("GBM:", cross_val_score(gb, X, y, cv=5, scoring="roc_auc").mean())
A taxa de aprendizado (learning_rate) controla o tamanho de cada passo. Valores menores exigem mais árvores mas generalizam melhor — o trade-off central do boosting. Regra: learning_rate baixo + muitas árvores + early stopping.
XGBoost — gradient boosting industrializado
XGBoost adicionou regularização explícita (L1/L2 nas folhas), tratamento nativo de faltantes, paralelização e uma aproximação de segunda ordem (usa também a curvatura da perda). É o padrão-ouro de desempenho em muitos problemas tabulares.
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.2,
stratify=y, random_state=42)
xgb = XGBClassifier(
n_estimators=2000, # alto — o early stopping decide o real
learning_rate=0.03,
max_depth=4,
subsample=0.8, # amostra de linhas por árvore (estocasticidade)
colsample_bytree=0.8, # amostra de colunas por árvore
reg_lambda=1.0, # regularização L2
reg_alpha=0.0, # regularização L1
eval_metric="auc",
early_stopping_rounds=50, # para quando a validação não melhora
random_state=42,
)
xgb.fit(X_tr, y_tr, eval_set=[(X_val, y_val)], verbose=False)
print(f"Melhor iteração: {xgb.best_iteration}")
Early stopping é essencial: define n_estimators alto e deixa a validação decidir quando parar, evitando overfitting e escolhendo o número de árvores automaticamente.
LightGBM e CatBoost
- LightGBM cresce as árvores por folha (leaf-wise) em vez de por nível, e usa histogramas para binning — muito mais rápido em datasets grandes. Cuidado: leaf-wise overfitta mais fácil; controle com
num_leavesemin_child_samples.
from lightgbm import LGBMClassifier
lgbm = LGBMClassifier(
n_estimators=2000, learning_rate=0.03, num_leaves=31,
subsample=0.8, colsample_bytree=0.8, reg_lambda=1.0,
random_state=42, verbose=-1,
)
lgbm.fit(X_tr, y_tr, eval_set=[(X_val, y_val)],
callbacks=[__import__("lightgbm").early_stopping(50, verbose=False)])
- CatBoost trata variáveis categóricas nativamente (com target encoding ordenado, que evita vazamento por construção) e usa "ordered boosting" para reduzir viés de predição. Excelente quando há muitas categóricas e ótimo com pouca sintonia.
Qual escolher? LightGBM pela velocidade em bases grandes; CatBoost quando há muitas categóricas ou você quer bons resultados com pouco tuning; XGBoost como referência robusta e madura. Na prática, teste os três.
Regularização em boosting
Como o boosting pode overfittar, o controle vem de vários lados combinados:
learning_ratebaixo (+ mais árvores).max_depth/num_leaves— profundidade das árvores.subsample(linhas) ecolsample_bytree(colunas) — introduzem estocasticidade (stochastic gradient boosting).reg_lambda/reg_alpha— L2/L1 nos pesos das folhas.min_child_weight/min_child_samples— mínimo de amostras por folha.- Early stopping — o regularizador mais eficaz.
Sintonize esses com otimização bayesiana (Módulo 5) e valide com nested CV.
Stacking — aprendendo a combinar
Voting usa uma regra fixa (média/voto). Stacking treina um meta-modelo que aprende a melhor forma de combinar as predições dos modelos base. A chave para não vazar: as predições que alimentam o meta-modelo devem ser out-of-fold.
from sklearn.ensemble import StackingClassifier, RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
stack = StackingClassifier(
estimators=[
("xgb", XGBClassifier(n_estimators=300, learning_rate=0.05,
max_depth=4, eval_metric="logloss", random_state=42)),
("rf", RandomForestClassifier(n_estimators=300, random_state=42)),
("svc", SVC(probability=True, random_state=42)),
],
final_estimator=LogisticRegression(), # meta-modelo simples
cv=5, # gera predições out-of-fold para treinar o meta-modelo
stack_method="predict_proba",
n_jobs=-1,
)
print("Stacking:", cross_val_score(stack, X, y, cv=5, scoring="roc_auc").mean())
Boas práticas de stacking:
- Modelos base diversos (algoritmos diferentes que erram de formas diferentes) — a diversidade é o que dá ganho.
- Meta-modelo simples (regressão logística/linear) para não overfittar sobre as predições.
- Predições out-of-fold obrigatórias (o
cvdoStackingClassifiercuida disso). - Ganhos costumam ser incrementais e custam complexidade — avalie se o trade-off compensa em produção.
Boosting em R
library(tidymodels)
xgb_spec <- boost_tree(
trees = 1000, tree_depth = tune(), learn_rate = tune(),
loss_reduction = tune(), sample_size = tune(), mtry = tune()
) |> set_engine("xgboost") |> set_mode("classification")
# stacks:: -> pacote 'stacks' do tidymodels para ensembles de stacking
library(stacks)
# combina múltiplos workflows tunados em um único stack via meta-modelo
Boosting treina modelos em sequência, cada um corrigindo os erros do anterior, e por isso ataca o viés — o oposto do bagging, que ataca a variância. O AdaBoost repondera os exemplos difíceis; o gradient boosting treina cada modelo nos resíduos, que são o gradiente da perda, o que equivale a fazer descida de gradiente no espaço de funções. XGBoost, LightGBM e CatBoost são as implementações de ponta, com regularização, early stopping e tratamento nativo de faltantes; o LightGBM é rápido por crescer folha a folha, e o CatBoost lida com categóricas sem pré-processamento. A receita central é taxa de aprendizado baixa, muitas árvores e early stopping, com o overfitting contido por profundidade, subsample, colsample e regularização L1 e L2. Já o stacking treina um meta-modelo simples sobre predições out-of-fold de modelos base diversos: é mais poderoso que o voting, e também mais complexo de montar e validar.
Exercícios
-
Qual a diferença fundamental entre bagging e boosting em termos de como treinam os modelos e qual componente do erro atacam?
✓ Resposta:No bagging, os modelos são treinados em paralelo e de forma independente, cada um em uma amostra bootstrap; a agregação (média/voto) reduz a variância. No boosting, os modelos são treinados sequencialmente e de forma dependente: cada novo modelo corrige os erros do ensemble acumulado até então, reduzindo o viés. Consequentemente, bagging usa modelos base fortes (árvores profundas, alta variância) e boosting usa modelos fracos (árvores rasas, alto viés). Boosting tende a atingir menor erro, mas exige mais cuidado com overfitting e regularização.
-
No gradient boosting, o que cada nova árvore tenta prever, e como isso se relaciona com "descida de gradiente"?
✓ Resposta:Cada nova árvore é treinada para prever o gradiente negativo da função de perda avaliado nas predições atuais do ensemble — que, no caso da perda quadrática, equivale aos resíduos (erros) atuais. Ao adicionar essa árvore (multiplicada pela taxa de aprendizado) ao ensemble, damos um passo na direção que mais reduz a perda. Isso é exatamente descida de gradiente, mas realizada no espaço de funções: em vez de atualizar parâmetros numéricos, adicionamos funções (árvores) que apontam na direção de menor perda. Por isso o nome "gradient boosting".
-
Por que a combinação "learning_rate baixo + muitas árvores + early stopping" é a receita padrão do boosting?
✓ Resposta:Um
learning_ratebaixo faz cada árvore contribuir com um passo pequeno, o que suaviza o aprendizado e melhora a generalização (evita que o modelo se ajuste rápido demais aos dados de treino). Passos pequenos, porém, exigem mais árvores para atingir bom desempenho — daí definirn_estimatorsalto. Para não overfittar nem desperdiçar computação, usa-se early stopping: monitora-se a perda em um conjunto de validação e interrompe-se o treino quando ela para de melhorar, escolhendo automaticamente o número ideal de árvores. A combinação equilibra viés, variância e custo, sendo mais robusta que taxas altas com poucas árvores. -
Por que é crucial que as predições usadas para treinar o meta-modelo no stacking sejam "out-of-fold"?
✓ Resposta:Se o meta-modelo fosse treinado com predições que os modelos base fizeram sobre os mesmos dados em que foram treinados, essas predições seriam otimistas (os modelos base já "viram" essas amostras e tendem a acertá-las), transmitindo overfitting ao meta-modelo e causando vazamento. Usar predições out-of-fold — geradas por validação cruzada interna, onde cada predição vem de um modelo que não treinou naquela amostra — garante que o meta-modelo aprenda a combinar predições que refletem o comportamento em dados não vistos, como ocorrerá em produção. Isso torna a estimativa honesta e o ensemble generalizável. O
cvdoStackingClassifierimplementa isso automaticamente. -
Cite uma situação em que você preferiria CatBoost e uma em que preferiria LightGBM, justificando.
✓ Resposta:Preferiria CatBoost quando o dataset tem muitas variáveis categóricas (especialmente de alta cardinalidade), pois ele as trata nativamente com um target encoding ordenado que evita vazamento, poupando trabalho de pré-processamento e frequentemente entregando ótimos resultados com pouca sintonia. Preferiria LightGBM quando o dataset é muito grande e a velocidade de treino importa, pois seu crescimento leaf-wise e o binning por histogramas o tornam substancialmente mais rápido e eficiente em memória — cuidando de controlar
num_leaves/min_child_samplespara não overfittar. Em ambos os casos, o ideal é testar empiricamente, pois o vencedor varia por problema.