Kaggle CIFAR-10の話

以前、Kaggle CIFAR-10 に参加していると書きましたが、これが2週間ほど前に終わりました。コンペはまだ Validating Final Results の状態なのですが、2週間たっても終わらず、いつ終わるのか謎なのと、多分結果は変わらないと思うので先に書きます。

CIFAR-10は、次のような32x32の小さな画像にネコ、犬、鳥など10種類の物体が写っているので、与えられた画像に何が写っているか当てる問題です。
f:id:ultraist:20141108185409p:plain
(Kaggle CIFAR-10のデータセットは、通常のCIFAR-10と結果の互換性がありますが、チート防止に画像のハッシュ値が変わるように改変されているのと、テストセットに29万枚のジャンクイメージが含まれています。)

自分の結果は、0.9415 (正解率94.15%)で、Classification datasets results によると、state-of-the-artが91.78%なので、それを上回って人間による精度である94%に達しているのですが、このスコアでなんと5位でした。1位はDeepCNetで、95.53%という驚異の精度を出しています。2位もDeepCNetとDropConnectの結果を合わせたものなので、DeepCNet最強だったという感じです(DeepCNetのコードはコンペ終了前に公開されていました)。3、4位は手法を公開していないので不明です。

自分の手法

kaggle-cifar10-torch7 - Github
でコードを公開しています。Torch7で実装しています。

オリジナル性が高いものはなく、よくある手法をいくつか組み合わせたのと、VGG(University of OxfordのVisual Geometry Group)がILSVRC2014で使ったモデルをCIFAR-10に調節しただけものです。

やったことは

  • 学習データを36倍に増化(Data Augmentation)
  • GCN + ZCA Whiteningで正規化
  • VGGのモデルをベースにしたConvolutional Neural Network(CNN)を学習
  • 上記のモデルを重みの初期値とMini-Batch-SGDの更新順を変えて6個学習し、各分類器の平均を予測として出力

です。

学習データを36倍に増化(Data Augmentation)

ニューラルネットワークは、経験的にはオーバーフィッティングしなければ層数や素子数が多いほどよいというのがあって、よりDeeeepしたいという思いはありますがデータが少ないとオーバーフィッティングしてしまうので、データを増して複雑なモデルでもオーバーフィッティングしにくいようにしました。
コツとしては、できるだけ"あり得る範囲"の変換により増やすということです。画像の場合、人工的なノイズや歪めたりでいくらでもパターンが増やせますが、テストセットに出てこないようなパターンで学習データの分布を歪めてしまうとよくないので、元のデータとは違うけどテストには出てくるパターンに変換できるのが理想です。
今回は次の3つのメソッドを使いました。

Cropping
CIFAR-10の学習画像は32x32ですが、これを24x24の部分画像に分解します。4px置きに切り出すと、3x3の9パターンが切り出せるのでデータを9倍に増やせます。(学習画像は小さくなります)
Scaling
Croppingでの切り出しサイズを28x28にして2px置きに切り出すと3x3の9パターンが切り出せます。この部分画像を24x24にリサイズ(ズームアウト)して学習画像とします。
Horizontal reflection
左右反転です。これまで増やした画像を左右逆の2のパターンに分けて2倍に増やします。

これで(9 + 9) * 2 = 36倍になります。学習画像は32x32ではなく、24x24になります。
予測時は、予測対象の画像を同じ方法で36倍に増やして、各画像に対して予測を行い、それらを平均して予測結果としています。当然、予測にかかる時間も増えます。
この処理によって、2〜3%くらい精度がよくなりました。

f:id:ultraist:20141108184751p:plain
1行目がCropping、2行目がCropping+Scaling、3、4行目がHorizontal reflectionです。

GCN + ZCA Whiteningで正規化

GCN(Global Contrast Normalization)は、standardizeとかz-scoreとか言われるものと同じで、データ全体から各要素の平均と標準偏差を求めて、平均を引いて標準偏差で割ります。入力の値域が-2〜2くらいに正規化されて、スケールの異なる軸があった場合でもその範囲にそろえられます。また、よく出る値は平均に近くなり、あまり出ない値は大きな絶対値を持つようになります。スケールの大きな軸の影響を抑えるのと、学習時の収束が速くなる効果があります。

ZCA Whiteningは、データ全体の分散共分散行列の固有ベクトルで主軸変換を行なって、変換後の空間でstandardizeを行なって元の空間に戻すというものです。自然画像は、あるピクセルはその近隣のピクセルと相関が強いという特徴があるので、この相関を消すことで色の情報を持ったままエッジ検出を行ったような結果が得られます。元の空間に戻すのは、CNNが元画像の構造を前提としているからです。
ZCA Whiteningは、An Analysis of Single-Layer Networks in Unsupervised Feature Learning - Andrew Ngですごくよい結果を出した前処理で、僕もこの手法を実装したことがあるのですが、この手法においてはZCA Whiteningをするかしないかで、CIFAR-10の精度が15%くらい変わります。ただ、最近のDeep CNNではほとんど差がでないので、必要なかったのではないかと思っています。その前のモデル(Network In Network)ではほんの少しだけ精度が改善できていたのと、外して変わらない精度が出るか試している余裕がなかったので、そのまま慣性で入れています。

VGGのモデルをベースにしたDeep Convolutional Neural Networkを学習

[1409.1556] Very Deep Convolutional Networks for Large-Scale Image Recognition で提案されているものをベースにしたDeep Convolutional Neural Networkで分類器を作りました。

伝統的なCNNでCIFAR-10用のアーキテクチャを作ると、conv 5x5 -> maxpool -> conv 5x5 -> maxpool -> conv 5x5 -> maxpool -> fc(fully connected) -> softmaxのようになるのですが、このconv 5x5の部分を3x3カーネルを2つか3つ並べたものに置き換えます。
これで

  • 層数が増える
  • 線形の大きなカーネル非線形(convごとにReLUを挟んでいるため)の3x3を並べたものに置き換えるので表現力が上がる
  • 大きなカーネルで一回畳み込むよりも小さなカーネル複数回畳み込んだほうが計算量が少ない(5x5 > 3x3x2, 7x7 > 3x3x3)

というような効果があります。また3x3カーネルに1pxのpaddingを加えると、畳み込み層によって画像サイズが縮小されなくなるので、理論上は無限に層数を増やせるようになります。これはCIFAR-10のような入力画像が小さい場合に嬉しいです(畳込みでサイズが減っていくと増やせる層数に限界があるため)。

最終的に使ったアーキテクチャは、

conv 3x3 -> conv 3x3 -> maxpool -> conv 3x3 -> conv 3x3 -> maxpool -> conv 3x3 -> conv 3x3 -> conv 3x3 -> conv 3x3 -> maxpool -> fc -> fc -> softmax

というDeeeepなものです。詳しくはソースコードのページに表を書いているので参照してください。

上記のモデルを重みの初期値とMini-Batch-SGDの更新順を変えて6個学習し、各識別器の平均を予測として出力

ニューラルネットワークを使ったモデルで簡単に精度を上げる方法として、いくつかのモデルを学習して平均を取るというのがあります。ニューラルネットワークは初期値依存があるのと大域最適化はされないので、乱数のseedが異なると(微妙に)異なる結果を出力するモデルが学習されます。なので、いくつか学習して平均を取ると結果が安定します。

よくやるのはBagging(Committee Network)ですが、Baggingはサンプリングの割合など調節しなければならないのと今回はこれをやろうと思ったのが終了3日前で、調節する余裕がなく一発勝負だったため、良くなることはあっても悪くなることはないだろうという考えで以下の設定で行いました。

  • 同じ学習データ
  • 異なる初期重み
  • 異なる更新順

また今回使ったモデルは学習に20時間程度かかり、単体マシンで学習していては2つしか学習できないので、EC2のSpot InstanceでGPU Instanceを6個たち上げて並行して学習しました。

結果的には、シングルモデルだと93.33%、6モデルの平均で94.15%だったので、この処理で0.85%改善できていました。

その他の話

VGGの論文が発表される前は、Network In Networkを使っていました。これは最終的には92.4%の精度を出せたので、悪くはなかったと思います。

最後の方では、このままではどうやってもDeepCNetに勝てないと思ったので、GoogLeNetを実装してみたのですが、学習がクッソ遅い上validationで88%しか出なかったので諦めました(調節が足りないのか、問題に合っていないのか、なにか間違っているのか分かっていない)。

この2つのモデルは、参考としてソースコードのディレクトリに置いてあります。(nin_model.luaとinception_model.lua)

lbpcascade_animefaceをgithubに置きました

見る前からスター !lbpcascade_animeface · GitHub

OpenCV用のアニメ顔検出器 lbpcascade_animeface をgithubに置きました。
内容は2011年から変わっていません。
妙なブログ記事ではなく参照しやすくなったのではないかと思います。
全宇宙に拡散するためスターをお願いします。
ちなにみこれはImage::AnimeFaceとは全く別のものです。

見た後にもスター !lbpcascade_animeface · GitHub

AfSIS 反省会

Kaggleで行われていたAfrica Soil Property Prediction Channgleに参加して、結果は1233チーム中148位だった。
反省会です!!

ちなみに参加していなかった人には何を言っているか全く分からないと思いますのでご了承ください。

今回のコンペは、yag_aysさんも書いているけど、public LBとprivate LBで、かなり大きな順位変動があって、public LB1位だった人が500位台まで飛んだり、上位を狙うのは極めて難しい内容だった。ただこれは結果が出る前から明らかだったことで、それを見越してprivate LBで上位に入るように考えていたんだけど、いろいろ予想が外れてしまい失敗してしまった。

オレの予想と現実の違い

予想
  • private 1位は0.46くらい
  • (この間に多くても20人くらい)
  • オレは0.48〜0.50くらい
  • Abhishekのコードは0.55以上
現実
  • private 1位は0.46892
  • (この間に150人くらい)
  • オレは0.50290
  • Abhishekのコードは0.50558

現実は厳しい!!

だいたい以下のようなことを考えていた。

  • 自分の投稿やフォーラムの話、Sentinel LandscapeベースのCVからpublic LBより0.1くらい悪いスコアになるだろう
  • public LB上位(0.36~0.38)のうち何人かは本当にうまくいっていて残るだろうから1位は0.46くらい
  • 自分の投稿は、Sentinel LandscapeベースのCVで0.48くらい、public LBの計算に使われている簡単なデータセットはprivateから除かれるのでこれよりちょっと悪くなるだろう
  • AbhishekのコードはSentinel LandscapeベースのCVで0.61、locationベースのCVで0.56くらいという話があったので(自分では検証していない!!)よくても0.55くらいだろう
  • 参加者の多くは、Abhishekのコードをベースにしているので0.5には届かないだろう

多くの人はprivate LBで爆死するので、爆死しないように気をつけておけばそれほど頑張らなくても上位に入れるだろうと考えていたんだけど、この予想が外れ、Abhishekのコードがなんと0.50というスコアを持っていたため、そこから少しでも改善できた人達150人くらいに抜かれてしまった。

反省点としては、他の人が勝手に爆死することを期待したり、Kaggleの天才データサイエンティスト達を舐めてたりといったふざけた態度でのぞまず、自分のスコアを上げる方向で頑張るべきだった。マル。

自分のソリューション

ちなみに自分の方法は以下のような感じだった。

  1. spectra特徴からCO2の領域を削除
  2. spectraにfirst derivative filterを適用
  3. spectraをPCA Whiteningで160次元に圧縮
  4. 圧縮したspectra特徴, spatial特徴(衛星から取ったやつ), depth(表層か下層か)を入力可能な特徴とする
  5. huber loss functionのneural networkで回帰
  6. neural networkのアーキテクチャやハイパーパラメーターはSentinel Landscapeに基づくCVで良い結果が出るように調節する (spatial特徴を使うか使わないか、depthを使うか使わないかの組み合わせも含めて)

1は、ホストが推薦していたことで、やったほうが結果もよかったのでやった。
2は、CVではやってもやらなくてもほぼ違いはなかったので迷ったけど、データをプロットしてみるとデータごとに異なる謎の直流成分(定数のズレ)が入っていて、どういう理由で入ったのか分からないけど、おそらくホスト側でデータを正規化したときに入ったんじゃないかと思ったのと、ホストが用意しているベースラインのベンチマークコードではfirst derivativeを使っていて、これを使うと直流成分は消えるので、消したほうが安心だろうと思って適用することにした。
3もやるか迷ったものだけど、正規化のパターンとして

  • z-socre
  • ZCA Whitening
  • PCA Whitening (512,256,160,128 component選択)

を試してみて、z-socreは一番結果が悪かったので無しで、ZCAとPCAはほんのちょっとだけZCAのほうがよかった。ただ、今回のデータは学習データが約1000件で入力が約8000次元あるので、できるなら圧縮したほうがいいだろうと思っていたのと、入力次元を少なくするとneural networkの学習時間がかなり短くなので、実験回数が増やせて、この微かな差は挽回できるのではないか、と考えて160次元に圧縮することにした。
結果からするとこれは失敗だった。ZCAバージョンも投稿していたんだけど、private LBではZCAバージョンのほうが微かによくて(0.4991)、こっちを選んでいれば95位くらいだった。(そんな変わらない)

4.はspectra特徴だけで学習するか、spatialやdepthも含めるかという話だけど、これは目的変数ごとに検証した結果以下のようにした。
Ca: spectra
P: spectra + depth
pH: spectra + spatial + depth
SoC: spectra + spatial + depth
Sand: spectra + depth

5.は、この問題には非線形回帰がよいことはちょっとやってみれば分かることで、非線形回帰ができるモデルとして自分の得意なneural networkを選んだ。重要なのは損失関数で、MSEではなくHuber lossを使うことにした。これは、学習データの目的変数に明らかに大きすぎる外れ値っぽいデータが少しだけ入っていて、MSEを最小化するとその影響がすごく出てしまうので、ロバストにしたほうがいいだろうということで選んだ。CVでもMSEよりHuber lossのほうが良い結果が出ていた。
ちなみにSupport Vector Regression(Abhishekのコード)が良い結果を出していた理由も、損失関数にsquared lossを使っていないことだと思っていて、というか、SVRで良い結果が出るという話が出たあとで、なぜSVRだけ良い結果が出るのか考えた結果、ε-sensitive lossのおかげだろうと思ったので、自分もそれに習って性質が似ていてneural networkで扱いやすそうなHuber lossを使ってみることを思いついた。

6.は一番重要なところで、CVのfoldをランダムサンプリングではなく、Sentinel Landscapeという単位を壊さないように分けるようにする。
これが重要な理由は、データページに書いてある。

The training and test data have been split along Sentinel Landscape levels because we are primarily interested in predicting soil properties at new Sentinel Landscapes.

http://www.kaggle.com/c/afsis-soil-properties/data

学習データとテストデータは、Sentinel Landscape levelsで分けられていると明確に書いてある。Sentinel Landscapeは土壌をサンプリングした時の一番大きい空間単位で、

60 Sentinel Landscapes
16 Sampling Clusters per Sentinel Landscape
10 Sampling Plots per Sampling Cluster
2 composite Soil Samples (topsoil & subsoil) per Sampling Plot

と書かれている。つまりこのコンペのデータは、階層構造を持っているので、データ単位でランダムサンプリングして同一Sentinel Landscape内のデータを学習/テストに分けてCVしてしまうと、最終スコアリングに使われる学習/テストと異なる条件になってしまって、おそらく同一Sentinel Landscapes内でオーバーフィッティングしていたほうがいいスコアを出すデータセットができてしまう。なので、CVでは、同一Sentinel Landscape内のデータが学習とテストに分かれないように注意してfoldを分ける必要がある。
ちなみに各データがどのSentinel Landscapeに属するかを表すIDは振られていないので(データの説明に書くならふれよ!!と思った)、TMAPという項目をキーとして使う。TMAPは年間降水量かなにかの変数だけど、衛星から取ったデータの空間解像度の悪さからこの値をキーとしてデータをまとめるとほぼSentinel Landscapeの単位でまとめれるという議論がフォーラムであって、その話をしている人達はかなり信頼できる人達だったので、オレもこれを使うようにした。

コンペ後のフォーラムを見ると、上位のほうがみんなSentinel LandscapeベースのCVをしていたってことはないようだけど、ただ少なくともlocationベース(同じ位置から取られたSubsoil/Topsoilのデータが学習/テストで別れないようにする)でやらないと、オーバーフィッティングしてたほうが高いスコアが出てしまうので、順位落とした人の多くは間違った条件のCVでハイパーパラメータを調節した結果、元になったAbhishekのコードよりも悪い結果になってしまったのでは、と思います。

最後にpublic LBが信頼できないだろうと思った理由。

  • 計算に使っているデータが少なすぎる(90件くらい)
  • CVに比べて異常によいスコア(public LB: 約0.40, CV: 約0.50、しかもこの超簡単なデータセットはprivate LBの計算から除外されるので、private LBはいっそう難しくなる)
  • CVと負の相関すらあるように見える!!
  • 信頼できそうな人達がみんな信頼できないと言っていた(重要)

追記

なんとかツリー系、入力が信号で説明変数間に強い相関があるのと、次元に対して学習データが少なすぎるのとで、変数選択がうまくいかなくて、決定木をベースとしたものは良い結果が出せなかったのでは、と思っているけど詳しくないので分かりません。
ただ100位以内の多くは、SVRGBMの結果を混ぜたものみたいです。

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())
   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ッス よく分からないけどマジスゲーッス」みたいな反応になるよう、よろしくお願いいたします。

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