TensorFlow的`conv2d_transpose()`操作有什么作用?

Min*_*ark 35 conv-neural-network tensorflow

conv2d_transpose()操作的文档没有清楚地解释它的作用:

conv2d的转置.

在Deconvolutional Networks之后,此操作有时称为"反卷积" ,但实际上是conv2d的转置(渐变)而不是实际的反卷积.

我查看了文档指出的文件,但没有帮助.

这个操作有什么作用以及为什么要使用它的例子?

Ste*_*ven 34

这是我在网上看到的最好的解释卷积转置的工作原理是怎么在这里.

我会给出自己的简短说明.它使用分数步幅应用卷积.换句话说,将输入值(使用零)间隔开,以将过滤器应用于可能小于过滤器大小的区域.

至于为什么人们会想要使用它.它可以用作一种具有学习权重的上采样,而不是双线性插值或其他一些固定形式的上采样.

  • 所以在理论上你可以通过使用常规的conv2d来实现类似的结果,但是首先要对原始图像进行平铺,以便开始时更大?而conv2d_transpose只是一种更容易做同样事情的方法吗? (2认同)

Yix*_*ing 23

这是从"渐变"角度来看的另一个观点,即为什么TensorFlow文档说conv2d_transpose()"实际上是转换(渐变)的转化而不是实际的反卷积".有关实际计算的详细信息conv2d_transpose,我强烈推荐这篇文章,从第19页开始.

四个相关功能

tf.nn有二维卷积4个密切相关,而混乱的功能:

  • tf.nn.conv2d
  • tf.nn.conv2d_backprop_filter
  • tf.nn.conv2d_backprop_input
  • tf.nn.conv2d_transpose

一句话摘要:它们都只是2d卷积.它们的区别在于它们的输入参数排序,输入旋转或转置,步幅(包括小数步幅大小),填充等等.tf.nn.conv2d在手中,可以通过转换输入和更改conv2d参数来实现所有其他3个操作.

问题设置

  • 前向和后向计算:
# forward
out = conv2d(x, w)

# backward, given d_out
=> find d_x?
=> find d_w?
Run Code Online (Sandbox Code Playgroud)

在前向计算中,我们计算输入图像x与滤波器的卷积,w结果是out.在反向计算中,假设我们给出了d_out,这是梯度wrt out.我们的目标是找到d_xd_w,分别是梯度xw.

为便于讨论,我们假设:

  • 所有步幅都是 1
  • 所有的in_channelsout_channels1
  • 使用VALID填充
  • 奇数过滤器大小,这避免了一些不对称的形状问题

简答

从概念上讲,根据上述假设,我们有以下关系:

out = conv2d(x, w, padding='VALID')
d_x = conv2d(d_out, rot180(w), padding='FULL')
d_w = conv2d(x, d_out, padding='VALID')
Run Code Online (Sandbox Code Playgroud)

其中rot180是2D矩阵旋转180度(左右翻转和自顶向下的翻转),FULL是指"应用过滤器无论它部分地与输入重叠"(见theano文档).注意,这仅适用于上述假设,但是,可以更改conv2d参数以概括它.

关键要点:

  • 输入梯度d_x是输出梯度d_out和重量的卷积,并进行了w一些修改.
  • 权重梯度d_w是输入x和输出梯度的卷积,并进行了d_out一些修改.

答案很长

现在,让我们给出一个实际工作代码示例,说明如何使用上面的4个函数来计算d_xd_w给出d_out.这显示了 conv2d, conv2d_backprop_filterconv2d_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')
Run Code Online (Sandbox Code Playgroud)

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')
Run Code Online (Sandbox Code Playgroud)

请参阅完整的脚本的执行tf_rot180,tf_pad_to_full_conv2d,tf_NHWC_to_HWIO.在脚本中,我们检查不同方法的最终输出值是否相同; 也可以使用numpy实现.

  • @RobertLugg `f` 是在 `x` 上计算的任意可微函数(这里通常是标量值)。`tf_rot180` 是用于说明目的的自定义函数,有关详细信息,请参阅答案中提供的完整脚本。 (2认同)

sim*_*o23 12

conv2d_transpose()只需转置权重并将它们翻转180度.然后它应用标准的conv2d()."Transposes"实际上意味着它改变了权重张量中"列"的顺序.请查看以下示例.

这里有一个使用带有stride = 1和padding ='SAME'的卷积的示例.这是一个简单的案例,但同样的推理可以应用于其他案例.

说我们有:

  • 输入:MNIST图像28x28x1,形状= [28,28,1]
  • 卷积层:32个7x7过滤器,权重形状= [7,7,1,32],名称= W_conv1

如果我们对输入进行卷积,那么意志的激活就会形成:[1,28,28,32].

 activations = sess.run(h_conv1,feed_dict={x:np.reshape(image,[1,784])})
Run Code Online (Sandbox Code Playgroud)

哪里:

 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
Run Code Online (Sandbox Code Playgroud)

要获得"反卷积"或"转置卷积",我们可以通过这种方式对卷积激活使用conv2d_transpose():

  deconv = conv2d_transpose(activations,W_conv1, output_shape=[1,28,28,1],padding='SAME')
Run Code Online (Sandbox Code Playgroud)

或者使用conv2d()我们需要转置和翻转权重:

  transposed_weights = tf.transpose(W_conv1, perm=[0, 1, 3, 2])
Run Code Online (Sandbox Code Playgroud)

这里我们将"colums"的顺序从[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]))
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用conv2d()计算卷积:

  strides = [1,1,1,1]
  deconv = conv2d(activations,transposed_and_flipped_weights,strides=strides,padding='SAME')
Run Code Online (Sandbox Code Playgroud)

我们将获得与以前相同的结果.使用conv2d_backprop_input()也可以获得完全相同的结果:

   deconv = conv2d_backprop_input([1,28,28,1],W_conv1,activations, strides=strides, padding='SAME')
Run Code Online (Sandbox Code Playgroud)

结果显示在这里:

测试conv2d(),conv2d_tranposed()和conv2d_backprop_input()

我们可以看到结果是一样的.要以更好的方式查看它,请查看我的代码:

https://github.com/simo23/conv2d_transpose

这里我使用标准的conv2d()复制conv2d_transpose()函数的输出.


pen*_*gon 5

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)
Run Code Online (Sandbox Code Playgroud)