Machine learning do zero · Aula 5.3 · 28 de 33 aulas prontas · 12 min
Gradient boosting: árvores que corrigem erros
Em resumo
Cada árvore corrige os erros das anteriores. Depois de 200 árvores com passo 1, o treino cai para 2287 e o teste sobe para 5944, pior que uma reta (4095).
Aquecimento Duas perguntas de aulas anteriores. Um minuto, e a aula nunca fica bloqueada.
Na lição 5.2, uma floresta aleatória tirava a média de muitas árvores construídas de forma independente, e acrescentar árvores não fazia mal. O gradient boosting constrói as árvores uma depois da outra. Cada árvore nova aprende os resíduos, isto é, quanto o conjunto de árvores anterior ainda erra em cada exemplo. Ela soma sua correção multiplicada pela taxa de aprendizado, um número entre 0 e 1. As árvores são pequenas, muitas vezes com uma só pergunta (um toco) ou com três. Em tabelas de tamanho médio, os modelos de árvores, com o boosting à frente, costumam vencer as redes neurais (Grinsztajn et al., 2022). Mas o boosting tem um traço que a floresta não tem: as correções seguintes podem começar a corrigir ruído.
Escolha uma resposta e o seu grau de certeza.
Responda para liberar o experimento.
Experimento interativo
Este experimento interativo funciona em um navegador com JavaScript ativado. O texto ao redor descreve o que ele mostra.
Ver a resposta
Resposta certa: Pior que a reta, porque as árvores seguintes começaram a corrigir o ruído
Pior: o MSE de teste sobe para 5944, embora o de treino tenha caído para 2287. Cada árvore aprende os resíduos das anteriores, e depois de algumas rodadas o que sobra nos resíduos é sobretudo o ruído aleatório de pacientes específicos. Em outras 20 divisões, 200 árvores assim sempre perdem para a reta no teste.
Experimente
- Comece onde o lab abre: passo 1, profundidade 2, 200 árvores. Quantos degraus tem a função do conjunto e quão afastadas estão as curvas de treino e de validação?
- Baixe o número de árvores. Com quantas árvores o erro de validação é o mais baixo? Só então olhe o teste.
- Reduza o passo para 0,1 e pegue um toco. Como fica agora a curva de validação à medida que as árvores aumentam?
- Compare a melhor validação do boosting com a linha tracejada da reta. Qual fica mais embaixo?
Depois de conferir no lab, vamos dar nome ao que você vê.
A ideia em palavras simples
A primeira árvore faz algumas perguntas sobre o IMC e prevê a progressão média da doença em cada grupo. Ela erra, então calculamos os resíduos: quanto falta até a verdade em cada paciente. A segunda árvore já não prevê a doença, só esses resíduos. O resultado dela vezes a taxa de aprendizado é somado ao da primeira. A terceira árvore aprende o que sobra depois das duas, e assim por diante. O conjunto cresce a partir de pequenas correções.
No começo, as correções capturam o padrão real: mais IMC, mais progressão. Depois que o padrão está capturado, o que sobra nos resíduos é sobretudo ruído, os desvios aleatórios dos pacientes com que o modelo aprende. As árvores seguintes aprendem justamente isso. Com passo 1 e árvores de três perguntas, o treino cai de 3713 com uma árvore para 2287 com 200, enquanto o teste sobe de 4319 para 5944. Isso é sobreajuste, como numa árvore profunda demais, só que desta vez ele cresce com o número de árvores. Numa floresta aleatória, cada árvore vê os dados por conta própria, e a média apaga os erros delas. No boosting, cada árvore se soma aos erros das anteriores.
Por isso o número de árvores, o passo e a profundidade são escolhidos pela validação, não pelo teste. O lab calcula o erro de validação com uma validação cruzada em 5 partes dentro dos 309 pacientes de treino. O mais baixo é um toco com passo 0,1 e 50 árvores: validação 3960. A reta tem 3823 na mesma validação, então só com o IMC quem vence é a reta. O teste confirma isso só no fim: 4154 para o boosting contra 4095 para a reta.
Jak to policzono
- Dados: Diabetes (Efron et al., 2004), no scikit-learn sob a licença BSD-3-Clause. 442 pacientes, 10 medidas, progressão da doença depois de um ano. Usamos só o IMC. Divisão: 309 pacientes para aprender, 133 para o teste.
- Modelo: gradient boosting com árvores de regressão de profundidade 1 ou 2 só sobre o IMC, perda quadrática, passo de 0,05 a 1, até 200 árvores; uma regressão linear sobre o IMC para comparar. Os números do lab batem com o
GradientBoostingRegressordo scikit-learn. - Validação: validação cruzada em 5 partes dentro dos 309 pacientes de treino, as mesmas partes para cada ajuste e para a reta.
Por que funciona
O resíduo y − F(x) é a direção em que a previsão precisa se mover para o erro quadrático cair o mais rápido possível. Cada árvore dá, portanto, um pequeno passo de descida do gradiente, só que no espaço das funções e não dos pesos. Daí o nome: gradient boosting. Um passo pequeno significa que uma árvore corrige só parte do erro. São precisas mais árvores, mas cada uma acrescenta menos ruído. Em outras 20 divisões, 200 árvores de três perguntas com passo 0,1 têm no teste um MSE de 3747 a 4802, sempre menos que as mesmas árvores com passo 1 (de 4617 a 6983).
Isso não quer dizer que o boosting sempre vença uma reta. Só com o IMC, a relação com a doença é quase linear, e as árvores só conseguem imitar uma reta com degraus. Em outras 20 divisões, o boosting escolhido pela validação vence a reta no teste em 7. O boosting brilha onde há muitos atributos e o efeito deles é de limiar e emaranhado. Aí as árvores seguintes capturam dependências que uma reta não consegue expressar.
Um parente mais antigo, o AdaBoost, em vez de resíduos muda os pesos dos exemplos. Os exemplos em que o conjunto erra pesam mais na rodada seguinte. No verbete do Atlas sobre AdaBoost, o primeiro toco em tumores de mama classifica errado 10,3% dos tumores de treino. Antes da segunda rodada, esses tumores mal classificados carregam exatamente 50% do peso total, então o segundo toco aprende sobretudo com eles.
Um erro comum
Verdadeiro ou falso: “mais rodadas de boosting sempre ajudam”. Falso. Cada rodada melhora o treino, mas o teste só até certo ponto. Com passo 1 e árvores de três perguntas, na validação o melhor é 1 árvore, e 200 árvores dão 5944 no teste em vez de 4319. No código abaixo, o teste sobe de 4079 depois de uma árvore para 6437 depois de 200, enquanto o treino cai de 3827 para 2044.
O caminho seguro é um passo pequeno, muitas árvores e a parada antecipada da lição 3.6: pare de acrescentar árvores quando o erro de validação parar de cair. No código abaixo, a parada fica com 83 de 1000 árvores.
Quiz
Três perguntas para você se testar. Escolha uma resposta ou abra a resposta embaixo de cada pergunta. No teclado: as setas escolhem, Enter ou Espaço confirma.
Pontos-chave
- O gradient boosting constrói árvores em sequência: cada uma aprende os resíduos do conjunto anterior e soma uma correção vezes a taxa de aprendizado.
- Ao contrário da floresta, o boosting pode sobreajustar: com passo 1, o teste sobe de 4319 para 5944 enquanto o treino cai.
- Escolha o número de árvores, o passo e a profundidade pela validação ou pela parada antecipada, nunca pelo teste.
- O boosting nem sempre vence uma reta: só com o IMC, a reta tem 3823 na validação e o melhor boosting 3960.
Mais a fundo: fórmula, código, prática
A matemática
O conjunto depois de m árvores: F_m(x) = F_{m−1}(x) + η · h_m(x), em que F_0 é a média de y no treino, η é a taxa de aprendizado e a árvore h_m ajusta os resíduos r_i = y_i − F_{m−1}(x_i).
Para a perda quadrática L = (y − F)² / 2, a derivada em relação a F é −(y − F), então o resíduo é menos o gradiente da perda. Cada árvore dá um passo de descida do gradiente no espaço das funções. No AdaBoost, o peso de um exemplo mal classificado é multiplicado por e^α, em que α = ln((1 − ε) / ε) e ε é o erro ponderado do toco.
No código (Python)
O mesmo experimento no scikit-learn: 200 árvores com passo 1, etapa por etapa, depois três ajustes comparados por validação cruzada dentro do treino, e a parada antecipada.
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split, cross_val_score, KFold
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error as mse
X, y = load_diabetes(return_X_y=True, as_frame=True)
x_tr, x_te, y_tr, y_te = train_test_split(X[["bmi"]], y, test_size=0.3, random_state=0)
big = GradientBoostingRegressor(n_estimators=200, max_depth=2, learning_rate=1.0, random_state=0).fit(x_tr, y_tr)
for m, (p_tr, p_te) in enumerate(zip(big.staged_predict(x_tr), big.staged_predict(x_te)), start=1):
if m in (1, 10, 50, 200):
print(f"step 1, depth 2, {m:3d} trees: train MSE {mse(y_tr, p_tr):.0f}, test MSE {mse(y_te, p_te):.0f}")
cv = KFold(5, shuffle=True, random_state=0)
models = {"line": LinearRegression(), "step 1, 200 trees": big,
"step 0.1, 50 stumps": GradientBoostingRegressor(n_estimators=50, max_depth=1, learning_rate=0.1, random_state=0)}
for name, model in models.items():
val = -cross_val_score(model, x_tr, y_tr, cv=cv, scoring="neg_mean_squared_error").mean() # validação só dentro do treino
print(f"{name}: validation MSE {val:.0f}")
stop = GradientBoostingRegressor(n_estimators=1000, max_depth=1, learning_rate=0.1, validation_fraction=0.2, n_iter_no_change=10, random_state=0).fit(x_tr, y_tr)
print(f"early stopping kept {stop.n_estimators_} of 1000 trees; test MSE {mse(y_te, stop.predict(x_te)):.0f}, line {mse(y_te, models['line'].fit(x_tr, y_tr).predict(x_te)):.0f}")
Saída
step 1, depth 2, 1 trees: train MSE 3827, test MSE 4079
step 1, depth 2, 10 trees: train MSE 3013, test MSE 4798
step 1, depth 2, 50 trees: train MSE 2122, test MSE 6233
step 1, depth 2, 200 trees: train MSE 2044, test MSE 6437
line: validation MSE 3956
step 1, 200 trees: validation MSE 5728
step 0.1, 50 stumps: validation MSE 4030
early stopping kept 83 of 1000 trees; test MSE 4062, line 3921
A divisão é diferente da do lab, então os números também são um pouco diferentes. O quadro é o mesmo: com passo 1, o treino cai de 3827 para 2044, enquanto o teste sobe de 4079 para 6437. Na validação, a reta (3956) vence o passo pequeno (4030), e 200 árvores com passo 1 ficam com o pior resultado (5728). A parada antecipada fica com 83 árvores.
Baixar o notebook (.ipynb). Abra no Jupyter ou envie para o Google Colab; ele baixa sozinho os dados que faltarem.
Para quem pratica
- No scikit-learn:
HistGradientBoostingRegressoreHistGradientBoostingClassifiersão rápidos em tabelas grandes, lidam sozinhos com dados faltantes e têm parada antecipada (early_stopping). Fora do scikit-learn: XGBoost, LightGBM, CatBoost. - Um começo típico: passo de 0,05–0,1, árvores rasas, muitas árvores e parada antecipada na validação. Ajuste a profundidade e o passo com validação cruzada (lição 3.5) e deixe o teste para o fim.
- O boosting é mais sensível a ruído nos rótulos que uma floresta: cada exemplo errado fica nos resíduos e as árvores seguintes tentam corrigi-lo. Com dados sujos, ajudam um passo menor, a amostragem de linhas (
subsample) e uma regularização mais forte das árvores. - Antes de recorrer ao boosting, meça um ponto de referência simples (lição 1.3). Quando a relação é quase linear, uma reta pode vencer.
Isto foi útil?
Obrigado! Isso nos ajuda a melhorar.
Vá mais fundo no Atlas
Verbetes da enciclopédia sobre as ideias desta aula: