機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その5）

##1.はじめに
仕事では順序関係があるカテゴリ分類のデータを扱うことが多いので、Rのmlrパッケージを使った機械学習にチャレンジしてみました。
ちなみに、仕事はIT関係では全くないので、全くの素人分析屋です。

前回までです。
[機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その１）](https://qiita.com/kenkenvw/items/a16af7ac95a1f4e2620a)
[機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その２）](https://qiita.com/kenkenvw/items/035273992afb98febe5e)
[機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その３）](https://qiita.com/kenkenvw/items/87b00fda2a3c89ae6aa1)
[機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その４）](https://qiita.com/kenkenvw/items/f330a4413540ae26e6f3)

前回からXgboostを使っています。今回は回帰でやってみます。

##2.データとタスクの定義
早速、タスクを定義していきます。
まずは、分類問題としてやってみます。
xgboostのクラス分類は０から始める必要があるため、1を引きます。

```{r}
library(tidyverse)
library(xgboost)
library(mlr)
library(mlbench)
library(parallel)　#detectCores()を使ってCPUコア数を取得する
library(parallelMap)
data(BostonHousing, package = "mlbench")
df = BostonHousing
df$medvC = cut(df$medv,breaks =3,labels =c(1,2,3))
n = nrow(df)
train.set = scan("train.txt")　　　　　#第1回目で保存したもの
test.set = setdiff(1:n, train.set)

df$medv = NULL
df$medvC=as.numeric(df$medvC)
task_BostonHousing = makeRegrTask(id = "BostonHousing", data = df, target = "medvC")
task_BostonHousing = createDummyFeatures(task_BostonHousing)
task_BH_train =subsetTask(task_BostonHousing ,subset =train.set )
task_BH_train

$>Read 354 items
$>Supervised task: BostonHousing
$>Type: regr
$>Target: medvC
$>Observations: 354
$>Features:
$>   numerics     factors     ordered functionals 
$>         14           0           0           0 
$>Missings: FALSE
$>Has weights: FALSE
$>Has blocking: FALSE
$>Has coordinates: FALSE
```
##3.学習器の構築
makeLearner関数は、回帰なので"regr.xgboost"を指定します。

```{r}
set.seed(123)
parallelStartSocket(detectCores())
regr.lrn = makeLearner("regr.xgboost", objective= "reg:linear")

$>Starting parallelization in mode=socket with cpus=8.
```
##3.1評価関数の作成
予測した連続値から、クラスを分ける閾値の関数(SQWK)を作成します。

```{r}
SQWKfun = function(x = seq(1.5, 2.5, by = 1), pred) {
  preds = pred$data$response
  true = pred$data$truth 
  cuts = c(min(preds), x[1], x[2],  max(preds))
  preds = as.numeric(Hmisc::cut2(preds, cuts))
  err = Metrics::ScoreQuadraticWeightedKappa(preds, true, 1, 3)
  return(-err)
}

SQWK = makeMeasure(id = "SQWK", minimize = FALSE, properties = c("regr"), best = 1, worst = 0,
  fun = function(task, model, pred, feats, extra.args) {
    return(-SQWKfun(x = seq(1.5, 2.5, by = 1), pred))
  })
parallelExport("SQWK", "SQWKfun")

$>Exporting objects to slaves for mode socket: SQWK,SQWKfun
```
#4.ベイズ最適化を用いたハイパーパラメータのチューニング
まず、調整するハイパーパラメータの範囲を設定します。
リサンプリングは交差検証法を用います。
なお、第2回で用いた層化抽出法は回帰タスクでは使えません。

```{r}
ps = makeParamSet(
  
  makeNumericParam("eta", lower = 0.01, upper = 0.3),
  makeNumericParam("colsample_bytree", lower = 1, upper = 2, trafo = function(x) x/2),
  makeIntegerParam("min_child_weight", lower = 1, upper = 20),
  makeIntegerParam("nrounds", lower = 10,  upper = 300),
  makeIntegerParam("max_depth", lower = 1, upper = 8)
  )
rdesc = makeResampleDesc("CV",iters = 7)
```
ベイズ最適化によるハイパーパラメータのチューニングを実行します。

```{r}
library(Hmisc)
library(mlrMBO)
mbo.ctrl = makeMBOControl()
mbo.ctrl = setMBOControlTermination(mbo.ctrl, iters = 5)
ctrl = makeTuneControlMBO( mbo.control = mbo.ctrl)
res6 = tuneParams(learner = regr.lrn, 
                     task = task_BH_train, 
                     resampling =rdesc , 
                      par.set = ps, 
                      control = ctrl, 
                      show.info = TRUE, 
                     measures = SQWK)

res6
parallelStop()

$>[Tune] Started tuning learner regr.xgboost for parameter set:
$>
$>With control class: TuneControlMBO
$>Imputation value: -0
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 20.
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 1.
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 1.
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 1.
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 1.
$>Mapping in parallel: mode = socket; level = mlrMBO.feval; cpus = 8; elements = 1.
$>[Tune] Result: eta=0.0869; colsample_bytree=0.794; min_child_weight=15; nrounds=137; $>max_depth=7 : SQWK.test.mean=0.7915147
$>Tune result:
$>Op. pars: eta=0.0869; colsample_bytree=0.794; min_child_weight=15; nrounds=137; max_depth=7
$>SQWK.test.mean=0.7915147
$>Stopped parallelization. All cleaned up.
```
結果は、eta=0.0869; colsample_bytree=0.794; min_child_weight=15; nrounds=137; max_depth=7のときにkappaが0.79となりました。
第３回の結果が0.78でしたので、ほぼ同じです。

##5.最適なパラメータの設定とクラス分け閾値の最適化
交差検証法を使って、SQWK関数を使ったクラス分けの最適な閾値を探します。

```{r}
regr.lrn = setHyperPars(regr.lrn, par.vals = res6$x)
cv = crossval(regr.lrn, task_BH_train, iter = 7, measures = SQWK, show.info = TRUE)
parallelStop()
optCuts = optim(seq(1.5, 2.5, by = 1), SQWKfun, pred = cv$pred)
optCuts$par

$>Resampling: cross-validation
$>Measures:             SQWK      
$>[22:24:44] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 1:    0.7269116 
$>[22:24:44] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 2:    0.6842105 
$>[22:24:45] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 3:    0.7252747 
$>[22:24:45] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 4:    0.7017544 
$>[22:24:45] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 5:    0.7403055 
$>[22:24:46] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 6:    0.6921529 
$>[22:24:46] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[Resample] iter 7:    0.7660819 

$>Aggregated Result: SQWK.test.mean=0.7195274
$>[1] 1.388531 2.380424
```
交差検証の結果は0.72でした。
クラス１：　-inf　-　1.388531
クラス２：　1.388531　-　2.380424
クラス３：　2.380424　-　inf
に閾値が最適化されました。
##6.テストデータによる評価
それでは、準備ができたので学習を実行し、テストデータによる評価を行います。

```{r}
library(Metrics)
fit6 = train(regr.lrn, task_BH_train)
testTask =subsetTask(task_BostonHousing,subset =test.set )
pred = predict(fit6, testTask)
preds = as.numeric(Hmisc::cut2(pred$data$response, c(-Inf, optCuts$par, Inf)))
ScoreQuadraticWeightedKappa(preds,pred$data$truth)
table(pred$data$truth,preds)

$>[22:28:30] WARNING: amalgamation/../src/objective/regression_obj.cu:170: reg:linear is now $>deprecated in favor of reg:squarederror.
$>[1] 0.804699
$>   preds
$>     1  2  3
$>  1 52 19  0
$>  2  5 54  3
$>  3  0  1 18
```
全体の評価関数であるkappaは0.80と大幅に向上しました。
kappaの指標は0.6でgood、0.8ではexcellentになります。
クラス3の誤分類率は、ランダムフォレストによる回帰の第3回の結果が5/19、偽陽性率が1/13であり、Xgboostである今回は、誤分類率　1/19、偽陽性率　3/21になったため、ランダムフォレストの場合と比べると、誤分類率は向上しましたが、偽陽性率は少し悪くなりました。クラス３の分類精度を重視すれば、Xgboostに分配があがるかもしれません。

##7.総評
BostonHousingのデータを使って5回にわたり、順序関係があるカテゴリ分類として扱ってきましたが、結局のところ、モデルの違いよりも、回帰を使った分類問題にすることで、最も良い精度がでました。有名なXgboostがもっと精度が高くなることを期待したのですが、今回の結果では、ランダムフォレストとあまり違いは見られませんでした。

##8.PyCaretを使ってみた
pythonには自動的に機械学習モデルを比較してくれるPyCaretという便利なパッケージが最近リリースされたので、今回のBostonHousingの訓練データを使って比較してみました。
![sankou.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161102/7a97baa0-d729-08f3-416c-7712b2ed2d8e.png)

今までと同様に7分割交差検証法で比較した結果ですが、ランダムフォレストが一番でした。
ベストはkappa0.71と、ほぼこれまでの結果と同じような値になっています。