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
- Per què combinar models funciona
- Bagging: bootstrap + agregació
- Random Forest en detall
- Random Forest sobre el churn de MercaFresh
- Voting classifiers: hard i soft
- Stacking: un model que aprèn a combinar
- Boosting: la família seqüencial (presentació)
- Taula comparativa de les tres famílies
- 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:
- Cada estimador és millor que l'atzar (encara que sigui per poc).
- 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:
- 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.
- Entrenament: entrena un model (típicament un arbre profund, sense podar) en cada mostra.
- 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_depthamb 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_assenyalarecencia,frequenciaiincidenciescom 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_estimatorscom 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_estimatorssí 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 ambpermutation_importancesobre dades de validació abans de treure conclusions de negoci. - Usar soft voting amb models sense probabilitats fiables. Una SVM sense
probability=Trueno donapredict_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
StackingClassifiergestioni 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
RandomForestClassifieramb 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
- 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.65com 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. - Corba d'estabilització del bosc. Sobre el churn de MercaFresh, avalua
RandomForestClassifierambn_estimatorsa[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? - Torneig d'ensembles. Compara amb la mateixa CV: (a) el millor model individual del mòdul 4 que vas obtenir a 06-03, (b)
VotingClassifiersoft 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
- 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.
- 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_deptha 06-05, aquí no hi ha tram descendent per molts arbres que afegeixis — la corba convergeix, no es capgira. - 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
- Què és el Machine Learning?
- Història i evolució del Machine Learning
- Tipus de Machine Learning
- Aplicacions del Machine Learning
- El flux de treball d'un projecte de Machine Learning
Mòdul 2: Fonaments d'Estadística i Probabilitat
- Conceptes bàsics d'estadística
- Distribucions de probabilitat
- Correlació i covariància
- Inferència estadística
- Teorema de Bayes
Mòdul 3: Preprocessament de Dades
- Neteja de dades
- Gestió de dades mancants
- Transformació de dades
- Codificació de variables categòriques
- Normalització i estandardització
- Enginyeria de característiques
Mòdul 4: Algorismes de Machine Learning Supervisat
- Regressió lineal
- Regressió logística
- Arbres de decisió
- Màquines de suport vectorial (SVM)
- K veïns més propers (K-NN)
- Naive Bayes
- Xarxes neuronals
Mòdul 5: Algorismes de Machine Learning No Supervisat
- Clustering: K-means
- Clustering jeràrquic
- Anàlisi de components principals (PCA)
- Anàlisi d'agrupament DBSCAN
- Visualització de dades amb t-SNE i UMAP
Mòdul 6: Avaluació i Validació de Models
- Divisió de dades: entrenament, validació i prova
- Mètriques d'avaluació
- Validació creuada
- Corba ROC i AUC
- Overfitting i underfitting
Mòdul 7: Tècniques Avançades i Optimització
- Regularització: Ridge, Lasso i Elastic Net
- Ensemble Learning
- Gradient Boosting
- Xarxes neuronals profundes (Deep Learning)
- Optimització d'hiperparàmetres
Mòdul 8: Implementació i Desplegament de Models
- Frameworks i biblioteques populars
- Implementació de models en producció
- Manteniment i monitoratge de models
- Consideracions ètiques i de privadesa
Mòdul 9: Projectes Pràctics
- Projecte 1: Predicció de preus d'habitatges
- Projecte 2: Classificació d'imatges
- Projecte 3: Anàlisi de sentiments a les xarxes socials
- Projecte 4: Detecció de fraus
- Projecte 5: Segmentació de clients
