A la lliçó anterior vam deixar presentat el boosting com la família seqüencial dels ensembles: models que s'entrenen l'un rere l'altre, cadascun corregint els errors de l'anterior. Aquesta lliçó la dediquem per complet al seu representant estrella, el gradient boosting, probablement l'algorisme més guanyador de l'última dècada en dades tabulars: domina les competicions de Kaggle i resol en producció problemes exactament com el churn i la demanda de MercaFresh. Entendràs la seva mecànica amb un exemple numèric a mà, aprendràs a manejar els seus dos controls principals (learning rate i nombre d'estimadors), coneixeràs les implementacions que importen — sklearn, HistGradientBoosting, XGBoost, LightGBM — i l'enfrontaràs al Random Forest de la lliçó anterior.

Contingut

  1. La idea: aprendre dels residus
  2. Exemple numèric a mà: 3 iteracions
  3. Learning rate i n_estimators: l'equilibri
  4. Gradient boosting a scikit-learn
  5. HistGradientBoosting: la versió moderna
  6. XGBoost i LightGBM: els dominadors del tauler
  7. Boosting sobre el churn de MercaFresh: duel amb Random Forest
  8. El taló d'Aquil·les: overfitting i els seus frens
  9. Interpretació: feature importance i SHAP

La idea: aprendre dels residus

Random Forest entrena arbres independents i promitja. Gradient boosting fa el contrari: entrena arbres petits i febles en cadena, on cadascun té una única missió: predir l'error que van deixar els anteriors.

En regressió, amb error quadràtic, el procés és molt intuïtiu:

  1. Comença amb una predicció trivial: la mitjana de y (el nostre vell conegut DummyRegressor de 06-02).
  2. Calcula els residus: residu = y_real - prediccio_actual.
  3. Entrena un arbre petit que predigui aquests residus a partir de les features.
  4. Actualitza: prediccio_nova = prediccio_actual + η · prediccio_de_l_arbre, on η (eta) és el learning rate, un factor que encongeix cada correcció.
  5. Torna al pas 2, tantes vegades com digui n_estimators.

El nom "gradient" ve del fet que els residus són, matemàticament, el gradient (amb el signe canviat) de la funció de pèrdua quadràtica: cada arbre fa un pas de descens de gradient — el mateix que vam usar a 04-01 per ajustar la regressió lineal — però en l'espai de les prediccions en lloc de l'espai dels coeficients. Amb altres pèrdues (p. ex. logística per a classificació) se segueix el mateix esquema substituint el residu pel "pseudoresidu" corresponent; per això el mètode generalitza a classificació sense canviar de filosofia.

flowchart LR
    M0[Prediccio inicial: mitjana] --> R1[Residus 1]
    R1 --> A1[Arbre 1 apren residus]
    A1 --> M1[Prediccio += eta x arbre 1]
    M1 --> R2[Residus 2 mes petits]
    R2 --> A2[Arbre 2]
    A2 --> M2[Prediccio += eta x arbre 2]
    M2 --> R3[Residus 3 encara menors]

Exemple numèric a mà: 3 iteracions

Prenguem 4 clients de MercaFresh i la seva despesa mensual (en €). Per poder seguir els comptes usarem learning rate η = 1 (sense encongiment) i "arbres" simplificats que prediuen la mitjana del residu del seu grup (clients de freqüència alta vs. baixa):

Client Freqüència Despesa real (y)
Anna alta 120
Bruno alta 100
Carla baixa 40
David baixa 60

Iteració 0. Predicció inicial = mitjana global = (120+100+40+60)/4 = 80 per a tots.

Client Predicció Residu (y − pred)
Anna 80 +40
Bruno 80 +20
Carla 80 −40
David 80 −20

Iteració 1. L'arbre 1 divideix per freqüència i prediu la mitjana del residu de cada grup: +30 per a freqüència alta, −30 per a baixa. Actualitzem:

Client Predicció nova Residu nou
Anna 80 + 30 = 110 +10
Bruno 80 + 30 = 110 −10
Carla 80 − 30 = 50 −10
David 80 − 30 = 50 +10

L'error s'ha reduït dràsticament: de residus ±40/±20 a ±10.

Iteració 2. Dins de cada grup els residus ja sumen zero, així que un arbre que només veiés la freqüència prediria 0; suposem que l'arbre 2 troba una altra feature (p. ex. l'antiguitat) que separa l'Anna d'en Bruno i en David de la Carla, i prediu +10/−10 segons correspongui:

Client Predicció final Residu
Anna 110 + 10 = 120 0
Bruno 110 − 10 = 100 0
Carla 50 − 10 = 40 0
David 50 + 10 = 60 0

Tres passos (predicció base + 2 arbres) i l'entrenament està clavat. I aquí hi ha la moralitat doble: la suma de correctors febles assoleix una precisió que cap no aconseguiria sol… però també acabem de veure com de ràpid aquest mètode pot memoritzar el conjunt d'entrenament — amb residu zero en entrenament, tot el que quedi és ajust al soroll. A la pràctica, η = 1 gairebé no s'usa mai: encongir cada pas és el primer fre contra l'overfitting.

Learning rate i n_estimators: l'equilibri

Els dos controls principals estan acoblats:

  • learning_rate (η): quant aporta cada arbre. Valors típics: 0.01–0.3.
  • n_estimators: quants arbres s'encadenen.

La relació és de compensació: si redueixes η a la meitat, necessites aproximadament el doble d'arbres per arribar al mateix punt. Per què molestar-se llavors a abaixar η? Perquè molts passos petits generalitzen millor que pocs passos grans: cada correcció suau deixa marge als arbres següents per esmenar el rumb, mentre que un pas gran pot perseguir soroll irreversiblement.

Configuració Comportament
η alt (0.3) + pocs arbres Ràpid d'entrenar; risc d'overfitting i de passos bruscos
η baix (0.01–0.05) + molts arbres Més lent; millor generalització; necessita early stopping per no passar-se
η alt + molts arbres Recepta gairebé segura d'overfitting

A diferència del Random Forest — on més arbres només estabilitzava —, en boosting n_estimators sí que és un control de complexitat: cada arbre addicional continua reduint l'error d'entrenament, i a partir d'un cert punt comença a pujar el de validació. És exactament la silueta de les corbes de 06-05, i el seu antídot natural serà l'early stopping que veurem de seguida.

Gradient boosting a scikit-learn

La implementació clàssica: GradientBoostingRegressor i GradientBoostingClassifier.

from sklearn.ensemble import GradientBoostingClassifier

gb = GradientBoostingClassifier(
    n_estimators=300,     # arbres encadenats
    learning_rate=0.05,   # aportacio de cada arbre
    max_depth=3,          # arbres deliberadament petits (febles)
    subsample=0.8,        # cada arbre veu el 80% de les files (toc de bagging)
    random_state=42,
)

Fixa't en max_depth=3: en boosting els arbres són nans a propòsit (1–4 nivells). L'aprenent feble és una decisió de disseny: la potència ve de la seqüència, no de l'individu. subsample < 1 afegeix aleatorietat a l'estil bagging ("stochastic gradient boosting"), que sol millorar la generalització.

HistGradientBoosting: la versió moderna

Des de scikit-learn 0.21 existeix una reimplementació molt més ràpida: HistGradientBoostingClassifier/Regressor. El seu truc és discretitzar cada feature en un histograma de com a màxim 255 contenidors, de manera que cercar la millor divisió ja no requereix ordenar valors continus: es recorren els contenidors. En datasets de desenes de milers de files és ordres de magnitud més ràpida, i a més:

  • Maneja valors mancants de manera nativa (aprèn cap a quina branca enviar els NaN, sense imputació prèvia — tot i que el que vam aprendre a 03-02 continua sent necessari per a la resta de models).
  • Incorpora early stopping integrat (early_stopping=True) amb una fracció de validació interna.
  • Suporta features categòriques natives (categorical_features).
from sklearn.ensemble import HistGradientBoostingClassifier

hgb = HistGradientBoostingClassifier(
    learning_rate=0.05,
    max_iter=1000,          # equivalent a n_estimators
    early_stopping=True,    # deixa d'afegir arbres quan la validacio no millora
    validation_fraction=0.15,
    n_iter_no_change=20,    # paciencia: 20 iteracions sense millora
    random_state=42,
)

Regla pràctica actual: per a gradient boosting dins de l'ecosistema sklearn, usa la versió Hist per defecte; la clàssica queda per a datasets petits o per compatibilitat.

XGBoost i LightGBM: els dominadors del tauler

Fora de sklearn viuen les dues biblioteques que van popularitzar el gradient boosting modern (s'instal·len a part: pip install xgboost lightgbm):

XGBoost (2014) LightGBM (2017)
Aportació clau Regularització L1/L2 en els arbres, maneig de mancants, paral·lelització eficient Creixement per fulles (leaf-wise) i histogrames: encara més ràpid i amb menys memòria
Fama L'algorisme que "guanyava tots els Kaggle" a mitjans dels 2010 Estàndard actual en indústria per a tabular gran
API Pròpia + wrapper compatible amb sklearn (XGBClassifier) Ídem (LGBMClassifier)

Per què aquesta família domina en dades tabulars (taules de clients, transaccions, sensors…)? Perquè els arbres capturen sense esforç interaccions i no-linealitats, ignoren l'escala de les features, digereixen barreges de variables numèriques i categòriques, i el boosting esprem fins a l'última gota de senyal — tot amb temps d'entrenament de segons o minuts. En aquest terreny solen batre fins i tot les xarxes profundes, com discutirem amb honestedat a 07-04.

Totes dues biblioteques ofereixen early stopping contra un conjunt de validació explícit, la manera professional de fixar n_estimators:

# Exemple amb l'API sklearn d'XGBoost
from xgboost import XGBClassifier

xgb = XGBClassifier(n_estimators=2000, learning_rate=0.05, max_depth=3,
                    early_stopping_rounds=50, eval_metric="logloss")
xgb.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
print(xgb.best_iteration)   # quants arbres s'han usat realment

Es demana un n_estimators deliberadament alt i es deixa que la validació decideixi on parar: si després de 50 rondes la mètrica no millora, s'atura i es queda amb la millor iteració. Observa que reapareix la disciplina de 06-01: el conjunt de validació usat per parar no pot ser la prova final.

Boosting sobre el churn de MercaFresh: duel amb Random Forest

Enfrontem el campió de la lliçó anterior amb el nou aspirant, sobre el mateix dataset de churn amb features RFM i la mateixa validació creuada estratificada de 06-03.

import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.ensemble import (RandomForestClassifier,
                              HistGradientBoostingClassifier)

# Dataset de churn de MercaFresh (identic al de 07-02)
rng = np.random.default_rng(42)
n = 1000
recencia = rng.gamma(2, 15, n)
frequencia = rng.poisson(5, n) + 1
monetari = rng.gamma(3, 40, n)
antiguitat = rng.uniform(1, 60, n)
incidencies = rng.poisson(0.5, n)
logits = 0.05 * recencia - 0.4 * frequencia + 0.6 * incidencies - 0.01 * antiguitat - 0.5
y = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)
X = pd.DataFrame({"recencia": recencia, "frequencia": frequencia,
                  "monetari": monetari, "antiguitat": antiguitat,
                  "incidencies": incidencies})

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

models = {
    "Random Forest (07-02)": RandomForestClassifier(
        n_estimators=300, random_state=42, n_jobs=-1),
    "HistGradientBoosting": HistGradientBoostingClassifier(
        learning_rate=0.05, max_iter=500, max_depth=3,
        early_stopping=True, random_state=42),
}

for nom, model in models.items():
    f1 = cross_val_score(model, X, y, cv=cv, scoring="f1")
    print(f"{nom:24s} F1: {f1.mean():.3f} +/- {f1.std():.3f}")

En datasets petits i amb senyal senzill com aquest, tots dos solen quedar molt igualats (de vegades guanya el bosc). El patró general a la pràctica és:

  • Random Forest: excel·lent resultat amb zero ajust; difícil d'espatllar. La baseline forta.
  • Gradient boosting: sostre més alt — amb els seus hiperparàmetres ben triats sol gratar uns punts més de mètrica, especialment en datasets grans amb interaccions complexes — però exigeix aquest ajust (que automatitzarem a 07-05) i vigilància de l'overfitting.

Per a MercaFresh la lectura de negoci és concreta: si el bosc dona F1 0.82 sense esforç i el boosting ben afinat dona 0.85, aquests tres punts són desenes de clients recuperables cada mes correctament prioritzats per la campanya de retenció — normalment mereix l'esforç.

El taló d'Aquil·les: overfitting i els seus frens

El boosting persegueix residus, i els residus acaben sent soroll: si el deixes córrer, memoritza l'entrenament (ho vam veure a l'exemple a mà, que va arribar a residu zero). És l'alumne avantatjat de les patologies de 06-05, i per això ve equipat amb més frens que cap altre algorisme:

Fre Paràmetre típic Efecte
Learning rate baix learning_rate=0.01–0.1 Passos petits, correccions suaus
Early stopping early_stopping / early_stopping_rounds Talla la seqüència quan la validació deixa de millorar
Arbres petits max_depth=2–4 (o num_leaves baix a LightGBM) Limita la complexitat de cada corrector
Submostreig de files subsample<1 Aleatorietat estil bagging, descorrelaciona errors
Submostreig de columnes colsample_bytree<1 (XGBoost/LightGBM) Feature bagging, com a Random Forest
Regularització a les fulles reg_alpha, reg_lambda (XGBoost/LightGBM) L'L1/L2 de 07-01 aplicat als valors de les fulles

Fixa't en l'última fila: la regularització de 07-01 reapareix dins dels arbres. Les tècniques d'aquest mòdul no són compartiments estancs, són peces combinables. La recepta defensiva estàndard: learning rate baix + early stopping + arbres poc profunds, i validar sempre amb la CV del mòdul 6.

Interpretació: feature importance i SHAP

Com el Random Forest, els models de boosting ofereixen feature_importances_ (a sklearn clàssic i a XGBoost/LightGBM) amb les mateixes virtuts i els mateixos biaixos que vam discutir a 07-02: útils com a rànquing global, perilloses amb features correlacionades.

Per a explicacions serioses, l'estàndard actual és SHAP (SHapley Additive exPlanations, biblioteca shap): assigna a cada feature de cada predicció individual una contribució amb signe — "a aquest client el model li dona un 78% de churn: la recència de 45 dies aporta +0.20, les seves 2 incidències +0.12 i la seva antiguitat de 5 anys −0.08". Això és or per a l'equip de retenció de MercaFresh, que necessita saber no només qui se'n va sinó per què. No ho desenvolupem aquí — és matèria d'interpretabilitat avançada i reapareixerà en el context ètic de 08-04 —, però convé que sàpigues que existeix i que es porta especialment bé amb els models d'arbres.

Errors Comuns i Consells

  • Apujar n_estimators "perquè més és millor". Cert a Random Forest, fals en boosting: aquí cada arbre extra afegeix complexitat. Fixa un sostre alt i deixa que l'early stopping decideixi.
  • Ajustar el learning rate sense tocar el nombre d'arbres. Van acoblats: si abaixes η de 0.1 a 0.01, multiplica el pressupost d'arbres per ~10 o el model quedarà infraentrenat.
  • Usar arbres profunds com a base. max_depth=10 en boosting sol ser una recepta de sobreajustament; la força és a la seqüència de correctors febles, no en correctors forts.
  • Fer early stopping contra el conjunt de prova. El conjunt que decideix quan parar participa en l'entrenament a tots els efectes: és una fuita de les de 06-01. Usa una partició de validació o la validació interna del mateix algorisme, i reserva la prova intacta.
  • Escalar les features "per si de cas". No fa mal, però és innecessari: com tots els mètodes d'arbres, el boosting és insensible a l'escala. Estalvia't aquest pas del pipeline (i recorda que sí que és obligatori per a SVM, K-NN o els models regularitzats de 07-01).
  • Consell: en projectes reals, entrena primer un Random Forest (baseline forta, 07-02) i després un HistGradientBoosting/LightGBM amb early stopping. Si el boosting no supera clarament el bosc, queda't amb el bosc: menys hiperparàmetres per mantenir.

Exercicis

  1. Boosting a mà, quarta iteració. Repeteix l'exemple numèric de la lliçó però amb learning rate η = 0.5: recalcula prediccions i residus de les iteracions 1 i 2 i afegeix-hi una iteració 3 (assumeix que cada arbre continua predint la mitjana del residu del seu grup, alternant la divisió per freqüència i la que separa dins de cada grup). S'arriba a residu zero? Què il·lustra això sobre η?
  2. L'acoblament η–n_estimators. Sobre el churn de MercaFresh, entrena GradientBoostingClassifier(max_depth=3, subsample=0.8) amb les combinacions (η=0.3, 50 arbres), (η=0.1, 150), (η=0.03, 500) i (η=0.3, 500). Avalua l'F1 amb la CV estratificada. Confirmen els resultats la taula de la secció 3?
  3. Corba d'early stopping. Amb train_test_split (estratificat) separa un 20% de validació del churn. Entrena GradientBoostingClassifier(n_estimators=400, learning_rate=0.1, max_depth=3) i usa staged_predict_proba per calcular la log-loss en entrenament i en validació després de cada arbre. Dibuixa totes dues corbes: en quina iteració hauria parat un early stopping amb paciència 20? Quina forma de 06-05 hi reconeixes?

Solucions

  1. Amb η = 0.5, la iteració 1 suma la meitat de la correcció: prediccions 95/95/65/65 i residus +25/+5/−25/−5. La iteració 2 (correcció per grups de freqüència: mitjana de residus ±15, aplicada al 50%) deixa prediccions 102.5/102.5/57.5/57.5 i residus +17.5/−2.5/−17.5/+2.5; la iteració 3 continua reduint però no arriba a zero. Il·lustració: amb η < 1 el model s'aproxima asimptòticament a l'ajust perfecte en entrenament — no el clava mai de cop —, i això dona marge per aturar la seqüència (early stopping) en el punt de millor generalització abans de memoritzar el soroll.
  2. Les tres primeres combinacions (pressupost equilibrat η·n_estimators ≈ 15) donen F1 semblants, amb un lleuger avantatge habitual per a η baix + molts arbres i menys desviació entre folds. La quarta (η=0.3 amb 500 arbres, pressupost 150) rendeix pitjor en validació tot i ser la que té menys error d'entrenament: overfitting pur. Confirma la taula: el que importa és el producte equilibrat, i passar-se de pressupost passa factura.
  3. La log-loss d'entrenament descendeix monòtonament (el boosting sempre pot continuar llimant residus). La de validació baixa, toca mínim — típicament entre les iteracions 80 i 200 amb aquestes dades — i després ascendeix suaument: la silueta clàssica de la corba de validació de 06-05, ara amb "nombre d'arbres" a l'eix x. L'early stopping amb paciència 20 hauria tallat unes 20 iteracions després del mínim, quedant-se amb el model de la millor iteració. Aquest exercici és exactament el que early_stopping=True automatitza per dins.

Conclusió

El gradient boosting materialitza la promesa del boosting: una predicció inicial trivial més una cadena d'arbres nans, cadascun entrenat sobre els residus del conjunt anterior, amb el learning rate encongint cada pas — descens de gradient en l'espai de les prediccions. Has vist els seus dos controls acoblats (η i n_estimators), les seves implementacions per ordre de modernitat (clàssica de sklearn, HistGradientBoosting, XGBoost i LightGBM), el seu duel amb Random Forest sobre el churn de MercaFresh i, sobretot, el seu caràcter: l'algorisme més potent en dades tabulars és també el més propens a memoritzar, i per això viatja envoltat de frens — learning rate baix, early stopping, arbres petits, submostreig i la regularització de 07-01 aplicada a les seves fulles. Amb arbres i ensembles hem arribat molt lluny partint de models senzills; la lliçó següent explora l'altra gran via cap a la potència: apilar capes de neurones fins a construir xarxes profundes, el deep learning que l'MLP de 04-07 va deixar anunciat.

Curs de Machine Learning

Mòdul 1: Introducció al Machine Learning

Mòdul 2: Fonaments d'Estadística i Probabilitat

Mòdul 3: Preprocessament de Dades

Mòdul 4: Algorismes de Machine Learning Supervisat

Mòdul 5: Algorismes de Machine Learning No Supervisat

Mòdul 6: Avaluació i Validació de Models

Mòdul 7: Tècniques Avançades i Optimització

Mòdul 8: Implementació i Desplegament de Models

Mòdul 9: Projectes Pràctics

Mòdul 10: Recursos Addicionals

© Copyright 2026. Tots els drets reservats