Tensorflow: свертки с различным фильтром для каждого образца в мини-серии
Я хотел бы иметь 2d-свертку с фильтром, который зависит от образца в мини-партии в tensorflow. Есть идеи, как это можно сделать, особенно если количество образцов на мини-партию неизвестно?
конкретно, у меня есть входные данные 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
не может иметь измерение мини-партии. Есть идеи, как решить эту проблему?
4 ответов
Я думаю, что предлагаемый трюк на самом деле не правильно. Что происходит с tf.conv3d()
layer-это то, что входные данные свертываются по измерению глубины (=фактическая партия), а затем суммируются по результирующим картам объектов. С padding='SAME'
результирующее количество выходов тогда оказывается таким же, как размер партии, поэтому один обманывается!
EDIT: я думаю, что возможный способ сделать свертку с разными фильтрами для разных элементов мини-пакета включает "взлом" глубинной свертки. Самонадеянный размер партии 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()
(Я думаю)
они обходят его, добавляя дополнительное измерение, используя
tf.expand_dims(inp, 0)
чтобы создать "поддельный" размер пакета. Тогда используйте
tf.nn.conv3d()
операция, в которой глубина фильтра соответствует размеру партии. Это приведет к свертке каждого фильтра только с одним образцом в каждой партии.
к сожалению, вы не решите проблему с переменным размером партии таким образом, только свертки.
принятый ответ немного неверен в том, как он относится к измерениям, поскольку они изменяются путем заполнения = "VALID" (он относится к ним, как если бы заполнение = "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)
вы могли бы использовать 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
2D свертки операций.