Реализация im2col в TensorFlow

Я хочу реализовать операцию, подобную 2D-свертке в TensorFlow. Согласно моему пониманию, наиболее распространенный подход к реализации свертки - это первое применение im2col операция с изображением (см. здесь - п. "реализация как умножение матриц") - операция, преобразующая изображение в 2D-матрицу с отдельными "кусками" изображения, к которым ядро применяется в виде сплющенных столбцов.

в других словами, этот отрывок из вышеупомянутого связанного ресурса объясняет, что im2col не красиво:

[... Например, если вход [227x227x3] (в формате Высота x Ширина x n_channels) и он должен быть свернут с фильтрами 11x11x3 на шаге 4, тогда мы возьмем [11x11x3] блоки пикселей на входе и растянем каждый блок в вектор столбца размера 11*11*3 = 363. Итерация этого процесса на входе при шаге 4 дает (227-11)/4+1 = 55 местоположений по ширине и высоте, что приводит к выходной матрице X_col of im2col размера [363 x 3025], где каждый столбец представляет собой растянутое рецептивное поле, и всего их 55*55 = 3025. Обратите внимание, что, поскольку поля восприятия перекрываются, каждое число во входном томе может дублироваться в несколько отдельных столбцов.

как я понял из TensorFlow docs, это то, что делается внутри tf.nn.conv2d как хорошо.

теперь, я бы хотелось бы реализовать сказанное im2col операция в TensorFlow отдельно (так как я хочу иметь доступ к этому промежуточному результату). Поскольку это включает копирование значений нетривиальным способом, как я сам построю относительно эффективный вычислительный граф для этой операции? Аналогично, как можно было бы реализовать обратную операцию?

1 ответов


вы можете легко сделать это с помощью extract_image_patches.

эта функция ставит каждого filter_size x filter_size патч изображения в глубину, дающую [batch_size, height, width, 9] тензор.

Для сравнения tf.nn.conv2d вы можете реализовать оператор Sobel для изображений

import tensorflow as tf
import numpy as np

image = np.arange(10 * 10 * 1).reshape(1, 10, 10, 1)

images = tf.convert_to_tensor(image.astype(np.float32))

filter_size = 3
sobel_x = tf.constant([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], tf.float32)
sobel_x_filter = tf.reshape(sobel_x, [3, 3, 1, 1])

image_patches = tf.extract_image_patches(images,
                                         [1, filter_size, filter_size, 1],
                                         [1, 1, 1, 1], [1, 1, 1, 1],
                                         padding='SAME')


actual = tf.reduce_sum(tf.multiply(image_patches, tf.reshape(sobel_x_filter, [9])), 3, keep_dims=True)
expected = tf.nn.conv2d(images, sobel_x_filter, strides=[1, 1, 1, 1], padding='SAME')

with tf.Session() as sess:
    print sess.run(tf.reduce_sum(expected - actual))

это дает вам 0.0 как они эквивалентны. Для этого не нужна обратная функция.

редактировать:

как я понял из TensorFlow docs, это то, что сделано внутренне с ТФ.НН.conv2d, а также.

нет, не совсем. TF на GPU, например, полагается на CuDNN, который является более сложный зверь (winograd, ptx,...). Только в некоторых случаях он использует im2col подход здесь на CPU и квантованной версии здесь.