conv2d_transpose()
操作的文档没有明确解释它的作用:
conv2d的转置。
此操作有时称为“反卷积” Deconvolutional Networks,但实际上是转置(渐变) 转而不是实际的反卷积。
我查看了该文件指出的文件,但没有帮助。
这项操作有什么作用以及为什么要使用它的例子?
答案 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_x
和d_w
,它们是渐变w.r.t. <{1}}和x
。
为便于讨论,我们假设:
w
1
和in_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_out
和d_x
给定d_w
。这说明了如何
d_out
,
conv2d
,
conv2d_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_rot180
,tf_pad_to_full_conv2d
的实施情况。在脚本中,我们检查不同方法的最终输出值是否相同;也可以使用numpy实现。
答案 2 :(得分:11)
conv2d_transpose()只需转置权重并将它们翻转180度。然后它应用标准的conv2d()。 “Transposes”实际上意味着它改变了权重张量中“列”的顺序。请查看以下示例。
这里有一个使用带有stride = 1和padding ='SAME'的卷积的示例。这是一个简单的案例,但同样的推理可以应用于其他案例。
说我们有:
如果我们对输入进行卷积,那么意志的激活就会形成:[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 :
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的这张幻灯片对我们的问题很有用:
答案 5 :(得分:0)