Materiały ze spotkania eRementarz #4: uczenie maszynowe z pakietem mlr

Pierwsze tegoroczne spotkanie STWUR-a poprowadził Mateusz Staniak, a zorganizował Jarosław Chilimoniuk. Zachęcamy do zapoznania się z materiałami.

Materiały

[https://github.com/STWUR/eRementarz4]

Work flow

  • Przygotowanie danych (preprocessing)
  • Zadanie (task)
  • Wybór modelu (benchmark)
  • Strojenie parametrów (tuning)
  • Ocena modelu

Dane

library(mlr)
library(dplyr)
library(ggplot2)

mieszkania <- read.csv("https://raw.githubusercontent.com/STWUR/STWUR-2017-06-07/master/data/mieszkania_dane.csv") %>%
  filter(!is.na(cena_m2))

head(mieszkania)
##   n_pokoj metraz cena_m2  rok pietro pietro_maks   dzielnica
## 1       3  89.00    5270 2007      2           2      Krzyki
## 2       4 163.00    6687 2002      2           2   Psie Pole
## 3       3  52.00    6731 1930      1           2 Śródmieście
## 4       4  95.03    5525 2016      1           2      Krzyki
## 5       4  88.00    5216 1930      3           4 Śródmieście
## 6       2  50.00    5600 1915      3           4      Krzyki

Przygotowanie danych

Typowe zadania:

  • standaryzacja danych - normalizeFeatures
  • łączenie mało licznych poziomów zmiennych jakościowych - mergeSmallFactorLevels
  • wybranie części obserewacji - subsetTask
  • imputacja danych brakujących - impute
  • i inne…

Zadania (task)

  • Obsługiwane klasy problemów
makeClassifTask()
makeRegrTask()
makeClusterTask()
makeCostSensTask()
makeMultilabelTask()
makeSurvTask()

Nasz problem

m2_task <- makeRegrTask(id = "mieszkanie",
                        data = mieszkania,
                        target = "cena_m2")
  • Alternatywnie
m2_task_ndz <- makeRegrTask(id = "mieszkanie_ndz",
                            data = select(mieszkania, -dzielnica),
                            target = "cena_m2")

Uwaga

  • fixup.data = "warn": czyszczenie danych (aktualnie tylko usuwanie pustych poziomów)
  • check.data = TRUE: sprawdzanie poprawności danych (aktualnie: NA i puste poziomy zmiennej odpowiedzi)

Metody uczenia (learner)

  • Ogromna liczba dostępnych metod
listLearners(obj = "regr")[1:6, c(1, 3:4)]
##              class  short.name package
## 1     regr.cforest     cforest   party
## 2       regr.ctree       ctree   party
## 3    regr.cvglmnet    cvglmnet  glmnet
## 4 regr.featureless featureless     mlr
## 5     regr.gausspr     gausspr kernlab
## 6         regr.glm         glm   stats
  • Jak radzi sobie zwykła regresja liniowa?
reg_lm <- makeLearner("regr.lm")
  • A jak inne popularne metody?
reg_rf <- makeLearner("regr.randomForest")
reg_nnet <- makeLearner("regr.nnet")
  • Inaczej:
lrns <- makeLearners(c("lm", "randomForest", "nnet"),
                    type = "regr")
  • takie wywołania tworzą obiekty typu Learner
  • metody są zaimplementowane w odpowiednich pakietach - mlr jest nakładką
  • różne metody - różne wsparcie dla brakujących wartości, wag itd

  • Uwaga: w ten sposób wszystkie hiperparametry mają ustawione wartości domyślne

Informacje o metodzie

getLearnerProperties(reg_rf)
## [1] "numerics" "factors"  "ordered"  "se"       "oobpreds" "featimp"
getLearnerParamSet(reg_rf)
##                      Type  len       Def                 Constr Req
## ntree             integer    -       500               1 to Inf   -
## se.ntree          integer    -       100               1 to Inf   Y
## se.method        discrete    - jackknife bootstrap,jackknife,sd   Y
## se.boot           integer    -        50               1 to Inf   -
## mtry              integer    -         -               1 to Inf   -
## replace           logical    -      TRUE                      -   -
## strata            untyped    -         -                      -   -
## sampsize    integervector <NA>         -               1 to Inf   -
## nodesize          integer    -         5               1 to Inf   -
## maxnodes          integer    -         -               1 to Inf   -
## importance        logical    -     FALSE                      -   -
## localImp          logical    -     FALSE                      -   -
## nPerm             integer    -         1            -Inf to Inf   -
## proximity         logical    -     FALSE                      -   -
## oob.prox          logical    -         -                      -   Y
## do.trace          logical    -     FALSE                      -   -
## keep.forest       logical    -      TRUE                      -   -
## keep.inbag        logical    -     FALSE                      -   -
##             Tunable Trafo
## ntree          TRUE     -
## se.ntree       TRUE     -
## se.method      TRUE     -
## se.boot        TRUE     -
## mtry           TRUE     -
## replace        TRUE     -
## strata        FALSE     -
## sampsize       TRUE     -
## nodesize       TRUE     -
## maxnodes       TRUE     -
## importance     TRUE     -
## localImp       TRUE     -
## nPerm          TRUE     -
## proximity     FALSE     -
## oob.prox      FALSE     -
## do.trace      FALSE     -
## keep.forest   FALSE     -
## keep.inbag    FALSE     -

Ustawianie hiperparametrów

  • przy tworzeniu learnera:
reg_rf2 <- makeLearner("regr.randomForest",
                       par.vals = list(ntree = 1000))
  • po utworzeniu learnera:
reg_rf2 <- setHyperPars(reg_rf, ntree = 1000)
getHyperPars(reg_rf2)
## $ntree
## [1] 1000

Porównywanie modeli

porownanie <- benchmark(learners = list(reg_lm, reg_rf, reg_nnet),
                        tasks = list(m2_task, m2_task_ndz),
                        resampling = cv5)
save(porownanie, file = "porownanie.rda")
load("porownanie.rda")
porownanie
##          task.id        learner.id mse.test.mean
## 1     mieszkanie           regr.lm       2084932
## 2     mieszkanie regr.randomForest       1119241
## 3     mieszkanie         regr.nnet       2674150
## 4 mieszkanie_ndz           regr.lm       2481207
## 5 mieszkanie_ndz regr.randomForest       1748378
## 6 mieszkanie_ndz         regr.nnet       2672237

Wyniki porównania

# getBMRAggrPerformances(porownanie)
# getBMRPerformances(porownanie)
plotBMRBoxplots(porownanie)

plot of chunk wynikpor

Różne kryteria

listMeasures(obj = "regr")
##  [1] "rsq"         "rae"         "rmsle"       "mse"         "rrse"       
##  [6] "medse"       "mae"         "timeboth"    "timepredict" "medae"      
## [11] "featperc"    "mape"        "rmse"        "kendalltau"  "sse"        
## [16] "arsq"        "expvar"      "msle"        "sae"         "timetrain"  
## [21] "spearmanrho"

Wytrenowanie pojedynczego modelu

  • podstawowe wywołanie:
m2_rf <- train(reg_rf2, m2_task)
  • uwaga: można samodzielnie zdefiniować zbiór uczący
uczacy <- sample(1:nrow(mieszkania), floor(0.7*nrow(mieszkania)))
testowy <- setdiff(1:nrow(mieszkania), uczacy)
m2_rf_czesc <- train(reg_rf2, m2_task, subset = uczacy)
  • przewidywane wartości:
pred <- predict(m2_rf, task = m2_task)
## Error in predict.randomForest(.model$learner.model, newdata = .newdata, : missing values in newdata
head(getPredictionResponse(pred))
## Error in getPredictionResponse(pred): nie znaleziono obiektu 'pred'
pred2 <- predict(m2_rf_czesc, newdata = mieszkania[testowy, ])
## Error in predict.randomForest(.model$learner.model, newdata = .newdata, : missing values in newdata
head(getPredictionResponse(pred2))
## Error in getPredictionResponse(pred2): nie znaleziono obiektu 'pred2'

Strojenie parametrów

all_params <- makeParamSet(
  makeDiscreteParam("mtry", values = 2:5),
  makeDiscreteParam("nodesize", values = seq(5, 45, by = 10))
)
m2_params <- tuneParams(reg_rf2, task = m2_task,
                        resampling = cv3,
                        par.set = all_params,
                        control =  makeTuneControlGrid())

Wynik

m2_params
## Tune result:
## Op. pars: mtry=3; nodesize=5
## mse.test.mean=1.15e+06
par_data <- generateHyperParsEffectData(m2_params)
plotHyperParsEffect(par_data, x = "mtry", y = "nodesize", z = "mse.test.mean", plot.type = "heatmap")

plot of chunk wyswietlpar

reg_rf2 <- setHyperPars(reg_rf2, mtry = 3)
m2_rf2 <- train(reg_rf2, m2_task)

Inne kontrolki

makeTuneControlCMAES()
makeTuneControlIrace()
makeTuneControlRandom()

Cena mieszkania

moje <- data.frame(n_pokoj = 3L,
                   metraz = 60.00,
                   rok = 2010L,
                   pietro = 3L,
                   pietro_maks = 5L,
                   dzielnica = "Srodmiescie")
moje$dzielnica <- factor(moje$dzielnica,
                         levels = levels(mieszkania$dzielnica))
predict(m2_rf2, newdata = moje)
## Error in predict.randomForest(.model$learner.model, newdata = .newdata, : missing values in newdata

Wizualizacja modelu

pdp <- generatePartialDependenceData(m2_rf2,
                                     m2_task,
                                     features = colnames(mieszkania)[-c(3, 7)])
plotPartialDependence(pdp)

plot of chunk plotpdp

Podsumowanie

  • Tworzenie zadania: makeRegrTask, makeClassifTask itd
  • Metoda uczenia: makeLearner, makeLearners
  • Porównanie kilku modeli: benchmark
  • Ustawianie hiperparametrów: setHyperPars
  • Wytrenowanie pojedynczego modelu: train
  • Strojenie parametrów: tuneParams

Podziękowanie

Dziękujemy firmie Kruk i Wydziałowi Biotechnologii UWr.