Я выхожу из этого, что вы намерены иметь привязанные веса, то есть если первая операция была матричным умножением с W
, тогда выход будет сгенерирован с помощью W.T
, сопряженной матрицы. В вашем случае вы, таким образом, будете искать сопряженный оператор свертки с последующей подвыборкой.
(EDIT: Я ошибочно сделал вывод, что вы можете использовать любой фильтр для «deconvolve», если вы получите правильные формы. Говоря о присоединенном, все еще информативно. .)
Поскольку оператор свертки и подвыборки операторы линейный оператор, позволяет обозначать их C
и S
соответственно и заметить, что свертка + подвыборки изображение x
будет
S C x
и что операции присоединенной на y
(который живет в том же пространстве, S C x
) будет
C.T S.T y
Теперь S.T не что иное, увеличив частоту до исходного размера изображения путем добавления нулей вокруг всех записей y
до нужного размера не получается.
С вашего поста, вы, кажется, чтобы быть в курсе сопряженного оператора свертки с шага (1, 1) - это свертка с обратными фильтрами и обратной border_mode
, то есть с filters.dimshuffle(1, 0, 2, 3)[:, :, ::-1, ::-1]
и переключателем от border_mode='valid'
к border_mode='full'
.
Конкатенация повышающей дискретизации и свертки с обратным фильтром, и вы получаете сопряженное соединение.
Примечание: могут быть способы использования градиента T.grad
или T.jacobian
, чтобы получить это автоматически, но я не уверен, как это делается точно.
EDIT: Там, я записал :)
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
Конечно, вы можете пропустить 'set_subtensor' со всеми нулями если вы решительно рассеиваете фильтр на то, что он видит на четных и нечетных шагах ... (возможно, было бы полезно понять эту градиентную вещь) – eickenberg
Другое примечание: если вы не используете привязанные веса и сопряженность, вы также можете использовать другие значения, кроме нуля, в повышающей дискретизации (например, ближайший сосед). – eickenberg
Спасибо, нулевое заполнение между скрытыми нейронами именно то, что я искал! (Удивительно, насколько просто это на самом деле ...). Я надеялся, что будет какая-то реализация, которая позволит избежать ненужного вычисления сверток (например, те, которые связаны с нашим нулем), я считаю, что эти шаги часто используются для повышения скорости), поэтому я попытаюсь посмотреть на grad/jacobian. – Koen