对于Unet网络中的上采样,主要有三种实现形式:上采用、反卷积、上池化,通常情况应用的是反卷积。
1、Upsampling(上采样)
在FCN、U-net等网络结构中,涉及到了上采样:
上采样指的是: 任何可以让 图像变成更高分辨率 的技术(上池化、反卷积、插值等)
可以是
重采样、插值
:将输入图片进行
rescale 到一个想要的尺寸,而且计算每个点的像素点,使用如
双线性插值,等插值方法
对其余点进行插值来完成上采样过程。
2、上池化
Unpooling是在CNN中常用的来表示max pooling的逆操作。这是论文《Visualizing and Understanding Convolutional Networks》中产生的思想,如下图所示:
两者区别:
Unsampling 阶段没有使用Max Pooling的位置信息,复制最大值扩充Feature Map
UnPooling 阶段是在Max pooling的时候保留最大值的位置信息,用 0 扩充Feature Map
3、反卷积
卷积: 反卷积:
卷积、反卷积的输入输出关系正好相反
如果不考虑通道以卷积运算的反向运算来计算反卷积运算的话,还可以通过离散卷积的方法来求反卷积。
反卷积 又被称为Transposed(转置) Convolution
卷积层的前向传播过程就是反卷积层的反向传播过程,
卷积层的反向传播过程就是反卷积层的前向传播过程。
因为卷积层的前向反向计算分别为乘 C和 CT,而反卷积层的前向反向计算分别为乘 CT和(CT)T ,所以它们的前向传播和反向传播刚好交换过来。
-
使用反卷积可以进行上采样
-
反卷积具有可供学习的参数,不需要利用插值方法
最大的区别在于反卷积过程是有
参数要进行学习
的(类似卷积过程),
理论上反卷积可以实现UnPooling和unSampling
广义的上采样:由于上采样是指将图像
上采样到更高分辨率的任何技术
,因此我们可以讲:通过反卷积、UnPooling进行上采样
反卷积:
仅仅是将卷积变换过程中的步骤反向变换一次而已,通过将卷积核转置,与卷积后的结果再做一遍卷积,所以它还有一个名字叫做转置卷积。
代码示例:
'''
一 反卷积实例
'''
import tensorflow as tf
import numpy as np
#模拟数据
img = tf.Variable(tf.constant(1.0,shape=[1,4,4,1]))
kernel =tf.Variable(tf.constant([1.0,0,-1,-2],shape=[2,2,1,1]))
#分别进行VALID和SAME操作
conv = tf.nn.conv2d(img,kernel,strides=[1,2,2,1],padding='VALID')
cons = tf.nn.conv2d(img,kernel,strides=[1,2,2,1],padding='SAME')
#VALID填充计算方式 (n - f + 1)/s向上取整
print(conv.shape)
#SAME填充计算方式 n/s向上取整
print(cons.shape)
#在进行反卷积操作
contv = tf.nn.conv2d_transpose(conv,kernel,[1,4,4,1],strides=[1,2,2,1],padding='VALID')
conts = tf.nn.conv2d_transpose(cons,kernel,[1,4,4,1],strides=[1,2,2,1],padding='SAME')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print('kernel:\n',sess.run(kernel))
print('conv:\n',sess.run(conv))
print('cons:\n',sess.run(cons))
print('contv:\n',sess.run(contv))
print('conts:\n',sess.run(conts))
反池化(上池化):
属于池化的逆操作,是无法通过池化的结果还原出全部的原始数据,因此池化的过程只保留主要信息,舍去部分信息。如果想从池化后的这些主要信息恢复出全部信息,由于存在着信息缺失,这时只能通过补位来实现最大程度的信息完整。
代码示例:
'''
二 反池化操作
'''
def max_pool_with_argmax(net,stride):
'''
重定义一个最大池化函数,返回最大池化结果以及每个最大值的位置(是个索引,形状和池化结果一致)
args:
net:输入数据 形状为[batch,in_height,in_width,in_channels]
stride:步长,是一个int32类型,注意在最大池化操作中我们设置窗口大小和步长大小是一样的
'''
#使用mask保存每个最大值的位置 这个函数只支持GPU操作
_, mask = tf.nn.max_pool_with_argmax( net,ksize=[1, stride, stride, 1], strides=[1, stride, stride, 1],padding='SAME')
#将反向传播的mask梯度计算停止
mask = tf.stop_gradient(mask)
#计算最大池化操作
net = tf.nn.max_pool(net, ksize=[1, stride, stride, 1],strides=[1, stride, stride, 1], padding='SAME')
#将池化结果和mask返回
return net,mask
def un_max_pool(net,mask,stride):
'''
定义一个反最大池化的函数,找到mask最大的索引,将max的值填到指定位置
args:
net:最大池化后的输出,形状为[batch, height, width, in_channels]
mask:位置索引组数组,形状和net一样
stride:步长,是一个int32类型,这里就是max_pool_with_argmax传入的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
#定义一个形状为4x4x2的张量
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])
#最大池化操作
pooling1 = tf.nn.max_pool(img,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#带有最大值位置的最大池化操作
pooling2,mask = max_pool_with_argmax(img,2)
#反最大池化
img2 = un_max_pool(pooling2,mask,2)
with tf.Session() as sess:
print('image:')
image = sess.run(img)
print(image)
#默认的最大池化输出
result = sess.run(pooling1)
print('max_pool:\n',result)
#带有最大值位置的最大池化输出
result,mask2 = sess.run([pooling2,mask])
print('max_pool_with_argmax:\n',result,mask2)
#反最大池化输出
result = sess.run(img2)
print('un_max_pool',result)