tmdb05

ds1
tidymodels
statlearning
tmdb
random-forest
num
Published

May 17, 2023

Aufgabe

Melden Sie sich an für die Kaggle Competition TMDB Box Office Prediction - Can you predict a movie’s worldwide box office revenue?.

Sie benötigen dazu ein Konto; es ist auch möglich, sich mit seinem Google-Konto anzumelden.

Bei diesem Prognosewettbewerb geht es darum, vorherzusagen, wieviel Umsatz wohl einige Filme machen werden. Als Prädiktoren stehen einige Infos wie Budget, Genre, Titel etc. zur Verfügung. Eine klassische “predictive Competition” also :-) Allerdings können immer ein paar Schwierigkeiten auftreten ;-)

Aufgabe

Erstellen Sie ein Boosting-Modell mit Tidymodels!

Hinweise

  • Für den Start empfehle ich, etwaige Vorverarbeitung erstmal klein zu halten. Nach dem Motto: Erstmal das Modell zum Laufen kriegen, dann erst verbessern.
  • Tunen Sie die typischen Parameter.
  • Reichen Sie das Modell bei Kaggle ein und berichten Sie Ihren Score.
  • Im Übrigen sind Sie frei in Ihrem Vorgehen.











Lösung

Pakete starten

library(tidyverse)
library(tidymodels)
library(tictoc)
library(easystats)
library(doParallel)  # mehrere CPUs nutzen
library(finetune)  # Anova Race

Daten importieren

d_train_path <- "https://raw.githubusercontent.com/sebastiansauer/Lehre/main/data/tmdb-box-office-prediction/train.csv"
d_test_path <- "https://raw.githubusercontent.com/sebastiansauer/Lehre/main/data/tmdb-box-office-prediction/test.csv"

d_train <- read_csv(d_train_path)
d_test <- read_csv(d_test_path)

Werfen wir einen Blick in die Daten:

glimpse(d_train)
glimpse(d_test)

CV

cv_scheme <- vfold_cv(d_train)

Rezept 1

Begrenzen wir uns der Einfachheit halber auf folgende Prädiktoren, zumindest fürs Erste:

preds_chosen <- 
  c("budget", "popularity", "runtime")


d_train <- 
  d_train %>% 
  select(any_of(preds_chosen), revenue)
rec1 <- 
  recipe(revenue ~ ., data = d_train) %>% 
  #update_role(id, new_role = "id") %>% 
  #step_novel() %>% 
  step_impute_bag() %>% 
  step_center(all_numeric_predictors()) %>% 
  step_scale(all_numeric_predictors())
rec1

Boosting braucht nicht unbedingt skalierte Prädiktoren (sd=1), aber es kann helfen, zu z-transformieren.

Rezept checken

d_train_baked <- prep(rec1) %>% bake(new_data = NULL)
d_train_baked

Viele Modelle können nicht arbeiten mit nominalen Prädiktoren oder mit fehlenden Werten. Daher sollte man im Rezept diese Fehler vorab abfangen.

Ein letzter Blick:

describe_distribution(d_train_baked)

Sieht ok aus.

Modell 1

Tipp: Mit {usemodels} kann man sich den Code für einen Workflow (inkl. dem typischen Kladderadatsch) schon mal ausgeben lassen. Praktisch.

library(usemodels)
model_boost1 <- 
  boost_tree(mtry = tune(), 
             min_n = tune(), 
             learn_rate = tune(),
             tree_depth = tune()) %>% 
  set_engine("xgboost") %>% 
  set_mode("regression")

Workflow 1

wf1 <-
  workflow() %>% 
  add_model(model_boost1) %>% 
  add_recipe(rec1)

Tipp: Gewöhnen Sie sich ein konsistentes Schema zu Benennung Ihrer Objekte an. Z.B. Workflow-Objekte mit wf1, wf2 etc. Fit-Objekte mit fit_boost1, fit_rf1, etc. Da gibt’s viele Wege, keine einzelne richtige Lösung.

Modell fitten (und tunen)

Tipp: Wenn Sie Ihr Rezept ändern, nicht vergessen, das Workflow-Objekt, wf1 in diesem Fall, neu zu berechnen. Vergisst man gerne mal…

Eine professioneller Lösung wäre ein Tool, das für Sie prüft, welche Objekte Sie aktualisieren müssen, z.B. das R-Paket {targets}.

Schalten wir, um Zeit zu sparen, noch mehrere Rechenkerne frei.

cores <- parallel::detectCores(logical = FALSE)
cores

Wenn man auf mehreren Kernen gleichzeitig rechnet, braucht man natürlich auch mehr (Arbeits-)Speicher (RAM). Wenn Ihre Maschine wenig (freien) Arbeitsspeicher hat, dann kann man nicht (oder nicht sinnvoll) auf mehreren Kernen gleichzeitig arbeiten.

doParallel::registerDoParallel(4)
tic()
fit_boost1 <-
  wf1 %>% 
  tune_race_anova(
    resamples = cv_scheme,
            grid = 5)  # der kleine Wert ist NUR um Rechenzeit zu sparen
toc()

Rechenzeit auf diesem Rechner:

Es könnte sich lohnen, das Modellobjekt abzuspeichern, da die Rechenzeit doch ganz schön lang sein kann. ABER Achtung: Sie dürfen dann nicht vergessen, das Objekt auf der Festplatte zu aktualisieren. Diese Strategie ist nicht ungefährlich: Leicht vergisst man das Aktualisieren.

Mit dem Parameter grid kann man die Anzahl der Tuningparameter-Kandidaten festlegen, vgl. hier:

grid

A data frame of tuning combinations or a positive integer. The data frame should have columns for each parameter being tuned and rows for tuning parameter candidates. An integer denotes the number of candidate parameter sets to be created automatically.

Der Standardwert (Default) beträgt 10.

Ein Blick in die Hinweise zum Fitten, ob beim Fitten etwas Ungewöhnliches passiert ist:

fit_boost1[[".notes"]][[1]]

Und weiter reingezoomt, falls es Hinweise geben sollte (ist hier nicht der Fall, nur der Info halber):

fit_boost1[[".notes"]][[1]] %>% select(note) %>% slice_head(n=1)

Dran denken: Wenn Sie das Modell aus irgendwelchen Gründen neu fitten, müssen Sie “flussabwärts”, also danach kommenden Objekte, auch neu berechnen.

Bester Modellkandidat

bestmodel_params <- select_best(fit_boost1)
wf_final <-
  wf1 %>% 
  finalize_workflow(bestmodel_params)

wf_final

Final fit

fit_final <-
  wf_final %>% 
  fit(d_train)

Final Predict

d_test_baked <- 
  rec1 %>% 
  prep() %>% 
  bake(new_data = d_test)

d_test_baked %>% 
  glimpse()
d_test$revenue <- NA
final_preds <- 
fit_final %>% 
  predict(d_test)

Categories:

  • ds1
  • tidymodels
  • statlearning
  • tmdb
  • random-forest
  • num