CNNで各層にzero paddingを入れる意味

いろいろなCNNの実装を見ていると、畳み込み層の前にzero padding(Torch7だとSpatialZeroPadding)を入れているものが多くて、自分も使っているのですが、これにどんな意味があるのが正直良く分かっていないので、詳しい文献などあれば紹介してください。以上。

自分で使っていてこれで終わるのもアレなので自分の理解。

悪い点: 入力にzero padding入れると変なデータを学習してしまうのではないか?

いろいろな実装参考にしたのと、自分で試した感じだと、畳み込みカーネルサイズの半分以下のpaddingは特に悪い影響はないようです。
逆に、端っこにしか出てこないパターンを識別しやすくなるのでは?とか。

良い点: 畳み込み演算の回数が増えるのでパラメーターの更新が多く実行される

weight sharingしているカーネルのパラメーターは入力画像に対する畳み込み演算の回数だけ更新されるので、paddingにより入力サイズが増えると1回のbackwardで更新されるパターンが増えて、overfittingしにくくなるのではないか。

良い点: 大きなカーネルを使ったり、層数を増やしたりできる

CNNでは畳込みとpoolingを行うごとに入力のサイズが小さくなっていくので、(特に)最初の入力画像が小さい場合に、大きなカーネルを使ったり層数を増やすことができない。zero paddingでサイズを水増していくと、大きなカーネルで畳み込んだり、層数を増やしたりできる。

以上です。

Torch7で複雑なモデルを書くときに便利な技

Torch7はnnに用意されている部品を組み合わせることで複雑なモデルを作れて便利なのですが、複雑なモデルは入力ベクトル(orテンソル)を様々形に変換しながら実行するので、どの時点でどのサイズになっているか分からなくて書くのが難しいというのがあります。
たとえば、伝統的なCNNは、Torch7で以下のように書くのですが

require 'nn'
function cnn_model()
   local model = nn.Sequential()
                                                                
   -- convolution layers                                        
   model:add(nn.SpatialConvolutionMM(3, 128, 5, 5, 1, 1))
   model:add(nn.ReLU())
   model:add(nn.SpatialMaxPooling(2, 2, 2, 2))

   model:add(nn.SpatialConvolutionMM(128, 256, 5, 5, 1, 1))
   model:add(nn.ReLU())
   model:add(nn.SpatialMaxPooling(2, 2, 2, 2))

   model:add(nn.SpatialZeroPadding(1, 1, 1, 1))
   model:add(nn.SpatialConvolutionMM(256, 512, 4, 4, 1, 1))
   model:add(nn.ReLU())

   -- fully connected layers                                    
   model:add(nn.SpatialConvolutionMM(512, 1024, 2, 2, 1, 1))
   model:add(nn.ReLU())
   model:add(nn.Dropout(0.5))
   model:add(nn.SpatialConvolutionMM(1024, 10, 1, 1, 1, 1))

   model:add(nn.Reshape(10))
   model:add(nn.SoftMax())

   return model
end

このSpatialConvolutionMMの畳み込みカーネルが入力サイズをはみ出していないかとか、SpatialMaxPoolingの入力が奇数になっていて端っこが処理されていないのではないかとか気になります。
こういう場合、以下のようなデバッグプリントを入れると、各層でどのようなサイズになっているか分かります。

require 'nn'
function cnn_model()
   local model = nn.Sequential()
   local debug_input = torch.Tensor(3, 24, 24):uniform()

   -- convolution layers                                    
   model:add(nn.SpatialConvolutionMM(3, 128, 5, 5, 1, 1))
   model:add(nn.ReLU())

   -- model:cuda() -- 必要ならcudaにする
   print(model:forward(debug_input):size())
   model:add(nn.SpatialMaxPooling(2, 2, 2, 2))
   print(model:forward(debug_input):size())

   model:add(nn.SpatialConvolutionMM(128, 256, 5, 5, 1, 1))
   model:add(nn.ReLU())
   print(model:forward(debug_input):size())
   model:add(nn.SpatialMaxPooling(2, 2, 2, 2))
   print(model:forward(debug_input):size())

   model:add(nn.SpatialZeroPadding(1, 1, 1, 1))
   model:add(nn.SpatialConvolutionMM(256, 512, 4, 4, 1, 1))
   model:add(nn.ReLU())
   print(model:forward(debug_input):size())

   -- fully connected layers                                
   model:add(nn.SpatialConvolutionMM(512, 1024, 2, 2, 1, 1))
   model:add(nn.ReLU())
   model:add(nn.Dropout(0.5))
   print(model:forward(debug_input):size())
   model:add(nn.SpatialConvolutionMM(1024, 10, 1, 1, 1, 1))
   print(model:forward(debug_input):size())

   model:add(nn.Reshape(10))
   model:add(nn.SoftMax())

   return model
end
cnn_model()

実行結果

% th t.lua

 128
  20
  20
[torch.LongStorage of size 3]


 128
  10
  10
[torch.LongStorage of size 3]


 256
   6
   6
[torch.LongStorage of size 3]


 256
   3
   3
[torch.LongStorage of size 3]


 512
   2
   2
[torch.LongStorage of size 3]


 1024
    1
    1
[torch.LongStorage of size 3]


 10
  1
  1
[torch.LongStorage of size 3]

各層で出力がどのようなサイズになるかは、ちゃんと計算すれば分かるのですが、僕のように暗算を得意としない人間には計算するのが非常にだるいのでデバッグプリントを入れる技がとても便利です。

CIFAR-10でstate of the artのスコアが出せる、インターネットに落ちている中で最強のコード

DecMeg2014をやっているときにCUDA使いて〜と思うことがあったので、最近、GTX760というGPUを購入して、Kaggle PlaygroundのCIFAR-10(有名な物体認識のデータセット)で試していたのですが、CIFAR-10のstate of the artである0.912を微妙に超える精度(0.9173)が出せるようになったのでソースコードを公開します。

nagadomi/kaggle-cifar10-torch7 · GitHub

この結果は、"ベンチマークサイト Kaggle"で現在3位にランキングされています。

内容的には特に面白いことはしていなくて、cropping,scaling,horizontal reflectionで学習データを180万件(36x)まで増殖させたあとでNIN(Network In Network)というConvolutional Neural Networkの畳み込み層をMLPにしたモデルを学習しているだけです(層数等、論文実装とは違います)。
データ増やすしすぎだろ、と思うかもしれませんが、自分が試した感じだと、NINを使うとデータを増やせば増やすほど精度が上がります。またNINは層数が多くなるので、データが少ないと学習がうまくいかないというのもあります。学習データが1万件程度だと全く学習できません(ほとんどのクラスの精度が0%になってしまう)。そういうわけで、めちゃくちゃデータを増やして学習するので、学習時間がめちゃくちゃかかります。CUDAを使って1 epochに90分くらいかかり、10 epochくらい回さないと精度が上がりらないので、15時間くらいはかかります。

実装は、Torch7で行なっています。Torch7は、Pylearn2やcaffe、cuda-convnetなどと比べるとマイナーなNeural Networkのライブラリですが、Neural language modelsというパッケージ(nn)を使うと複雑な構造のNeural Networkも自然なコードで書けるので、プログラミングが得意な人にとっては、最強の開発環境ではないかと個人的には思っています。DecMeg2014では、Torch7で書いた複雑奇怪なNeural Networkで5位になっているので、実用上問題無いレベルで使えると思います。
ただ、CUDAの実装は微妙なものが多く、例えば、SpatialMaxPoolingのCUDA実装はkernelとstrideのサイズが同じじゃないと動かなかったりします。kernelをstrideより大きくするoverlapping poolingは画像認識において精度が向上できることが知られていて、cuda-convnetのサンプルなど精度を重視している実装はほとんどこれを使っているのですが、Torch7が対応してないので、このコードでは使っていません。
ということで、まだまだ改善の余地はあるので、興味ある方はこのコードの知見を活かし、Kaggleで2位になったあとソースコードgithubに置いておいてください。
(1位はたぶん無視していい存在だと思う)

追記 (2014/8/28)

epoch 20まで増やしたら、0.92210で現在2位になった。
時間かかってもちゃんと実験するべきだった。

追記 (2014/11/8)

最終結果を書きました。ソースコードの内容も変更されています。
Kaggle CIFAR-10の話 - デー

Kaggle Masterになりました

ビッグデータを世界の天才たちが紐解くKaggle における上位プレーヤーの称号 Kaggle Master を得ました。
Kaggle Masterを得るには、

1. 2つの公開コンペで上位10%に入る
2. そのうち1つは10位以内に入る

という条件があるのですが、4月に終った Large Scale Hierarchical Text Classification で3位、今日終った
Greek Media Monitoring Multilabel Classification (WISE 2014)で9位だったので、最短2コンペでストレート Kaggle Master を得ることになりました。

これにより、天才の中の天才、超天才データサイエンティースト悪魔男爵ultraistterさんであることが示されてしまったため、僕がここで意味不明なことを言っていた場合でも、「ハイハイワロスワロス」ではなく、「さすがKaggle Masterッス よく分からないけどマジスゲーッス」みたいな反応になるよう、よろしくお願いいたします。

ちなみに統計学は全くわかりません。

最近のautomakeでmake checkの挙動を戻す方法

開発環境をUbuntu 13.10にアップグレードしたら、自分が作っているライブラリのビルドでmake checkしたときの挙動が変わっていた。どうもautomakeのバージョンが上がったせいで挙動が変わってしまったようだ。
これまでは、テストプログラムがなんかいろいろ出力しながら実行されて、失敗するとエラー行を表示していたのが、テストプログラムの出力(stdout,stder)がファイルに書き出されて一切表示されなくなってしまった。

調べてみるとautomake 1.13からparallel test harnesとかいうのデフォルトになっていて、これについてはよく知らないけど、名前からして並列テストをするもので、並列実行しているプログラムの出力をターミナルに出すと混じってしまうので、ファイルに出すようになったのだと思う。

自分は並列テストを必要としていないので、とにかく過去のバージョンと同じになるように戻したい!

automake 1.13以降で過去のバージョンと同じようにテストプログラムを実行する方法

automakeのオプションにserial-testsを渡すと、make checkの挙動が過去のバージョンと同じになる。
これは、configure.acにおいてAM_INIT_AUTOMAKEの引数で指定すればよい。

AM_INIT_AUTOMAKE([foreign])

などとなっていた行を

AM_INIT_AUTOMAKE([foreign serial-tests])

と変更する。これでserial-test driverが使われるようになって、過去のバージョンと同じように動く。

ただし、これをautomake 1.11など古いバージョンで実行すると、

configure.ac:9: option `serial-tests' not recognized
autoreconf: automake failed with exit status: 1

などと言われてエラーになる。
serial-testsというオプションがないのだ。クッソ!

現在と過去、すべてにおいて過去のバージョンと同じようにテストプログラムを実行する方法

ひとつの方法として、automakeのバージョンを判定して、バージョンによりAM_INIT_AUTOMAKEの引数を変更するm4マクロを書くというのがある。
しかし、自分は、automakeの歴史についての知識に基づいたノウハウとか持ちたくないのと、そもそもautomakeのテスト機能を駆使してテストを書いていたわけでなく、make checkしたときに自分で書いたテストプログラムが実行されればそれでいいので、もうautomakeのテストの仕組みを使わないことにした。
ということで、TESTSでテストプログラムを指定するのをやめて、check_PROGRAMでビルドするテストプログラムを指定した後、check-localのフックでテストプログラムを実行するようにした。

Makefile.amで

check_PROGRAMS = oreore_test
TESTS = $(check_PROGRAMS)

などとしていた行を

check_PROGRAMS = oreore_test

check-local:
        $(builddir)/oreore_test$(EXEEXT)

と変更する。これでautomake 1.11と1.13で同じような感じで動くようになった。あまりいい方法とは思えないけど、ならどうすればいいのか...

L4D2の特殊感染者BOTのプレイスタイルをカスタマイズしてみた

@キモト L4D2のAI改造試したけど、なんかいろいろ方法考えた上でFakeClientのキー入力を書き換えることでBotの動きを制御する方法が簡単でいいのではと思った。server.soのdisassemble読んでExtension書くのはつらいし、いろんなcvarを参照しながらTeleportEntityで制御するのもつらそうだし、キー入力なら自分で普段やっていることであり簡単ではと。もっといい方法あるかもしれないけど。

ただNavMeshとかマップに関する情報が読めてないので、あまり高度なことはできてない。適当に動き回ったり、ターゲットの状態を調べて攻撃するか判定するくらい。

何かどれくらいできるか探りながら試しただけで、あまりよく考えてないカスタマイズです。

類似検索を更新した

よくなったけど俺以外には違いが分からない気がする。

花: http://flowers.libotama.so/

アニメ顔: http://animeface3.libotama.so/

やってることは前のままだけど、特徴点検出の閾値からVLADの次元、LMCAの学習アルゴリズムに至るまで、ざまざまなところに勘で決めてたパラメーターがあったのを実験により最適化するという地獄のような作業を行うことで精度が1割くらいマシになった。

相変わらずgithubに反映してないけど、もうマジでこれで固めるから……

あとやりたいのは

  • 説明書く
  • Windows用のバイナリ作る
  • Debianパッケージ(バイナリ)作る

くらい。

バイナリは便利というかソースコードからのコンパイルが面倒なので作りたいけど、POPCNT命令が使えるとN倍速いとかできれば有効にしたいCPU依存の処理があって、そういうのコンパイル時に判定するように作っているから、i686とか大きなターゲット向けだと使えない。どうするかなーと迷ってる。迷ってるというかこのへんの事情に詳しくないので調べないといけない。