2019年5月29日水曜日

KerasチュートリアルのCNNがうまくいかない

(2019/05/31:末尾に追記しました)
 深層学習を勉強する機運が高まったので「PythonとKerasによるディープラーニング」を読んでいる。第5章ではまずMNIST数字分類にCNNを使うのだが、サンプルコードを写経したプログラムがうまくいかないので困っている。誰か教えてください。
 こちらがそのノートブック。MNISTデータ読み込みなどが終わった後、前半ではサンプルコード通りのネットワークを持つ分類器を使っている。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten_1 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                36928     
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0

その結果がこれである。

Epoch 1/5
60000/60000 [==============================] - 97s 2ms/step - loss: 14.0193 - acc: 0.1115
Epoch 2/5
60000/60000 [==============================] - 88s 1ms/step - loss: 14.4351 - acc: 0.1044
Epoch 3/5
60000/60000 [==============================] - 86s 1ms/step - loss: 14.4351 - acc: 0.1044
Epoch 4/5
60000/60000 [==============================] - 91s 2ms/step - loss: 14.4351 - acc: 0.1044
Epoch 5/5
60000/60000 [==============================] - 88s 1ms/step - loss: 14.4351 - acc: 0.1044

 テストデータでの正答率も0.1028と何も学んでいない。どこかでコードミスっているか?と思い、サンプルコードをgit cloneしてそのまま実行してもやはり同じようになった。
 次に上のモデルのConv2D_3が妙に気になったので消してみたのが後半。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_4 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 1600)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 64)                102464    
_________________________________________________________________
dense_4 (Dense)              (None, 10)                650       
=================================================================
Total params: 121,930
Trainable params: 121,930
Non-trainable params: 0

 その結果がこれである。

Epoch 1/5
60000/60000 [==============================] - 85s 1ms/step - loss: 0.1748 - acc: 0.9473
Epoch 2/5
60000/60000 [==============================] - 87s 1ms/step - loss: 0.0534 - acc: 0.9835
Epoch 3/5
60000/60000 [==============================] - 85s 1ms/step - loss: 0.0391 - acc: 0.9881
Epoch 4/5
60000/60000 [==============================] - 84s 1ms/step - loss: 0.0309 - acc: 0.9911
Epoch 5/5
60000/60000 [==============================] - 84s 1ms/step - loss: 0.0259 - acc: 0.9922

 テストデータでの正答率も0.9899と高い。この結果からはこのConv2D_3が悪かったんかなぁという推測を生む以上の何かは得られなかった。
 その次に、イヌとネコの分類器を作るパートでも同様にサンプルコード通りに試してみた。そのノートブックがこちら
 モデルのサマリはこんな感じ。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

 実行結果はこんな感じ。

Epoch 1/30
100/100 [==============================] - 199s 2s/step - loss: 3.9247 - acc: 0.4875 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 2/30
100/100 [==============================] - 190s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 3/30
100/100 [==============================] - 197s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 4/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 5/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 6/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 7/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 8/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 9/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 10/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 11/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 12/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 13/30
100/100 [==============================] - 194s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 14/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 15/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 16/30
100/100 [==============================] - 195s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 17/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 18/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 19/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 20/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 21/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 22/30
100/100 [==============================] - 195s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 23/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 24/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 25/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 26/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 27/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 28/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 29/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 30/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000

 2つの分類で正答率0.5なのでやはりダメダメである。何故サンプルコード通りでうまくいかないのかが全くわからない。それとlossが全く動いていないのが気になる。この手の「同じ値を出力し続ける」ようなことって、記憶が曖昧だがメモリ足りない時とかに起きるようなことがあった気がするなぁなんて思いながら結果を眺めていた。
 おそらくMNISTでConv2D_3が入るとうまくいかない理由と、イヌネコ分類器がうまくいかない理由は一緒なんだろうという気がしている。ただその原因が全く分からない。ググっても同じような人は見当たらない。どこかでタイポしているのだろうか。なんなのか。今日はずっとこれの原因を探していたがもう限界である。

(追記)
 ノートブック
 やはりオーバーフローの類が原因として濃厚だったためバッチサイズを10にしたところ、たまに学習がうまくいくようになった。ただやはりうまく学習するモデルとそうでないモデルの違いがよくわからず模索した跡が、ノートブックのmodel1~3の部分。
 そんな時に、学習と関係ないコマンドを追加しただけなのにうまく学習が進んでいたモデルがうまく学習しなかったところでハッと気づいた。そこで上のmodel2を10回繰り返したのが、ノートブック後半部。
 結果としてうまくいったりうまくいかなかったりするものとが出てきたので、なるほどなと。こういうこともあるのだな。

 しかし、「局所解にハマってlossがなかなか下がらない」ならともかく、「lossがドカンと上がってしまう」というのは、一体何が起きているのか直感的によくわからないですね。やっぱり詳しい人教えてください。

2019年5月1日水曜日

影が重なる時に伸びるように見えるやつ(2)

 今回のソースコードはこちら。

 まあ前回とほとんど変わってないです。変わったところは2点。

(1)integrate.quadをリストラし、単純な区分求積法で代用
 その結果、有限の連続光源の再現時に中央上に出現していた明るさ0未満の領域がなくなった。


 また、dが10^-2のオーダーだった時に出現していた明るい部分のボツボツの影も消えた。 


 そんなわけで、前回の反意図的な部分はintegrate.quadが影響していた可能性が高いです。

(2)照明パターンを追加
 (イメージとしては蛍光灯が間隔をあけて並んでいるような、)連続光源があったりなかったりするパターンを考えていなかったので、system_dottedクラスを作って実験。照明の長さ0.5、間隔0.5での結果。


 あっ、キレイ…( ^ω^)

 割と満足した結果が得られたのでこの話題は終わりかな。
 この記事書いてて思い出したものとして、「レンズの目の前にある物体の周りの像がゆがむ」のも再現できるかなーと思ったので、そのうち暇つぶしでやります。

(追記)
 新しい照明パターン、これあってんのか?a大きくしてると明るかった部分が明るくなったり暗くなったりするんか?

(追記2)
 まず画像をよく見ると、縞模様は縦と斜めの2つが出現している。思考実験上、縦の縞模様は光の当たり方(照明との距離、光の当たる角度)の影響で説明できそうなので、問題を簡単にするためにもまず縦の縞模様から取り組む。
 光の減衰の影響を減らして全体的にベターッと照らせば距離や角度の影響を除くことができそうなので、天井を上げてみる。これまでは天井の高さH=3としていたが、H=5, 10, 50と変えてみたところ次のようになった。




 H=5では変化は明らかでない。H=10で縦の縞模様が消えきるかと思ったが、意外にも斜めの模様だけ消えて(実際は見えないだけと思われる)、縦だけくっきり残っている。H=50にするとどちらも消えた(同)。
 この結果と思考実験から、縦模様に関してはもういいでしょう。
 積分のステップ幅の影響も考えて変えてみたが、模様に変化なし。
 光源の間隔が短くなったら分岐点が増えたりするかと考えて変えてみたが、模様に変化なし。
 触れたくない事実として、aはここまで0から1までしか動かしていない。なのでここまで見ていた影はかなり狭い範囲での出来事を確認していたことになる。これはaを大きくすると積分区間も計算しなければならないxも増えるからなのだが、他に考えることもなくなってしまったのでしぶしぶ着手する。ついでに座標軸もちゃんとググって正しくしたよ偉い!(1行追加するだけだったので早くググればよかったと後悔している(アホ))
 a=3, 5としたもの。




 どうもxの幅1くらいの範囲で起きているようである。板P2の左端通過0.5前~0.5後ってところやね。
 現時点でこの縞模様が正しいものなのかそうでないのかの区別がついていないのが問題である。今後は思考実験でこれを再現できるかどうかと、コードの確認と、2つを続けていかなければならなさそうである・・・。