【R】Tidymodelsで機械学習
1.はじめに
正直、良くわかっていないのですが、Julia SilgeさんのYoutubeの解説”Predictive modeling in R with tidymodels and NFL attendance”を見ていたら、わかったような錯覚に陥ったので、備忘録も兼ねて書いておきます。解説のほぼほぼ写しです。
R
のパッケージTidyverse
はデータハンドリングや可視化のツールとして認知されていますが、それと親和性が良い(?)Tidymodels
というパッケージがあります。
僕は、機械学習のことはほとんどわからないので、認識違いもたくさんありそうですが。
1.データの準備
2.モデルの学習
3.モデルの評価
をしていると思います。
2.データ準備
データは、TidyTuesday
という小規模カンファレンス(?)での課題データである米NFLフットボールのデータです。今回の解説では、このデータを用いて、いろいろな要素から週間観客数(weekly_attendance)を予測しようというものです(おそらく。これも違っていたらどうしよう。。。)。そもそもが、NFLのことをほとんど知らず、興味もないので、その点からして問題のような気がします。。。
ライブラリを準備します。僕の環境では、後々、ランダムフォレストのエンジンでranger
を使うのですが、ranger
というパッケージをインストールしておかないとコードが動きませんでした。
library(tidyverse) library(tidymodels) library(ranger)
データを取得し、成形します。
attendance <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-04/attendance.csv') standings <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-04/standings.csv') games <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-04/games.csv') attendance_joined<-attendance %>% left_join(standings, by = c("year", "team_name", "team")) attendance_joined %>% filter(!is.na(weekly_attendance)) %>% ggplot(aes(fct_reorder(team_name, weekly_attendance), weekly_attendance, fill = playoffs)) + geom_boxplot(outlier.alpha = 0.5) + coord_flip()
成形したデータを色々確認してみます。まず、箱ひげ図でデータを確認してみます。
各チームごとの週間観客数をプレイオフあり/なしで表示しています。ここでは、週間観客数の順に並び変えています。次にmargin_of_victory(どれだけで優勝できるか、かな?)に対するヒストグラムを表示してみます。
attendance_joined %>% distinct(team_name, year, margin_of_victory, playoffs) %>% ggplot(aes(margin_of_victory, fill = playoffs)) + geom_histogram(position = "identity", alpha = 0.7 )
margin_of_victoryによって、分布が異なっていますので、予測する要素として使えそうです。最後に、週ごとの観客数を箱ひげ図で描いてみます。
attendance_joined %>% mutate(week = factor(week)) %>% ggplot(aes(week, weekly_attendance, fill = week)) + geom_boxplot(show.legend = FALSE, outlier.alpha = 0.4)
週ごとの差は小さいようです。若干下がり気味?以上の確認結果を踏まえ、週間観客数に影響を与えそうなパラメータだけのデータをattendance_df
として再構築します。
# Model attendance_df<-attendance_joined %>% filter(!is.na(weekly_attendance)) %>% select(weekly_attendance, team_name, year, week, margin_of_victory, strength_of_schedule, playoffs)
3.機械学習モデル
3.1 トレーニングデータとテストデータ
準備したデータをトレーニングデータnfl_train
とテストデータnfl_test
に、initial_split
関数を使い、分割します。。
set.seed(1234) attendance_split<-attendance_df %>% initial_split(strata = playoffs) nfl_train<-training(attendance_split) nfl_test<-testing(attendance_split)
それぞれ、以下のようになります。
> head(nfl_train)
# A tibble: 6 x 7
weekly_attendance team_name year week margin_of_victory strength_of_schedule playoffs
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <chr>
1 77434 Cardinals 2000 1 -14.6 -0.7 No Playoffs
2 66009 Cardinals 2000 2 -14.6 -0.7 No Playoffs
3 71801 Cardinals 2000 4 -14.6 -0.7 No Playoffs
4 66985 Cardinals 2000 5 -14.6 -0.7 No Playoffs
5 44296 Cardinals 2000 6 -14.6 -0.7 No Playoffs
6 38293 Cardinals 2000 7 -14.6 -0.7 No Playoffs
> head(nfl_test)
# A tibble: 6 x 7
weekly_attendance team_name year week margin_of_victory strength_of_schedule playoffs
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <chr>
1 65356 Cardinals 2000 12 -14.6 -0.7 No Playoffs
2 50289 Cardinals 2000 14 -14.6 -0.7 No Playoffs
3 37452 Cardinals 2000 16 -14.6 -0.7 No Playoffs
4 65711 Cardinals 2000 17 -14.6 -0.7 No Playoffs
5 73025 Falcons 2000 3 -10.1 1.5 No Playoffs
6 66019 Falcons 2000 7 -10.1 1.5 No Playoffs
3.2 機械学習モデル
モデルは、線形回帰(Linear regression
)とランダムフォレストの2つを作って比較してみます。モデル化に使うパッケージをset_engine()
で指定して、学習はfit()
で完了です。
まずは、Linear regressionです。
lm_spec<-linear_reg() %>% set_engine(engine = "lm") lm_fit<-lm_spec %>% fit(weekly_attendance ~ ., data = nfl_train) # get 1st model (Liner model)
次にランダムフォレストです。
rf_spec<-rand_forest(mode="regression") %>% set_engine("ranger") rf_fit<-rf_spec %>% fit(weekly_attendance ~ ., data = nfl_train) # get 2nd model (Random forest model)
4.機械学習モデルの評価
テストデータを使った予測は、predict()
で行います。予め言っておくと、コメントにも書いてありますが、このモデル(というか、データ)は良くありませんでした。。。。あとでわかったことですが。これには、Juliaさんも苦笑い(笑)。とにかく、進みます。
# Evaluate model # not great choices result_train<-lm_fit %>% predict(new_data = nfl_train) %>% mutate(truth = nfl_train$weekly_attendance, model = "lm") %>% bind_rows(rf_fit %>% predict(new_data = nfl_train) %>% mutate(truth = nfl_train$weekly_attendance, model = "rf")) result_test<-lm_fit %>% predict(new_data = nfl_test) %>% mutate(truth = nfl_test$weekly_attendance, model = "lm") %>% bind_rows(rf_fit %>% predict(new_data = nfl_test) %>% mutate(truth = nfl_test$weekly_attendance, model = "rf"))
この結果を、rms(root mean square)で評価します。
result_train %>% group_by(model) %>% rmse(truth = truth, estimate = .pred)
# A tibble: 2 x 4
model .metric .estimator .estimate
<chr> <chr> <chr> <dbl> rmse root mean squre
1 lm rmse standard 8343. <- train と test でほぼ同じ
2 rf rmse standard 6106.<- train と test で異なる(悪い)本来は良いはず
result_test %>% group_by(model) %>% rmse(truth = truth, estimate = .pred)
# A tibble: 2 x 4 #model .metric .estimator .estimate #<chr> <chr> <chr> <dbl> # 1 lm rmse standard 8239. # 2 rf rmse standard 8627.
上のコメントにも書きましたが、lmモデルよりrfモデルの方がrmsが大きくなってしまいました。本来なら、ランダムフォレストの方が精度が良いはずなのですが。
で、どういうことが起こったのか、プロットして確認します。
result_test %>% mutate(train = "testing") %>% bind_rows(result_train %>% mutate(train = "training")) %>% ggplot(aes(truth, .pred, color = model)) + geom_abline(lty = 2, color = "gray80", size = 1.5) + geom_point(alpha = 0.5) + facet_wrap(~train)
確かに、ランダムフォレストの方が当てはまりが悪い。。。
で、ここからが理解できなかったのですが、何やらデータがおかしいとか。。。再度、チャレンジすることにしました。
5.機械学習モデル 再び
5.1 データ修正(?)
## Let's try again! # better choices # Resampling training data to get better estimate set.seed(1234) nfl_folds<-vfold_cv(nfl_train, strata = playoffs) rf_res<-fit_resamples( rf_spec, weekly_attendance ~ ., resamples = nfl_folds, control = control_resamples(save_pred = TRUE) )
5.2 もう一回学習(?)
rf_res %>% collect_metrics()
# > rf_res %>%
# + collect_metrics()
# A tibble: 2 x 5
.metric .estimator mean n std_err
<chr> <chr> <dbl> <int> <dbl>
1 rmse standard 8258. 10 114.
2 rsq standard 0.164 10 0.00957
5.3 再度の学習の成果を
# visualisation rf_res %>% unnest(.predictions) %>% ggplot(aes(weekly_attendance, .pred, color = id )) + geom_abline(lty = 2, color = "gray80", size = 1.5) + geom_point(alpha = 0.5)
これは、当てはまりがいい!
6.さいごに
何となく、機械学習についてわかった気がする。R
のパッケージを使うと簡単なような気がする。でも、最後が良くわからなかったので、いつか復習したい。今回のところは、何となく分かっただけでも満足。
NFLの事をあまりに知らなかったので、こちらの公式サイトも見ました。また、Tidymodels
に関しては、こちらのサイトも参考にさせていただきました。