LoginSignup
5
4

More than 5 years have passed since last update.

chainerでx方向にconv、y方向にdeconvする

Posted at

概要

あるタスクで,X方向の特徴量を少なくしつつ,Y方向の特徴量を多くしたくなった.
例えば大きさ(100, 100)の画像を,conv/deconvを使って(50,200)にしたい.
これを解決する方法は大きく分けて2つある.

  1. conv→deconv もしくは deconv→conv
  2. 引き伸ばし→conv

最初の方法だと2層構造になってしまうため避けたい.
そのため引き伸ばしてconvする手法を検討し,実装した.

しかし良い実装方法が思いつかずfunctions.deconvolution_2dを用いた.
可能であればもっとスマートな実装をしたい.

背景

convolutionを使えば,位置情報を維持しつつより少ない特徴量に写像できる.

x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
shape = chainer.links.Convolution2D(1, 1, ksize=(4, 1), stride=(2, 1), pad=(1, 0))(x).shape
# shape: (1, 1, 50, 100)

deconvolutionを使えば,位置情報を維持しつつより多い特徴量に写像できる.

x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
shape = chainer.links.Deconvolution2D(1, 1, ksize=(1, 4), stride=(1, 2), pad=(0, 1))(x).shape
# shape: (1, 1, 100, 200)

しかし,ある次元では少ない特徴量に,別の次元では多い特徴量に写像するLayerはたぶんない.

手法検討

conv→deconv/deconv→conv

一番シンプルな実装だが,どうしても2層構造になり,勾配が消失しやすくなりそうなので避けたい.

conv->deconv
x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
x = chainer.links.Convolution2D(1, 1, ksize=(4, 1), stride=(2, 1), pad=(1, 0))(x)
x = chainer.links.Deconvolution2D(1, 1, ksize=(1, 4), stride=(1, 2), pad=(0, 1))(x)
# x.shape: (1, 1, 50, 200)
deconv->conv
x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
x = chainer.links.Deconvolution2D(1, 1, ksize=(1, 4), stride=(1, 2), pad=(0, 1))(x)
x = chainer.links.Convolution2D(1, 1, ksize=(4, 1), stride=(2, 1), pad=(1, 0))(x)
# x.shape: (1, 1, 50, 200)

引き伸ばし→conv

2つ考えた.まずその1.
functions.unpooling_2dを使って引き伸ばしたあと,convで小さくする.

unpooling->conv
x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
x = chainer.functions.unpooling_2d(x, ksize=(1, 2))
x = chainer.links.Convolution2D(1, 1, ksize=(4, 4), stride=(2, 1), pad=(1, 2))(x)
# x.shape: (1, 1, 50, 200)

続いてその2.
functions.deconvolution_2dを使って引き伸ばしたあと,convで小さくする.
1010101010...といったマスクを作って,deconvで引き伸ばす感じ.

upsample->conv
x = numpy.random.rand(1, 1, 100, 100).astype(numpy.float32)
x = chainer.functions.deconvolution_2d(x, W=numpy.array([0, 1, 0], numpy.float32).reshape(1, 1, 1, 3), stride=(1, 2))
x = chainer.links.Convolution2D(1, 1, ksize=(4, 4), stride=(2, 1), pad=(1, 1))(x)
# x.shape: (1, 1, 50, 200)

考察

どれがいいのだろう.

  • conv->deconv
    • convしたときに特徴量がやたら少なくなっているので良くなさそう
    • メモリ使用量は少ない
  • deconv->conv
    • この2層構造はupsample->convと同値・・・な気がする
    • deconvのあとに活性化関数を挟むならありかも
  • unpooling->conv
  • upsample->conv
    • 良い実装方法が思いつかなかった
    • 専用のLayerを定義できればメモリ削減できそう

今後

そもそも2Dではなく,links.ConvolutionNDを使って3次元convする際に適用するつもりなのだが,functions.unpooling_ndが無いことに気づいた.どうしよう.

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4