★データ解析備忘録★

ゆる〜い技術メモ

「機械学習のパラメータをベイズ最適化でチューニング」をRで超簡単に書けるパッケージを作った

0. 要約

機械学習のハイパーパラメータをベイズ最適化でチューニングするのは既存パッケージの組み合わせで頑張ればできたのですが、頑張らなくても簡単に書けるパッケージを作りました。

github.com

これにより、例えば iris データをXGboostの3-foldクロスバリデーション、エラー率評価でフィッティングさせたければ以下のように書くことでハイパーパラメーターである eta, max_depth, nround, subsample, bytree をベイズ最適化でチューニングしてくれます。

res0 <- xgb_cv_opt(data = iris,
                   label = Species,
                   objectfun = "multi:softmax",
                   evalmetric = "merror",
                   classes = 3,
                   n_folds = 3)

データセット名、ラベルの列名、XGboostのモデルを組むのに必要なこと、何回クロスバリデーションするかを書けば勝手にやってくれます。

ベイズ最適化そのものについては、hoxo_mさんの資料が分かりやすいと思います。

www.slideshare.net

1. はじめに

きっかけ

そもそもこのパッケージを作ろうと思ったきっかけは六本木データサイエンティストの方のブログ記事でした。

{rBayesianOptimization}パッケージによるベイズ最適化で機械学習パラメータチューニングをお手軽に - 六本木で働くデータサイエンティストのブログ

{rBayesianOptimization} パッケージと機械学習の諸手法のパッケージ({e1071} や {xgboost}) を組み合わせるとパラメータチューニングをベイズ最適化でお手軽にできるよって話なんですが、いくらお手軽にできるといっても実際には以下の手順が必要でした。

  1. データを整形し、(機械学習側の)パッケージに適した形にする
  2. どのパラメータをチューニングするか決め、最大化する関数を作成
  3. パラメータをどの範囲に設定するか決め、ベイズ最適化を実行

なかなか面倒です。というのも、1 ではパッケージごとにデータの変形の仕方が違ったりします。例えば、 e1071::svm() にデータを流しこむにはラベルの列を factor 型にしておかなければならないし、 {xgboost} パッケージに流すには Matrix::sparse.model.matrix() 関数や xgboost::xgb.DMatrix() 関数で適切な形に変形してやらなければなりません。

また、いくらお手軽にできるとはいえ現実のデータでやると局所解に引っかかるなどの理由で最適化がうまくいかないことがあります。この場合、 rBayesianOptimization::BayesianOptimization() 関数の中で kappa を調節したり、獲得関数の最大化戦略の選択など、ベイズ最適化自体にもチューニングのような作業が必要です。

そのたびにあちこち変えながらやっているとエラーになったり、コードの見通しが悪くなるといった問題がありました。

そこで、これを一つの関数にしてしまいたかったというのがパッケージ作成の動機です。

そして、パッケージを作った

github.com

パッケージの原型ができたのは実は2017年3月頃で、Tokyo.RのLTなどでちょろっと話はしていたんですがいかんせん機能がしょぼかったのを幾つか改良して現在のバージョン(0.3.3)になっています。

あと、格好をつけたくてヘキサゴンのバッジマークも作ってしまいました笑

2. インストール

CRANに上げたので、 install.packages() でインストールできます。

install.packages("MlBayesOpt")

開発版はGitHubからインストールできます。

# install.packages("githubinstall")
githubinstall::githubinstall("MlBayesOpt")

# install.packages("devtools")
devtools::install_github("ymattu/MlBayesOpt")

3. 使い方

手法とか

現在対応してるのは以下の機械学習手法です。

  • SVM
    • ホールド・アウト(svm_opt())
    • クロスバリデーション (svm_cv_opt())
  • Random Forest ホールド・アウトのみ (rf_opt())
  • XGboost
    • ホールド・アウト(xgb_opt())
    • クロスバリデーション (xgb_cv_opt())

内部的にはそれぞれ、 {e1071}, {ranger}, {xgboost} を使っているため、これらで使われている引数のうち主なものは使えます。SVMではカーネルの選択もできます。

評価について

基本的には Test Accuracy を返すのですが、XGboostのクロスバリデーションだけは evalmetric 引数で指定したものを返します。さらに、ここで error, merror, logloss, mlogloss などモデルとしては最小化したいものを選んだ場合はマイナスの値が返ってきます。たとえば merror でエラー5%であれば、 -0.05 が返ってきます。

細かいことは、パッケージのヘルプを御覧ください。

流し込むデータについて

各モデルの計算では、ラベルを表す列以外全部の変数を学習に使うことになってるので、 data 引数で指定したデータはラベルの列と特徴量だけの状態にしてください。当然、ここまでのデータは自分で作る必要があります。

そこさえできてしまえば、ラベルの列の型が factor 型だとかはあまり気にしなくても関数がよしなに調整してくれます。

細かいチューニングについて

パラメータの探索範囲を変えたい

デフォルトでよく使われる範囲を指定するようになってますが、探索範囲を変えたいときは関数の中で指定してやればOKです。たとえば、RBFカーネルSVMgammacost の探索範囲を変えたいときは以下のように書きます。

res0 <- svm_cv_opt(data = iris,
                   label = Species,
                   n_folds = 3,
      gamma_range = c(10^(-2), 1),
                   cost_range = c(10^(-2), 1),
                   init_points = 10,
                   n_iter = 1)
ベイズ最適化の引数を変えたい

変えるとしたら、 kappa, acq (最大化戦略の方法)あたりでしょうか。

res0 <- svm_cv_opt(data = iris,
                   label = Species,
                   n_folds = 3,
                   init_points = 10,
                   n_iter = 1,
                   acq = "ucb",
                   kappa = 3)

4. 余談

実はCRANにアクセプトされるのに結構苦労しました。その話は後日...