一、参考资料
上采样和上卷积的区别
怎样通俗易懂地解释反卷积?
卷积和池化的区别、图像的上采样(upsampling)与下采样(subsampled)
[读论文]用全卷积Res网络做深度估计
对抗生成网络GAN系列——DCGAN简介及人脸图像生成案例
深度学习中常用的几种卷积(上篇):标准二维卷积、转置卷积、1*1卷积(附Pytorch测试代码)
反卷积(Deconvolution)、上采样(UnSampling)与上池化(UnPooling)
二、相关介绍
1. 上采样(UpSampling
)
1.1 上采样的概念
在应用在计算机视觉的深度学习领域,由于输入图像通过卷积神经网络(CNN)提取特征后,输出的尺寸往往会变小,而有时我们需要将图像恢复到原来的尺寸以便进行进一步的计算(e.g.图像的语义分割)。这个采用扩大图像尺寸,实现图像由小分辨率到大分辨率的映射的操作,叫做上采样(UpSample)。简单理解,下采样是缩小图像的尺寸,上采样则是放大图像的尺寸。
理论上来说,上采样放大图像并不能带来更多关于该图像的信息,因此图像的质量将不可避免地受到影响。
1.2 常用上采样方法
一般来说,将缩小的图像还原成原始图像大多数都是采用插值算法,即将新元素插入到图像的像素点之间。常用的插值算法有:最近邻插值(Nearest neighbor interpolation)、双线性插值(Bi-linear interpolation),均值插值,中值插值。另外,深度学习领域,常用的上采样方法还包括:转置卷积(Transposed Convolution),反池化(UnPooling)。
1.3 上采样的应用
- FCN、UNet:对输入图片进行像素级分割,使用上采样操作还原图片分辨率。
- GAN:对输入图片进行学习并生成图片,使用上采样操作将提取的特征图还原到原图尺寸。
2. 下采样(subsampling
)
下采样(subsampling)又称为降采样(downsampling),主要目的是缩小图像尺寸,使得图像符合显示区域的大小,或者生成对应图像的缩略图。在计算机视觉中,CNN特征提取过程即为下采样过程。
对于一幅图像I尺寸为 MxN
,对其进行s倍下采样,即得到 (M/s)x(N/s)
尺寸的得分辨率图像,并且s是M和N的公约数。如果是矩阵形式的图像,就是把原始图像sxs窗口内的图像变成一个像素,该像素点的值就是窗口内所有像素的均值。
3. 反池化vs上采样vs转置卷积的通俗理解
图(a)表示反池化( UnPooling
)过程,特点是在 Maxpooling
的时候保留最大值的位置信息,之后在UnPooling
阶段使用该信息扩充 Feature Map
,除最大值位置以外,其余补0。
图(b)表示上采样( UnSampling
)过程,特点是在 UnSampling
阶段没有使用 MaxPooling
时的位置信息,而是直接将内容复制来扩充 Feature Map
。从图中即可看到两者结果的不同。
图©表示转置卷积(Conv2DTranspose
)过程,特点是在 Conv2DTranspose
阶段需要学习参数(类似标准卷积过程),理论上来说, Conv2DTranspose
可以实现 UnPooling
和 UnSampling
,只要卷积核的参数设置合理。
4. 反池化vs转置卷积的可视化
原始论文:[5]
图(a)是输入层;图(b)是14x14反卷积的结果;图©是28x28的 UnPooling
结果;图(d)是28x28的Conv2DTranspose
结果;图(e)是56*56的 Unpooling
结果;图(f)是56x56 Conv2DTranspose
的结果;图(g)是112x112 的UnPooling
的结果;图(h)是112x112的 Conv2DTranspose
的结果;图(i)和图(j)分别是224x224的 UnPooling
和 Conv2DTranspose
的结果。
三、上采样相关方法
1. 转置卷积(Transposed Convolution)
关于转置卷积的详细介绍,请参考另一篇博客:深入浅出理解转置卷积Conv2DTranspose
2. 反池化(UnPooling
)
原始论文:[6]
UnPooling
是在CNN中常用的来表示 max pooling
的逆向过程(逆操作),可以提高特征图的空间分辨率。鉴于 max pooling
不可逆,论文使用了switch变量集合来记录 max pooling
阶段的位置,对于一个训练好的模型switch变量集合是已知的,跑一遍程序即可。
简单来说,UnPooling
记住 max pooling
阶段最大item 的位置,比如一个3x3的矩阵,max pooling
的size为2x2,stride
为1,其余位置至为0。
2.1 方法一
最大池化的逆向过程,在最大值的位置补最大值,其他位置补0。
2.2 方法二
原始论文:[1]、[4]
用2x2的块进行反池化操作,左上角为实值,其他位置补0。
2.3 方法三
原始论文:[3]
在卷积神经网络中, max pooling
操作是不可逆的,但可以通过一组转换变量(switch variables
)来记录每个池化区域内最大值的位置,从而获得近似的反池化。switch variables
对应 Pooling indices
。3D max pooling
如下图所示:
上图中,特征图z划分为2x2的区域,每个区域用不同颜色表示。对每个区域进行最大池化操作,得到池化图p和转换图s。其中,k表示维度,转换图s记录了每个区域最大值的位置。
2.4 方法四
原始论文:[6]
2.5 方法五
原始论文:[5]
3. 上卷积(up-convolution
)
原始论文:[1]
3.1 up-convolution
结构
UpConv
:2×2 unpooling (zero-insertion) followed by a single 5×5 convolution.
上卷积(Up-convolution
,简称UpConv
),其结构如下图所示:
上卷积的结构:unpooling+convolution
,可以理解为:上采样+convolution
。
标准卷积的结构:convolution+pooling
,可以理解为:convolution+下采样
。
论文中提到,每次 up-convolution
后增加一个卷积层可以显著提高生成图像的质量。
3.2 up-convolution
计算步骤
当在卷积层之前进行unpooling
操作时,可以将 unpooling+convolution
视为与标准CNN中执行的 convolution+pooling
步骤相反的步骤。
up-convolution
的计算过程,包含三个步骤:
unpooling
过程。把特征图中的一个元素映射成2×2的尺寸,左上角(the top left corner
)为输入值(entry value
),其他三个位置为零值;convolution
过程。用5×5的卷积进行卷积计算;- ReLu激活过程。
4. fast up-convolution
(改进版)
原始论文:[1]
4.1 fast up-convolution
结构
基于普通up-convolution
改进的 fast up-convolution
,其结构如下图所示:
4.2 fast up-convolution
计算步骤
上图中,top为普通的 up-convolution
,bottom为 fast up-convolution
。原始特征图与4个不同的 filters
进行卷积得到4个不同的特征图,再将4个特征图交叉合并为一个 feature map
,最终输出结果与普通的 up-convolution
结果相同。
5. 上投影(up-projection
)
原始论文:[1]
UpProj
:2 × 2 unpooling (zero-insertion) followed by a two-branched residual structure that computes a total of three convolutions (two 5 × 5 and one 3 × 3).
上投影(up-projection
,简称UpProj
),其结构如下图所示:
6. NNConv5
原始论文:[7]
NNConv5
:5 × 5 convolution followed by nearest-neighbor interpolation[8] with a scale factor of 2.
NNConv5
的结构如下图所示:
四、PyTorch中的上采样方法
在PyTorch中,Upsample支持不同的缩放模式,包括最近邻方法、双线性插值、三线性插值等。在PyTorch中,可以通过nn模块下的Upsample
或者functional模块下的interpolate
函数来进行缩放操作。
1. torch.nn.Upsample
(推荐)
官方文档:torch.nn.Upsample
1.1 函数原型
CLASS torch.nn.Upsample(size=None,
scale_factor=None,
mode='nearest',
align_corners=None,
recompute_scale_factor=None)
参数解释
- size (int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int], optional) – output spatial sizes.
- scale_factor (float or Tuple[float] or Tuple[float, float] or Tuple[float, float, float], optional) – multiplier for spatial size. Has to match input size if it is a tuple.
- mode (str, optional) – the upsampling algorithm: one of
'nearest'
,'linear'
,'bilinear'
,'bicubic'
and'trilinear'
. Default:'nearest'
. - align_corners (bool, optional) – if
True
, the corner pixels of the input and output tensors are aligned, and thus preserving the values at those pixels. This only has effect whenmode
is'linear'
,'bilinear'
,'bicubic'
, or'trilinear'
. Default:False
. - recompute_scale_factor (bool, optional) – recompute the scale_factor for use in the interpolation calculation. If recompute_scale_factor is
True
, then scale_factor must be passed in and scale_factor is used to compute the output size. The computed output size will be used to infer new scales for the interpolation. Note that when scale_factor is floating-point, it may differ from the recomputed scale_factor due to rounding and precision issues. If recompute_scale_factor isFalse
, then size or scale_factor will be used directly for interpolation.
Shape
- Input: ( N , C , W i n ) , ( N , C , H i n , W i n ) o r ( N , C , D i n , H i n , W i n ) (N, C, W_{in}), (N,C,H_{in},W_{in}) or (N, C, D_{in}, H_{in}, W_{in}) (N,C,Win),(N,C,Hin,Win)or(N,C,Din,Hin,Win)
- Output: ( N , C , W o u t ) , ( N , C , H o u t , W o u t ) o r ( N , C , D o u t , H o u t , W o u t ) (N, C, W_{out}), (N, C, H_{out}, W_{out}) or (N, C, D_{out}, H_{out}, W_{out}) (N,C,Wout),(N,C,Hout,Wout)or(N,C,Dout,Hout,Wout), where
D o u t = ⌊ D i n × scale _ factor ⌋ H o u t = [ H i n × scale _ factor ] W o u t = ⌊ W i n × scale _ factor ⌋ \begin{gathered} D_{out} =\left\lfloor D_{in}\times\text{scale}\_\text{factor}\right\rfloor \\ H_{out} =\begin{bmatrix}H_{in}\times\text{scale}\_\text{factor}\end{bmatrix} \\ W_{out} =\left\lfloor W_{in}\times\text{scale}\_\text{factor}\right\rfloor \end{gathered} Dout=⌊Din×scale_factor⌋Hout=[Hin×scale_factor]Wout=⌊Win×scale_factor⌋
1.2 代码示例
示例一
input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
"""
tensor([[[[1., 2.],
[3., 4.]]]])
"""
m = nn.Upsample(scale_factor=2, mode='nearest')
m(input)
"""
tensor([[[[1., 1., 2., 2.],
[1., 1., 2., 2.],
[3., 3., 4., 4.],
[3., 3., 4., 4.]]]])
"""
m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
m(input)
"""
tensor([[[[1.0000, 1.2500, 1.7500, 2.0000],
[1.5000, 1.7500, 2.2500, 2.5000],
[2.5000, 2.7500, 3.2500, 3.5000],
[3.0000, 3.2500, 3.7500, 4.0000]]]])
"""
m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
m(input)
"""
tensor([[[[1.0000, 1.3333, 1.6667, 2.0000],
[1.6667, 2.0000, 2.3333, 2.6667],
[2.3333, 2.6667, 3.0000, 3.3333],
[3.0000, 3.3333, 3.6667, 4.0000]]]])
"""
# Try scaling the same data in a larger tensor
input_3x3 = torch.zeros(3, 3).view(1, 1, 3, 3)
input_3x3[:, :, :2, :2].copy_(input)
"""
tensor([[[[1., 2., 0.],
[3., 4., 0.],
[0., 0., 0.]]]])
"""
m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
# Notice that values in top left corner are the same with the small input (except at boundary)
m(input_3x3)
"""
tensor([[[[1.0000, 1.2500, 1.7500, 1.5000, 0.5000, 0.0000],
[1.5000, 1.7500, 2.2500, 1.8750, 0.6250, 0.0000],
[2.5000, 2.7500, 3.2500, 2.6250, 0.8750, 0.0000],
[2.2500, 2.4375, 2.8125, 2.2500, 0.7500, 0.0000],
[0.7500, 0.8125, 0.9375, 0.7500, 0.2500, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
"""
m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
# Notice that values in top left corner are now changed
m(input_3x3)
"""
tensor([[[[1.0000, 1.4000, 1.8000, 1.6000, 0.8000, 0.0000],
[1.8000, 2.2000, 2.6000, 2.2400, 1.1200, 0.0000],
[2.6000, 3.0000, 3.4000, 2.8800, 1.4400, 0.0000],
[2.4000, 2.7200, 3.0400, 2.5600, 1.2800, 0.0000],
[1.2000, 1.3600, 1.5200, 1.2800, 0.6400, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
"""
示例二
import torch.nn as nn
# 缩放比例为2,模式为最近邻方法
upsample = nn.Upsample(scale_factor=2, mode='nearest')
# 缩放比例为3,4,5,模式为双线性插值
upsample2 = nn.Upsample(scale_factor=(3,4,5), mode='bilinear')
2. torch.nn.functional.interpolate
(限制较多)
官方文档:torch.nn.functional.interpolate
2.1 函数原型
torch.nn.functional.interpolate(input,
size=None,
scale_factor=None,
mode='nearest',
align_corners=None,
recompute_scale_factor=None,
antialias=False)
参数解释
- input (Tensor) – the input tensor.
- size (int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int]) – output spatial size.
- scale_factor (float or Tuple[float]) – multiplier for spatial size. If scale_factor is a tuple, its length has to match the number of spatial dimensions; input.dim() - 2.
- mode (str) – algorithm used for upsampling:
'nearest'
|'linear'
|'bilinear'
|'bicubic'
|'trilinear'
|'area'
|'nearest-exact'
. Default:'nearest'.
- align_corners (bool, optional) – Geometrically, we consider the pixels of the input and output as squares rather than points. If set to
True
, the input and output tensors are aligned by the center points of their corner pixels, preserving the values at the corner pixels. If set toFalse
, the input and output tensors are aligned by the corner points of their corner pixels, and the interpolation uses edge value padding for out-of-boundary values, making this operation independent of input size whenscale_factor
is kept the same. This only has an effect whenmode
is'linear'
,'bilinear'
,'bicubic'
or'trilinear'
. Default:False
. - recompute_scale_factor (bool, optional) – recompute the scale_factor for use in the interpolation calculation. If recompute_scale_factor is
True
, then scale_factor must be passed in and scale_factor is used to compute the output size. The computed output size will be used to infer new scales for the interpolation. Note that when scale_factor is floating-point, it may differ from the recomputed scale_factor due to rounding and precision issues. If recompute_scale_factor isFalse
, then size or scale_factor will be used directly for interpolation. Default:None
. - antialias (bool, optional) – flag to apply anti-aliasing. Default:
False
. Using anti-alias option together withalign_corners=False
, interpolation result would match Pillow result for downsampling operation. Supported modes:'bilinear'
,'bicubic'
.
2.2 代码示例
import torch.nn.functional as F
# 缩放比例为2,模式为最近邻方法
upsample = F.interpolate(x, scale_factor=2, mode='nearest')
# 缩放比例为3,4,5,模式为双线性插值
upsample2 = F.interpolate(x, scale_factor=(3,4,5), mode='bilinear')
3. 针对图像缩放的上采样示例
下面给出一个以图像为例子的Upsample代码示例:
from PIL import Image
import torchvision.transforms as transforms
import torch.nn.functional as F
# 加载图像文件
img = Image.open('test.jpg')
# 定义Upsample操作
upsample = F.interpolate(x, scale_factor=2, mode='bilinear')
# 定义数据转换操作
transform = transforms.Compose([
transforms.Resize(size=[256,256]), # 缩放到256*256大小
transforms.ToTensor() # 转化为张量类型
])
# 数据转换
img_tensor = transform(img)
# 进行Upsample操作
img_tensor_upsampled = upsample(img_tensor.unsqueeze(0)).squeeze().detach()
# 转返回图像格式
img_upsampled = transforms.ToPILImage()(img_tensor_upsampled.cpu())