Hoppa till innehållet
AI-grafen
E· Universitetklassisk-ml· ca 60 min· utvecklande· verifierad 2026-09-20

Ensembler: random forest och boosting

Kunna träna random forest och gradient boosting och förklara varför ensembler generaliserar bättre.

Förkunskaper

Intuition

Ett enskilt beslutsträd är instabilt: byt ut några datapunkter och trädet ser helt annorlunda ut. Hög varians, låg bias.

Ensembler löser det på två helt olika sätt:

Bagging (random forest)Boosting (XGBoost m.fl.)
Träden tränasoberoende, parallelltsekventiellt
Varje träd serett bootstrap-urval + slumpade kolumneralla data, men viktade mot tidigare fel
Kombineras genomröstning eller medelvärdesummering med lärhastighet
Minskarvariansbias (och varians)
Djupa träd?ja, fullvuxnanej, grunda (3–8 nivåer)
Risk för överanpassninglåghög utan reglering

Bagging utgår från att medelvärdet av många brusiga men obiaserade skattningar har lägre varians. Därför ska varje träd vara djupt och överanpassat — medelvärdesbildningen städar upp.

Boosting bygger i stället varje nytt träd för att rätta föregående träds fel. Därför ska varje träd vara grunt: det ska bidra med en liten korrigering, inte en hel lösning.

Formellt

Varför bagging fungerar. Med BB oberoende skattare med varians σ2\sigma^2 och parvis korrelation ρ\rho har medelvärdet variansen

ρσ2+1−ρBσ2\rho\sigma^2 + \frac{1-\rho}{B}\sigma^2

Den andra termen försvinner när BB växer — men den första gör det inte. Därför är det inte antalet träd som är begränsningen utan hur korrelerade de är.

Det är precis varför random forest slumpar kolumner vid varje delning, inte bara rader: utan det skulle alla träd välja samma starka feature högst upp och bli nästan identiska. Kolumnsamplingen (max_features) är den parameter som sänker ρ\rho, och därför den viktigaste.

Gradient boosting är gradientnedstigning i funktionsrummet. Varje nytt träd anpassas till förlustens negativa gradient med avseende på nuvarande prediktion:

Fm(x)=Fm−1(x)+η⋅hm(x),hm≈−∂L∂Fm−1F_{m}(x) = F_{m-1}(x) + \eta \cdot h_m(x), \qquad h_m \approx -\frac{\partial L}{\partial F_{m-1}}

Lärhastigheten η\eta (ofta 0,05–0,1) och antalet träd byter mot varandra: halvera η\eta och du behöver ungefär dubbelt så många träd.

Parametrar som betyder något:

Random forestGradient boosting
n_estimators (fler är aldrig sämre)n_estimators (fler kan överanpassa)
max_features — viktigastlearning_rate — viktigast
min_samples_leafmax_depth (3–8)
subsample, colsample
tidigt stopp på validering

Skillnaden i första raden är avgörande: i random forest kan du alltid lägga till träd, i boosting måste antalet regleras.

OOB-skattning är random forests bonus: varje träd ser bara cirka 63 % av datan, så de återstående 37 % fungerar som en gratis valideringsmängd. Ingen separat uppdelning behövs.

Out-of-fold-boosting i praktiken: använd alltid tidigt stopp mot en valideringsmängd. Utan det är antalet träd en gissning, och gissar du för högt överanpassar modellen tyst.

Kod

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, HistGradientBoostingClassifier

X, y = make_classification(n_samples=4000, n_features=30, n_informative=8,
                           n_redundant=5, flip_y=0.05, random_state=0)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0)

for namn, m in (("ett träd", DecisionTreeClassifier(random_state=0)),
                ("random forest", RandomForestClassifier(n_estimators=300, random_state=0,
                                                         oob_score=True)),
                ("boosting", HistGradientBoostingClassifier(random_state=0,
                                                            early_stopping=True))):
    m.fit(Xtr, ytr)
    extra = f"  OOB {m.oob_score_:.3f}" if hasattr(m, "oob_score_") else ""
    print(f"{namn:<14} träning {m.score(Xtr, ytr):.3f}  test {m.score(Xte, yte):.3f}{extra}")

# Varför kolumnsampling: korrelationen mellan träden avgör
for mf in ("sqrt", 0.5, 1.0):
    rf = RandomForestClassifier(n_estimators=200, max_features=mf, random_state=0)
    print(f"  max_features={str(mf):<5} CV {cross_val_score(rf, X, y, cv=5).mean():.4f}")
#  max_features=1.0 betyder att alla träd ser alla kolumner → mer korrelerade → sämre

# Lärhastighet och antal träd byter mot varandra
for lr, n in ((0.3, 50), (0.1, 150), (0.05, 300)):
    gb = HistGradientBoostingClassifier(learning_rate=lr, max_iter=n,
                                        early_stopping=False, random_state=0)
    print(f"  lr={lr:<5} n={n:<4} CV {cross_val_score(gb, X, y, cv=5).mean():.4f}")

# Boosting UTAN tidigt stopp överanpassar tyst
for n in (50, 200, 1000):
    gb = HistGradientBoostingClassifier(max_iter=n, learning_rate=0.1,
                                        early_stopping=False, random_state=0).fit(Xtr, ytr)
    print(f"  n={n:<5} träning {gb.score(Xtr, ytr):.3f}  test {gb.score(Xte, yte):.3f}")

Sista blocket är poängen: träningspoängen fortsätter uppåt medan testpoängen planar ut eller faller. I random forest händer inte det — där kan du lägga till träd utan risk.

Behärskning innebär

  • Förklarar bagging och boosting
  • Tränar och ställer in båda
  • Vet varför ensembler generaliserar bättre

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser