1.はじめに
仕事では順序関係があるカテゴリ分類のデータを扱うことが多いので、Rのmlrパッケージを使った機械学習にチャレンジしてみました。
ちなみに、仕事はIT関係では全くないので、全くの素人分析屋です。
前回までです。
機械学習パッケージmlrを使った順序関係があるカテゴリ分類(その1)
機械学習パッケージmlrを使った順序関係があるカテゴリ分類(その2)
機械学習パッケージmlrを使った順序関係があるカテゴリ分類(その3)
機械学習パッケージmlrを使った順序関係があるカテゴリ分類(その4)
前回からXgboostを使っています。今回は回帰でやってみます。
2.データとタスクの定義
早速、タスクを定義していきます。
まずは、分類問題としてやってみます。
xgboostのクラス分類は0から始める必要があるため、1を引きます。
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"を指定します。
set.seed(123)
parallelStartSocket(detectCores())
regr.lrn = makeLearner("regr.xgboost", objective= "reg:linear")
$>Starting parallelization in mode=socket with cpus=8.
3.1評価関数の作成
予測した連続値から、クラスを分ける閾値の関数(SQWK)を作成します。
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回で用いた層化抽出法は回帰タスクでは使えません。
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)
ベイズ最適化によるハイパーパラメータのチューニングを実行します。
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となりました。
第3回の結果が0.78でしたので、ほぼ同じです。
5.最適なパラメータの設定とクラス分け閾値の最適化
交差検証法を使って、SQWK関数を使ったクラス分けの最適な閾値を探します。
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でした。
クラス1: -inf - 1.388531
クラス2: 1.388531 - 2.380424
クラス3: 2.380424 - inf
に閾値が最適化されました。
6.テストデータによる評価
それでは、準備ができたので学習を実行し、テストデータによる評価を行います。
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になったため、ランダムフォレストの場合と比べると、誤分類率は向上しましたが、偽陽性率は少し悪くなりました。クラス3の分類精度を重視すれば、Xgboostに分配があがるかもしれません。
7.総評
BostonHousingのデータを使って5回にわたり、順序関係があるカテゴリ分類として扱ってきましたが、結局のところ、モデルの違いよりも、回帰を使った分類問題にすることで、最も良い精度がでました。有名なXgboostがもっと精度が高くなることを期待したのですが、今回の結果では、ランダムフォレストとあまり違いは見られませんでした。
8.PyCaretを使ってみた
pythonには自動的に機械学習モデルを比較してくれるPyCaretという便利なパッケージが最近リリースされたので、今回のBostonHousingの訓練データを使って比較してみました。
今までと同様に7分割交差検証法で比較した結果ですが、ランダムフォレストが一番でした。
ベストはkappa0.71と、ほぼこれまでの結果と同じような値になっています。