
の続きです。前回は首尾よくデータのテキストファイルを R に読み込ませることができました。今回からは、いろいろなモデルで分類していきます。いちばんはじめはロジスティック回帰です。L1 + L2 の正則化の Elastic-Net Logistic Regression でやってみます。
はじめに spam を数値型の変数からファクター型の変数にかえておきます。

Elanstic-Net は glmnet パッケージを使ってできますが、今回は他のモデルもあとで試す予定なので、tidymodels パッケージの中で実行します。

df のデータフレームをトレーニング用のデータフレームとテスト用のデータフレームにわけます。initial_split() 関数、training() 関数、testing() 関数を使います。

strata = spam としたので、spam の比率が df_train と df_test で同じくらいになっているはずです。確認してみます。

どちらも 0 (スパムメールでない) が 0.606 で同じ比率です。
では、glmnet エンジンでの分類をしていきます。まずはレシピを作成します。

今回のデータは、説明変数はすべて数値型なので、前処理は step_normalize() の標準化だけをしました。
モデルを作成します。logistic_reg() で set_engine("glmnet") とします。

glmnet パッケージの lambda に相当するのが penalty で、alpha に対応するのが mixture です。どちらも後でチューニングするので、ここでは tune() としておきます。
レシピとモデルを合わせてワークフローとします。workflow() 関数です。

クロスバリデーションの設定をします。vfold_cv() 関数を使います。

grid_regular() 関数でチューングリッドの作成をします。level = 7 としたので、7 x 7 = 49 とおりのパラメータの組み合わせのグリッドです。

では、tune_grid() 関数でチューニングを実行します。

select_best() 関数で最良のパラメータを取り出します。

penalty = 0.001 でした。mixture = 0.95 なので LASSO 回帰よりのモデルですね。
finalize_workflow() 関数でこの最良のパラメータを使った最終ワークフローを作成します。

fit() 関数でモデルをフィットします。

df_test のデータフレームのデータを使って spam を予測します。predict() 関数を使います。

混合行列 (Confusion Matrix) を作成します。table() 関数を使いました。

けっこう当たっている感じですね。正解率を計算します。

正解率は 93% です。なかなかいいですね。
ROCのAUCを計算します。roc_auc() 関数を使います。

0.973 でした。いい値だと思います。
ROC 曲線を描きます。roc_curve() 関数で作画に必要なデータを生成し、ggplot() でグラフにしました。


ROC 曲線の形状もかなり良い成績の形状です。
Elastic-Net Logistic Regression は利点としてモデルの解釈がわかりやすい、ということがあります。モデルの係数を確認しましょう。
extract_fit_engine() 関数でエンジンを取り出します。

エンジンから係数を取り出します。

係数のデータをデータフレームにします。

グラフで上位10の変数を確認します。


$, 000, free, remove という言葉が多いと、スパムメールの確率が高くなります。
george, hp, meeting, edu, cs という言葉が多いと、スパムメールの確率が低くなります。
確かにスパムメールは、you can get $1,000 for free! とかいう文章が多いですから納得の結果ですね。
今回は以上です。
次回は
です。
はじめから読むには、
です。
今回のコードは以下になります。
#
# spamをファクター型にする
df$spam <- as.factor(df$spam)
#
# tidymodels パッケージの読み込み
library(tidymodels)
#
# トレーニング用のデータとテスト用のデータを作成
set.seed(67)
split <- initial_split(df, prop = 3/4, strata = spam)
df_train <- training(split)
df_test <- testing(split)
#
# spamの比率の確認
df_train |> count(spam) |> mutate(prop = n / sum(n))
df_test |> count(spam) |> mutate(prop = n / sum(n))
#
# 1. Elastic-Net Logistic Regression
# 1.1. レシピを作成
glmnet_rec <- recipe(spam ~ ., data = df_train) |>
step_normalize(all_predictors())
#
# 1.2 モデルを作成
glmnet_mod <- logistic_reg(
penalty = tune(),
mixture = tune()
) |>
set_engine("glmnet")
#
# 1.3 ワークフロー作成
glmnet_wf <- workflow() |>
add_recipe(glmnet_rec) |>
add_model(glmnet_mod)
#
# 1.4 クロスバリデーションの設定
set.seed(93)
cvfolds <- vfold_cv(df_train, v = 10, strata = spam)
#
# 1.5 チューングリッドの作成
glmnet_grid <- grid_regular(
penalty(range = c(-5, 1)),
mixture(range = c(0.05, 0.95)),
levels = 7
)
#
# 1.6 チューニング実行
set.seed(1)
glmnet_tuned <- tune_grid(
glmnet_wf,
grid = glmnet_grid,
resamples = cvfolds,
metrics = metric_set(roc_auc)
)
#
# 1.7 最良のパラメータ
glmnet_params <- select_best(glmnet_tuned, metric = "roc_auc")
glmnet_params
#
# 1.8 最終ワークフロー作成
glmnet_final_wf <- finalize_workflow(glmnet_wf, glmnet_params)
#
# 1.9 最終ワークフローでfit
set.seed(1)
glmnet_fit <- fit(glmnet_final_wf, data = df_train)
#
# 1.10 テスト用のデータで予測
glmnet_pred <- bind_cols(
df_test |> select(spam),
predict(glmnet_fit, new_data = df_test, type = "class"),
predict(glmnet_fit, new_data = df_test, type = "prob")
)
#
# 1.11 混合行列
table(glmnet_pred$.pred_class, glmnet_pred$spam)
#
# 正解率
glmnet_pred |>
summarize(accuracy = mean(spam == .pred_class))
#
# 1.12 ROCのAUC
glmnet_pred |> roc_auc(truth = spam, .pred_0)
#
# 1.13 ROC曲線
glmnet_roc <- glmnet_pred |> roc_curve(truth = spam, .pred_0)
glmnet_roc |>
ggplot(aes(x = 1 - specificity, y = sensitivity)) +
geom_line(color = "blue", linewidth = 1) +
geom_abline(color = "red", lty = "dashed", linewidth = 1) +
labs(title = "Elastic-Net Logistic Regression ROC Curve",
x = "False Positive Rate",
y = "True Positive Rate") +
theme_minimal()
#
# モデルの係数の確認
# step 1. エンジンを取り出す
glmnet_engine <- extract_fit_engine(glmnet_fit)
#
# step 2. エンジンから係数を取り出す
glmnet_coef <- coef(glmnet_engine, s = glmnet_params$penalty) |> as.matrix()
colnames(glmnet_coef) <- "coef"
#
# step 3. 係数のマトリックスをデータフレームにする
glmnet_coef_df <- as.data.frame(glmnet_coef) |>
mutate(var_name = rownames(glmnet_coef)) |> as_tibble() |>
relocate(var_name) |> mutate(abs_coef = abs(coef)) |>
mutate(direction = if_else(coef > 0, "positive", "negative")) |>
arrange(desc(abs_coef))
#
# step 4. 上位10の係数のグラフ
glmnet_coef_df |>
mutate(var_name = reorder(var_name, abs_coef)) |>
slice_max(order_by = abs_coef, n = 10) |>
ggplot(aes(x = var_name, y = abs_coef, fill = direction)) +
geom_col() +
coord_flip() +
theme_minimal()
#
(冒頭の画像は、Bing Image Creator で生成しました。プロンプトは、landscape of large field of Zantedeschia, Calla flowers under the bule sky, photo です。)