Tensorflow нейронной сети свертки с разными по размеру изображений
Я пытаюсь создать глубокий CNN, который может классифицировать каждый отдельный пиксель в изображении. Я копирую архитектуру из изображения ниже, взятого из этой бумаги. В статье упоминается, что деконволюции используются так, что возможен любой размер ввода. Это можно увидеть на изображении ниже.
В настоящее время я жестко закодировал свою модель, чтобы принимать изображения размер 32x32x7, но я хотел бы принять любой размер входных. какие изменения мне нужно внести в мой код, чтобы принять ввод переменного размера?
x = tf.placeholder(tf.float32, shape=[None, 32*32*7])
y_ = tf.placeholder(tf.float32, shape=[None, 32*32*7, 3])
...
DeConnv1 = tf.nn.conv3d_transpose(layer1, filter = w, output_shape = [1,32,32,7,1], strides = [1,2,2,2,1], padding = 'SAME')
...
final = tf.reshape(final, [1, 32*32*7])
W_final = weight_variable([32*32*7,32*32*7,3])
b_final = bias_variable([32*32*7,3])
final_conv = tf.tensordot(final, W_final, axes=[[1], [1]]) + b_final
2 ответов
динамический заполнители
Tensorflow позволяет иметь несколько dynamic (a.к. a. None
) размеры в заполнители. Движок не сможет обеспечить корректность при построении графика, поэтому клиент отвечает за подачу правильного ввода, но он обеспечивает большую гибкость.
Итак, я ухожу...
x = tf.placeholder(tf.float32, shape=[None, N*M*P])
y_ = tf.placeholder(tf.float32, shape=[None, N*M*P, 3])
...
x_image = tf.reshape(x, [-1, N, M, P, 1])
to...
# Nearly all dimensions are dynamic
x_image = tf.placeholder(tf.float32, shape=[None, None, None, None, 1])
label = tf.placeholder(tf.float32, shape=[None, None, 3])
так как вы намерены изменить вход в 5D в любом случае, так почему не используйте 5D в x_image
С самого начала. В этот момент второе измерение label
произвольная, но мы обещание tensorflow, что он будет соответствовать x_image
.
динамические формы в деконволюции
далее хорошая вещь о tf.nn.conv3d_transpose
является то, что его выходная форма может быть динамической. Поэтому вместо этого:
# Hard-coded output shape
DeConnv1 = tf.nn.conv3d_transpose(layer1, w, output_shape=[1,32,32,7,1], ...)
... вы можете сделать это:
# Dynamic output shape
DeConnv1 = tf.nn.conv3d_transpose(layer1, w, output_shape=tf.shape(x_image), ...)
таким образом, свертка транспонирования может быть применяется к любой изображение и результат будут принимать форму x_image
это было фактически передано во время выполнения.
обратите внимание, что статическая форма x_image
is (?, ?, ?, ?, 1)
.
все свертки сети
окончательный и самый важный кусок головоломки, чтобы сделать вся сеть свернутый, и это включает в себя ваш последний плотный слой. Плотный слой должны определите свои размеры статически, который принуждает вся нейронная сеть фиксирует размеры входного изображения.
к счастью для нас, Springenberg at al описывает способ замены слоя FC слоем CONV в "стремление к простоте: вся сверточная сеть" бумаги. Я собираюсь использовать свертку с 3 1x1x1
фильтры (см. Также этот вопрос):
final_conv = conv3d_s1(final, weight_variable([1, 1, 1, 1, 3]))
y = tf.reshape(final_conv, [-1, 3])
если мы гарантируем, что final
имеет те же размеры, что и DeConnv1
(и другие), это сделает y
правильная форма, которую мы хотим: [-1, N * M * P, 3]
.
объединение всех вместе
ваша сеть довольно большая, но все деконволюции в основном следуют одному и тому же шаблону, поэтому я упростил свой доказательств -- принципиальной схемы код только для одной деконволюции. Цель состоит в том, чтобы показать, какая сеть способна обрабатывать изображения произвольного размера. Последнее замечание: размеры изображения могут варьироваться между партии, но в партии они должны быть одинаковыми.
полный код:
sess = tf.InteractiveSession()
def conv3d_dilation(tempX, tempFilter):
return tf.layers.conv3d(tempX, filters=tempFilter, kernel_size=[3, 3, 1], strides=1, padding='SAME', dilation_rate=2)
def conv3d(tempX, tempW):
return tf.nn.conv3d(tempX, tempW, strides=[1, 2, 2, 2, 1], padding='SAME')
def conv3d_s1(tempX, tempW):
return tf.nn.conv3d(tempX, tempW, strides=[1, 1, 1, 1, 1], padding='SAME')
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def max_pool_3x3(x):
return tf.nn.max_pool3d(x, ksize=[1, 3, 3, 3, 1], strides=[1, 2, 2, 2, 1], padding='SAME')
x_image = tf.placeholder(tf.float32, shape=[None, None, None, None, 1])
label = tf.placeholder(tf.float32, shape=[None, None, 3])
W_conv1 = weight_variable([3, 3, 1, 1, 32])
h_conv1 = conv3d(x_image, W_conv1)
# second convolution
W_conv2 = weight_variable([3, 3, 4, 32, 64])
h_conv2 = conv3d_s1(h_conv1, W_conv2)
# third convolution path 1
W_conv3_A = weight_variable([1, 1, 1, 64, 64])
h_conv3_A = conv3d_s1(h_conv2, W_conv3_A)
# third convolution path 2
W_conv3_B = weight_variable([1, 1, 1, 64, 64])
h_conv3_B = conv3d_s1(h_conv2, W_conv3_B)
# fourth convolution path 1
W_conv4_A = weight_variable([3, 3, 1, 64, 96])
h_conv4_A = conv3d_s1(h_conv3_A, W_conv4_A)
# fourth convolution path 2
W_conv4_B = weight_variable([1, 7, 1, 64, 64])
h_conv4_B = conv3d_s1(h_conv3_B, W_conv4_B)
# fifth convolution path 2
W_conv5_B = weight_variable([1, 7, 1, 64, 64])
h_conv5_B = conv3d_s1(h_conv4_B, W_conv5_B)
# sixth convolution path 2
W_conv6_B = weight_variable([3, 3, 1, 64, 96])
h_conv6_B = conv3d_s1(h_conv5_B, W_conv6_B)
# concatenation
layer1 = tf.concat([h_conv4_A, h_conv6_B], 4)
w = tf.Variable(tf.constant(1., shape=[2, 2, 4, 1, 192]))
DeConnv1 = tf.nn.conv3d_transpose(layer1, filter=w, output_shape=tf.shape(x_image), strides=[1, 2, 2, 2, 1], padding='SAME')
final = DeConnv1
final_conv = conv3d_s1(final, weight_variable([1, 1, 1, 1, 3]))
y = tf.reshape(final_conv, [-1, 3])
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=label, logits=y))
print('x_image:', x_image)
print('DeConnv1:', DeConnv1)
print('final_conv:', final_conv)
def try_image(N, M, P, B=1):
batch_x = np.random.normal(size=[B, N, M, P, 1])
batch_y = np.ones([B, N * M * P, 3]) / 3.0
deconv_val, final_conv_val, loss = sess.run([DeConnv1, final_conv, cross_entropy],
feed_dict={x_image: batch_x, label: batch_y})
print(deconv_val.shape)
print(final_conv.shape)
print(loss)
print()
tf.global_variables_initializer().run()
try_image(32, 32, 7)
try_image(16, 16, 3)
try_image(16, 16, 3, 2)
теоретически, это возможно. вам нужно установить размер изображения входного и меточного держателя изображения на none
, и пусть график динамически выводит размер изображения из входных данных.
однако, должны быть осторожны, когда вы определяете график. Нужно использовать tf.shape
вместо tf.get_shape()
. первые динамически выводят форму только тогда, когда вы session.run
, последний может получить форму при определении графика. Но когда размер входного сигнала установлен в none
, последнее не получает true изменить (возможно, просто нет).
и, чтобы сделать вещи сложными, если вы используете tf.layers.conv2d
или upconv2d
, иногда эти функции высокого уровня не нравится tf.shape
, потому что, похоже, они предполагают, что информация о форме доступна во время построения графика.
Я надеюсь, что у меня есть рабочий пример, чтобы показать вышеперечисленных пунктов. Я поставлю этот ответ в качестве заполнителя и вернусь и добавлю больше вещей, если у меня будет шанс.