反卷积和反池化

反卷积

反卷积是指通过测量输出已知输入重构未知输入的过程。在神经网络中,反卷积过程并不具备学习的能力,仅仅是用于可视化一个已经训练好的卷积网络模型,没有学习训练的过程。如下图:即为VGG16反卷积神经网络的结构,展示了一个卷积网络与反卷积网络结合的过程。

反卷积就是将中间的数据,按照前面卷积、池化等过程,完全相反地做一遍,从而得到类似原始输入的数据。

反卷积原理

反卷积可以理解为卷积操作的逆操作。千万不要将反卷积操作可以复原卷积操作的输入值,反卷积并没有这个功能,它仅仅是将卷积变换过程中的步骤反向变换一次。通过将卷积核转置,与卷积后的结果再做一遍卷积,所以反卷积还有个名字叫做转置卷积。

反卷积操作:

(1) 首先是将卷积核反转( 并不是转置,而是上下左右方向进行递序操作)。也就是对卷积核做180o翻转。
(2) 再将卷积结果作为输入, 做补0的扩充操作, 即往每一个元素后面补0。 这一步是根据步长来的, 对每一个元素沿着步长的方向补( 步长-1) 个0。 例如, 步长为1就不用补0了。
(3) 在扩充后的输入基础上再对整体补0。以原始输入的shape作为输出, 按照前面介绍的卷积padding规则, 计算pading的补0位置及个数, 得到的补0位置要上下和左右各自颠倒一下。
(4) 将补0后的卷积结果作为真正的输入,反转后的卷积核为filter, 进行步长为1的卷积操作。

需要注意的是,通过反卷积并不能还原卷积之前的矩阵,只能从大小上进行还原,反卷积的本质还是卷积,只是在进行卷积之前,会进行一个自动的padding补0,从而使得输出的矩阵与指定输出矩阵的shape相同。框架本身,会根据我们自己设定的反卷积值来计算输入矩阵的尺寸,如果shape不符合,会出现报错。

反卷积函数

在Tensorflow中,反卷积是通过函数tf.nn.conv2d_transpose来实现的。

conv2d_transpose(value,filter,output_shape,strides,padding=”SAME”,data_format=”NHWC”,name=None):

value:代表卷积操作之后的张量,需要进行反卷积的矩阵

filter:代表卷积核,参数格式[height,width,output_channels,in_channels]

output_shape:设置反卷积输出矩阵的shape

strides:反卷积步长

padding:补0方式,SAME和VALID方式

data_format:string类型的量,’NHWC’和’NCHW’其中之一,这是tensorflow新版本中新加的参数,它说明了value参数的数据格式。’NHWC’指tensorflow标准的数据格式[batch, height, width, in_channels],‘NCHW’指Theano的数据格式,[batch, in_channels,height, width],默认值是’NHWC’。

name:操作名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np
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("img",sess.run(img))
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]))

反卷积应用场景

由于反卷积的特性,可以用于信道均衡、图像恢复等问题。而在神经网络的研究中, 反卷积更多的是充当可视化的作用。 对于一个复杂的深度卷积网络,通过每层若干个卷积核的变换, 我们无法知道每个卷积核关注的是什么, 变换后的特征是什么样子。 通过反卷积的还原, 可以对这些问题有个清晰的可视化, 以各层得到的特征图作为输入, 进行反卷积, 得到反卷积结果, 用以验证显示各层提取到的特征图。

反池化

反池化属于池化的逆操作,是无法通过池化的结果还原出全部的原始数据。因为池化的过程就是只保留主要信息,舍去部分信息。如想从池化后的这些主要信息恢复出全部信息,则存在信息缺失,这时只能通过补位来实现最大程度的信息完整

反池化原理

池化有两种最大池化和平均池化,反池化与其对应。

反平均池化比较简单。首先还原成原来的大小,然后将池化结果中的每个值都填入其对应于原始数据区域中的相应未知即可。

反最大池画要求在池化过程中记录最大激活值的坐标位置,然后在反池化时,只把池化过程中最大激活值所在位置坐标的值激活,其它的值置为0。这个过程只是一种近似,因为在池化过程中,除了最大值所在的位置,其它的值是不为0的

反池化操作

Tensorflow中没有反池化操作的函数。对于最大池化,也不支持输出最大激活值的位置,但是同样有个池化的反向传播函数tf.nn.max_pool_with_argmax。该函数可以输出位置,需要我们自己封装一个反池化函数。

首先重新定义最大池化函数:

1
2
3
4
5
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

在上面代码里,先调用tf.nn.max_pool_with_argmax函数获得每个最大值的位置mask,再将反向传播的mask梯度停止,接着再用tf.nn.max_pool函数计算最大池化操作,然后将mask和池化结果一起返回。

注意:tf.nn.max_pool_with_argmax的方法只支持GPU操作,不能在cpu机器上使用。

接下来定义一个数组,并使用最大池化函数对其进行池化操作,比较一下tensorflow自带的tf.nn.max_pool函数是否一样,看看输出的mask

1
2
3
4
5
6
7
8
9
10
11
12
13
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])
pooling=tf.nn.max_pool(img,[1,2,2,1],[1,2,2,1],padding="SAME")
encode,mask=max_pool_with_argmax(img,2)
with tf.Session() as sess:
print("image:",sess.run(img))
print("pooling:",sess.run(pooling))
print("encode",sess.run([encode,mask]))

从输出结果可以看到,定义的最大池化与原来的版本输出是一样的。mask的值是将整个数据flat(扁平化)后的索引,但却保持与池化结果一致的shape。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def unpool(net, mask, stride):

ksize = [1, stride, stride, 1]
input_shape = net.get_shape().as_list()
#计算new shape
output_shape = (input_shape[0], input_shape[1] * ksize[1], input_shape[2] * ksize[2], input_shape[3])
#计算索引
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
#转置索引
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

上面代码的思路是找到mask对应的索引,将max的值填到指定地方。接着直接调用上面代码的函数:

1
2
3
4
img2=unpool(encode,mask,2)
with tf.Session() as sess:
result=sess.run(img2)
print ("reslut:\n",result)

反最大池化整体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
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]]
])
def unpool(net, mask, stride):

ksize = [1, stride, stride, 1]
input_shape = net.get_shape().as_list()
#计算new shape
output_shape = (input_shape[0], input_shape[1] * ksize[1], input_shape[2] * ksize[2], input_shape[3])
#计算索引
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
#转置索引
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.reshape(img,[1,4,4,2])
pooling=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)
with tf.Session() as sess:
print("image:",sess.run(img))
print("pooling:",sess.run(pooling))
print("encode",sess.run([encode,mask]))

result,mask2=sess.run([encode, mask])
print ("encode:\n",result,mask2)
result=sess.run(img2)
print ("reslut:\n",result)
----本文结束,感谢您的阅读。如有错,请指正。----
大哥大嫂过年好!支持我一下呗
0%