Tensorflow: свертки с разными фильтрами для каждого образца в мини-пакете

Я хотел бы иметь 2d-свертку с фильтром, который зависит от образца в мини-пакете в тензорном потоке. Любые идеи, как это можно сделать, особенно если количество образцов на мини-партию неизвестно?

Конкретно, у меня есть входные данные inp формы MB x H x W x Channels, и у меня есть фильтры F формы MB x fh x fw x Channels x OutChannels.

Предполагается, что

inp = tf.placeholder('float', [None, H, W, channels_img], name='img_input').

Я хотел бы сделать tf.nn.conv2d(inp, F, strides = [1,1,1,1]), но это не разрешено, потому что F не может иметь мини-пакетное измерение. Любая идея, как решить эту проблему?


person patapouf_ai    schedule 06.02.2017    source источник
comment
Возможно, вы могли бы использовать tf.expand_dims, чтобы добавить «поддельное измерение мини-пакета», а затем использовать tf.nn.conv3d, где глубина фильтра соответствует размеру пакета. Не уверен, насколько хорошо это будет работать с переменным размером партии.   -  person Robert Lacok    schedule 06.02.2017
comment
@RobertLacok звучит как отличная идея. Единственная проблема заключается в том, что если я это сделаю, я не буду знать размер нового пространственного измерения (размер мини-пакета). Но попробую... может все-таки получится...   -  person patapouf_ai    schedule 06.02.2017
comment
Я предполагаю, что вам нужно знать его верхнюю границу и инициализировать веса (фильтры) с этим измерением. Затем во время выполнения вы можете сделать что-то вроде batch_size = tf.shape(input)[0], чтобы вывести измерение и использовать только часть фильтра. Чисто предложение, хотя я никогда не пробовал ничего подобного, поэтому это может вызвать проблемы.   -  person Robert Lacok    schedule 06.02.2017
comment
@RobertLacok, я думаю, что мне удалось использовать tf.nn.conv3d. Если вы хотите написать ответ, я приму его.   -  person patapouf_ai    schedule 07.02.2017
comment
batch_size = tf.shape(input)[0] не работает, потому что размер неизвестен, но кажется, что он не нужен.   -  person patapouf_ai    schedule 07.02.2017
comment
Хорошо, спасибо, что вернулись. Не стесняйтесь редактировать ответ, если вы узнали больше по пути.   -  person Robert Lacok    schedule 07.02.2017


Ответы (4)


Я думаю, что предложенный трюк на самом деле не правильный. Что происходит со слоем tf.conv3d(), так это то, что входные данные сворачиваются по измерению глубины (=фактическая партия) И затем суммируются с полученными картами объектов. С padding='SAME' результирующее количество выходов оказывается таким же, как размер партии, так что можно одурачить!

РЕДАКТИРОВАТЬ: я думаю, что возможный способ выполнить свертка с разными фильтрами для разных элементов мини-пакета включает в себя «взлом» свертки по глубине. Предположим, что размер партии MB известен:

inp = tf.placeholder(tf.float32, [MB, H, W, channels_img])

# F has shape (MB, fh, fw, channels, out_channels)
# REM: with the notation in the question, we need: channels_img==channels

F = tf.transpose(F, [1, 2, 0, 3, 4])
F = tf.reshape(F, [fh, fw, channels*MB, out_channels)

inp_r = tf.transpose(inp, [1, 2, 0, 3]) # shape (H, W, MB, channels_img)
inp_r = tf.reshape(inp, [1, H, W, MB*channels_img])

out = tf.nn.depthwise_conv2d(
          inp_r,
          filter=F,
          strides=[1, 1, 1, 1],
          padding='VALID') # here no requirement about padding being 'VALID', use whatever you want. 
# Now out shape is (1, H, W, MB*channels*out_channels)

out = tf.reshape(out, [H, W, MB, channels, out_channels) # careful about the order of depthwise conv out_channels!
out = tf.transpose(out, [2, 0, 1, 3, 4])
out = tf.reduce_sum(out, axis=3)

# out shape is now (MB, H, W, out_channels)

В случае, если MB неизвестно, его можно определить динамически, используя tf.shape() (я думаю)

person drasros    schedule 18.09.2017
comment
Я отредактировал ответ, потому что перепутал некоторые размеры/транспонирование... - person drasros; 19.09.2017
comment
... и суть с тестами, которые я сделал (используя 1d convs, но это не делает разница) - person drasros; 19.09.2017
comment
Обратите внимание, что этот (в противном случае отличный) ответ неверен в его нынешнем виде. Если padding = "VALID", то строка out = tf.reshape(out, [H, W, MB, channels, out_channels) должна читаться как out = tf.reshape(out, [H-fh+1, W-fw+1, MB, channels, out_channels) Ваша форма верна, если вы используете padding = "SAME". Смотрите мой ответ ниже для правильного лечения обоих случаев. - person Žiga Sajovic; 07.05.2018
comment
Есть небольшая опечатка: должно быть: inp_r = tf.reshape(inp_r, [1, H, W, MB*channels_img]) вместо inp_r = tf.reshape(inp, [1, H, W, MB*channels_img]) - person bastak; 18.12.2019

Вы можете использовать tf.map_fn следующим образом:

inp = tf.placeholder(tf.float32, [None, h, w, c_in]) 
def single_conv(tupl):
    x, kernel = tupl
    return tf.nn.conv2d(x, kernel, strides=(1, 1, 1, 1), padding='VALID')
# Assume kernels shape is [tf.shape(inp)[0], fh, fw, c_in, c_out]
batch_wise_conv = tf.squeeze(tf.map_fn(
    single_conv, (tf.expand_dims(inp, 1), kernels), dtype=tf.float32),
    axis=1
)

Важно указать dtype для map_fn. По сути, это решение определяет batch_dim_size операций двумерной свертки.

person Jos van de Wolfshaar    schedule 28.02.2018

Принятый ответ немного неверен в том, как он обрабатывает размеры, поскольку они изменяются с помощью padding = «VALID» (он обрабатывает их так, как если бы padding = «SAME»). Следовательно, в общем случае код вылетит из-за этого несоответствия. Я прикрепляю его исправленный код, в котором правильно обработаны оба сценария.

inp = tf.placeholder(tf.float32, [MB, H, W, channels_img])

# F has shape (MB, fh, fw, channels, out_channels)
# REM: with the notation in the question, we need: channels_img==channels

F = tf.transpose(F, [1, 2, 0, 3, 4])
F = tf.reshape(F, [fh, fw, channels*MB, out_channels)

inp_r = tf.transpose(inp, [1, 2, 0, 3]) # shape (H, W, MB, channels_img)
inp_r = tf.reshape(inp_r, [1, H, W, MB*channels_img])

padding = "VALID" #or "SAME"
out = tf.nn.depthwise_conv2d(
          inp_r,
          filter=F,
          strides=[1, 1, 1, 1],
          padding=padding) # here no requirement about padding being 'VALID', use whatever you want. 
# Now out shape is (1, H-fh+1, W-fw+1, MB*channels*out_channels), because we used "VALID"

if padding == "SAME":
    out = tf.reshape(out, [H, W, MB, channels, out_channels)
if padding == "VALID":
    out = tf.reshape(out, [H-fh+1, W-fw+1, MB, channels, out_channels)
out = tf.transpose(out, [2, 0, 1, 3, 4])
out = tf.reduce_sum(out, axis=3)

# out shape is now (MB, H-fh+1, W-fw+1, out_channels)
person Žiga Sajovic    schedule 07.05.2018

Они могут обойти это, добавив дополнительное измерение, используя

tf.expand_dims(inp, 0)

для создания «поддельного» размера партии. Затем используйте

tf.nn.conv3d()

операция, при которой глубина фильтра соответствует размеру пакета. Это приведет к свертыванию каждого фильтра только с одним образцом в каждой партии.

К сожалению, таким образом вы не решите проблему переменного размера пакета, только свертки.

person Robert Lacok    schedule 07.02.2017