原文链接: TensorFlow 反卷积 反池化
上一篇: TensorFlow 梯度计算
下一篇: TensorFlow cifar 反卷积
参考
https://www.cnblogs.com/zyly/p/8991412.html
反卷积是指,通过测量输出和已知输入重构未知输入的过程。在神经网络中,反卷积过程并不具备学习的能力,仅仅是用于可视化一个已经训练好的卷积神经网络,没有学习训练的过程。反卷积有着许多特别的应用,一般可以用于信道均衡、图像恢复、语音识别、地震学、无损探伤等未知输入估计和过程辨识方面的问题。
在神经网络的研究中,反卷积更多的是充当可视化的作用,对于一个复杂的深度卷积网络,通过每层若干个卷积核的变换,我们无法知道每个卷积核关注的是什么,变换后的特征是什么样子。通过反卷积的还原,可以对这些问题有个清晰的可视化,以各层得到的特征图作为输入,进行反卷积得到反卷积结果,以验证显示各层提取到的特征图。
一 反卷积原理
反卷积可以理解为卷积操作的逆操作,这里千万不要当成反卷积操作可以复原卷积操作的输入值,反卷积并没有那个功能,它仅仅是将卷积变换过程中的步骤反向变换一次而已,通过将卷积核转置,与卷积后的结果再做一遍卷积,所以它还有一个名字叫做转置卷积。
举个例子:假如你想要查看Alexnet 的conv5提取到了什么东西,我们就用conv5的特征图后面接一个反卷积网络,然后通过:反池化、反激活、反卷积,这样的一个过程,把本来一张13*13大小的特征图(conv5大小为13*13),放大回去,最后得到一张与原始输入图片一样大小的图片(227*227)。
虽然它不能还原出原来卷积的样子,但是在作用上有类似的效果,你可以将带有小部分缺失的信息最大化的恢复,也可以用来恢复被卷积生成后的原始输入。
反卷积的具体操作比较复杂,这里不介绍如何具体实现反卷积,在tensorflow中反卷积的是通过函数tf.nn.conv2d_transpose()来实现的:
def conv2d_transpose(value,
filter,
output_shape,
strides,
padding="SAME",
data_format="NHWC",
name=None):
具体参数说明如下:
- value:代表通过卷积操作之后的张量,一般用NHWC类型。如果是NHWC类型,形状[batch, height, width, in_channels],如果是NCHW类型,形状为[batch, in_channels, height, width]。
- filter:代表卷积核,形状为[height, width, output_channels, in_channels]。
- output_shape:反卷积输出的张量形状,它必须是能够生成value参数的原数据的形状,如果输出形状不对,函数会报错。
- strides:代表原数据生成value时使用的步长。
- padding:代表原数据生成value时使用的填充方式,是用来检查输入形状和输出形状是否合规的。
- data_format: 'NHWC' and 'NCHW' 类型。
- name:名称。
返回反卷积后的形状,按照output_shape指定的形状。
查看该函数的实现代码,我们可以看到反卷积的操作其实是使用了gen_nn_ops.conv2d_backprop_input()函数来实现的,相当于在TensorFlow中利用了卷积操作在反向传播的处理函数中做反卷积操作,即卷积操作的反向传播就是反卷积操作。
注意:在使用反卷积的网络中,定义占位符中不能存在None,必须指定具体的数,不然会报错。
import tensorflow as tf
img = tf.Variable(tf.constant(1.0, shape=[1, 4, 4, 1]))
filter = tf.Variable(tf.constant([1.0, 0, -1, -2], shape=[2, 2, 1, 1]))
conv = tf.nn.conv2d(img, filter, strides=[1, 2, 2, 1], padding='VALID')
cons = tf.nn.conv2d(img, filter, strides=[1, 2, 2, 1], padding='SAME')
print(conv.shape)
print(cons.shape)
contv = tf.nn.conv2d_transpose(conv, filter, [1, 4, 4, 1], strides=[1, 2, 2, 1], padding='VALID')
conts = tf.nn.conv2d_transpose(cons, filter, [1, 4, 4, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print("conv:\n", sess.run([conv, filter]))
print("cons:\n", sess.run([cons]))
print("contv:\n", sess.run([contv]))
print("conts:\n", sess.run([conts]))
conv:
[array([[[[-2.],
[-2.]],
[[-2.],
[-2.]]]], dtype=float32), array([[[[ 1.]],
[[ 0.]]],
[[[-1.]],
[[-2.]]]], dtype=float32)]
cons:
[array([[[[-2.],
[-2.]],
[[-2.],
[-2.]]]], dtype=float32)]
contv:
[array([[[[-2.],
[ 0.],
[-2.],
[ 0.]],
[[ 2.],
[ 4.],
[ 2.],
[ 4.]],
[[-2.],
[ 0.],
[-2.],
[ 0.]],
[[ 2.],
[ 4.],
[ 2.],
[ 4.]]]], dtype=float32)]
conts:
[array([[[[-2.],
[ 0.],
[-2.],
[ 0.]],
[[ 2.],
[ 4.],
[ 2.],
[ 4.]],
[[-2.],
[ 0.],
[-2.],
[ 0.]],
[[ 2.],
[ 4.],
[ 2.],
[ 4.]]]], dtype=float32)]
反池化
反池化属于池化的逆操作,是无法通过池化的结果还原出全部的原始数据,因此池化的过程只保留主要信息,舍去部分信息。如果想从池化后的这些主要信息恢复出全部信息,由于存在着信息缺失,这时只能通过补位来实现最大程度的信息完整。
池化层常用的有最大池化和平均池化,其反池化也需要与其对应。
import tensorflow as tf
def max_pool_with_argmax(net, stride):
_, mask = tf.nn.max_pool_with_argmax(net, ksize=[1, stride, stride, 1], strides=[1, stride, stride, 1],
padding='SAME')
mask = tf.stop_gradient(mask)
net = tf.nn.max_pool(net, ksize=[1, stride, stride, 1], strides=[1, stride, stride, 1], padding='SAME')
return net, mask
def unpool(net, mask, stride):
ksize = [1, stride, stride, 1]
input_shape = net.get_shape().as_list()
# calculation new shape
output_shape = (input_shape[0], input_shape[1] * ksize[1], input_shape[2] * ksize[2], input_shape[3])
# calculation indices for batch, height, width and feature maps
one_like_mask = tf.ones_like(mask)
batch_range = tf.reshape(tf.range(output_shape[0], dtype=tf.int64), shape=[input_shape[0], 1, 1, 1])
b = one_like_mask * batch_range
y = mask // (output_shape[2] * output_shape[3])
x = mask % (output_shape[2] * output_shape[3]) // output_shape[3]
feature_range = tf.range(output_shape[3], dtype=tf.int64)
f = one_like_mask * feature_range
# transpose indices & reshape update values to one dimension
updates_size = tf.size(net)
indices = tf.transpose(tf.reshape(tf.stack([b, y, x, f]), [4, updates_size]))
values = tf.reshape(net, [updates_size])
ret = tf.scatter_nd(indices, values, output_shape)
return ret
img = tf.constant([
[[0.0, 4.0], [0.0, 4.0], [0.0, 4.0], [0.0, 4.0]],
[[1.0, 5.0], [1.0, 5.0], [1.0, 5.0], [1.0, 5.0]],
[[2.0, 6.0], [2.0, 6.0], [2.0, 6.0], [2.0, 6.0]],
[[3.0, 7.0], [3.0, 7.0], [3.0, 7.0], [3.0, 7.0]]
])
img = tf.reshape(img, [1, 4, 4, 2])
pooling2 = tf.nn.max_pool(img, [1, 2, 2, 1], [1, 2, 2, 1], padding='SAME')
encode, mask = max_pool_with_argmax(img, 2)
img2 = unpool(encode, mask, 2)
print(img.shape)
print(encode.shape)
print(mask.shape)
print(img2.shape)
with tf.Session() as sess:
print("image:")
print(sess.run(img))
result = sess.run(pooling2)
print("pooling2:\n", result)
result, mask2 = sess.run([encode, mask])
print("encode:\n", result, mask2)
result = sess.run(img2)
print("reslut:\n", result)
image:
[[[[0. 4.]
[0. 4.]
[0. 4.]
[0. 4.]]
[[1. 5.]
[1. 5.]
[1. 5.]
[1. 5.]]
[[2. 6.]
[2. 6.]
[2. 6.]
[2. 6.]]
[[3. 7.]
[3. 7.]
[3. 7.]
[3. 7.]]]]
pooling2:
[[[[1. 5.]
[1. 5.]]
[[3. 7.]
[3. 7.]]]]
encode:
[[[[1. 5.]
[1. 5.]]
[[3. 7.]
[3. 7.]]]] [[[[ 8 9]
[12 13]]
[[24 25]
[28 29]]]]
reslut:
[[[[0. 0.]
[0. 0.]
[0. 0.]
[0. 0.]]
[[1. 5.]
[0. 0.]
[1. 5.]
[0. 0.]]
[[0. 0.]
[0. 0.]
[0. 0.]
[0. 0.]]
[[3. 7.]
[0. 0.]
[3. 7.]
[0. 0.]]]]