Theano で Convolutional Neural Net
先日の Theano で Multi Layer Perceptron - まんぼう日記 のつづき.Theano で convolution + pooling する層を作ってみます.
関連記事:
- MNISTのデータを読み込むPythonプログラム - まんぼう日記 mnist0117.py
- (2015-02-10追記)Theano で Multi Layer Perceptron & Convolutional Neural Net - まんぼう日記 以下のプログラムをさらに改良&整理してます
準備として前回のプログラムの改良から
convolution + pooling のプログラムを書きはじめたら,パラメータがいっぱいあってぐちゃぐちゃになりそうやったので,その前に前回のMLPのプログラムを改良することにしました.前回のプログラムでは,層毎の入出力を定義する nnet0130.py はクラス使ってませんでしたが,今度はひとつの層を一つのインスタンスとする方向で.これなら後でパラメータいろいろでてきてもインスタンス変数使ってすっきりかなと.
ソースはこちら:
- mnist0117.py cf. MNISTのデータを読み込むPythonプログラム - まんぼう日記
- nnet0207.py 一つの層の入出力を定義する
- mlp_2layer0207.py nnet0207 を使って2層MLPの入出力と学習則を書いたもの
- mlp_3layer0207.py 同3層MLP
- mnist_mlp0207.py 上記を使ってMNISTの識別実験をするプログラム
実験結果は当然 Theano で Multi Layer Perceptron - まんぼう日記 と同じなので省略.
convolution + pooling の層を書く
で,nnet0207 と同じのりで convolution + pooling の層の入出力を定義します.以下のページを参考にしました.
-
Convolutional Neural Networks (LeNet) — DeepLearning 0.1 documentation Convolutional Neural Net について
-
conv – Ops for convolutional neural nets — Theano 0.6 documentation nnet.conv2d
-
downsample – Down-Sampling — Theano 0.6 documentation max_pool_2d
できたプログラムはこちら: 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 を書くのとほとんど同じ.
ソースはこちら:
- convnet_2layer0207.py convnet0207 使って2層MLPの入出力と学習則を書いたもの
- convnet_3layer0207.py 同3層MLP
- mnist_convnet0207.py 上記を使ってMNISTの識別実験をするプログラム
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% やそう(チュートリアルに載ってる値)なので,こっちの方がずっとええです. しかも,あっちと違ってこっちは重みの正則化まだ入れてないし.
さて,次はどうしようかな.