このことから、重みを結び付けようとしていると推測できます。つまり、最初の操作が との行列乗算でW
ある場合、出力はW.T
、随伴行列で生成されます。あなたの場合、畳み込み演算子とそれに続くサブサンプリングの随伴を探します。
(編集:私は間違って推測しました。形状が正しい限り、「デコンボリューション」に任意のフィルターを使用できます。ただし、随伴について話すことは依然として有益です。後で仮定を緩和することができます。)
畳み込み演算子とサブサンプリング演算子は線形演算子であるため、それらをC
および でそれぞれ示し、画像の畳み込み + サブサンプリングが次のようになることS
を観察します。x
S C x
y
と( と同じ空間に存在する)に対する随伴演算は次のS C x
ようになります。
C.T S.T y
y
現在、ST は、正しいサイズが得られるまで のすべてのエントリの周りにゼロを追加することにより、元の画像サイズにアップサンプリングすることに他なりません。
あなたの投稿から、ストライド (1, 1) の畳み込み演算子の随伴を認識しているようです。これは、逆フィルターと逆border_mode
のfilters.dimshuffle(1, 0, 2, 3)[:, :, ::-1, ::-1]
畳み込みborder_mode='valid'
ですborder_mode='full'
。
アップサンプリングとこの逆フィルター畳み込みを連結すると、求める随伴が得られます。
注: 勾配を利用したり、これを自動的に取得しT.grad
たりする方法があるかもしれませんが、これが正確にどのように行われるかはわかりません。T.jacobian
編集:そこに、私はそれを書き留めました:)
import theano
import theano.tensor as T
import numpy as np
filters = theano.shared(np.random.randn(4, 3, 6, 5).astype('float32'))
inp1 = T.tensor4(dtype='float32')
subsampled_convolution = T.nnet.conv2d(inp1, filters, border_mode='valid', subsample=(2, 2))
inp2 = T.tensor4(dtype='float32')
shp = inp2.shape
upsample = T.zeros((shp[0], shp[1], shp[2] * 2, shp[3] * 2), dtype=inp2.dtype)
upsample = T.set_subtensor(upsample[:, :, ::2, ::2], inp2)
upsampled_convolution = T.nnet.conv2d(upsample,
filters.dimshuffle(1, 0, 2, 3)[:, :, ::-1, ::-1], border_mode='full')
f1 = theano.function([inp1], subsampled_convolution)
f2 = theano.function([inp2], upsampled_convolution)
x = np.random.randn(1, 3, 10, 10).astype(np.float32)
f1x = f1(x)
y = np.random.randn(*f1x.shape).astype(np.float32)
f2y = f2(y)
p1 = np.dot(f1x.ravel(), y.ravel())
p2 = np.dot(x.ravel(), f2y[:, :, :-1].ravel())
print p1 - p2
p1
に等しいことp2
は、f2 が f1 の随伴であることを確証します