Что же ТФ.НН.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
несколько:
когда вы свое ядро вы получите следующий вывод: , которая рассчитывается следующим образом:
- 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)
и будет эквивалентно тому, который мы рассчитали вручную.
хорошо, я думаю, что это самый простой способ объяснить все это.
ваш пример - 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 x
s ниже представляют центры фильтра для каждого выходной пиксель.
.....
.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).