Bagging

A agregação de bootstrap, também chamada de bagging é projetada para melhorar a estabilidade e a precisão dos algoritmos de regressão e classificação. Por meio da média do modelo, o bagging ajuda a reduzir a variação e minimizar o sobreajuste. Embora seja normalmente aplicado a métodos de árvore de decisão, pode ser usado com qualquer tipo de método. Bibliotecas que vamos precisar usar para realizar o experimento:

# Helper packages
library(dplyr)       # for data wrangling
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)     # for awesome plotting
library(doParallel)  # for parallel backend to foreach
## Loading required package: foreach
## Loading required package: iterators
## Loading required package: parallel
library(foreach)     # for parallel processing with for loops

# Modeling packages
library(caret)       # for general model fitting
## Loading required package: lattice
library(rpart)       # for fitting decision trees
library(ipred)       # for fitting bagged decision trees

A função bagging() vem do pacote ipred e usamos nbagg para controlar quantas iterações incluir no modelo empacotado e coob = TRUE indica o uso da taxa de erro OOB (Out of bag). Por padrão, bagging() usa rpart::rpart() para decision tree base learners, mas outros base learners estão disponíveis. Como o bagging apenas agrega um base learner, podemos ajustar os parâmetros do base learner normalmente. Aqui, passamos parâmetros para rpart() com o parâmetro control e construímos árvores profundas (sem poda) que requerem apenas duas observações em um nó para serem divididas.
Para ilustrar, vamos utilizar o dataset AmesHousing

ames <- AmesHousing::make_ames()
head(ames)
## Warning: `...` is not empty.
## 
## We detected these problematic arguments:
## * `needs_dots`
## 
## These dots only exist to allow future extensions and should be empty.
## Did you misspecify an argument?
## # A tibble: 6 x 81
##   MS_SubClass MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##   <fct>       <fct>            <dbl>    <int> <fct>  <fct> <fct>    
## 1 One_Story_~ Resident~          141    31770 Pave   No_A~ Slightly~
## 2 One_Story_~ Resident~           80    11622 Pave   No_A~ Regular  
## 3 One_Story_~ Resident~           81    14267 Pave   No_A~ Slightly~
## 4 One_Story_~ Resident~           93    11160 Pave   No_A~ Regular  
## 5 Two_Story_~ Resident~           74    13830 Pave   No_A~ Slightly~
## 6 Two_Story_~ Resident~           78     9978 Pave   No_A~ Slightly~
## # ... with 74 more variables: Land_Contour <fct>, Utilities <fct>,
## #   Lot_Config <fct>, Land_Slope <fct>, Neighborhood <fct>, Condition_1 <fct>,
## #   Condition_2 <fct>, Bldg_Type <fct>, House_Style <fct>, Overall_Qual <fct>,
## #   Overall_Cond <fct>, Year_Built <int>, Year_Remod_Add <int>,
## #   Roof_Style <fct>, Roof_Matl <fct>, Exterior_1st <fct>, Exterior_2nd <fct>,
## #   Mas_Vnr_Type <fct>, Mas_Vnr_Area <dbl>, Exter_Qual <fct>, Exter_Cond <fct>,
## #   Foundation <fct>, Bsmt_Qual <fct>, Bsmt_Cond <fct>, Bsmt_Exposure <fct>,
## #   BsmtFin_Type_1 <fct>, BsmtFin_SF_1 <dbl>, BsmtFin_Type_2 <fct>,
## #   BsmtFin_SF_2 <dbl>, Bsmt_Unf_SF <dbl>, Total_Bsmt_SF <dbl>, Heating <fct>,
## #   Heating_QC <fct>, Central_Air <fct>, Electrical <fct>, First_Flr_SF <int>,
## #   Second_Flr_SF <int>, Low_Qual_Fin_SF <int>, Gr_Liv_Area <int>,
## #   Bsmt_Full_Bath <dbl>, Bsmt_Half_Bath <dbl>, Full_Bath <int>,
## #   Half_Bath <int>, Bedroom_AbvGr <int>, Kitchen_AbvGr <int>,
## #   Kitchen_Qual <fct>, TotRms_AbvGrd <int>, Functional <fct>,
## #   Fireplaces <int>, Fireplace_Qu <fct>, Garage_Type <fct>,
## #   Garage_Finish <fct>, Garage_Cars <dbl>, Garage_Area <dbl>,
## #   Garage_Qual <fct>, Garage_Cond <fct>, Paved_Drive <fct>,
## #   Wood_Deck_SF <int>, Open_Porch_SF <int>, Enclosed_Porch <int>,
## #   Three_season_porch <int>, Screen_Porch <int>, Pool_Area <int>,
## #   Pool_QC <fct>, Fence <fct>, Misc_Feature <fct>, Misc_Val <int>,
## #   Mo_Sold <int>, Year_Sold <int>, Sale_Type <fct>, Sale_Condition <fct>,
## #   Sale_Price <int>, Longitude <dbl>, Latitude <dbl>

Para separar os dados em treino e teste, vamos utilizar o pacote rsample

library(rsample)
set.seed(321)
split_1 <- initial_split(ames, prop = 0.7)
ames_train <- training(split_1)
ames_test <- testing(split_1)

Bagging

Agora que já separamos nossos dados em treino e teste, vamos implementar o bagging

ames_bag0 <- bagging(
  formula = Sale_Price ~.,
  data = ames_train,
  nbagg = 100,
  coob = TRUE,
  control = rpart.control(minsplit = 2 , cp = 0 )
)

Vizualisando o resultado

ames_bag0
## 
## Bagging regression trees with 100 bootstrap replications 
## 
## Call: bagging.data.frame(formula = Sale_Price ~ ., data = ames_train, 
##     nbagg = 100, coob = TRUE, control = rpart.control(minsplit = 2, 
##         cp = 0))
## 
## Out-of-bag estimate of root mean squared error:  26557.21

Uma coisa a notar é que, normalmente, quanto mais árvores, melhor. À medida que adicionamos mais árvores, estamos calculando a média de mais árvores de decisão de alta variância. Logo no início, vemos uma redução dramática na variância (e, portanto, nosso erro), mas, eventualmente, o erro normalmente se estabilizará, sinalizando que um número adequado de árvores foi alcançado. Freqüentemente, precisamos de apenas 50–100 árvores para estabilizar o erro (em outros casos, podemos precisar de 500 ou mais). Para os dados do Ames, vemos que o erro está se estabilizando com pouco mais de 100 árvores, então provavelmente não obteremos muitas melhorias simplesmente ensacando mais árvores.