Что же ТФ.НН.conv2d делать в tensorflow?

Я смотрел на документы tensorflow о tf.nn.conv2d здесь. Но я не могу понять, что он делает или чего пытается достичь. Он говорит на документы","

#1: сглаживает фильтр до 2-D матрицы с формой

[filter_height * filter_width * in_channels, output_channels].

что это? Это элементарное умножение или просто матричное умножение? Я также не мог понять два других момента, упомянутых в документах. У меня есть напишите их ниже:

# 2: извлекает патчи изображения из входного тензора, чтобы сформировать виртуальный тензор формы

[batch, out_height, out_width, filter_height * filter_width * in_channels].

# 3: для каждого патча справа-умножает матрицу фильтра и вектор патча изображения.

было бы очень полезно, если бы кто-нибудь мог привести пример, фрагмент кода (очень полезный), возможно, и объяснить, что там происходит и почему операция такая.

Я попробовал кодировать небольшую часть и распечатать форму операции. И все же я не понимаю.

Я пробовал что-то вроде этого:

op = tf.shape(tf.nn.conv2d(tf.random_normal([1,10,10,10]), 
              tf.random_normal([2,10,10,10]), 
              strides=[1, 2, 2, 1], padding='SAME'))

with tf.Session() as sess:
    result = sess.run(op)
    print(result)

Я понимаю кусочки сверточных нейронных сетей. Я изучал их!--33-->здесь. Но реализация на tensorflow-это не то, что я ожидал. Так что вопрос встал сам собой.

редактировать: Итак, я реализовал гораздо более простой код. Но я не могу понять, что происходит. Я имею в виду, как результаты таковы. Было бы чрезвычайно полезно, если бы кто-нибудь мог сказать мне, какой процесс дает этот результат.

input = tf.Variable(tf.random_normal([1,2,2,1]))
filter = tf.Variable(tf.random_normal([1,1,1,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
init = tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)

    print("input")
    print(input.eval())
    print("filter")
    print(filter.eval())
    print("result")
    result = sess.run(op)
    print(result)

выход

input
[[[[ 1.60314465]
   [-0.55022103]]

  [[ 0.00595062]
   [-0.69889867]]]]
filter
[[[[-0.59594476]]]]
result
[[[[-0.95538563]
   [ 0.32790133]]

  [[-0.00354624]
   [ 0.41650501]]]]

5 ответов


2D свертка вычисляется аналогичным образом, можно было бы вычислить 1D свертка: вы скользите ядром по входу, вычисляете умножение по элементам и суммируете их. Но вместо того, чтобы ваше ядро / вход были массивом, здесь они являются матрицами.


в самом основном примере нет отступа и шага=1. Предположим, ваш input и kernel несколько: enter image description here

когда вы свое ядро вы получите следующий вывод: enter image description here, которая рассчитывается следующим образом:

  • 14 = 4 * 1 + 3 * 0 + 1 * 1 + 2 * 2 + 1 * 1 + 0 * 0 + 1 * 0 + 2 * 0 + 4 * 1
  • 6 = 3 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 0 * 1 + 1 * 0 + 2 * 0 + 4 * 0 + 1 * 1
  • 6 = 2 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 2 * 1 + 4 * 0 + 3 * 0 + 1 * 0 + 0 * 1
  • 12 = 1 * 1 + 0 * 0 + 1 * 1 + 2 * 2 + 4 * 1 + 1 * 0 + 1 * 0 + 0 * 0 + 2 * 1

ТФ это conv2d функция вычисляет свертки в партиях и использует немного другой формат. Для ввода это [batch, in_height, in_width, in_channels] для ядра это [filter_height, filter_width, in_channels, out_channels]. Поэтому нам нужно предоставить данные в правильном формате:

import tensorflow as tf
k = tf.constant([
    [1, 0, 1],
    [2, 1, 0],
    [0, 0, 1]
], dtype=tf.float32, name='k')
i = tf.constant([
    [4, 3, 1, 0],
    [2, 1, 0, 1],
    [1, 2, 4, 1],
    [3, 1, 0, 2]
], dtype=tf.float32, name='i')
kernel = tf.reshape(k, [3, 3, 1, 1], name='kernel')
image  = tf.reshape(i, [1, 4, 4, 1], name='image')

после этого свертка вычисляется с помощью:

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "VALID"))
# VALID means no padding
with tf.Session() as sess:
   print sess.run(res)

и будет эквивалентно тому, который мы рассчитали вручную.


на примеры с прокладкой / шагами, взгляните вот!--8-->.


хорошо, я думаю, что это самый простой способ объяснить все это.


ваш пример - 1 Изображение, размер 2x2, с 1 каналом. У вас есть 1 фильтр с размером 1x1 и 1 канал (размер-Высота x Ширина X каналы x количество фильтров).

для этого простого случая результирующее изображение канала 2x2, 1 (размер 1x2x2x1, количество изображений X Высота x Ширина x каналы) является результатом умножения значения фильтра на каждый пиксель изображение.


теперь давайте попробуем больше каналов:

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

здесь изображение 3x3 и фильтр 1x1 имеют по 5 каналов. Полученное изображение будет 3x3 с 1 каналом (размер 1x3x3x1), где значением каждого пикселя является точечный продукт по каналам фильтра с соответствующим пикселем во входном изображении.


теперь с фильтром 3x3

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

здесь мы получаем изображение 1x1 с 1 каналом (размер 1x1x1x1). Значение сумма 9, 5-элементных точечных изделий. Но вы можете просто назвать это 45-элементным точечным продуктом.


теперь с большим изображения

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

выход 3x3 1-канальное изображение (размер 1x3x3x1). Каждое из этих значений представляет собой сумму 9, 5-элемент точечной продуктов.

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

.....
.xxx.
.xxx.
.xxx.
.....

теперь с" тем же " дополнением:

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')

это дает выходное изображение 5x5 (размер 1x5x5x1). Это делается путем центрирования фильтра в каждой позиции на изображении.

любой из 5-элементных точечных продуктов, где фильтр торчит за краем изображения, получает значение нуля.

поэтому углы только суммы 4, 5-элемент точечной продуктов.


теперь с несколько фильтры.

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')

это все еще дает выходное изображение 5x5, но с 7 каналами (размер 1x5x5x7). Где каждый канал производится одним из фильтров в наборе.


теперь с шагами 2,2:

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

теперь результат все еще имеет 7 каналов, но только 3x3 (размер 1x3x3x7).

это потому, что вместо центрирования фильтров в каждой точке изображения фильтры центрируются в каждой другой точке изображения, предпринимая шаги (шаги) шириной 2. The xниже представлен центр фильтра для каждого выходного пикселя на входном изображении.

x.x.x
.....
x.x.x
.....
x.x.x

и, конечно, первое измерение ввода-это количество изображений, поэтому вы можете применить его к партии из 10 изображений, например:

input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

это выполняет ту же операцию, для каждого изображения независимо, давая стек из 10 изображений в результате (размер 10x3x3x7)


чтобы добавить к другим ответам, вы должны подумать о параметрах в

filter = tf.Variable(tf.random_normal([3,3,5,7]))

как '5', соответствующее количеству каналов в каждом фильтре. Каждый фильтр представляет собой 3d-куб с глубиной 5. Глубина фильтра должна соответствовать глубине входного изображения. Последний параметр, 7, следует рассматривать как количество фильтров в пакете. Просто забудьте об этом 4D, и вместо этого представьте, что у вас есть набор или пакет из 7 фильтров. Вы создаете 7 кубов фильтров с Размеры (3,3,5).

это намного проще визуализировать в области Фурье, так как свертка становится точечным умножением. Для входного изображения размеров (100,100,3) вы можете переписать размеры фильтра как

filter = tf.Variable(tf.random_normal([100,100,3,7]))

чтобы получить одну из 7 выходных карт объектов, мы просто выполняем точечное умножение Куба фильтра с кубом изображения, затем суммируем результаты по измерению каналов/глубины (здесь это 3), сворачиваясь в 2d (100,100) карте. Сделайте это с каждым кубом фильтра, и вы получите 7 2D-карт объектов.


Я попытался реализовать conv2d (для моего изучения). Ну, я так и написал:

def conv(ix, w):
   # filter shape: [filter_height, filter_width, in_channels, out_channels]
   # flatten filters
   filter_height = int(w.shape[0])
   filter_width = int(w.shape[1])
   in_channels = int(w.shape[2])
   out_channels = int(w.shape[3])
   ix_height = int(ix.shape[1])
   ix_width = int(ix.shape[2])
   ix_channels = int(ix.shape[3])
   filter_shape = [filter_height, filter_width, in_channels, out_channels]
   flat_w = tf.reshape(w, [filter_height * filter_width * in_channels, out_channels])
   patches = tf.extract_image_patches(
       ix,
       ksizes=[1, filter_height, filter_width, 1],
       strides=[1, 1, 1, 1],
       rates=[1, 1, 1, 1],
       padding='SAME'
   )
   patches_reshaped = tf.reshape(patches, [-1, ix_height, ix_width, filter_height * filter_width * ix_channels])
   feature_maps = []
   for i in range(out_channels):
       feature_map = tf.reduce_sum(tf.multiply(flat_w[:, i], patches_reshaped), axis=3, keep_dims=True)
       feature_maps.append(feature_map)
   features = tf.concat(feature_maps, axis=3)
   return features

надеюсь, я сделал это правильно. Проверено на MNIST, имело очень близкие результаты (но эта реализация медленнее). Надеюсь, это вам поможет.


в дополнение к другим ответам, операция conv2d работает на c++ (cpu) или cuda для gpu-машин, которые требуют сглаживать и изменять данные определенным образом и использовать умножение матрицы gemmBLAS или cuBLAS(cuda).