XGBoostパラメータのまとめとランダムサーチ実装

P&Dアドベントカレンダー6日目です！2回目の登場です！

今回は、前回と同様に**XGBoost**についてです。

前回の記事はこちらです！
[XGBoostによる機械学習(Rを用いて実装)]
(https://qiita.com/drafts/ae91fde4a0fe3f3a48c2/edit)

#パラメータチューニング

機械学習の基本的な手順は

1. 使用する機械学習手法の決定
2. 実装方法と環境構築
3. **パラメータチューニング**
4. モデルを使っての予測
5. **予測結果の評価**

の５ステップです。

手順1はXGBoostを用いるので**勾配ブースティング**
手順2は使用する言語をR言語、開発環境をRStudio、用いるパッケージは**XGBoost**(その他GBM、LightGBMなどがあります)といった感じになります。
手順4は前回の記事の「XGBoostを用いて学習&評価」がそれになります。


そして手順3、5についてですがこの2つが機械学習においての**鬼門**と言われる部分です。
前回の記事で出てきたようにXGBoostにはいくつかパラメータが存在します。
手順3の時に自分で複数あるパラメータの値を決定し、手順5で予測結果を評価します。
これを繰り返して"どのパラメータ"が最も予測精度が良いかを検討していきます。

#XGBoostパラメータ

XGBoostは大きく分けて4つパラメータが存在します。

1. General Parameters(全体パラメータ)
2. **Booster Parameters**(ブースターパラメータ)
3. Learning Parameters(学習タスクパラメータ)
4. Command Line Parameters(コマンドラインパラメータ)

パラメータチューニングの対象となるパラメータは2のBooster ParametersとCommand Line Parametersのnroundsのみです。

##General Parameters(全体パラメータ)

####booster [デフォルト = gbtree]
引数　・gbtree
　　　・dart
　　　・gblinear

gbtree、dart、gblinesrのいずれかのブースターを使用するかを決めます。
gbtreeもしくはdartを選択すると**ツリーモデル**、gblinearを選択すると**線形モデル**となります。

ツリーモデルと線形モデルを比較すると、ツリーモデルの方が予測精度が良いとされています。

XGBoostは、正確に言うと勾配ブースティングであり、勾配ブースティング木ではないです。
この**booster**パラメータで「gbtree」を選択することによって勾配ブースティング木(**GBDT:Gradient Boosting Decision Tree**)になります。

####silent [デフォルト = 0]
引数　・0 起動中のメッセージを出力
　　　・1 サイレントモードのため出力しない

####nthread [デフォルトでは自動的にフルコアになる]
XGBoostを実行するために使用される並列スレッド数

##Booster Parameters(ブースターパラメータ)
####eta [デフォルト = 0.3] 範囲 0~1
過学習防止のためのパラメータで学習率を調整することができます。
小さくすると、モデルの分類性を高めることが可能です。
値によっては繰り返し回数が増えるほど(nroundsの値が大きいほど)過学習が進み精度が悪化する恐れがあります。

####gamma [デフォルト = 0] 範囲 0~∞
決定木の葉の追加による損失減少の下限を意味します。
決定木の葉は多ければ多いほど木が複雑となり、過学習を起こしやすくなります。
このパラメータはその葉の数に対するペナルティーです。
ですので、値が大きいほどアルゴリズムは保守的になります。

####max_depth [デフォルト = 6] 範囲 0~∞
決定木の深さの最大値を意味します。
値が大きいとほどモデルが複雑になるため、過学習する可能性が高くなります。

####min_child_weight [デフォルト = 1] 範囲 0~∞
決定木の葉の重みの下限を意味します。
重みの合計値がmin_child_weight未満であれば、それ以上の分割は行われません(つまり決定木が大きくならないようにします)。
そしてこの下限が大きいほど単調な決定木となり、過学習を抑制します。

####max_delta_step [デフォルト = 0] 範囲 0~∞
値を設定することで、各決定木の重みの推定に制約をかけることができます。
0の場合は制約なしとなり、整数値を設定するとモデルをより保守的にします。
あまり必要のないパラメータではありますが、不均衡データの分類の際には用いるらしいです。

####subsample [デフォルト = 1] 範囲 0~1
各決定木においてランダムに抽出される標本(データ)の割合を意味します。
つまりirisで言うと150個あるデータを全て使わず、決められた割合だけ使用するということです。
小さくすることで過学習を避けることができますが、保守的なモデルとなります。

####colsample_bytree [デフォルト = 1] 範囲 0~1
各決定木においてランダムに抽出される列の割合を意味します。
簡単に言えば、この値を設定することで説明変数を全て使わずに設定された割合でランダムに抽出された説明変数のみを使用してモデルを作成します。

####colsample_bylevel [デフォルト = 1] 範囲 0~1
決定木の各レベル単位での分割における列のsubsample比率を意味します。
言い方を変えると、決定木が分割する時に毎回説明変数を抽出しているということです。
subsampleとcolsample_bytreeを設定していればこのパラメータまでチューニングする必要はありません。

####lambda [デフォルト = 1]
決定木の葉の重みに関するL2正則化項を意味します。
値を大きくすることで過学習を防止します。

####alpha [デフォルト = 0]
決定木の葉の重みに関するL1正則化項を意味します。
値を大きくすることで過学習を防止します。

##Learning Parameters(学習タスクパラメータ)
####objective [デフォルト = reg:linear]
最小化させるべき損失関数を指定します。

引数　・reg:linear(線形回帰)
　　　・reg:logistic(ロジスティック回帰)
　　　・binary:logistic(2項分類で確率を返す)
　　　・multi:softmax(多項分類でクラスの値を返す)

multi:softmaxを指定した場合、num_classの指定が必要となります。

前回の記事ではirisデータの分類を行いました。
irisデータは”セトナ(setona)”、"バーシクル(versicolor)"、"バージニカ(virginica)"の3種類あるので多項分類の「multi:softmax」を指定し、「num_class = 3」と設定しています。

####eval_metric

テストデータに対する評価指標を意味します。
デフォルトの基準はobjectiveパラメータによって決まります。

引数　・rmse(2乗平均平方根誤差)
　　　・logloss(負の対数尺度)
　　　・error(2-クラス分類のエラー率)
　　　・merror(多クラス分類のエラー率)
　　　・mlogloss(多クラスの対数損失)
　　　・auc(ROC曲線下の面積で性能の良さを表す)
　　　・mae(平均絶対誤差)

このパラメータはobjectiveパラメータによって決まるので無理に設定する必要はありません。

##4. Command Line Parameters(コマンドラインパラメータ)
####data
トレーニングデータのパスを指定します。

####nrounds
ブースティングを行う回数(決定木の本数)を意味します。
クロスバリデーションを行うことで最適なブースティング回数を求めることができます。



こちらのページを参考にさせていただきました。
[XGBoost Parameters]
(https://xgboost.readthedocs.io/en/latest/parameter.html)
[Complete Guide to Parameter Tuning in XGBoost]
(https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/)
[xgboostのパラメータ(puyokwの日記)]
(http://puyokw.hatenablog.com/entry/2015/04/11/040941)
[XGBoostやパラメータチューニングの仕方に関する調査(かものはしの分析ブログ)]
(http://kamonohashiperry.com/archives/209)
[機械学習アルゴリズム~XGBoost~]
(http://rtokei.tech/machine-learning/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%80%9Cxgboost%E3%80%9C/)


また、なぜこれらのパラメータがXGBoostのどこで用いられているのか、なぜモデルに影響を与えるのかなどといったXGBoostの概念や理論について知りたい人はこちらのページがおすすめです。
[XGBoostの概要(ともにゃん的データ分析ブログ)]
(http://kefism.hatenablog.com/entry/2017/06/11/182959)
[XGBoostの主な特徴と理論の概要]
(https://qiita.com/yh0sh/items/1df89b12a8dcd15bd5aa)
[Gradient Boosted Tree (XGBoost)の取り扱い説明書]
(https://qiita.com/nyk510/items/7922a8a3c1a7b622b935)

#ランダムサーチ実装
代表的なパラメータチューニング手法が2つあります。

1つ目は**グリッドサーチ**といいます。
あらかじめパラメータの候補値を指定しておきます。そしてその候補パラメータを組み合わせて学習を思考することにより最適なパラメータを求める手法です。

2つ目は**ランダムサーチ**といいます。
初めにパラメータの設定範囲と試行回数を指定します。そして指定値範囲内から無作為に抽出したパラメータにより学習を試行することにより最適なパラメータを求める手法です。

参考ページはこちらです。
[【機械学習】パラメータチューニングについて]
(https://qiita.com/nanairoGlasses/items/93d764f549943d42d5e6)


##実装
前回の記事で作成したプログラムにランダムサーチを行うコードを追加します。
チューニングを行う対象パラメータは今回は6つに絞りました。

```R
install.packages("xgboost")    #xgboostをしようするためのパッケージ
install.packages("data.table") #data.table型を用いるのに必要

##パッケージ呼び出し
library(xgboost)
library(data.table)
library(Matrix) ##dgCMatrix型を用いるのに必要

odd.number <- 2*(1:75)-1 #1~150の奇数のみ
odd.data <- iris[odd.number,] #irisデータの奇数番目
even.data <- iris[-odd.number,] #irisデータの偶数番目

iris.data <- rbind(odd.data,even.data) #再び併合
y <- as.integer(iris.data[,5])-1 #目的変数
y <- as.data.frame(y)
data <- transform(iris.data,y=y)
data <- data[,-5]

#データの分割
model.data <- data[1:75,] #学習データ
pre.data <- data[76:150,] #評価データ

##data.table型へ変換
model.data.dt <- data.table(model.data,keep.rownames=F)
pre.data.dt <- data.table(pre.data,keep.rownames=F)


##data.table型からdgCMatrixへ変換
model.data.mx <- sparse.model.matrix(y~.,model.data.dt)
model.data.dm <- xgb.DMatrix(model.data.mx,label=model.data.dt$y)

pre.data.mx <- sparse.model.matrix(y~.,pre.data.dt)
pre.data.dm <- xgb.DMatrix(pre.data.mx,label=pre.data.dt$y)


#パラメータチューニング
set.seed(123)
iter <- 1

for(iter in 1:100) {
  param <- list(eta = runif(1, .01 , .5),
                gamma = runif(1, 0 , .2),
                max_depth = sample(3:10, 1),
                min_child_weight = sample(1:10, 1),
                subsample = runif(1, 0.0, 1.0),
                lambda = runif(1, 0.0, 1.0)
  )

  result <- xgb.train(params=param, data=model.data.dm, label=model.data.dt$y, num_class=3, objective="multi:softmax",booster="gbtree", nrounds=100, verbose=1)
  pred <- predict(object=result,newdata=pre.data.dm)
  
  for(i in 1:length(pred)){
    if(pred[i]==0) {pred[i]="setosa"}
    else if(pred[i]==1) {pred[i]="versicolor"}
    else {pred[i]="virginica"}
  }
  print(table(even.data[,5],pred))
  print(param)
  
}
```


param部分を乱数で決定しているだけです。
irisデータはそもそものデータ数が少ないのでランダムサーチによるチューニングしてもあまり変化はないかもしれません...

パラメータチューニングを自動で行なってくれるcaretパッケージを用いての実装の方がいいのかもしれません...


[Rによる機械学習：caretパッケージの使い方]
(https://logics-of-blue.com/r%E3%81%AB%E3%82%88%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%EF%BC%9Acaret%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/)