備忘録とか日常とか

学んだこととかを書きます。

theanoのconv2dとmax_pool_2dが遅いので何とかする(2)

前回に引き続いてpylearn2のFilterActs()の使い方を書いてく。主にパラメータ。
参考元はここ

partial_sum

FilterActs()はpartial_sumという、メモリ使用量を調整するパラメータを持っている(当然使用量が増えれば速度性能は上がる)。cuda-convnetのドキュメントによると

partialSum is a parameter that affects the performance of the weight gradient computation. It’s a bit hard to predict what value will result in the best performance (it’s problem-specific), but it’s worth trying a few. Valid values are ones that divide the area of the output grid in this convolutional layer. For example if this layer produces 32-channel 20x20 output grid, valid values of partialSum are ones which divide 20*20 = 400.

とある。勾配計算に影響するが、適切な値を見つけるのは簡単ではないようだ。とりあえずconvolutional layerのoutputのグリッドサイズの約数を指定すればいいらしい。
デフォルトではpartial_sum = None となっており、この状態ではメモリ使用量は最小限に抑えられる。スピードアップを図るなら適当な数値を定める。一般的には1を指定しておけば、どんなoutputにも対応できてある程度高速化もできるのでおすすめされている。
こんな感じ。

conv_op = FilterActs(partial_sum=1)  # partial_sumを指定
contiguous_input = gpu_contiguous(input)
contiguous_filters = gpu_contiguous(filters)
out = conv_op(contiguous_input, contiguous_filters)
stride

フィルタのstrideを指定する。theanoのsubsumpleと異なる点は、タプルではなくintでなければならない。ゆえに縦横同じstepでしかフィルタを動かせない。デフォルトではstride=1。
こんな感じ。

conv_op = FilterActs(partial_sum=1, stride=2)  # strideも指定
contiguous_input = gpu_contiguous(input)
contiguous_filters = gpu_contiguous(filters)
out = conv_op(contiguous_input, contiguous_filters)
pad(Zero-padding)

Zero-paddingに関するパラメータ。strideと同様にint型で指定し、その数だけ縦横にZero-paddingする。例によって上下と左右で異なる数は指定できない。あとtheanoのconv2dには"full" convolution か"valid" convolutionが指定できたが、cuda-convnetではできない。なので手動でpaddingする数を決める("full"ならpad=filter_size-1とする)。
こんな感じ。

conv_op = FilterActs(partial_sum=1, pad=filter_size - 1)
contiguous_input = gpu_contiguous(input)
contiguous_filters = gpu_contiguous(filters)
out = conv_op(contiguous_input, contiguous_filters)

ここまでFilterActs()について述べてきたが、poolingの際はMaxPool()というオブジェクトで設定する。theanoでmax_pool_2d()を使うところで

from pylearn2.sandbox.cuda_convnet.pool import MaxPool
from theano.sandbox.cuda.basic_ops import gpu_contiguous

pool_op = MaxPool(ds=2, stride=2)
contiguous_input = gpu_contiguous(input)
out = pool_op(contiguous_input)

とする。dsはプーリングの範囲、strideはそのままストライド。これもint型で指定する。くどいようだが縦横で異なる数値は指定できない。ds > stride とすれば、overlapしながらpoolingすることになる。
大切なのは、MaxPool()は正方形の画像に対してしか適用できないことだ。FilterActs()にはそのような制限はない。なので必要であれば、FilterActs()とtheanoのmax_pool_2d()を組み合わせて使うことでこの制限を回避できるという(速度は遅くなるがmax_pool_2d()だけ使うよりマシ?)。


追記:2015/12/21】
FilterActs()は、ストライドを2以上に指定して使用すると畳みこむフィルタがはみ出した場合自動的に0パディングを行う。
例えば6x6の特徴マップに3x3のフィルタをストライド2として畳みこむ場合、以下の図のようになる。
f:id:may46onez:20151221223401p:plain
ここではみ出した分だけ自動的に画素値0のピクセルでパディングされる。
フィルタの畳みこみは左上のピクセルから始まるので、何もしないままだと特徴マップの右端と下端だけにパディングがされることになる。まああまり気にすることはないのかもしれないけど、大きいサイズのフィルタを畳みこむ場合は注意が必要。必要ならばパラメータのpadを調整して、バランスよくゼロパディングするとよいかも。


他にもwrapperはあるが、ざっと見た感じmaxpool以外は用意されてないっぽい? 間違いかもしれないので
Cuda-Convnet — Pylearn2 dev documentation
Google Code Archive - Long-term storage for Google Code Project Hosting.
で探してみてください。他にもいろいろ書いてます。
最後投げやりだけど許してください。。



参考元には完成形のコードとかもあるのでそっち見たほうがわかりやすいかもです。