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

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

##1.1mlrについて
Rでは機械学習のパッケージとしてcaretが有名ですが、mlrもよく使われているみたいです。mlrについてはcaretよりも情報が少ない感じがしますので、備忘録代わりに、少しずつ投稿していこうと思っています。
まずmlrについては、下記の参考記事を見てください。　
　[Rの機械学習パッケージmlrのチュートリアル（タスクの作成から予測まで)](https://qiita.com/nozma/items/bedcb35cba925764247a)

##2.データの準備
順序関係があるカテゴリ分類として、package(mlbench)にある米国ボストン市郊外における地域別の住宅価格のデータセットを変形してみたいと思います。
始めに必要なパッケージをインストールしておきます。

```{r}
library(ranger)
library(mlr)
library(mlbench)

data(BostonHousing, package = "mlbench")
df = BostonHousing
```
**データの概要**
crim:　町ごとの一人当たりの犯罪率
zn:　25,000平方フィートを超える区画に分けられた住宅地の割合。
indus:　町あたりの非小売業の割合(面積比）
chas: Charles River dummy variable (= 1 路が川と接している場合; 0 その他).
nox: 窒素酸化物濃度（1000万分の1）
rm: 住居あたりの部屋の平均数
age: 1940年より前に建てられた所有者が居住するユニットの割合。（データセットの調査年度が1978年）
dis: 5つのボストン雇用センターまでの距離の加重平均
rad: 環状高速道路へのアクセシビリティのインデックス
tax: 10,000ドルあたりの固定資産税率
ptratio: 町別の生徒-教師比率
black:　=1000（Bk-0.63）^ 2, ここでBkは、町の黒人の割合。
lstat: 人口に下層クラスが占める割合（%）
medv:所有者が居住する住宅の中央値（\ $ 1000s）

```{r}
df$medvC = cut(df$medv,breaks = 3,labels = c(1,2,3))
table(df$medvC)

$>  1   2   3 
$>215 243  48 
```
さて、通常はmedvを目的変数とした回帰モデルになるのですが、今回はmedvを3クラスのカテゴリーに分けました。
今回の解析の目的はクラス分類で、特にクラス3を判別させたい動機があるとします。
クラス3は48と他のクラスと比べて少なくなっています。
また、クラス（1,2,3）には順序関係があります。

##3.タスクの定義
まずタスクを定義します。
タスクとは問題の概要をまとめたオブジェクトのことで、問題の種類や、扱うデータセットに関する情報が含まれます。
最初に、train data とtest dataに分けます。
7割をtrain dataとして残りをtest data にします。
train.setは後で同じものを使うので保存しておきます。

```{r}
set.seed(123)
n = nrow(df)
train.set = sample(n, size = n*0.7)
test.set = setdiff(1:n, train.set)
write(train.set,file = "train.txt")
```
ターゲット（目的変数）は分類の場合、factorでなければいけないため、factor化してからタスクの定義をします。
分類の場合はmakeClassifTask()、回帰の場合はmakeRegrTask()を使います。

```{r}
df$medv = NULL
df$medvC = as.factor(df$medvC)
task_BostonHousing = makeClassifTask(id = "BostonHousing", data = df, target = "medvC")
task_BH_train = subsetTask(task_BostonHousing ,subset =train.set )
task_BH_train

$>Supervised task: BostonHousing
$>Type: classif
$>Target: medvC
$>Observations: 354
$>Features:
$>   numerics     factors     ordered functionals 
$>         12           1           0           0 
$>Missings: FALSE
$>Has weights: FALSE
$>Has blocking: FALSE
$>Has coordinates: FALSE
$>Classes: 3
$>  1   2   3 
$>144 181  29 
$>Positive class: NA
```
訓練データの概要を示しました。
説明変数にfactorsが一つ入っていますが、後は全てnumericsとなっています。
欠測値もありません。
クラス分類は、やや不均衡クラスとなっています。



#4.学習器の構築
学習器はmakeLearner関数で作成します。
このとき、どのような学習手法を使うのかを指定します。加えて以下の要素を指定できます。

**ハイパーパラメータの指定**
予測後の出力方法(例えば、分類問題において予測されたクラスラベルなのか、確率なのか)
ID(いくつかの手法ではこのIDを出力やプロット時の名前として利用できる)

まず、ランダムフォレスト（ranger）でモデルを作成してみます。
Permutationベースの変数重要度をみるために、importanceをpermutationに指定します。


```{r}
set.seed(123)
classif.lrn= makeLearner(  "classif.ranger",importance="permutation")
```
##4.ハイパーパラメータのチューニング及びリサンプリング
ハイパーパラメータのチューニングのための方法を指定します。
makeParamSetを使います。
mtryはintegarなのでmakeIntegerParamを用います。
そしてグリッドサーチ法を用います（makeTuneControlGrid）。
リサンプリングの手法は（makeResampleDesc）７分割のクロスヴァリデーションを指定します。


>mlrではmakeResampleDesc関数を使ってリサンプリング手法を設定する。
この関数にはリサンプリング手法>の名前とともに、手法に応じてその他の情報(例えば繰り返し数など)を指定する。サポートしているサンプリング手法は以下のとおりである。
CV: クロスバリデーション(Cross-varidation)
LOO: 一つ抜き法(Leave-one-out cross-varidation)
RepCV: Repeatedクロスバリデーション(Repeated cross-varidation)
Bootstrap: out-of-bagブートストラップとそのバリエーション(b632等)
Subsample: サブサンプリング(モンテカルロクロスバリデーションとも呼ばれる)
Holdout: ホールドアウト法 

ランダムフォレストには作成する決定木の数(ntree)と、１つ１つの決定木を作成する際に使用する特徴量の数(mtry)の２つのパラメータがありますが、ここでは、決定木の数(ntree)は固定して、特徴量の数(mtry)をパラメータをグリッドサーチ法でチューニングします。


```{r}
ps = makeParamSet(makeIntegerParam("mtry", lower = 1, upper = 13))
ctrl = makeTuneControlGrid(resolution =  13)
rdesc = makeResampleDesc("CV",iters = 7)
rdesc

$>Resample description: cross-validation with 7 iterations.
$>Predict: test
$>Stratification: FALSE
```

##5.パラメータのチューニングの実行
設定が終わりましたので、tuneParamsを使って、チューニングを実行します。
モデルの評価関数は順序型クラス分類なのでkappaを指定します。
mlrではparallelMapパッケージで並列処理を実行できます。

```{r}
library("parallel")　#detectCores()を使ってCPUコア数を取得する
library("parallelMap")
set.seed(123)
parallelStartSocket(detectCores())
res = tuneParams(classif.lrn, task = task_BH_train, resampling = rdesc, par.set = ps, control = ctrl,measures = kappa)
parallelStop()　#parallelStart~で並列処理が始まり、parallelStopで止める

$>Starting parallelization in mode=socket with cpus=8.
$>[Tune] Started tuning learner classif.ranger for parameter set:
$>With control class: TuneControlGrid
$>Imputation value: 1
$>Exporting objects to slaves for mode socket: .mlr.slave.options
$>Mapping in parallel: mode = socket; level = mlr.tuneParams; cpus = 8; elements = 13.
$>[Tune] Result: mtry=11 : kappa.test.mean=0.7013263
$>Stopped parallelization. All cleaned up.
```
##6.チューニングした結果を学習器にセット
チューニングの結果は、mtry=11のときに、クロスバリデーションによるベストがkappaは0.70となりました。
setHyperParsを使って、学習器にハイパーパラメータ（mtry=11）をセットします。

```{r}
classif.lrn = setHyperPars(classif.lrn, par.vals = res$x)
```

##7.訓練データを用いた学習器の訓練
早速、訓練データを使ってモデルを作成します。

```{r}
fit = train(classif.lrn, task_BH_train)
```
##8.テストデータの作成
モデルの評価をするため、訓練に用いていないテストデータを作成します。

```{r}
testTask = subsetTask(task_BostonHousing ,subset =test.set )
testTask

$>Supervised task: BostonHousing
$>Type: classif
$>Target: medvC
$>Observations: 152
$>Features:
$>   numerics     factors     ordered functionals 
$>         12           1           0           0 
$>Missings: FALSE
$>Has weights: FALSE
$>Has blocking: FALSE
$>Has coordinates: FALSE
$>Classes: 3
$> 1  2  3 
$>71 62 19 
Positive class: NA
```
テストデータは152のデータでクラス１とクラス２がほぼ同数、クラス３が他のクラスの半分以下の構成になっています。
##9.予測の実行と評価

```{r}
pred = predict(fit, testTask)
performance(pred,measures = kappa)
calculateConfusionMatrix(pred,sums = T)

$>    kappa 
$>0.6910121 
$>        1  2  3 -err.- -n-
$>1      57 14  0     14  71
$>2       9 52  1     10  62
$>3       0  4 15      4  19
$>-err.-  9 18  1     28  NA
$>-n-    66 70 16     NA 354　　
```

ランダムフォレストによる判別分析のテストデータによる検証結果は、kappaは0.69です。通常、kappaは0.6を超えると分類性能は良いとされるので、まずまずの結果です。
クラス３誤分類率は4/19＝0.21、偽陽性率は1/16＝0.06となりました。偽陽性率は低いですが、誤分類率が２割であるため、全てのクラス３を補足することがやや難しいです。

さて、次回もさらにモデルの検討を進めてみます。
[機械学習パッケージmlrを使った順序関係があるカテゴリ分類（その２）](https://qiita.com/kenkenvw/items/035273992afb98febe5e)

##10.参考
[mlrパッケージチュートリアル - Quick Walkthrough編](https://nozma.github.io/mlr_tutorial/)