バナナでもわかる話

計量経済学・統計学が専門の大学院生です。統計学・経済学・投資理論・マーケティング等々に関する勉強・解説ブログ。ときどき趣味も。極力数式は使わずイメージで説明出来るよう心掛けていますが、時々暴走します。

【初心者向け】SVMのパラメタチューニング

前回ソフトマージンサポートベクターマシンの話をして、ラディアルカーネルにはパラメータが2つあるというところまでやりました。
bananarian.hatenablog.com


前回はパラメータをいじって、極端な値の場合は、マズそうだといったことを確認して終わったわけですが、結局どんな値を使えばいいのでしょうか。今回はそれについてやっていきます。


チューニング

前回のようにパラメータの値を変化させて、どんな値が適切かを見る作業を、機械学習の用語でパラメタのチューニングと呼びます。

前回は手作業で値を変化させたため、少しの値しか確認できませんでしたが、要はこの作業をコンピュータに任せてしまえば、もっとたくさんの候補から最適なものを決定することが出来ますね。


コンピュータが評価を行うには、まず「どんな性質を持つパラメータが適当か」ということを定義しておかなければなりません。
この評価基準がイマイチだとあまり良い結果は期待できないわけですが、今回はパッケージで簡単にやる方法をお伝えしますので、


誤判別率(どれくらいミスったか)


を基準に各パラメータの値を評価していきます。


ラディアルカーネル確認

ラディアルカーネルは二つのパラメータを持つのでした。

コストパラメータ:C
カーネルパラメータ: \gamma


Cは罰則です。この値が大きいほどミスを認めない分類器になります。
 \gammaは複雑さに関するパラメータです。



使うデータを作る

仮のデータセットを作ります。

#e1071パッケージを使う
library(e1071)
#標準正規分布に従うデータを200×2だけ2列分生成
x=matrix(rnorm(200*2), ncol=2);
#最初の150個のデータを+2だけずらす
x[1:150,]=x[1:150,]+2;
#次の50個のデータを-2だけずらす
x[151:200,]=x[151:200,]-2
#+2ずらしたものを1,-2ずらしたものを2と名前を付ける
y=c(rep(1,150), rep(2,50));
#データの生成
dat=data.frame(x=x,y=as.factor(y))
#100個適当に取り出して訓練データとする
train=sample(200,100)
train_dat=dat[train,]

以上、適当に乱数を発生させたあとに1,2でわけるために2だけずらし、そこから訓練データを適当にとってきました。


ブートストラップ法

元のサンプルを母集団に見立てて、そこからリサンプリングを行うことをブートストラップ法と呼びます。
要はこんな感じです。

f:id:bananarian:20180910203922p:plain


これを利用してサンプルサイズ \bar{n}のブートストラップ標本をB個取り出し、各ブートストラップ標本における誤判別率を出し、誤判別率の平均を取ることで、平均値が最小になるようなパラメータの組を採用するというものを考えます。



これは普通にコードを書こうとすると面倒なわけですが、e1071パッケージのtune.svm関数で簡単に出来ちゃいます。
そこで \bar{n}=(データ数の9割), B=400として試してみます。

model=tune.svm(y~., data=train_dat, kernel ="radial", 
gamma=10^(-10:-5),cost=10^(1:10),
tunecontrol=tune.control(sampling="boot",nboot=400,boot.size=9/10),best.model)
summary(model)

こんな感じでベストなパラメータが見つかります。

- best parameters:
 gamma  cost
 1e-09 1e+08
- best performance: 0 

本来ならば、チューニングは1回で済ますことなく、範囲を絞っていって何度も行うべきなのですが、とりあえず今回は1回でやめておきます。


というわけでgamma=0.0000001,cost=10000000が最適であるという結果になります。ちなみにベストパフォーマンスとは、要は今回はミスが少ない方が良いということだったわけで、誤判別率が表示されています。今回は完全に分離可能だったようで、ミスなく分離できているようです。まあ人工的につくったものなんでそりゃそうですよね。


クロスバリデーション(CV)

データの一部を抜き取って、残ったものを訓練データとして利用する方法をクロスバリデーションと呼びます。この方法であれば、訓練データと検証データに分離することが難しいような少なめのデータであってもチューニングを行うことが出来ますね


今回は1行消去CVを利用します。コードは次の通りです。

model=tune.svm(y~., data=train_dat, kernel ="radial", 
gamma=10^(-10:-5),cost=10^(1:10),
tunecontrol=tune.control(sampling="cross",cross=length(train)),best.model)
summary(model)
- best parameters:
 gamma cost
 1e-05 1000

- best performance: 0 


結果からgamma=0.0001,cost=1000が良いということがわかりました。


テストデータを使ってうまくいくか試す

クロスバリデーションから得られたチューニングパラメータを使って、テストデータに対して分類を行ってみましょう。

#テストデータの用意
test_dat=dat[-train,]
#トレーニングデータとチューニングパラメータでモデルを用意
model=svm(y~., data=train_dat, kernel ="radial",gamma=0.00005,cost=1000,probability=TRUE)
#テストデータに対して適用してみる
test_pred=predict(model,newdata=test_dat,probability=TRUE)
#判別がうまくいったかtable関数で確認
table(test_dat[,3],test_pred)
   test_pred
     1  2
  1 77  0
  2  0 23

ミスなく分類できていますね。


まとめ

このように、パラメタチューニングを行って、テストデータで検証するというプロセスが予測においては不可欠になります。
また、今回はミス率0ですが、これは人工データだから発生する現象であって、普通はこんなことはありえません。むしろ何か怪しいと思うようにした方が無難です。