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)
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.