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

时间:2016-09-07 14:55:19

标签: tensorflow conv-neural-network

conv2d_transpose()操作的文档没有明确解释它的作用:

  

conv2d的转置。

     

此操作有时称为“反卷积”   Deconvolutional Networks,但实际上是转置(渐变)   转而不是实际的反卷积。

我查看了该文件指出的文件,但没有帮助。

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

6 个答案:

答案 0 :(得分:30)

这是我在网上看到的最好的解释,卷积转置的工作原理是here

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

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

答案 1 :(得分:22)

这是"渐变"的另一个观点。透视,即为什么TensorFlow文档说conv2d_transpose()"实际上是conv2d的转置(渐变)而不是实际的反卷积"。 有关conv2d_transpose中完成的实际计算的详细信息,我强烈推荐this article,从第19页开始。

四个相关功能

tf.nn中,2d卷积有4个密切相关且相当混乱的函数:

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

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

问题设置

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

# backward, given d_out
=> find d_x?
=> find d_w?

在正向计算中,我们使用过滤器x计算输入图像w的卷积,结果为out。 在反向计算中,假设我们给出d_out,这是渐变w.r.t. out。我们的目标是找到d_xd_w,它们是渐变w.r.t. <{1}}和x

为便于讨论,我们假设:

  • 所有步幅均为w
  • 所有1in_channels均为out_channels
  • 使用1填充
  • 奇数过滤器大小,这避免了一些不对称的形状问题

简答

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

VALID

out = conv2d(x, w, padding='VALID') d_x = conv2d(d_out, rot180(w), padding='FULL') d_w = conv2d(x, d_out, padding='VALID') 是2d矩阵旋转180度(左右翻转和自上而下翻转),rot180表示&#34;应用滤镜,只要它与输入部分重叠&# 34; (见theano docs)。注意这仅适用于上述假设,但是,可以更改conv2d参数以概括它。

关键要点:

  • 输入渐变FULL是输出渐变d_x和重量d_out的卷积,并进行了一些修改。
  • 权重渐变w是输入d_w和输出渐变x的卷积,并进行了一些修改。

长答案

现在,让我们提供一个实际工作代码示例,说明如何使用上述4个函数来计算d_outd_x给定d_w。这说明了如何 d_outconv2dconv2d_backprop_filter,和 conv2d_backprop_input彼此相关。 Please find the full scripts here

以4种不同的方式计算conv2d_transpose

d_x

以3种不同的方式计算# 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

请参阅full scripts了解# 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_rot180tf_pad_to_full_conv2d的实施情况。在脚本中,我们检查不同方法的最终输出值是否相同;也可以使用numpy实现。

答案 2 :(得分:11)

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

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

说我们有:

  • 输入:MNIST图像28x28x1,shape = [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])})

其中:

 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])

这里我们将“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]))

然后我们可以使用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')

结果如下所示:

Test of the conv2d(), conv2d_tranposed() and conv2d_backprop_input()

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

https://github.com/simo23/conv2d_transpose

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

答案 3 :(得分:1)

一个用于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)

答案 4 :(得分:0)

这是U-Net中使用的特殊情况下发生的情况的简单解释-这是转置卷积的主要用例之一。

我们对以下层感兴趣:

Conv2DTranspose(64, (2, 2), strides=(2, 2))

该层完全的作用是什么?我们可以复制它的作品吗?

这里是 answer

  • 首先,这种情况下的默认填充有效。这意味着我们没有填充。
  • 输出的大小将是2倍:如果输入(m,n),则输出将是(2m,2n)。这是为什么?请看下一点。
  • 从输入中获取第一个元素,然后乘以形状为(2,2)的滤波器权重。将其放入输出中。取下一个元素,相乘,然后将输出放在第一个结果旁边而不重叠。这是为什么?我们已经大步向前(2,2)。

这是示例输入和输出(请参见详细信息herehere):

In [15]: X.reshape(n, m)
Out[15]:
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
In [16]: y_resh
Out[16]:
array([[ 0.,  0.,  1.,  1.,  2.,  2.,  3.,  3.,  4.,  4.],
       [ 0.,  0.,  1.,  1.,  2.,  2.,  3.,  3.,  4.,  4.],
       [ 5.,  5.,  6.,  6.,  7.,  7.,  8.,  8.,  9.,  9.],
       [ 5.,  5.,  6.,  6.,  7.,  7.,  8.,  8.,  9.,  9.],
       [10., 10., 11., 11., 12., 12., 13., 13., 14., 14.],
       [10., 10., 11., 11., 12., 12., 13., 13., 14., 14.]], dtype=float32)

斯坦福大学cs231n的这张幻灯片对我们的问题很有用:

enter image description here

答案 5 :(得分:0)

任何线性变换,包括卷积,都可以表示为矩阵。转置卷积可以解释为在应用卷积矩阵之前对其进行转置。例如,考虑简单的一维卷积,其内核大小为3,步幅为2。

enter image description here

如果对卷积矩阵进行转置并将其应用于3元素矢量,则会得到转置卷积运算

enter image description here

现在开始,这看起来不再像卷积操作了。但是,如果我们先将一些零插入y向量,则可以等效地重写为

enter image description here

此示例演示了大步卷积算子的转置等效于通过插入零,然后添加一些附加填充,最后执行无大幅度卷积(即,大步幅= 1),以步幅的一个因子上采样。

对于高维转置卷积,在执行无约束卷积之前,对每个维都采用相同的“按零插入上采样”方法。