A la lliçó anterior vam millorar un model contenint-lo amb regularització. Aquesta lliçó pren el camí oposat: millorar combinant molts models perquè els seus errors individuals es cancel·lin entre si. És una de les idees més rendibles de tot el machine learning: els mètodes d'ensemble (conjunt) dominen les competicions sobre dades tabulars i són l'estàndard de facto en producció per a problemes com el churn de MercaFresh. Aquí entendràs per què combinar funciona, coneixeràs les tres grans famílies — bagging, boosting i stacking —, estudiaràs Random Forest en detall i l'enfrontaràs a l'arbre de decisió solitari de 04-03 sobre el nostre dataset de churn, amb la mateixa validació creuada del mòdul 6.

Contingut

  1. Per què combinar models funciona
  2. Bagging: bootstrap + agregació
  3. Random Forest en detall
  4. Random Forest sobre el churn de MercaFresh
  5. Voting classifiers: hard i soft
  6. Stacking: un model que aprèn a combinar
  7. Boosting: la família seqüencial (presentació)
  8. Taula comparativa de les tres famílies
  9. El preu: interpretabilitat

Per què combinar models funciona

Imagina que preguntes a 1.000 persones quantes taronges caben en un palet de MercaFresh. Cada estimació individual serà dolenta, però la mitjana de totes sol ser sorprenentment bona: els que es passen compensen els que es queden curts. És la "saviesa de les multituds", i funciona sota dues condicions:

  1. Cada estimador és millor que l'atzar (encara que sigui per poc).
  2. Els errors són independents (o almenys, poc correlacionats): no tots s'equivoquen en la mateixa direcció.

Amb models passa exactament igual. Si tens 100 classificadors que encerten el 70% de les vegades i els seus errors fossin independents, la votació per majoria encertaria molt més del 70%: perquè la majoria falli, haurien d'equivocar-se alhora més de 50, cosa improbable si cadascun falla en clients diferents.

La connexió amb 06-05 és directa: promitjar models redueix la variància. Un arbre de decisió profund té baix biaix però una variància altíssima (canvia del tot amb una altra mostra d'entrenament); si promitgem molts arbres diferents, el component aleatori de cadascun es cancel·la i queda el senyal comú. La condició crítica — i la més difícil — és la descorrelació: cent còpies del mateix model no aporten res, igual que preguntar mil vegades a la mateixa persona. Tot l'enginy dels mètodes d'ensemble està a fabricar diversitat:

Font de diversitat Tècnica que la usa
Entrenar cada model amb dades diferents (remostreig) Bagging / Random Forest
Limitar les features que veu cada model Random Forest (feature bagging)
Usar algorismes de famílies diferents Voting, Stacking
Entrenar en seqüència, cadascun sobre els errors de l'anterior Boosting

Bagging: bootstrap + agregació

Bagging = Bootstrap AGGregatING. La recepta:

  1. Bootstrap: genera B mostres del conjunt d'entrenament, cadascuna de la mateixa mida que l'original però mostrejada amb reemplaçament (un client pot aparèixer diverses vegades; d'altres, cap). Ja vam usar aquesta idea en inferència estadística (02-04); aquí serveix per fabricar B "versions alternatives" del dataset.
  2. Entrenament: entrena un model (típicament un arbre profund, sense podar) en cada mostra.
  3. Agregació: per predir, en classificació es vota; en regressió es promitja.

Cada mostra bootstrap deixa fora, de mitjana, un 37% de les observacions (la probabilitat que un exemple concret no surti en n extraccions amb reemplaçament és $(1-1/n)^n \approx e^{-1} \approx 0.37$). Aquest 37% exclòs tindrà un paper estel·lar de seguida: l'avaluació out-of-bag.

flowchart TB
    D["Dataset d'entrenament"] --> B1[Mostra bootstrap 1]
    D --> B2[Mostra bootstrap 2]
    D --> B3[Mostra bootstrap B]
    B1 --> A1[Arbre 1]
    B2 --> A2[Arbre 2]
    B3 --> A3[Arbre B]
    A1 --> V[Votacio / Mitjana]
    A2 --> V
    A3 --> V
    V --> P[Prediccio final]

Random Forest en detall

Random Forest és bagging d'arbres més un truc extra que multiplica la diversitat: el feature bagging. En cada divisió de cada arbre, en lloc d'avaluar totes les features (com feia l'arbre de 04-03), només es considera un subconjunt aleatori (per defecte, $\sqrt{p}$ features en classificació, controlat per max_features).

Per què és tan important? Sense ell, si una feature és molt dominant — en el nostre churn, recencia ho és —, tots els arbres la triarien per a la primera divisió i acabarien assemblant-se massa: errors correlacionats, poc guany en promitjar. En vetar features a l'atzar a cada node, s'obliga els arbres a explorar camins diferents i els seus errors es descorrelacionen.

Tres regals addicionals de Random Forest:

  • OOB score (out-of-bag): cada arbre s'avalua amb el 37% d'observacions que no va veure en el seu bootstrap. Agregant aquestes prediccions s'obté una estimació de rendiment en dades no vistes gratis, sense partició de validació a part. És un complement (no un substitut) de la validació creuada de 06-03.
  • Feature importance: quant redueix la impuresa cada feature, promitjat sobre tots els arbres i totes les divisions. Un rànquing de rellevància immediat (amb matisos que veurem a Errors Comuns).
  • Robustesa gairebé sense ajust: més arbres (n_estimators) no provoca mai overfitting per si mateix — només estabilitza la mitjana; el cost és temps de còmput. Els controls que sí que regulen la complexitat de cada arbre són els ja coneguts de 04-03 (max_depth, min_samples_leaf).

Random Forest sobre el churn de MercaFresh

Repetim l'experiment estrella del mòdul 6: predir el churn amb les features RFM que vam construir al mòdul 3, comparant l'arbre solitari de 04-03 amb el bosc, amb la mateixa StratifiedKFold de 06-03.

import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

# --- Dataset de churn de MercaFresh (features RFM del modul 3) ---
rng = np.random.default_rng(42)
n = 1000
recencia = rng.gamma(2, 15, n)            # dies des de l'ultima comanda
frequencia = rng.poisson(5, n) + 1        # comandes en l'ultim trimestre
monetari = rng.gamma(3, 40, n)            # despesa trimestral en EUR
antiguitat = rng.uniform(1, 60, n)        # mesos com a client
incidencies = rng.poisson(0.5, n)         # reclamacions

# Probabilitat de churn: puja amb recencia i incidencies, baixa amb frequencia
logits = 0.05 * recencia - 0.4 * frequencia + 0.6 * incidencies - 0.01 * antiguitat - 0.5
churn = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)

X = pd.DataFrame({"recencia": recencia, "frequencia": frequencia,
                  "monetari": monetari, "antiguitat": antiguitat,
                  "incidencies": incidencies})
y = churn

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

# --- Arbre solitari (04-03) vs Random Forest ---
arbre = DecisionTreeClassifier(max_depth=5, random_state=42)
bosc = RandomForestClassifier(n_estimators=300, oob_score=True,
                              random_state=42, n_jobs=-1)

for nom, model in [("Arbre (04-03)", arbre), ("Random Forest", bosc)]:
    f1 = cross_val_score(model, X, y, cv=cv, scoring="f1")
    print(f"{nom:15s} F1: {f1.mean():.3f} +/- {f1.std():.3f}")

# OOB score i feature importance (entrenant sobre tot el conjunt)
bosc.fit(X, y)
print(f"\nOOB accuracy: {bosc.oob_score_:.3f}")
print(pd.Series(bosc.feature_importances_, index=X.columns)
        .sort_values(ascending=False).round(3))

Observacions típiques del resultat:

  • El bosc millora l'F1 mitjà de l'arbre i, sobretot, redueix la desviació entre folds: menys variància, tal com prometia la teoria. No necessitem afinar max_depth amb corbes de validació com a 06-05: els arbres del bosc creixen profunds i la mitjana absorbeix el seu sobreajustament individual.
  • L'oob_score_ dona una xifra coherent amb la CV sense haver gastat ni un fold.
  • feature_importances_ assenyala recencia, frequencia i incidencies com les variables dominants — coherent amb el que l'equip de retenció de MercaFresh ja intuïa i amb com vam generar les dades.

Nota pràctica: els arbres no necessiten escalat (04-03), així que aquí el pipeline pot prescindir del StandardScaler; si l'ensemble inclogués models sensibles a l'escala (SVM, K-NN, logística), el Pipeline del mòdul 3 torna a ser obligatori.

Voting classifiers: hard i soft

El bagging fabrica diversitat remostrejant dades; una altra via és combinar algorismes de naturalesa diferent. A 06-03 vam organitzar un torneig entre els classificadors del mòdul 4; el VotingClassifier els converteix de rivals en equip:

  • Hard voting: cada model emet la seva classe i guanya la majoria.
  • Soft voting: es promitgen les probabilitats (predict_proba) i guanya la classe amb la mitjana més alta. Sol ser millor perquè incorpora la confiança de cada model, però exigeix que tots els membres produeixin probabilitats calibrades raonablement.
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# Membres heterogenis: logistica (04-02), K-NN (04-05), Naive Bayes (04-06)
voting = VotingClassifier(
    estimators=[
        ("logreg", Pipeline([("sc", StandardScaler()),
                             ("m", LogisticRegression(max_iter=1000))])),
        ("knn",    Pipeline([("sc", StandardScaler()),
                             ("m", KNeighborsClassifier(n_neighbors=15))])),
        ("nb",     GaussianNB()),
    ],
    voting="soft",
)

f1 = cross_val_score(voting, X, y, cv=cv, scoring="f1")
print(f"Voting (soft) F1: {f1.mean():.3f} +/- {f1.std():.3f}")

Cada membre sensible a l'escala porta el seu propi pipeline amb escalat: la disciplina antifuites de 06-01 es manté dins de l'ensemble. El voting brilla quan els membres tenen rendiments semblants però fallen en clients diferents; si un model és clarament pitjor que la resta, pot llastar la votació (millor treure'l o ponderar-lo amb weights).

Stacking: un model que aprèn a combinar

La votació combina amb una regla fixa (majoria o mitjana). L'stacking va un pas més enllà: entrena un metamodel que aprèn de les dades com combinar les prediccions dels models base. Per exemple, podria aprendre que "quan K-NN i la logística discrepen, la logística sol tenir raó en clients antics".

El detall fi és que el metamodel s'ha d'entrenar amb prediccions out-of-fold (obtingudes per validació creuada interna), no amb prediccions sobre les mateixes dades en què es van entrenar els models base — altrament n'heretaria el sobreajustament, una fuita del tipus que vam aprendre a témer a 06-01. StackingClassifier ho gestiona automàticament:

from sklearn.ensemble import StackingClassifier

stacking = StackingClassifier(
    estimators=voting.estimators,               # els mateixos models base
    final_estimator=LogisticRegression(),        # metamodel senzill
    cv=5,                                        # CV interna per a les meta-features
)
f1 = cross_val_score(stacking, X, y, cv=cv, scoring="f1")
print(f"Stacking F1: {f1.mean():.3f} +/- {f1.std():.3f}")

L'stacking és l'arma final de les competicions (sovint amb diverses capes de metamodels), però en projectes reals el seu guany sobre un bon voting o un bon bosc sol ser petit i el seu cost de manteniment, alt. Usa'l quan cada dècima de mètrica valgui diners.

Boosting: la família seqüencial (presentació)

Les tècniques anteriors entrenen els models en paral·lel i independents. El boosting els entrena en seqüència: cada nou model es concentra en els exemples (o errors) en què els anteriors van fallar. En lloc de reduir variància promitjant models forts, el boosting redueix biaix sumant molts models deliberadament febles (arbres petitíssims) que es corregeixen els uns als altres.

Aquesta inversió de filosofia té conseqüències importants (més potència, més risc d'overfitting, impossible paral·lelitzar la seqüència) i ha produït els algorismes més dominants en dades tabulars: Gradient Boosting, XGBoost, LightGBM. És matèria de la pròxima lliçó (07-03); aquí n'hi ha prou de situar-lo al mapa.

Taula comparativa de les tres famílies

Bagging / Random Forest Boosting Stacking
Entrenament Paral·lel, independent Seqüencial Paral·lel + metamodel
Models base Forts (arbres profunds), homogenis Febles (arbres petits), homogenis Heterogenis
Redueix sobretot Variància Biaix Tots dos (segons els membres)
Risc d'overfitting Baix Mitjà-alt (necessita frens) Mitjà (fuites si la CV interna es fa malament)
Sensibilitat als hiperparàmetres Baixa Alta Mitjana
Exemple sklearn RandomForestClassifier GradientBoostingClassifier (07-03) StackingClassifier

El preu: interpretabilitat

L'arbre únic de 04-03 tenia una virtut que cap ensemble no conserva: es podia dibuixar i explicar — "si recència > 30 dies i freqüència < 3 comandes, risc alt". Un bosc de 300 arbres és una caixa molt més opaca: ningú no pot seguir 300 votacions alhora.

Els pal·liatius existeixen — feature_importances_ dona un rànquing global, i a 07-03 mencionarem SHAP per a explicacions per client —, però la decisió de fons és de negoci: si l'equip de retenció de MercaFresh necessita justificar davant de direcció per què es truca a cada client, potser un arbre interpretable amb F1 0.78 val més que un bosc opac amb 0.82. Si l'únic que importa és encertar, l'ensemble guanya. Aquest compromís rendiment-interpretabilitat reapareixerà a les consideracions ètiques de 08-04.

Errors Comuns i Consells

  • Combinar models gairebé idèntics. Tres logístiques amb llavors diferents no formen multitud: sense diversitat no hi ha cancel·lació d'errors. Barreja famílies d'algorismes o usa remostreig/feature bagging.
  • Tractar n_estimators com un control de complexitat. A Random Forest, més arbres no sobreajusta: només estabilitza (i encareix). Els frens de complexitat són els de cada arbre (max_depth, min_samples_leaf). En boosting, compte: allà n_estimators que augmenta el risc d'overfitting, com veurem a 07-03.
  • Fiar-se cegament de feature_importances_. La importància per impuresa afavoreix les features contínues o d'alta cardinalitat i es reparteix arbitràriament entre features correlacionades. Contrasta amb permutation_importance sobre dades de validació abans de treure conclusions de negoci.
  • Usar soft voting amb models sense probabilitats fiables. Una SVM sense probability=True no dona predict_proba, i alguns models donen probabilitats mal calibrades que distorsionen la mitjana. Verifica els membres abans de votar.
  • Avaluar l'stacking amb la mateixa CV que genera les seves meta-features sense cura. Deixa que StackingClassifier gestioni la seva CV interna i avalua el conjunt complet amb una CV externa (com hem fet aquí): és la manera neta d'evitar fuites.
  • Consell: comença sempre per RandomForestClassifier amb paràmetres per defecte com a "baseline forta". És difícil d'espatllar i et diu ràpidament quant de senyal hi ha a les dades; després decideix si val la pena alguna cosa més sofisticada.

Exercicis

  1. El valor de la descorrelació. Simula 500 prediccions binàries d'un "comitè" de 25 classificadors que encerten individualment el 65% (usa rng.random((500, 25)) < 0.65 com a matriu d'encerts independents) i calcula l'encert de la votació per majoria. Repeteix fent que 20 dels 25 siguin còpies exactes del mateix classificador. Compara tots dos resultats.
  2. Corba d'estabilització del bosc. Sobre el churn de MercaFresh, avalua RandomForestClassifier amb n_estimators a [1, 5, 10, 25, 50, 100, 200, 400] usant la CV estratificada de la lliçó. Dibuixa l'F1 mitjà i la desviació estàndard en funció del nombre d'arbres. A partir de quants arbres deixa de compensar afegir-ne més? S'assembla aquesta corba a una corba d'overfitting com les de 06-05?
  3. Torneig d'ensembles. Compara amb la mateixa CV: (a) el millor model individual del mòdul 4 que vas obtenir a 06-03, (b) VotingClassifier soft amb tres models heterogenis, (c) RandomForestClassifier(n_estimators=300). Presenta la taula F1 mitjà ± desviació i decideix raonadament quin desplegaria MercaFresh, tenint en compte també la interpretabilitat.

Solucions

  1. Amb 25 classificadors independents al 65%, la majoria (≥13 encerts de 25) encerta al voltant del 93-94% dels casos: la multitud polvoritza l'individu. Amb 20 còpies idèntiques, la "votació" la decideix a la pràctica aquell únic classificador repetit i l'encert torna a ~65%. Moralitat numèrica de tota la lliçó: sense errors descorrelacionats, l'ensemble és teatre.
  2. L'F1 mitjà puja amb força fins a ~50-100 arbres i després s'aplana; la desviació entre folds es redueix de manera contínua. A partir de ~200 arbres les millores són soroll i només pagues còmput. No és una corba d'overfitting: a diferència de max_depth a 06-05, aquí no hi ha tram descendent per molts arbres que afegeixis — la corba convergeix, no es capgira.
  3. Resultat típic: el bosc encapçala la taula, el voting queda a prop (de vegades empata si els seus membres són bons i diversos) i el millor model individual queda per darrere amb més desviació entre folds. La decisió de desplegament no és automàtica: amb diferències de poques centèsimes d'F1, l'argument d'interpretabilitat pot inclinar la balança cap al model simple; amb diferències clares, el bosc guanya i les importàncies de features serveixen com a explicació global per a negoci.

Conclusió

Has vist per què la combinació de models funciona — errors descorrelacionats que es cancel·len, variància que es desploma en promitjar — i les tres maneres de fabricar-la: bagging (bootstrap + agregació, culminant en Random Forest amb el seu feature bagging, el seu OOB score i les seves importàncies), voting i stacking (diversitat per heterogeneïtat d'algorismes), i boosting, la família seqüencial que hem deixat presentada. Sobre el churn de MercaFresh, el bosc va superar l'arbre solitari de 04-03 amb menys variància entre folds i gairebé sense ajust, a canvi de sacrificar la interpretabilitat de l'arbre dibuixable. Queda pendent el membre més potent de la família: el boosting, on cada model neix per corregir els errors de l'anterior i que avui domina el machine learning sobre dades tabulars. És el tema de la lliçó següent: Gradient Boosting.

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