Ensemble Learning


Articoli di approfondimento

I metodi di ensemble vengono comunemente utilizzati per migliorare la precisione predittiva combinando le previsioni di più modelli di apprendimento automatico. Quando questi metodi vengono ideati per l'idea era quella di combinare i cosiddetti predittori "deboli" (Breiman Leo, "Bagging Predictors" (1996)). Tuttavia, un approccio più moderno è quello di creare un insieme di una collezione ben scelta di modelli forti ma diversi (Van der Laan, Polley e Hubbard 2007).
La costruzione di potenti modelli di ensemble ha molti parallelismi con la creazione di team di persone di successo nel mondo degli affari, della scienza, della politica e dello sport. Ogni membro del team fornisce un contributo significativo inoltre le debolezze e le distorsioni individuali sono compensate dai punti di forza degli altri membri.
Il tipo più semplice di ensemble è la media non ponderata delle previsioni dei modelli che formano una serie di modelli. In una media non ponderata, ogni modello assume lo stesso peso quando viene costruito un modello di ensemble. Più in generale, si può pensare di utilizzare le medie ponderate cosi da poter conferire più peso a quei modelli migliori. Ma un approccio ancora migliore potrebbe essere quello di stimare i pesi in modo più intelligente usando un altro livello di stima attraverso un algoritmo di classificazione. Questo approccio è chiamato stacking dei modelli.
Lo stacking dei modelli è un metodo di ensemble efficiente in cui le previsioni, generate utilizzando algoritmi di machine learning, vengono utilizzate come input in un algoritmo di apprendimento di secondo livello. Questo algoritmo di secondo livello viene addestrato per combinare in modo ottimale le previsioni del modello per formare una nuova serie di previsioni. Ad esempio, quando la regressione lineare viene utilizzata come modellazione di secondo livello, stima questi pesi minimizzando gli errori minimi quadrati. Tuttavia, la modellazione del secondo livello non è limitata ai soli modelli lineari; la relazione tra i predittori può essere più complessa, aprendo le porte all'impiego di altri algoritmi di apprendimento automatico.
L'overfitting è un problema particolarmente importante nello stacking dei modelli, perché vengono combinati tanti predittori che prevedono tutti lo stesso target questo può essere parzialmente causato dalla collinearità tra i predittori. Una possibile soluzione a questo problema è quella di generare una serie diversificata di modelli con vari metodi (come alberi decisionali, reti neurali e/o altri metodi) oppure utilizzare diversi tipi di dati così da cercare di inserire nel modello finale informazioni nuove non presenti nei modelli precedenti.
L'applicazione di modelli sovrapposti ai problemi dei big data nel mondo reale può produrre una maggiore accuratezza e robustezza delle previsioni rispetto ai singoli modelli. L'approccio dello stacking dei modelli sposta l'obbiettivo della modellizzazione dalla ricerca del modello migliore a quello di trovare una raccolta di modelli complementari davvero validi. Naturalmente, questo metodo comporta costi computazionali aggiuntivi sia perché è necessario addestrare un gran numero di modelli sia perché è necessario utilizzare la convalida incrociata per evitare un eccesso di adattamento.

Alcuni esempi dell’utilizzo di questo metodo sono: bagging, random forests e boosting che utilizzano come weak learners molti alberi che vengono combinatiper avere un unica previsione.

Ovviamente questi metodi aumentano i tempi computazionali e riducono interpretabilità.

Model stacking

Staked regression algorithm

  1. Creiamo \(\hat{f}^{-i}_l(x_i)\) per fare previsione con \(x_i\) usando un modello \(l\) fittato sul training data dove \(i\) si riferisce alle \(i\)-esime osservazioni del training \((x_i,y_i)\)

  2. Ottengo i pesi con la stima dei minimi quadrati: \[\hat{w}_1,\ldots,\hat{w}_L = \underset{w_1,\ldots,w_L}{\arg \min} \sum_{i=1}^{n} \left[ y_i - \sum_{l=1}^{L} w_l \hat{f}^{-i}_l(x_i) \right]^2\]

  3. Infine faccio le previsioni sul test set: \[\hat{f}_{\mathrm{stack}}(x^*_i) = \sum_{l=1}^{L} \hat{w}_l \hat{f}_l(x^*_i), \quad i=1,\ldots,m\]

Model stacking algorithm

  1. Partiziono il training data in \(k\) divisioni(\(k-folds\)) \(\mathcal{F}_1,\ldots,\mathcal{F}_K\)

  2. Per ogni divisione \(\mathcal{F}_k\), \(k=1,\ldots,K\) , unisco gli altri \(K-1\) insiemi di dati e li utilizzo per addestrare il modello, quindi:
    • faccio un ciclo For per \(l=1,\ldots,L\), addestro gli \(l\)-esimi modelli sul training e poi faccio la previsione sulla divione \(k\) \(\mathcal{F}_k\). Associo queste previsioni \[z_i = (\hat{f}_1^{-\mathcal{F}_k}(x_i),\ldots,\hat{f}_L^{-\mathcal{F}_k}(x_i)), \quad i \in \mathcal{F}_k\]
  3. For \(l=1,\ldots,L\), fit the \(l\)th model to the full training data and make predictions on the test data. Store these predictions \[z^*_i = (\hat{f}_1(x^*_i),\ldots,\hat{f}_L(x^*_i)), \quad i=1,\ldots,m\]

  4. Fit the stacking model \(\hat{f}_{\mathrm{stack}}\) using \[(y_1,z_1),\ldots, (y_n,z_n)\]

  5. Make final predictions \(\hat{y}^{*}_i = \hat{f}_{\mathrm{stack}}(z^*_i)\), \(i=1,\ldots,m\)


Boston data

rm(list=ls())
library(MASS)
set.seed(123)
istrain = rbinom(n=nrow(Boston),size=1,prob=0.5)>0
train <- Boston[istrain,]
n=nrow(train)
test = Boston[!istrain,-14]
test.y = Boston[!istrain,14]
m=nrow(test)

The training and test data are \[(x_1,y_1),\ldots,(x_n,y_n),\quad (x^*_1,y^*_1),\ldots,(x^*_m,y^*_m)\] with \(n=235\) and \(m=271\) for the Boston data set.

The response variable is medv, and the predictor variables are crim, zn, indus, chas, nox, rm, age, dis, rad, tax, ptratio, black, lstat


Staked regression con due modelli

fit1 = lm(medv ~ ., train)
library(rpart)
fit2 = rpart(medv ~ ., train)

  1. \(\hat{f}^{-i}_l(x_i)\) corrisponde alla previsione dell'i-esima osservazione del training set fatta sui dati di quest'ultimo togliendo ovviamente \((x_i,y_i)\) e usando il modello l-esimo.

  2. Calcoliamo i pesi: \[\hat{w}_1,\ldots,\hat{w}_L = \underset{w_1,\ldots,w_L}{\arg \min} \sum_{i=1}^{n} \left[ y_i - \sum_{l=1}^{L} w_l \hat{f}^{-i}_l(x_i) \right]^2\]

  3. Facciamo le previsioni sul test set: \[\hat{f}_{\mathrm{stack}}(x^*_i) = \sum_{l=1}^{L} \hat{w}_l \hat{f}_l(x^*_i), \quad i=1,\ldots,m\]
  4. Calcola l'errore quadratico medio per il test set \[\mathrm{MSE}^{\mathrm{stack}}_{\mathrm{Te}} = \frac{1}{m}\sum_{i=1}^{m} (y^*_i - \hat{f}_{\mathrm{stack}}(x^*_i) )^2\] e compararlo con\[\mathrm{MSE}^l_{\mathrm{Te}} = \frac{1}{m}\sum_{i=1}^{m} (y^*_i - \hat{f}_{l}(x^*_i) )^2, \quad l=1,\ldots,L.\]


## MSE stack:  21.95002
## MSE lm:  28.83767
## MSE rpart:  24.39206







Codice in R utile per fare ensemble

caretEnsemble

# K fold CV for regression
KCV <- trainControl(method="cv", 
                    number=K,
                    savePredictions="final",
                    index=createResample(train$y, K)
                    )
# list of models fit1 and fit2
List <- caretList(y ~. ,
                  data=train,
                  trControl=KCV,
                  methodList=c("fit1","fit2")
                  )
# ensemble fit
fit.ensemble <- caretEnsemble(List, metric="RMSE")
summary(fit.ensemble)

Boston data

# libraries
rm(list=ls())
library(MASS)
library(caret)
library(caretEnsemble)

# import data
set.seed(123)
split <- createDataPartition(y=Boston$medv, p = 0.5, list=FALSE)
train <- Boston[split,]
test = Boston[-split,-14]
test.y = Boston[-split,14]
nrow(test)

# cross-validation settings
K = 10
my_control <- trainControl(
  method="cv",
  number=K,
  savePredictions="final",
  index=createResample(train$medv, K)
  )

Library of models

model_list <- caretList(
  medv~., data=train,
  methodList=c("lm","ctree"), 
  tuneList=list(
    rf=caretModelSpec(method="rf", tuneLength=3)
  ),
  trControl=my_control
)

xyplot(resamples(model_list))
modelCor(resamples(model_list))

Ensemble

greedy_ensemble <- caretEnsemble(
  model_list, 
  metric="RMSE"
  )
summary(greedy_ensemble)

Test MSE

yhats <- lapply(model_list, predict, newdata=test)
lapply(yhats, function(yhat) mean((yhat - test.y)^2) )

yhat.en <- predict(greedy_ensemble, newdata=test)
mean((yhat.en - test.y)^2)