渋谷駅前で働くデータサイエンティストのブログ

元祖「六本木で働くデータサイエンティスト」です / 道玄坂→銀座→東京→六本木→渋谷駅前

H2OのRパッケージ{h2o}でお手軽にDeep Learningを実践してみる(2):学習データとパラメータを変えて試す

うっかりこのシリーズでJapan.Rで喋ることに決めてしまったせいで関係各方面からのプレッシャーを感じつつある今日この頃ですが。



ともあれ一度乗ってしまった舟なので、このままだらだら{h2o}でDeep Learningというお題でちょっとシリーズ的にやってみようと思います。前回は適当に決定境界を描くところまではやってみたので、今度はパラメータをいじりながら決定境界を描き分けるということをやってみます。なお、他の分類器との比較という点では以前の記事をご覧いただくのがよろしいかと。



そうそう、前回もちょっと書きましたが今回のシリーズではスモールデータしか扱いませんので、必然的にDeep Learningならではの利点が生きない局面も多いです。この点はそもそも大して手間をかけてない記事なのでどうか悪しからず、ということで。。。ちなみに最近話題のCaffeなんかも興味はありますし、さらに画像分類やるならConvolutional NNについても理解しなきゃいけないなぁというのはあるんですが。



http://ceromondo.blogspot.jp/2012/09/convolutional-neural-network.html


もっともこれ以上ゴリゴリやるとしたらC++を書けない僕としてはPythonバインディングで走らせる話になりそうなので、手を出すならまたシリーズを改めてやろうかと思ってます。


h2o.deeplearningでチューニング出来る各パラメータの意味


以前も強調しましたが、機械学習はパラメータを適切にチューニングしてこそ威力を発揮するもの。裏を返せば、パラメータがデタラメならただのゴミです。ということで、前回の記事でちろっと触れた各パラメータの意味をメモっておきます。


ちなみに、@先生がDeep Learningの動向についてまとめられた資料を公開されていて、これが非常に分かりやすいので参考までにリンクを張っておきます。

ここ最近のトレンドではRBMやautoencoderも既に使われなくなり始めていて、Maxoutでベタっと多段に組んでチューニングして合わせていくという方が段々主流になってきているということのようで。

activation

ここは活性化関数、つまりNNでは必須の閾値関数の形を定義するところです。この辺の話は以前のパッケージユーザーのための機械学習シリーズのNNの記事をお読みいただくか、Wikibooksの記事をご覧いただいた方が早いかと。


従来はsigmoidとかtanhとかが使われてきたわけですが、RectifierやMaxoutが最近では高性能ということで人気があるようです。なお従来型のsigmoid / tanhについては今は亡き現在某社におられる「教授」氏が書いた解説が弊社エンジニアブログにあります。


RectifierやMaxoutについては他にも以下のような資料があります。最近はautoencoderによるレイヤーごと最適化はやらずに、Maxoutでざっくりやる方が流行ってるとかかんとか。


ちなみにMaxoutは比較的最近提案されたばかりの活性化関数なんですが、H2Oは既に実装済みということで有難い限りです。


なお、Dropoutをつけるかどうかもここで指定する必要があります。Dropoutについては@先生のスライドにもありますが、要は中間層のユニットをランダムに途中で削除することで、ランダムフォレストっぽく学習データごとに作られるモデル同士の相関を下げることになり、汎化性能が上がるという効果が期待されます。昨年のNIPSではL1正則化とL2正則化のミックスのような数学的意味合いを持ち得るということを示した研究も出てました。

hidden

ここでユニット数を隠れ層ごとに整数値で与えてやります。1層しか与えない(例えばhidden = 3とか)とただのNNになるだけなので、H2Oを使う意味はありませんw 隠れ層ごとにユニット数をいじるのは、一般にはConvNetsで画像認識をやる時のように「解像度ごとに識別モデルを設定する」ような意味合いがある。。。と僕は勝手に理解してます*1

autoencoder

論理値(TRUE / FALSE)でautoencoderの有無を決めます。Maxoutを使うケースでは要らないようです(ってかむしろ入ったらどうするんだろう)。

epochs

繰り返し回数。

hidden_dropout_ratios

Dropoutをつける場合、各レイヤーごとにランダムに落とす信号の比率を決めることができます。デフォルトでは何レイヤーあろうが全て0.5。これは個々のレイヤーがどのような情報を処理しているかによってフレキシブルに変えた方が良いのでしょうが、そこに手を入れると色々ハマりそうです。。。


実際に各パラメータを変えながらXORパターンの決定境界を描き分けてみる


ということで、実際にh2o.deeplearning使いながら色々やってみましょう。事前にXORパターンのシンプル版複雑版、さらにグリッドデータは別に落としておきましょう。実はそのヘッダなしバージョンも別に作ってあるので、ヘッダなしのXORシンプル版複雑版グリッドも落としておいて下さい。


先に環境を整えておきましょう。

> xorc <- read.table("xor_complex.txt", header=T)
> xors <- read.table("xor_simple.txt", header=T)
> library(h2o)
> localH2O <- h2o.init(ip = "localhost", port = 54321, startH2O = TRUE, nthreads=-1)
> xorcData<-h2o.importFile(localH2O,path="xor_complex_wo_header.txt")
> xorsData<-h2o.importFile(localH2O,path="xor_simple_wo_header.txt")
> pgData<-h2o.importFile(localH2O,path="pgrid_wo_header.txt")


ここからやることは基本的には以下のルーチンだけです。コード中xorsとあるところはシンプル版ならxors、複雑版ならxorcに書き換えればOKです。冒頭のh2o.deeplearningの引数であるactivation, hiddenをいじっていきます。


ただし、今回は本当にサンプルサイズが小さい(100個だけ)なので、基本的にはサンプルサイズが大きい時に効果的なものはどれもまともに機能しないことが予想されるので、そこは悪しからずということで。

> res.dl<-h2o.deeplearning(x=1:2,y=3,data=xorsData,classification=T,activation="Tanh",hidden=c(10,10),epochs=20)
> prd.dl<-h2o.predict(res.dl,newdata=pgData)
> prd.dl.df<-as.data.frame(prd.dl)
> plot(xors[,-3],pch=19,col=c(rep('blue',50),rep('red',50)),cex=3,xlim=c(-4,4),ylim=c(-4,4), main="Tanh, (10,10)")
> par(new=T)
> contour(px,py,array(prd.dl.df[,1],dim=c(length(px),length(py))),xlim=c(-4,4),ylim=c(-4,4),col="purple",lwd=3,drawlabels=F)


その他細かいところは皆さんで適宜調整してください。


シンプルパターンの場合:2層

Rectifier

f:id:TJO:20141106231514p:plain

f:id:TJO:20141106231525p:plain

Maxout

f:id:TJO:20141106231621p:plain

f:id:TJO:20141106231538p:plain


こんな感じで、とりあえず10x10ユニットだと決定境界が描ける感じです。見ただけの印象ではTanhとRectifierならそこそこの決定境界になるようですね。もちろんユニット数を変えてみるのも面白いと思います。


困ったのがMaxoutで、Dropoutなしだと1クラスにしか分類されなくて決定境界が描けず。。。これはちょっと原理のところ真面目に勉強した方が良いかも。


複雑パターンの場合:2層

Rectifier

f:id:TJO:20141106232909p:plain

f:id:TJO:20141106232919p:plain

Maxout

f:id:TJO:20141106232934p:plain

f:id:TJO:20141106233003p:plain


複雑パターンになった途端にちょっと様相が変わって、Tanhが変てこな決定境界を描くようになりました。一方でシンプルパターンではまるで機能しなかったMaxoutがそこそこ良い決定境界を描くようになり、Maxout with Dropoutではそれなりに意味のありそうな決定境界を描いてくれています。


どちらでも安定している印象がRectifierで、それなりにいけてそうな決定境界をシンプルパターンでも複雑パターンでも描いています。これはちょっと面白いなぁと。


シンプルパターンの場合:3層

Rectifier

f:id:TJO:20141106234338p:plain


ちなみにMaxoutと、全てのDropout版は全然まともに動きませんでした。。。この辺はなかなか難しい気が。3層になったことでやや汎化性能が落ちてる気もします。


複雑パターンの場合:3層

Recitifier

f:id:TJO:20141106235236p:plain

f:id:TJO:20141106235245p:plain


はっきり言って複雑パターン×3層だとまともに動いている気がしないと言いますか。。。


最後に


上にも書きましたが、基本的にXORパターンデータだとそもそもサンプルサイズが小さいのであまりDeep Learningの良さが生きてこないんですよね。ということで、次回はもうちょっとDeep Learningの良さが生きそうなデータでやってみようかと思います。


そうそう、ユニット数なんですがHinton先生の論文なんか読んでると結構アホみたいに多めでも良いみたいです。もしかしたら特徴次元数の5倍ぐらいまではOKなんじゃないか的な。でもこれも結局グリッドサーチとかで決めた方がいいんでしょうね。。。

*1:いつも通り炎上ラーニング中なので間違っていたらバシバシ突っ込んで下さい