Rで何かをしたり、読書をするブログ

政府統計の総合窓口のデータや、OECDやUCIやのデータを使って、Rの練習をしています。ときどき、読書記録も載せています。

UCI Machine Learning RepositoryのBlood Transfusion Service Centerのデータの分析6 - ランダムフォレストモデルでの予測 - 何故、成績がよくないか?

www.crosshyou.info

の続きです。今回はランダムフォレストモデルで分析してみます。

まず、トレーニング用のデータフレームとテスト用のデータフレームを用意します。

モデルを設定します。rand_forest()でエンジンはrangerを使います。

レシピを設定します。ランダムフォレストモデルでは前処理は必要ないようです。

モデルとレシピを合わせて、ワークフローを設定します。

チューニング・グリッドを設定します。

チューニングを実行します。

autoplot()でチューニング結果を可視化します。

最良のパラメータを選択します。

最良のパラメータで学習します。

テスト用のデータで予測します。

混合行列をみてみます。

各種指標をみてみます。

ROC AUCをみてみます。

0.639と今までで一番悪いですね。

ROC曲線を描きます。

いままでのモデルのROC AUCをまとめると、

glmnetエンジンでのElastic-Netが一番ですね。そして、ランダムフォレストモデルが一番成績が悪いです。これは何故かをCopilot先生に聞いてみました。

①. 説明変数が 3 つしかない → 木系モデルの強みが出にくい
Random Forest や rpart は、
「特徴量が多い」「非線形性が強い」「相互作用が複雑」
という状況で力を発揮します。
でも今回のデータは:
- Recency
- Frequency
- Time
の 3変数だけで、しかも
線形に近い構造を持っています。
だから、木系モデルは「分割の選択肢が少なすぎて」
アンサンブルの多様性が生まれないんですよね。
結果として:
- glm(線形)
- glmnet(線形+正則化)
の方が自然に強くなります。

②. Random Forest の mtry が 1,2,3 で差が出なかった理由
 autoplot で確認した通り、
mtry の違いがほぼ影響しなかったのは、
説明変数が少なすぎて、どれを選んでも似たような木になる
からです。
これは「RF が弱い」のではなく、
データ構造が RF に向いていないというだけ。

③. min_n が大きいほど安定した理由
min_n を大きくすると木が浅くなり、
過学習が抑えられます。
今回のデータは単純なので、
- 深い木 → 過学習
- 浅い木 → 安定
という典型的なパターンが出ています。

④. ranger が一番低い AUC(0.639)なのは自然
これはむしろ「データの性質を正しく反映した結果」です。
- glm(線形)
- glmnet(線形+正則化)
- rpart(単純な木)
- ranger(複数の木の平均)
この中で、
線形モデルが最も適合しやすい構造をしているので、
glm / glmnet が上に来るのは当然なんですよね。

ということです。なるほど~と思いました。

 

今回は以上です。

はじめから読むには、

www.crosshyou.info

です。

今回のコードは、以下になります。

#
# 今回はランダムフォレストでやってみる
#
# トレーニング用のデータとテスト用のデータ
df_rf_train <- training(split) |> 
  mutate(Donated_Blood = as.factor(Donated_Blood))
df_rf_test <- testing(split) |> 
  mutate(Donated_Blood = as.factor(Donated_Blood))
#
# モデルを設定
rf_mod <- rand_forest(
  mtry = tune(),
  trees = 500,
  min_n = tune()
) |> 
  set_engine("ranger") |> 
  set_mode("classification")
#
# レシピを設定
rf_rec <- recipe(Donated_Blood ~ ., data = df_rf_train)
#
# ワークフローを設定
rf_wf <- workflow() |> 
  add_model(rf_mod) |> 
  add_recipe(rf_rec)
#
# チューニンググリッドの設定
# Step1. 説明変数が3つなので mtry は 1, 2, 3 に固定
mtry_values <- 1:3
# Step2. min_n は広めに(例:2〜100)
min_n_values <- seq(2, 100, by = 4)
# Step3. expand_grid()を使う
rf_grid <- expand_grid(
  mtry = mtry_values,
  min_n = min_n_values
)
rf_grid
#
# チューニング実行
rf_tuned <- tune_grid(
  rf_wf,
  resamples = folds,
  grid = rf_grid,
  control = control_grid(save_pred = TRUE),
  metrics = metric_set(roc_auc, accuracy)
)
#
# チューニング結果の可視化
autoplot(rf_tuned)
#
# 最良のパラメータを選択
best_params <- select_best(rf_tuned, metric = "roc_auc")
best_params
#
# 最良のパラメータで学習
rf_final_wf <- finalize_workflow(rf_wf, best_params)
rf_final_fit <- fit(rf_final_wf, df_rf_train)
#
# テスト用のデータで予測
rf_results <- df_rf_test |> 
  select(Donated_Blood) |> 
  bind_cols(
    predict(rf_final_fit, df_rf_test, type = "class"),
    predict(rf_final_fit, df_rf_test, type = "prob")
  )
rf_results
#
# 混合行列
rf_results |> 
  conf_mat(truth = Donated_Blood, .pred_class)
#
# 各種評価
metrics <- metric_set(accuracy, sensitivity, specificity)
metrics(rf_results, truth = Donated_Blood, estimate = .pred_class)
#
# ROC AUC
roc_auc(rf_results, truth = Donated_Blood, .pred_0)
#
# ROC曲線
# Step1. ROC曲線データを作成
rf_roc <- roc_curve(rf_results, truth = Donated_Blood, .pred_0) |> 
  mutate(model = "Random Forest")
#
# Step2. 結合
roc_all <- roc_all |> 
  bind_rows(rf_roc)
#
# Step3. プロット
roc_all |> 
  ggplot(aes(x = 1 - specificity, y = sensitivity, color = model)) +
  geom_path(linewidth = 1.2) +
  geom_abline(lty = 2, color = "gray50") +
  coord_equal() +
  labs(title = "ROC曲線(logit, glmnet, tree, random forest))",
       x = "1 - Specificity (False Posivie Rate)",
       y = "Sencitivity (True Positive Rate") +
  theme_bw()
#
# 今までのROC AUC
roc_auc(logit_result, truth = Donated_Blood, .pred_0)
roc_auc(glmnet_results, truth = Donated_Blood, .pred_0)
roc_auc(tree_results, truth = Donated_Blood, .pred_0)
roc_auc(rf_results, truth = Donated_Blood, .pred_0)
#

(冒頭の画像は、Bing Image Creatorで生成しました。プロンプトは、A beautiful, serene landscape photograph. Red roses bloom everywhere. です。)