Rのこと。

記事は引っ越し作業中。2023年中までに引っ越しを完了させてブログは削除予定

K-Fold Target Encoding

はじめに

ここではK-Fold Target Encodingについてまとめておく。下記のサイトでは、K-Fold Target Encodingの説明とPythonでの実装が乗っているので、それを参考にRで雑に書き直してみた。いつの日か関数化しよう…。

K-Fold Target Encoding

One-hot EncodingやDummy Encodingは、カテゴリ変数のカーディナリティが多くなると、データセットの次元が大きくなるため、役に立たない場合があるらしい。そこで、Target Encodingなどが役立つが、なにも考えずにTarget Encodingすると、リークしてしまい、モデルの汎化性能が良くなくなる。それを回避しつつ、Target Encodingするために、K-Fold Target Encodingを行う。

K-Fold Target Encodingとは、クロスバリデーションを行いながら、ターゲットの変数の値を、カテゴリカル特徴量のグループごとの平均などでエンコードすることで、カテゴリカルな特徴量を新たな特徴量に変換するエンコード方法。クロスバリデーションしないと行けない理由としては、単純に全データを使ってTarget Encodingしてしまうと、リークする可能性がでてくるため。なので、Target Encodingする際には、エンコードされた値を付与したいレコード群(フォールド群)には、自身のレコードのターゲットの値を含めず、アウトオブフォールド群で計算された平均値を付与する。それをクロスバリデーションの要領で行うことで、トレーニング用のモデルの特徴量として、モデルを構築する。テストデータに対するTarget Encodingの値は、テストデータに付与したK-Fold Target Encodingの値をカテゴリ毎に平均して付与する。

サンプルデータ

K-Fold Target Encodingの値と一致しているかを確認するため、サンプルデータ自体もK-Fold Target Encodingのデータを再現させていただいている。

library(cvTools)
df <- tibble(id = 1:25,
                 feature = c("A","B","B","B","B","A","B","A","A","B",
                       "A","A","B","A","A","B","B","B","A","A","B","B","B","A","A"),
                 target = c(1,0,0,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1,1,NA,NA,NA,NA,NA),
                 flg = rep(c("train", "test"), c(20, 5)))

train_df <- df %>% dplyr::filter(flg == "train")
test_df <- df %>% dplyr::filter(flg == "test")

K-Fold Target Encodingの雑な実装

フォールドのインデックスとクロスバリデーション回数は、参考サイトの図解にあわせて、k=5で、それにあわせてフォールドは連番で作成している。これはあくまでも解説のためなので、本番のデータの状態にあわせて変更する。たぶん、{purrr}で書き直せばもっとすっきりしそう。

# type = c("random", "consecutive", "interleaved")
k <- 5
folds <- cvFolds(nrow(train_df), K = k, type = "consecutive")
train_df <- train_df %>% dplyr::bind_cols(tibble(folds = folds$which))

res <- tibble()
for (i in 1:k) {
  fold_data     <- train_df %>% dplyr::filter(folds == i)
  out_fold_data <- train_df %>% dplyr::filter(folds != i)
  
  tmp <- out_fold_data %>% 
    dplyr::mutate(folds = i) %>% 
    dplyr::group_by(feature, folds) %>% 
    dplyr::summarise(cv_target_encoding = mean(target, na.rm = TRUE))
  
  res <- res %>% 
 dplyr::bind_rows(tmp)
}

res
# A tibble: 10 x 3
   feature folds cv_target_encoding
   <chr>   <int>              <dbl>
 1 A           1              0.556
 2 B           1              0.286
 3 A           2              0.625
 4 B           2              0.25 
 5 A           3              0.714
 6 B           3              0.333
 7 A           4              0.625
 8 B           4              0.25 
 9 A           5              0.5  
10 B           5              0.375

あとはこれをtrain_dfにもどし、そこからTarget Encodingの値を訓練データから平均して求め、テストデータに返す。left_join()のところでサブクエリっぽくなっている部分で訓練データから平均を計算している。雑ですんません。

train_df <- train_df %>% 
  dplyr::left_join(x = ., y = res, by = c("feature", "folds"))

test_df <- test_df %>% 
 dplyr::left_join(x = ., y = train_df %>% 
              dplyr::group_by(feature) %>% 
              dplyr::summarise(cv_target_encoding = mean(cv_target_encoding)),
            by = c("feature"))

df2 <- train_df %>% 
  dplyr::bind_rows(test_df)

df2 %>% 
  # 表示のため
  as.data.frame()

   id feature target   flg folds cv_target_encoding
1   1       A      1 train     1          0.5555556
2   2       B      0 train     1          0.2857143
3   3       B      0 train     1          0.2857143
4   4       B      1 train     1          0.2857143
5   5       B      1 train     2          0.2500000
6   6       A      1 train     2          0.6250000
7   7       B      0 train     2          0.2500000
8   8       A      0 train     2          0.6250000
9   9       A      0 train     3          0.7142857
10 10       B      0 train     3          0.3333333
11 11       A      1 train     3          0.7142857
12 12       A      0 train     3          0.7142857
13 13       B      1 train     4          0.2500000
14 14       A      0 train     4          0.6250000
15 15       A      1 train     4          0.6250000
16 16       B      0 train     4          0.2500000
17 17       B      0 train     5          0.3750000
18 18       B      0 train     5          0.3750000
19 19       A      1 train     5          0.5000000
20 20       A      1 train     5          0.5000000
21 21       B     NA  test    NA          0.2940476
22 22       B     NA  test    NA          0.2940476
23 23       B     NA  test    NA          0.2940476
24 24       A     NA  test    NA          0.6198413
25 25       A     NA  test    NA          0.6198413