読者です 読者をやめる 読者になる 読者になる

まんぼう日記

takataka's diary

Theano で Convolutional Neural Net

先日の Theano で Multi Layer Perceptron - まんぼう日記 のつづき.Theano で convolution + pooling する層を作ってみます.

 

関連記事:

 

準備として前回のプログラムの改良から 

convolution + pooling のプログラムを書きはじめたら,パラメータがいっぱいあってぐちゃぐちゃになりそうやったので,その前に前回のMLPのプログラムを改良することにしました.前回のプログラムでは,層毎の入出力を定義する nnet0130.py はクラス使ってませんでしたが,今度はひとつの層を一つのインスタンスとする方向で.これなら後でパラメータいろいろでてきてもインスタンス変数使ってすっきりかなと.

 

ソースはこちら:

実験結果は当然 Theano で Multi Layer Perceptron - まんぼう日記 と同じなので省略.

 

convolution + pooling の層を書く

で,nnet0207 と同じのりで convolution + pooling の層の入出力を定義します.以下のページを参考にしました.

 

できたプログラムはこちら: convnet0207.py

 

convolutionのステップでは,Xrow x Xcol の大きさで Xnch チャンネルある画像(例えばRGBカラー画像なら Xnch = 3 としたらよい)を,Wrow x Wcol のフィルタ Wnch 種類で畳み込みます.その出力は,Wnch x Yrow x Ycol という大きさ.

 

次の max-pooling のステップでは,ds の表すスケールで max-pooling によるダウンサンプリングをします.例えば ds = ( 2, 2 ) なら,隣接 2 x 2 の領域内の最大値を取り出すことで縦横半分の大きさにダウンサンプリングします.出力は,Wnch x Zrow x Zcol という大きさ.

 

その値に afunc で指定した活性化関数(シグモイドとか ReLu とか)をかけたものが最終的にこの層の出力となります.

 

convnet を使った2層&3層MLPのプログラム

さて,convnet0207 と nnet0207 を組み合わせて,2層(convolution + pooling の隠れ層 + softmax の出力層)と3層(もう一つconvolution + pooling の隠れ層を追加)のプログラムを書きます.のりは nnet0207 を使って mlp_2layer0207 と mlp_3layer0207 を書くのとほとんど同じ.

 

ソースはこちら:

 

MNIST の識別実験

先にお断りちうか言い訳しときますが,以下の実験は(これまでのと同様に)いいかげんです.ニューラルネットの学習は初期値に依存するので,本当は初期値を変えながら何度も試行を重ねるべきものです.学習定数とか慣性係数とか重みの初期値とかの設定もいいかげんやし.学習が収束したかどうかとかちゃんとチェックせずに同じ繰り返し回数で止めてるとこもあれですし.だから,「こっちの条件の方がよかった」みたいな話も,話半分というか話1%くらいにとっとくのがよいでせう.

実験条件
  • MNIST のデータを使うので,入力は Xrow x Xcol = 28 x 28, Xnch = 1, 出力 K = 10
  • 学習定数 eta = 0.5, 慣性係数 mu = 0.8, バッチサイズ batchsize = 100 の慣性項付きSGD
  • マシンは Mac Pro (Late 2013),  Intel Xeon E5 3.5GHz(6コア),メモリ64GB (前回や上記 Pylearn2 での実験とは違うマシン)

 

2層の場合

とりあえず適当に動かしてみた.afunc は linear です.

In [2]: %time %run mnist_convnet0207.py
### 2-layer convnet
# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (2, 2) H: 2304
# H: 2304
### training: NL = 50000 NV = 10000 K = 10 batchsize = 100
0 2.30414773606 90.966 90.73
10 0.0379495452887 1.21 1.97
20 0.021452008206 0.654 1.83
30 0.0169548339162 0.58 2.07
40 0.00591079987465 0.144 1.87
50 0.0018925089269 0.018 1.91
# NT = 10000
50 0.0855781332512 0.018 1.91 1.81
CPU times: user 30min 38s, sys: 19 s, total: 30min 57s
Wall time: 30min 24s

テストデータに対する誤識別率は 1.81%.前回の結果( Theano で Multi Layer Perceptron - まんぼう日記 )と比較すると,誤識別率はほとんど変わらず,時間が大幅に延びてます.

 

上記の W1nch のみ 16 から 64 に増やしてみると

# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 64 ds1: (2, 2) H: 9216
中略
50 0.0730611050785 0.0 1.74 1.64
CPU times: user 1h 58min 47s, sys: 2min 24s, total: 2h 1min 12s
Wall time: 1h 59min 21s

となって,時間がさらにかかるわりに誤識別率は下がりません.そこで,W1nch は 16 に戻して,ds1 を (2,2) から (4,4) にしてみると....

# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (4, 4) H: 576
中略
50 0.0556093143135 0.372 1.43 1.64
CPU times: user 28min 10s, sys: 16.7 s, total: 28min 26s
Wall time: 28min 18s

さらに W1nch も 64 にしてみると...

# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 64 ds1: (4, 4) H: 2304
中略
50 0.0471418802848 0.034 1.16 1.38
CPU times: user 1h 49min, sys: 2min 9s, total: 1h 51min 10s
Wall time: 1h 50min 42s

ds1 を (4,4) とした方がよいですね.その方がより大きな平行移動に対応できるからでしょうか.softmax層との間の結合重みの数が減るけど性能は落ちないと.また,ds1 を変えてもほとんど計算時間がかわりませんので,convolution の計算コストが支配的であることも分かります.

 

ちなみに,pylearn2 のサンプル動かしたときは,マルチスレッドで動くけども実質1コア分のCPU使用率で遅いのがいやん,だったわけですが( cf. Pylearn2 の tutorial でお勉強 - 5時間目 - まんぼう日記 ),上記の実験でも6スレッドで動くけど実質1コア分のCPU使用率でした (;_;)

 

さて,もうひとつ.afunc を ReLu にした実験.

# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (4, 4) H: 576
中略
50 0.0536758042328 0.414 1.4 1.42

同条件のlinearよりちょっとだけよいかな.

2015-02-10 追記: この実験ではしきい値を入れ忘れてました.ちゃんとした実験結果は Theano で Multi Layer Perceptron & Convolutional Neural Net - まんぼう日記 にあります(かわりありませんが).

 

3層の場合その1

convolution+poolingを2層使った3層netでの実験.afunc はいずれもlinearです.

 

まずは,Convolutional Neural Networks (LeNet) — DeepLearning 0.1 documentation のMNISTでの実験の条件に近い所で.

### 3-layer convnet
# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (2, 2) H1: 2304
# W2dim: (5, 5) W2nch: 16 ds2: (2, 2) H2: 256
### training: NL = 50000 NV = 10000 K = 10 batchsize = 100
0 2.30397444501 93.086 93.49
10 0.0297156974283 0.95 1.53
20 0.0168531124172 0.634 1.67
30 0.0225630587005 0.674 1.88
40 0.0191874580726 0.518 1.73
50 0.0478702376889 1.012 2.16
# NT = 10000
50 0.152915086662 1.012 2.16 1.93
CPU times: user 1h 46min 37s, sys: 20.5 s, total: 1h 46min 58s
Wall time: 1h 46min 56s

ちょっと学習が振動してる様子.学習定数とか繰り返し回数とかもう少し調整した方がよいかもしれませんが,放置して先へ.

 

2層の場合の結果をふまえて,ds1, ds2 とも (2,2) から (4,4) にしてみます.

# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (4, 4) H1: 576
# W2dim: (5, 5) W2nch: 16 ds2: (4, 4) H2: 16
中略
50 0.069687151596 0.0 1.34 1.28
CPU times: user 52min 11s, sys: 1min 58s, total: 54min 9s
Wall time: 54min 9s

やぱしこっちの方がよい感じ. 

 

上記の ds1 = (2,2) の方の実験は 50 epoch で約100分.2min/epochですな.pylearn2 での 20min/epoch ( Pylearn2 の tutorial でお勉強 - 5時間目 - まんぼう日記 )よりずっと速いですが,あっちは1層目2層目ともに linear じゃなく ReLu で,フィルタの数 W1nch, W2nch がともにこっちの4倍(いずれも64)で,pooling は ds = (4,4) やけど縦横2画素ずつずらしながらなのでこっちの ds = (2,2) と同じくらい(2層目の入力の大きさ的に),というわけで,あっちと同じ条件のプログラム書いて動かしたら同じくらい遅くなりそうです.結局,convolution がボトルネックですね.

 

3層の場合その2

MNISTくらい小さい画像ならconvolution+poolingは1層で十分ちゃうかと思って,2層目を普通の ReLu にした実験もやってみました.

### 3-layer 
# Xdim: (28, 28) Xnch: 1 W1dim: (5, 5) W1nch: 16 ds1: (4, 4) H1: 576
# H2: 400
### training: NL = 50000 NV = 10000 K = 10 batchsize = 100
0 2.30465530719 90.468 90.71
10 0.0105110151366 0.29 1.18
20 0.001184633703 0.012 0.96
30 0.000609231586144 0.008 0.99
40 0.000216548649597 0.0 0.92
50 0.000150985761616 0.0 0.93
# NT = 10000
50 0.0332950428917 0.0 0.93 0.78
CPU times: user 29min 35s, sys: 20.6 s, total: 29min 56s
Wall time: 27min 18s

ちうわけで,こっちで十分でした.上述の Pylearn2 で 20min/epoch かかる条件ではテスト誤識別率 0.74% やそう(チュートリアルに載ってる値)なので,こっちの方がずっとええです. しかも,あっちと違ってこっちは重みの正則化まだ入れてないし.

 

 

さて,次はどうしようかな.