##1.はじめに
仕事では順序関係があるカテゴリ分類のデータを扱うことが多いので、Rのmlrパッケージを使った機械学習にチャレンジしてみました。
ちなみに、仕事はIT関係では全くないので、全くの素人分析屋です。
##1.1mlrについて
Rでは機械学習のパッケージとしてcaretが有名ですが、mlrもよく使われているみたいです。mlrについてはcaretよりも情報が少ない感じがしますので、備忘録代わりに、少しずつ投稿していこうと思っています。
まずmlrについては、下記の参考記事を見てください。
Rの機械学習パッケージmlrのチュートリアル(タスクの作成から予測まで)
##2.データの準備
順序関係があるカテゴリ分類として、package(mlbench)にある米国ボストン市郊外における地域別の住宅価格のデータセットを変形してみたいと思います。
始めに必要なパッケージをインストールしておきます。
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)
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は後で同じものを使うので保存しておきます。
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()を使います。
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に指定します。
set.seed(123)
classif.lrn= makeLearner( "classif.ranger",importance="permutation")
##4.ハイパーパラメータのチューニング及びリサンプリング
ハイパーパラメータのチューニングのための方法を指定します。
makeParamSetを使います。
mtryはintegarなのでmakeIntegerParamを用います。
そしてグリッドサーチ法を用います(makeTuneControlGrid)。
リサンプリングの手法は(makeResampleDesc)7分割のクロスヴァリデーションを指定します。
mlrではmakeResampleDesc関数を使ってリサンプリング手法を設定する。
この関数にはリサンプリング手法>の名前とともに、手法に応じてその他の情報(例えば繰り返し数など)を指定する。サポートしているサンプリング手法は以下のとおりである。
CV: クロスバリデーション(Cross-varidation)
LOO: 一つ抜き法(Leave-one-out cross-varidation)
RepCV: Repeatedクロスバリデーション(Repeated cross-varidation)
Bootstrap: out-of-bagブートストラップとそのバリエーション(b632等)
Subsample: サブサンプリング(モンテカルロクロスバリデーションとも呼ばれる)
Holdout: ホールドアウト法
ランダムフォレストには作成する決定木の数(ntree)と、1つ1つの決定木を作成する際に使用する特徴量の数(mtry)の2つのパラメータがありますが、ここでは、決定木の数(ntree)は固定して、特徴量の数(mtry)をパラメータをグリッドサーチ法でチューニングします。
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パッケージで並列処理を実行できます。
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)をセットします。
classif.lrn = setHyperPars(classif.lrn, par.vals = res$x)
##7.訓練データを用いた学習器の訓練
早速、訓練データを使ってモデルを作成します。
fit = train(classif.lrn, task_BH_train)
##8.テストデータの作成
モデルの評価をするため、訓練に用いていないテストデータを作成します。
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のデータでクラス1とクラス2がほぼ同数、クラス3が他のクラスの半分以下の構成になっています。
##9.予測の実行と評価
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を超えると分類性能は良いとされるので、まずまずの結果です。
クラス3誤分類率は4/19=0.21、偽陽性率は1/16=0.06となりました。偽陽性率は低いですが、誤分類率が2割であるため、全てのクラス3を補足することがやや難しいです。
さて、次回もさらにモデルの検討を進めてみます。
機械学習パッケージmlrを使った順序関係があるカテゴリ分類(その2)
##10.参考
mlrパッケージチュートリアル - Quick Walkthrough編