Obróbka danych

żeby zadziałał gradient boosting z gbm

  1. zmienna \(y\) jako zmienna numeryczna, \(X\) jako zmienne czynnikowe (bez wyrzucania stałej kolumny powinno zadziałać)

  2. zmienna \(y\) jako zmienna numeryczna, \(X\) przekodowane na zmienne binarne (funkcja dummyVars nie przyjmie stałej kolumny)

Przykład jak szybko przekodować (one-hot encoder):

x1 <- factor(c("a", "a", "b", "c", "b"), levels=c("a", "b", "c"))
x2 <- factor(c("dobrze", "dobrze", "srednio", "srednio", "srednio"), levels=c("dobrze", "srednio"))
df <- data.frame(x1, x2)
df
##   x1      x2
## 1  a  dobrze
## 2  a  dobrze
## 3  b srednio
## 4  c srednio
## 5  b srednio
library(caret)
dmy <- dummyVars(" ~ .", data = df)
df_nowe <- data.frame(predict(dmy, newdata = df))
df_nowe
##   x1.a x1.b x1.c x2.dobrze x2.srednio
## 1    1    0    0         1          0
## 2    1    0    0         1          0
## 3    0    1    0         0          1
## 4    0    0    1         0          1
## 5    0    1    0         0          1

żeby zadziałał xgboost

  1. zmienna \(y\) jako zmienna numeryczna, \(X\) przekodowane na zmienne binarne, a potem przekształcone na macierz (xgb.DMatrix lub po prostu matrix, as.matrix)

Dane

Skorzystamy z danych Boston. Zmienna medv to mediana wartości domu i to będziemy modelować w zależności od innych zmiennych (przekodowałam na 1 - drogie domy, 0 - nie). Od razu robię podział na trening i zbiór testowy.

library(MASS)
df <- Boston
df$medv <- ifelse(df$medv < 25, "tani", "drogi")
df$medv <- make.names(df$medv)
df$medv <- factor(df$medv, levels=c("drogi", "tani"))

set.seed(123)
ind.train <- sample(1:nrow(Boston), size=350, replace=FALSE)
ind.test  <- setdiff(1:nrow(Boston), ind.train)

df_train <- df[ind.train,]
df_test <- df[ind.test,]

str(df)
## 'data.frame':    506 obs. of  14 variables:
##  $ crim   : num  0.00632 0.02731 0.02729 0.03237 0.06905 ...
##  $ zn     : num  18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
##  $ indus  : num  2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
##  $ chas   : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ nox    : num  0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
##  $ rm     : num  6.58 6.42 7.18 7 7.15 ...
##  $ age    : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
##  $ dis    : num  4.09 4.97 4.97 6.06 6.06 ...
##  $ rad    : int  1 2 2 3 3 3 5 5 5 5 ...
##  $ tax    : num  296 242 242 222 222 222 311 311 311 311 ...
##  $ ptratio: num  15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
##  $ black  : num  397 397 393 395 397 ...
##  $ lstat  : num  4.98 9.14 4.03 2.94 5.33 ...
##  $ medv   : Factor w/ 2 levels "drogi","tani": 2 2 1 1 1 1 2 1 2 2 ...

Komitety różnych klasyfikatorów

Pakiet caret i caretEnsemble

library(caret)
library(caretEnsemble)

Ustawienia opcji:

  • "repeatedcv" oznacza, że kroswalidacja number-krotna zostanie powtórzona repeats razy
  • classProbs czy prawdopodobieństwo klas ma być liczone
  • savePredictions czy wszystkie predykcje mają być zapisywane, czy tylko te dla najlepszego modelu
  • summaryFunction = twoClassSummary pozwoli jako optymalizowaną miarę zastosować AUC
control <- trainControl(method="repeatedcv", number=5, repeats=5, 
                        classProbs=TRUE, savePredictions='all',
                        summaryFunction = twoClassSummary)

Jakie modele będziemy łączyć:

algorithmList <- c('rpart', 'glm', 'lda')

Budujemy modele wymienione w algorithmList według control, optymalizujemy AUC (metric='ROC'). Dodatkowo wyliczana będzie czułość i specyficzność dla punktu odcięcia 0.5.

set.seed(123)
stack_models <- caretList(medv~., data=df_train, trControl=control, methodList=algorithmList, metric='ROC')

Funkcja resamples pozwala zebrać i podsumować wyniki wszystkich modeli łącznie:

stacking_results <- resamples(stack_models)

Na ile modele są ze sobą skorelowane:

summary(stacking_results)
## 
## Call:
## summary.resamples(object = stacking_results)
## 
## Models: rpart, glm, lda 
## Number of resamples: 25 
## 
## ROC 
##            Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## rpart 0.7532051 0.8002137 0.8872863 0.8632692 0.9086538 0.9476496    0
## glm   0.8792735 0.9273504 0.9433761 0.9421795 0.9615385 0.9882479    0
## lda   0.8579060 0.9081197 0.9326923 0.9261538 0.9497863 0.9764957    0
## 
## Sens 
##            Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## rpart 0.5000000 0.6111111 0.7222222 0.7333333 0.8333333 0.9444444    0
## glm   0.6111111 0.7777778 0.7777778 0.7977778 0.8333333 0.9444444    0
## lda   0.5000000 0.6111111 0.7222222 0.6933333 0.7222222 0.8888889    0
## 
## Spec 
##            Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## rpart 0.8653846 0.9230769 0.9423077 0.9376923 0.9615385 1.0000000    0
## glm   0.8461538 0.9230769 0.9423077 0.9369231 0.9423077 0.9807692    0
## lda   0.9038462 0.9423077 0.9615385 0.9623077 0.9807692 1.0000000    0
dotplot(stacking_results)

Budowa komitetów:

stackControl <- trainControl(method="repeatedcv", number=5, repeats=5, 
                             savePredictions='all', classProbs=TRUE,
                             summaryFunction = twoClassSummary)

Komitet 1 (predykcje modeli połączone modelem liniowym)

model.ensemble <- caretEnsemble(stack_models, trControl=stackControl, metric = "ROC")
plot(model.ensemble)

summary(model.ensemble)
## The following models were ensembled: rpart, glm, lda 
## They were weighted: 
## 3.5438 -1.8746 -3.4883 -1.9204
## The resulting ROC is: 0.9507
## The fit for each individual model on the ROC is: 
##  method       ROC      ROCSD
##   rpart 0.8632692 0.06100930
##     glm 0.9421795 0.02724764
##     lda 0.9261538 0.03601885

Komitet 2 (predykcje modeli połączone gradient boostingiem)

library(gbm)
model.gbm <- caretStack(stack_models, method="gbm", trControl=stackControl, metric = "ROC",  verbose = FALSE)
plot(model.gbm)

summary(model.gbm)

##         var  rel.inf
## glm     glm 54.28523
## lda     lda 27.05308
## rpart rpart 18.66169

Wyniki:

predykcje <- data.frame(predict(stack_models, df_test))
predykcje[,"komitet1"] <- predict(model.ensemble, newdata=df_test, type = "prob")
predykcje[,"komitet2"] <- predict(model.gbm, newdata=df_test, type = "prob")

##       rpart       glm       lda  komitet1  komitet2
## 1 0.9058062 0.9385965 0.9381788 0.9490393 0.9417293