Что делает операция "conv2d transpose ()" TensorFlow?
документация conv2d_transpose()
операция четко не объясняет, что она делает:
транспонирование conv2d.
эта операция иногда называется "деконволюция" после Сети Deconvolutional, но на самом деле является транспонированием (градиентом) conv2d, а не деконволюции.
Я просмотрел бумагу, на которую указывает док, но это не помогло.
что это операция do и каковы примеры того, почему вы хотите ее использовать?
4 ответов
Это лучшее объяснение, которое я видел в интернете, как работает транспонирование свертки здесь.
Я дам свое собственное краткое описание. Он применяет свертку с дробным шагом. Другими словами, расстояние между входными значениями (с нулями) для применения фильтра к области, которая потенциально меньше размера фильтра.
Что касается того, почему один хотел бы использовать его. Его можно использовать как своего рода upsampling с выученными весами в отличие от билинейная интерполяция или другой фиксированной форме апсемплинга.
вот еще одна точка зрения с точки зрения "градиентов", т. е. почему документация TensorFlow говорит conv2d_transpose()
это "на самом деле транспонирование (градиент) conv2d, а не фактической деконволюции". для получения более подробной информации о фактических вычислениях, выполненных в conv2d_transpose
, я настоятельно рекомендую в этой статье, начиная со страницы 19.
Четыре Взаимосвязанные Функции
на tf.nn
, есть 4 тесно связанных и довольно запутанные функции для 2d свертки:
tf.nn.conv2d
tf.nn.conv2d_backprop_filter
tf.nn.conv2d_backprop_input
tf.nn.conv2d_transpose
резюме одного предложения:они все просто 2d свертки. Их различия заключаются в упорядочении входных аргументов, вращении ввода или транспонировании, шагах (включая размер дробного шага), прокладках и т. д. С tf.nn.conv2d
в руке, одно может снабдить все 3 других ops путем преобразовывать входные сигналы и изменение conv2d
аргументов.
Настройки Проблема
- вперед и назад расчета:
# forward
out = conv2d(x, w)
# backward, given d_out
=> find d_x?
=> find d_w?
в прямом вычислении мы вычисляем свертку входного изображения x
фильтр w
и в результате out
.
В обратном вычислении предположим, что нам дано d_out
, который является градиентом w.r.т. out
. Наша цель-найти d_x
и d_w
, которые являются градиентом w.r.т. x
и w
соответственно.
для простоты обсуждения будем считать:
- весь размер шага, чтобы быть
1
- все
in_channels
иout_channels
are1
- использовать
VALID
обивка - нечетный размер фильтра, это позволяет избежать некоторых асимметричных проблем формы
Короткий Ответ:
концептуально, с предположениями выше, мы имеем следующее отношения:
out = conv2d(x, w, padding='VALID')
d_x = conv2d(d_out, rot180(w), padding='FULL')
d_w = conv2d(x, d_out, padding='VALID')
здесь rot180
это 2d-матрица, повернутая на 180 градусов (левый-правый флип и флип сверху вниз),FULL
означает " применить фильтр везде, где он частично перекрывается с входом "(см. Феано документы). Отмечает, что это справедливо только с вышеуказанными предположениями, однако, можно изменить аргументы conv2d, чтобы обобщить его.
ключевые моменты:
- входной градиент
d_x
свертка выходного градиентаd_out
и массойw
С некоторыми изменениями. - градиент веса
d_w
является сверткой входногоx
и выходной градиентd_out
с некоторыми изменениями.
Ответ
теперь давайте приведем фактический пример рабочего кода о том, как использовать 4 функции выше для вычисления d_x
и d_w
дано d_out
. Это показывает, как
conv2d
,
conv2d_backprop_filter
,
conv2d_backprop_input
, и
conv2d_transpose
есть связаны друг с другом.
пожалуйста, найдите полные сценарии здесь.
вычислений d_x
в 4-разному:
# Method 1: TF's autodiff
d_x = tf.gradients(f, x)[0]
# Method 2: manually using conv2d
d_x_manual = tf.nn.conv2d(input=tf_pad_to_full_conv2d(d_out, w_size),
filter=tf_rot180(w),
strides=strides,
padding='VALID')
# Method 3: conv2d_backprop_input
d_x_backprop_input = tf.nn.conv2d_backprop_input(input_sizes=x_shape,
filter=w,
out_backprop=d_out,
strides=strides,
padding='VALID')
# Method 4: conv2d_transpose
d_x_transpose = tf.nn.conv2d_transpose(value=d_out,
filter=w,
output_shape=x_shape,
strides=strides,
padding='VALID')
вычислений d_w
3 различными способами:
# Method 1: TF's autodiff
d_w = tf.gradients(f, w)[0]
# Method 2: manually using conv2d
d_w_manual = tf_NHWC_to_HWIO(tf.nn.conv2d(input=x,
filter=tf_NHWC_to_HWIO(d_out),
strides=strides,
padding='VALID'))
# Method 3: conv2d_backprop_filter
d_w_backprop_filter = tf.nn.conv2d_backprop_filter(input=x,
filter_sizes=w_shape,
out_backprop=d_out,
strides=strides,
padding='VALID')
смотрите полный скрипты для реализации tf_rot180
, tf_pad_to_full_conv2d
, tf_NHWC_to_HWIO
. В сценариях мы проверяем, что конечные выходные значения разных методов одинаковы; реализация numpy также доступно.
conv2d_transpose () просто переносит веса и переворачивает их на 180 градусов. Затем применяется стандартный conv2d (). "Транспонирует "практически означает, что он изменяет порядок" столбцов " в Тензоре Весов. Пожалуйста, проверьте пример ниже.
здесь есть пример, который использует свертки со stride=1 и padding='SAME'. Это простой случай, но то же самое рассуждение может быть применено и к другим случаям.
говорят, что мы есть:
- вход: MNIST изображение 28x28x1, shape = [28,28,1]
- сверточный слой: 32 фильтра 7x7, форма Весов = [7, 7, 1, 32], name = W_conv1
если мы выполним свертку ввода, то активации будут иметь форму: [1,28,28,32].
activations = sess.run(h_conv1,feed_dict={x:np.reshape(image,[1,784])})
где:
W_conv1 = weight_variable([7, 7, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = conv2d(x, W_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1
для получения "деконволюции" или "транспонированной свертки" мы можем использовать conv2d_transpose() для активации свертки в таким образом:
deconv = conv2d_transpose(activations,W_conv1, output_shape=[1,28,28,1],padding='SAME')
или с помощью conv2d () нам нужно транспонировать и переворачивать веса:
transposed_weights = tf.transpose(W_conv1, perm=[0, 1, 3, 2])
здесь мы меняем порядок "колумов" с [0,1,2,3] на [0,1,3,2].Итак, из [7, 7, 1, 32] получим тензор с формой=[7,7,32,1]. Затем переворачиваем гири:
for i in range(n_filters):
# Flip the weights by 180 degrees
transposed_and_flipped_weights[:,:,i,0] = sess.run(tf.reverse(transposed_weights[:,:,i,0], axis=[0, 1]))
тогда мы можем вычислить свертку с conv2d () как:
strides = [1,1,1,1]
deconv = conv2d(activations,transposed_and_flipped_weights,strides=strides,padding='SAME')
и мы получим тот же результат, что и раньше. Также очень такой же результат можно получить с conv2d_backprop_input (), используя:
deconv = conv2d_backprop_input([1,28,28,1],W_conv1,activations, strides=strides, padding='SAME')
результаты показаны здесь:
тест conv2d (), conv2d_tranposed () и conv2d_backprop_input ()
мы видим, что результаты совпадают. Чтобы увидеть это лучше, пожалуйста, проверьте мой код по адресу:
https://github.com/simo23/conv2d_transpose
здесь я реплицирую вывод функции conv2d_transpose (), используя стандартный conv2d ().
одно приложение для conv2d_transpose масштабируется, вот пример, который объясняет, как это работает:
a = np.array([[0, 0, 1.5],
[0, 1, 0],
[0, 0, 0]]).reshape(1,3,3,1)
filt = np.array([[1, 2],
[3, 4.0]]).reshape(2,2,1,1)
b = tf.nn.conv2d_transpose(a,
filt,
output_shape=[1,6,6,1],
strides=[1,2,2,1],
padding='SAME')
print(tf.squeeze(b))
tf.Tensor(
[[0. 0. 0. 0. 1.5 3. ]
[0. 0. 0. 0. 4.5 6. ]
[0. 0. 1. 2. 0. 0. ]
[0. 0. 3. 4. 0. 0. ]
[0. 0. 0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0. ]], shape=(6, 6), dtype=float64)