まんぼう日記

takataka's diary

Convolutional Neural Net で CIFAR-10 を識別してみる

最近ちまちま研究に勤しんでるたかたかです.8月中旬から9月上旬は書き入れ時やから今のうちにあれもこれも…といろいろ手を出して,ちょっとしっちゃかめっちゃかな感じ.で,自分の研究ネタを煮詰めて行くような気合(?)のいることやる気せえへんかったんで, NVIDIA cuDNN – GPU Accelerated Deep Learning をインストールする作業に逃避しようとしてみました( cf. Theano で GPU 使って Convolutional Neural Net - まんぼう日記 ).

 

そしたら,ダウンロードするには NVIDIA Developer として登録することが必要で,その際に登録フォームに使用目的なんかを書いてしかもその審査を受けなあかんということでした.先週金曜の午後に登録しましたが,まだ返事来ません.手作業ちうことか?  アメリカ西海岸が月曜朝を迎えるまで待つしかなさそう.

 

ちうわけで,そっちへの逃避に失敗したので,別の方向へ逃げました (^^;  頭使う仕事する気にならへんからプログラム書いて実験です.Theano で GPU 使って Convolutional Neural Net - まんぼう日記 のつづきで,Convolutional Neural Net (CNN) で CIFAR-10 の画像認識の実験をやった結果をまとめときます.まあ,単に逃避してるだけやのうて,そのうち自分の研究その他もろもろの役に立つはずやし.

 

どーでもえーことですが,実験プログラム動かして結果待ってる間ってなんでかしらんけど雑用が捗るので,たまってた家事(食器洗いとか洗濯とかカレー作ったりとか)も片付いて一石二鳥です (^^) 

 

 

実験目的

Theano で GPU 使って Convolutional Neural Net - まんぼう日記 の経緯をふまえて,次のような改良の効果を測ってみます.

  1. 画像からランダムに一部の領域を切り出して学習に使う
  2. 以前は Convolution-Pooling 層はひとつだけだったが,これを2段にしてみる

1. については,Alex Krizhevsky さんが cuda-convnet の Methodology や ImageNet Classification with Deep Convolutional Neural Networks ( http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf )  に書いてることです.平行移動させた画像も学習に使って不変性を獲得させることと,学習データを増やして過学習を起こしにくくすること,この二つがねらいです.2. については,まあその方が当然いいよねと.

 

実験条件

実験に使ったPCとかその辺は,続 GPGPU用マシンにCUDAをインストール - まんぼう日記 とそのリンク先をどうぞ(説明手抜き).今回の実験は全て GPU (Tesla K20C) 使ってます.

 

使用したデータ CIFAR-10 については CIFAR-10 と ZCA whitening - まんぼう日記 をどうぞ.今回は,全て ZCA 白色化してます

 

学習の条件等については,前回 Theano で GPU 使って Convolutional Neural Net - まんぼう日記 の記事と,以下のプログラムを参照してください(これまた説明手抜き).

 

実験に使ったプログラムはこちら:

  • cifar10.py & cifar10_sub150821.py (GitHub Gist): 実験データの読み込み,加工等
  • convnet150821.py & nnet150712.py (GitHub Gist): CNNのプログラム
  • ex150823.py & ex150823b.py (GitHub Gist): 実験用プログラム

 

実験1: 画像からランダムに一部の領域を切り出して学習に使うとよい?

 

CNNの構造

前回と同じ,Convolution - Pooling - ReLu - Softmax という構造の CNN を使います.各種パラメータ(の代表的な値)はこんなん:

  • 入力: 3チャンネル x 32画素 x 32 画素 または 3 x 24 x 24
  • Convolution層: カーネルサイズ 3 x 5 x 5 で1画素ずつずらしながら畳み込み,チャンネル数 64
  • Pooling層: max-pooling で縦横1/4にサブサンプリングしてから ReLu (しきい値はチャンネル毎),stride size は窓と同じ (4, 4).
  • ReLu層: 素子数 1000
  • Softmax層: 10クラス識別なので素子数 10

 

入力の作り方

CIFAR-10 の画像は 32 x 32 の大きさですが,これをそのまま学習画像として使う場合に加えて,そこからはみ出さないように 24 x 24 の大きさの部分画像を切り出して使う場合も実験します.後者の場合,学習時には,切り出す位置をランダムに選びさらに確率 1/2 でそれを左右反転させます.一方,テスト(と学習途中の識別率等の表示)の際には,32 x 32 の真ん中から切り出した 24 x 24 を反転なしで用います.

 

cifar10_sub150823.py をみてもらうとわかりますが,切り出し方は次の3通り:

  • translate: ミニバッチ中の画像ごとに位置および反転する/しないを個別にランダムに決める方法
  • translate2: 上記を簡略化して,一つのミニバッチ中では切り出し位置および反転する/しないを同じにする方法
  • clipcenter: 単純に真ん中から切り出す.反転なし

 

実験結果

 

こんなんなりました.border_mode は,valid ならはみ出さずに畳み込み,full ならはみ出しあり.詳しくは conv – Ops for convolutional neural nets — Theano 0.7 documentation 参照.test recog. rate は,テストデータに対する識別率,time は,実行にかかった経過時間(実時間)です.

 

inputtranslationborder_modetest recog. ratetime
32x32 - valid 75.9% 13m
32x32 - full 75.9% 30m
24x24 clipcenter valid 71.6% 11m
24x24 clipcenter full 72.7% 21m
24x24 translate valid 76.3% 10m
24x24 translate full 77.0% 21m
24x24 translate2 valid 76.9% 10m
24x24 translate2 full 77.6% 21m

 

ランダムに切り出す方法は,少しは効果があるようです.border_mode は full の方がよいかもですが,時間との兼ね合いもあって微妙.translate と translate2 では差がなさそうだったので,以降では translate2 のみを使うことにしました.

 

これらの実験では学習 epoch 数は全て 50 としてますが,学習終了時の出力を見ると,例えば上の表の一番下の条件では

50 | 0.3361 88.73 | 0.6653 78.42 | 0.6978 77.63

こんな感じで,学習データに対する識別率が 88.73% となっていて,学習が収束するまで進んでない感じもします(学習には translate2 を使ってるのに識別率計算時は clipcenter やからってのもあるかもですが).というわけで,この条件で 200 epoch まで学習させてみたらこうなりました.

200 | 0.0911 97.23 | 0.8566 78.55 | 0.8927 78.06

テスト識別率 78%.ちょっとだけ向上.交差エントロピーは上昇しちゃってるので,これ以上やると過学習しそう.

 

実験2: Conv-Poolを2段にすると?

 

CNNの構造

Convolution - Pooling - Convolution - Pooling - ReLu - Softmax という構造の CNN を使います.各種パラメータ(の代表的な値)はこんなん:

  • Convolution層: 実験1と同じ(カーネルサイズ 3 x 5 x 5 で1画素ずつずらしながら畳み込み,チャンネル数 64)
  • Pooling層: max-pooling で縦横1/4または1/2にサブサンプリングしてから ReLu (しきい値はチャンネル毎),stride size は窓と同じ.
  • ReLu層: 実験1と同じ(素子数 1000)

 

実験結果

 

こんなんなりました.ds1 は,入力に近い側の Pooling 層のサブサンプリング窓の大きさ((4,4)なら縦横1/4にする),ds2 は,2番目の Pooling 層の同様のパラメータです.24 x 24 の場合の translation は translate2 を使用,border_mode は全て full です.

 

inputds1ds2test recog. ratetime
24x24 (4,4) (4,4) 79.3% 31m
24x24 (4,4) (2,2) 80.1% 31m
24x24 (2,2) (4,4) 82.2% 36m
24x24 (2,2) (2,2) 81.4% 37m
32x32 (2,2) (2,2) 79.5% 51m

 

Conv-Pool を2段にすると良いですね.1段だけの前回の実験では,ds は (2,2) より (4,4) の方がよかったんですが,こちらはサンプリング窓サイズが小さい方がよい感じ.それぞれの段で平行移動を吸収すればよいから,小さくてもよいということでしょうか.

 

ところで,ここまでは全て ZCA 白色化を使ってますが,使わないで画素値そのままやとどうかなと思って試してみました.上の表の下から2番目の条件でやってみたら,テスト識別率は 78.8% でした.

 

考察

というわけで,

  • 画像からランダムに一部の領域を切り出して学習に使う
  • Conv-Pool を2段にする

という2点を主とする改良により,前回は 76% だったテスト識別率が 82% まで上がりました.

 

ちなみに, cuda-convnet には

13% error on CIFAR-10 in 25 minutes, with image translations and horizontal reflections

 という記述があります.つまりテスト識別率 87%.この結果との主な違いを列挙すると,こんな感じです.

 

  • データ
    • たかたかは ZCA してるが,あっちはしてない
  • ネットワークの構造
    • Conv-Pool-Conv-Pool-ReLu-Softmax の ReLu の層のかわりに,Locally-connected layer with unshared weights (重み共有なしの convolution 層と思えばよい)を2層使っている.
    • Pooling層で overlap ありの max-pooling をしてる.
  • 学習の仕方
    • テスト時に,32 x 32の中心部から切り出した 24 x 24 の画像に加えて四隅,さらにそれらの左右反転の計10通りの画像も使い,それらに対する出力の平均を用いている.
    • たかたかはさぼって validation 用データを何にも使っていないが,あちらはちゃんと validation データを使って学習終了判定をしており,さらにその後 validation データも入れて追加学習している.また,学習係数を途中で 1/10 にするなどのヒューリスティクスを入れてる.

 

これ以上がんばる気はありませんが,たかたかの勘では,これらの中では,学習の仕方の影響が最も大きいような気がします.