使用してレース方法xgboostモデルを調整し、ホームを予測する⚾️


これは私のシリーズの最新のものですscreencasts 使い方のデモtidymodels パッケージは、より複雑なモデルのチューニングを開始から.今週のエピソードSLICED , 競争的なデータサイエンスストリーミングショーは、競争者が最近の野球ゲームでホームランを予測するために競争していました.正直なところ野球はあまり知らない⚾ でもfinetune パッケージは最近リリースされました、そして、この挑戦はチューニングのためにレース方法を使う方法を示す良い機会を提供します.
ここでは、ビデオの代わりに、またはビデオに加えて読書を好む人々のために、私はビデオで使用されるコードです.

データを探る


我々のモデリングゴールは予測することですwhether a batter’s hit results in a home run ヒットについての与えられた特徴.提供される主なデータセットは、CSVファイルtraining.csv .
library(tidyverse)
train_raw <- read_csv("train.csv")

あなたは探索的なデータ分析とこのデータセットの可視化の多くを見て見ることができますが、ちょうどそれを理解するためにいくつかのプロットを確認しましょう.
ホームランはホームプレートの周りの物理空間にどのように分布しているか?
train_raw %>%
  ggplot(aes(plate_x, plate_z, z = is_home_run)) +
  stat_summary_hex(alpha = 0.8, bins = 10) +
  scale_fill_viridis_c(labels = percent) +
  labs(fill = "% home runs")


どのように起動速度とバットを残してボールの角度は、ホーム実行率に影響を与えるか?
train_raw %>%
  ggplot(aes(launch_angle, launch_speed, z = is_home_run)) +
  stat_summary_hex(alpha = 0.8, bins = 15) +
  scale_fill_viridis_c(labels = percent) +
  labs(fill = "% home runs")


どのようにペーシング、ボール、ストライキ、またはイニングの数のように、ホームランに影響を与える?
train_raw %>%
  mutate(is_home_run = if_else(as.logical(is_home_run), "yes", "no")) %>%
  select(is_home_run, balls, strikes, inning) %>%
  pivot_longer(balls:inning) %>%
  mutate(name = fct_inorder(name)) %>%
  ggplot(aes(value, after_stat(density), fill = is_home_run)) +
  geom_histogram(alpha = 0.5, binwidth = 1, position = "identity") +
  facet_wrap(~name, scales = "free") +
  labs(fill = "Home run?")


確かに多くの発見するが、モデリングに移動しましょう.

モデルを作る


我々の「データ予算」をセットアップすることによって、我々のモデリングを始めましょう私は、最初のデータセットからのsと1 sを分類モデリングのための要因に変えます.
library(tidymodels)

set.seed(123)
bb_split <- train_raw %>%
  mutate(
    is_home_run = if_else(as.logical(is_home_run), "HR", "no"),
    is_home_run = factor(is_home_run)
  ) %>%
  initial_split(strata = is_home_run)
bb_train <- training(bb_split)
bb_test <- testing(bb_split)

set.seed(234)
bb_folds <- vfold_cv(bb_train, strata = is_home_run)
bb_folds


## # 10-fold cross-validation using stratification 
## # A tibble: 10 × 2
## splits id    
## <list> <chr> 
## 1 <split [31214/3469]> Fold01
## 2 <split [31214/3469]> Fold02
## 3 <split [31214/3469]> Fold03
## 4 <split [31215/3468]> Fold04
## 5 <split [31215/3468]> Fold05
## 6 <split [31215/3468]> Fold06
## 7 <split [31215/3468]> Fold07
## 8 <split [31215/3468]> Fold08
## 9 <split [31215/3468]> Fold09
## 10 <split [31215/3468]> Fold10

機能工学のために、我々がすでにプレーヤーのピッチと利口に関する情報とともにEDAの間、探検した変数に集中しましょう.いくつかの欠落しているデータは、特にlaunch_angle and launch_speed , では、それらの値を解釈しましょう.
bb_rec <-
  recipe(is_home_run ~ launch_angle + launch_speed + plate_x + plate_z +
    bb_type + bearing + pitch_mph +
    is_pitcher_lefty + is_batter_lefty +
    inning + balls + strikes + game_date,
  data = bb_train
  ) %>%
  step_date(game_date, features = c("week"), keep_original_cols = FALSE) %>%
  step_unknown(all_nominal_predictors()) %>%
  step_dummy(all_nominal_predictors(), one_hot = TRUE) %>%
  step_impute_median(all_numeric_predictors(), -launch_angle, -launch_speed) %>%
  step_impute_linear(launch_angle, launch_speed,
    impute_with = imp_vars(plate_x, plate_z, pitch_mph)
  ) %>%
  step_nzv(all_predictors())

## we can `prep()` just to check that it works
prep(bb_rec)


## Data Recipe
## 
## Inputs:
## 
## role #variables
## outcome 1
## predictor 13
## 
## Training data contained 34683 data points and 15255 incomplete rows. 
## 
## Operations:
## 
## Date features from game_date [trained]
## Unknown factor level assignment for bb_type, bearing [trained]
## Dummy variables from bb_type, bearing [trained]
## Median Imputation for plate_x, plate_z, pitch_mph, ... [trained]
## Linear regression imputation for launch_angle, launch_speed [trained]
## Sparse, unbalanced variable filter removed bb_type_unknown, bearing_unknown [trained]

今、調整可能なXGBOOGモデル仕様を作成しましょう.スライスのような競争では、我々はおそらく時間制約のためにこれらのパラメータを調整したくないでしょう、しかし、代わりに最も重要ないくつかだけ.
xgb_spec <-
  boost_tree(
    trees = tune(),
    min_n = tune(),
    mtry = tune(),
    learn_rate = 0.01
  ) %>%
  set_engine("xgboost") %>%
  set_mode("classification")

xgb_wf <- workflow(bb_rec, xgb_spec)

xgboostを調整するレースを使用してください


現在我々can use tune_race_anova() to eliminate うまくやっていないパラメータの組み合わせ.この特定のスライスされたエピソードは、ログ損失に関して評価されていました.
library(finetune)
doParallel::registerDoParallel()

set.seed(345)
xgb_rs <- tune_race_anova(
  xgb_wf,
  resamples = bb_folds,
  grid = 15,
  metrics = metric_set(mn_log_loss),
  control = control_race(verbose_elim = TRUE)
)

我々は“レース”の間に可能なパラメータの組み合わせの方法を視覚化することができますどのように我々は明らかにすべての再サンプリングで不十分に行っていたパラメータの組み合わせを評価しないことによって時間のトンを保存して注意してください私たちは良いパラメータの組み合わせを続ける続けた.
plot_race(xgb_rs)


そして、我々はトップの結果を見ることができます.
show_best(xgb_rs)


## # A tibble: 1 × 9
## mtry trees min_n .metric .estimator mean n std_err .config          
## <int> <int> <int> <chr> <chr> <dbl> <int> <dbl> <chr>            
## 1 6 1536 11 mn_log_lo… binary 0.0981 10 0.00171 Preprocessor1_Mo…

使いましょうlast_fit() トレーニングデータに1つの最終時刻にフィットし、テストデータの1つの最終時刻を評価します.
xgb_last <- xgb_wf %>%
  finalize_workflow(select_best(xgb_rs, "mn_log_loss")) %>%
  last_fit(bb_split)

xgb_last


## # Resampling results
## # Manual resampling 
## # A tibble: 1 × 6
## splits id .metrics .notes .predictions .workflow
## <list> <chr> <list> <list> <list> <list>   
## 1 <split [34683… train/test… <tibble [2 … <tibble [0… <tibble [11,561… <workflo…

私たちはテストセットで予測を集めて、我々が欲しいものは何でもします.
collect_predictions(xgb_last) %>%
  mn_log_loss(is_home_run, .pred_HR)


## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 mn_log_loss binary 0.0975

これは、単一のモデルにはかなり良いですこのデータセットを使用してこれより良いスコアを達成したスライスの競合他社は、すべてのアンサンブルモデルを使用して、私は信じている.
また、VIPパッケージを使用して変数重要度を計算することもできます.
library(vip)
extract_workflow(xgb_last) %>%
  extract_fit_parsnip() %>%
  vip(geom = "point", num_features = 15)


レーシングメソッドを使用すると、より迅速に可能なパラメータのオプションの多くを調整する素晴らしい方法です.多分、私は次の火曜日にテストにそれを置きます.そして、私がスライスされたプレーオフの2番目と最終的なエピソードに参加します!