Недостаток классических CNN заключается в том, что нам нужно умножить количество фильтров, чтобы каждый фильтр был активирован отдельным объектом (например, зонтик активирует 67-й фильтр первой свертки и x-й фильтр k-я свертка, ...) Более того, CNN не учитывает сходство между пикселями. Чтобы улучшить качество сегментации / альфа-матирования с помощью нейронной сети, нам нужно придумать новые архитектуры. Недавно я наткнулся на эту статью: https://arxiv.org/pdf/1904.05373.pdf, и я нахожу эту идею очень интересной. Я хочу создать другой слой, основанный на той же идее, но для простоты скажем, я хочу реализовать PAC, как описано в упомянутой статье.
У меня есть несколько идей реализовать его с помощью TensorFlow Python API, но я думаю, что ни один из них не достаточно быстр. Идея состоит в том, что нам нужно взвесить веса каждого традиционного фильтра CNN с весами симметричной матрицы (ядра) с центром в каждой операции свертки (см. Рисунок 1 статьи), но функция tf.nn.conv2d
принимает только набор фильтров размером [filter_height, filter_width, filter_input, filter_output]
, чтобы свертка не менялась в зависимости от положения окна, с которым мы хотим сворачиваться.
Моя идея состояла в том, чтобы определить функцию, аналогичную функции tf.nn.conv2d
, используя тензор потока, а затем, если код достаточно быстр, взвешивать фильтр с весами симметричного ядра, а затем реализовать слой, расширяя tf.keras.Layer
(https://www.tensorflow.org/tutorials/eager/custom_layers). Проблема в том, что код Python, который у меня есть, который имитирует функцию tf.nn.conv2d
, на 100x медленнее , чем функция tf.nn.conv2d
...
Вот код, который я реализовал для имитации функции tf.nn.conv2d
.
import tensorflow as tf
def inner_conv_x(w, x, y, inp, filters, strides, f_sh, indices, updates):
# IDEA: need to pass the kernel and elementwise multiple
# inp[..] * filters * kernel[..]
val = tf.reduce_sum(
inp[y * strides[1]:f_sh[1] + y * strides[1], x * strides[2] : f_sh[2] + x * strides[2]] * \
filters, axis=(0, 1, 2))
idx = y * w + x
indices = indices.write(idx, [y, x])
updates = updates.write(idx, val)
return w, x + 1, y, inp, filters, strides, f_sh, indices, updates
def inner_conv(h, y, w, inp, filters, strides, f_sh, indices, updates):
cond2 = lambda width, step_x, *args: width > step_x
_, x, y, _, _, _, _, indices, updates = tf.while_loop(
cond2, inner_conv_x,
[w, 0, y, inp, filters, strides, f_sh, indices, updates])
return h, y + 1, w, inp, filters, strides, f_sh, indices, updates
def my_tf_conv2(inputs, filters, padding, strides):
f_sh = tf.shape(filters)
in_sh = tf.shape(inputs)
_, s_h, s_w, _ = strides
if padding.upper() == 'SAME':
out_h = tf.cast(tf.ceil(in_sh[1] / s_h), tf.int32)
out_w = tf.cast(tf.ceil(in_sh[2] / s_w), tf.int32)
padding_h = tf.maximum((out_h - 1) * s_h + f_sh[1] - in_sh[1], 0)
pad_h1 = padding_h // 2
pad_h2 = padding_h - pad_h1
padding_w = tf.maximum((out_w - 1) * s_w + f_sh[2] - in_sh[2], 0)
pad_w1 = padding_w // 2
pad_w2 = padding_w - pad_w1
inputs = tf.pad(inputs, [[0, 0]] + [[pad_h1, pad_h2], [pad_w1, pad_w2]] + [[0, 0]])
elif padding.upper() == 'VALID':
out_h = tf.cast(tf.ceil((h - f_sh[1] + 1) / s_h), tf.int32)
out_w = tf.cast(tf.ceil((w - f_sh[2] + 1) / s_w), tf.int32)
cond1 = lambda height, step_y, *args: height > step_y
res = []
inputs = tf.unstack(inputs)
for inp in inputs:
y, x = 0, 0
indices = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True)
updates = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)
inp = tf.expand_dims(inp, axis=-1)
_, y, _, _, _, _, _, indices, updates = tf.while_loop(
cond1,
inner_conv, [out_h, y, out_w, inp, filters, strides, f_sh, indices, updates],
parallel_iterations=2)
res.append(tf.scatter_nd(
indices.stack(),
updates.stack(),
shape=(out_h, out_w, f_sh[3])))
res = tf.stack(res)
return res
тогда вы можете проверить это следующим образом:
my_result2 = my_tf_conv2(tf_inputs, tf_filters, padding="SAME", strides=[1, 2, 2, 1])
with tf.Session() as sess:
sess.run(tf.initializers.global_variables())
output = sess.run(my_result2)
print(output)
В комментарии я объяснил свою идею. Дело в том, что он настолько медленный, что я еще не начал реализовывать полный уровень PAC ...
Мне было интересно. Кто-нибудь из вас может придумать способ эффективной реализации уровня PAC (а не в 100 раз медленнее или даже больше, как это будет в случае, если я продолжу свою реализацию в этом направлении)? Я думал об использовании tf.nn.depthwise_conv2d
может быть? Как вы думаете, мы можем реализовать эффективную версию только с использованием CUDA, а не высокоуровневого API-интерфейса python tenorflow?
Спасибо всем