Theano:在自动编码器中用步幅(子采样)重建卷积

Theano: Reconstructing convolutions with stride (subsampling) in an autoencoder

我想使用 Theano 训练一个简单的卷积自动编码器,它运行良好。但是,我看不出在使用子采样(步幅)时如何反转 conv2d 命令。有没有一种有效的方法来 "invert" 使用 stride 时的卷积命令,如下图所示?

例如,我想更改以下...

from theano.tensor.nnet.conv import conv2d
x = T.tensor4('x') 
y = T.tanh(  conv2d( x, W, border_mode='valid', subsample = (1,1) )  )
z = conv2d( y, Wprime, border_mode='full', subsample = (1,1)  )

...变成subsample = (2,2)的情况。第一层将按预期工作。但是,第二层将有效 "do a convolution with stride 1, then throw away half of the outputs"。这显然与我正在寻找的操作不同 - z 的神经元数量甚至不会与 x 的长度相同。第二个 conv2d 命令对 "reconstruct" 原来的 x 应该是什么?

我由此推断您打算绑定权重,即如果第一个运算是与 W 的矩阵乘法,那么输出将由 W.T 伴随矩阵生成。在你的情况下,你会因此寻找卷积运算符的伴随,然后是子采样。

(编辑:我推断错误,你可以使用任何过滤器来 'deconvolve',只要你的形状正确。不过,谈论伴随仍然是有益的。你将能够放松之后的假设。)

由于卷积运算符和二次采样运算符是线性运算符,让我们分别用 CS 表示它们并观察卷积 + 二次采样图像 x 将是

S C x

并且 y 上的伴随运算(与 S C x 位于相同的 space)将是

C.T S.T y

现在,S.T 只不过是通过在 y 的所有条目周围添加零直到获得正确的大小,对原始图像大小进行上采样。

从你的 post 来看,你似乎知道步长 (1, 1) 的卷积运算符的伴随 - 它是具有反向滤波器和反向 border_mode 的卷积,即与filters.dimshuffle(1, 0, 2, 3)[:, :, ::-1, ::-1] 并从 border_mode='valid' 切换到 border_mode='full'

连接上采样和这个反向滤波器卷积,你就得到了你想要的伴随。

注意:可能有一些方法可以利用梯度 T.gradT.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

的伴随