本文介绍了深度学习分割任务中Decoder阶段的多种上采样方法。包括规则上采样的Upsample(如双线性插值)、通过周期筛选实现的PixelShuffle、基于最大值位置还原的MaxUnPool2D、需学习参数的Conv2DTranspose(反卷积),以及不规则上采样的grid_sample,并结合示例展示了各方法的效果与特点。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

在做分割的时候,我们经常会遇到Decoder时对特征进行上采样,比较常见的就是下面这个:
x = F.interpolate(x, scale_factor=2., mode="bilinear")
但除了这个上采样的方法外,还有许多的方法都可以将当前Tensor的大小由[H, W]变为[H*r, W*r]。说不定把这些上采样方法换来换去能找到一个对当前的任务有着更好的效果的上采样方法。
下面我们对一些(了解有限,只知道这么多)常用的上采样方法进行一些介绍。
这里主要是导入一些库以及写几个ndarray和tensor转换的函数,方便显示。
示例图像方面准备了一张李化元(希望下集别刀了)大头照,其他的通道数很多的特征也差不多。
import cv2import numpy as npimport paddleimport paddle.nn as nnimport paddle.nn.functional as Fimport matplotlib.pyplot as plt
%matplotlib inlinedef to_tensor(image):
# H, W, C -> 1, C, H, W
# uint8 -> float32
# ndarray -> tensor
return paddle.to_tensor(image.transpose((2, 0, 1))[None], dtype="float32")def to_ndarray(tensor):
# 1, C, H, W -> H, W, C
# float32 -> uint8
# tensor -> ndarray
return tensor.squeeze().numpy().transpose((1, 2, 0)).astype("uint8")def show(im1, im2, im3=None):
plt.figure(figsize=(15, 5))
plt.subplot(131);plt.imshow(im1);plt.title("raw image") # 原图
plt.subplot(132);plt.imshow(im2);plt.title("down image") # 下采样后的图
if im3 is not None:
plt.subplot(133);plt.imshow(im3);plt.title("up image") # 上采样后的结果
plt.show()img_path = "lhy.jpg"img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB) # 图像准备
该OP用于调整一个batch中图片的大小。
输入为3-D Tensor时形状为(num_batches, channels, in_w),输入为4-D Tensor时形状为(num_batches, channels, in_h, in_w)或者(num_batches, in_h, in_w, channels),输入为5-D Tensor时形状为(num_batches, channels, in_d, in_h, in_w)或者(num_batches, in_d, in_h, in_w, channels),并且调整大小只适用于深度,高度和宽度对应的维度。
这个算子和前面说到的F.interpolate差不多,是一个规则上采样的方法,有最近邻法、双线性插值、双三次插值等。
以双线性插值为例,如下图所示,根据离待插值最近的个已知值来计算待插值,每个已知值的权重由距离待插值距离决定,距离越近权重越大。其中P就可以由R1和R2插值而来,而R1和R2分别由Q11/Q21和Q12/Q22插值得到。
图源:https://www.cnblogs.com/yssongest/p/5303151.html
下图看起来的效果不错,将一些锯齿都变得比较平滑了。
upnet = nn.Upsample(scale_factor=2., mode="BILINEAR") # 放大2倍,以双线性插值的方式进行half_img = cv2.resize(img, None, None, 0.5, 0.5, cv2.INTER_LINEAR) upsample_pred = upnet(to_tensor(half_img)) show(img, half_img, to_ndarray(upsample_pred))
<Figure size 1080x360 with 3 Axes>
该算子将一个形为[N, C, H, W]或是[N, H, W, C]的Tensor重新排列成形为 [N, C/r**2, H*r, W*r]或 [N, H*r, W*r, C/r**2] 的Tensor。这样做有利于实现步长(stride)为1/r的高效sub-pixel(亚像素)卷积。详见Shi等人在2016年发表的论文 Real Time Single Image and Video Super Resolution Using an Efficient Sub Pixel Convolutional Neural Network 。
PixelShuffle常用在超分之中,经过PixelShuffle上采样会减少通道层的数目。通过卷积先得到 r^2 个通道的特征图(特征图大小和输入低分辨率图像一致),然后通过周期筛选(periodic shuffing)的方法得到这个高分辨率的图像,其中rrr为上采样因子(upscaling factor),也就是图像的扩大倍率。根据下图中后面两个图,比较容易理解PixelShuffle是怎么做的。使用中也常常按照这个流程,先训练得到更多的特征图,再使用PixelShuffle上采样,这样就能保证通道数不变,并放大了图像长宽。
图源:https://www.jianshu.com/p/71d6a9374899
这里由于原图是3通道,长宽各扩大2倍则需要通道数为原来的1/4,因此在这里演示的时候为了显示,在使用PixelShuffle的时候将图像复制了4份叠在一起,组成了12通道的图像,然后在PixelShuffle后变为3通道。
upnet = nn.PixelShuffle(upscale_factor=2) # 放大2倍,通道数变为1/4half_img = cv2.resize(img, None, None, 0.5, 0.5, cv2.INTER_LINEAR) half_img = np.concatenate([half_img] * 4, axis=-1) # C: 3 -> 12pixelshuffle_pred = upnet(to_tensor(half_img)) show(img, half_img[:, :, :3], to_ndarray(pixelshuffle_pred))
<Figure size 1080x360 with 3 Axes>
该接口用于构建 MaxUnPool2D 类的一个可调用对象,根据输入的input和最大值位置计算出池化的逆结果。所有非最大值设置为零。
MaxPool和AvgPool分别是对图像进行最大池化和平均池化,而对应的反池化就可以将结果还原到原来的大小。由于AvgPool是一个取平均的操作,反池化直接复制出多份平均值即可,因此没有专门的AvgUnPool函数,直接用Upsample即可。而MaxUnPool需要将最大值还原到对应的位置,因此MaxPool可以设置保存最大值索引的位置,MaxUnPool则需要根据这个位置将最大值还原到原来的位置,并在其他位置补充零。如下图所示:
图源:https://blog.csdn.net/quiet_girl/article/details/84579038
下图中看起来有一些不同的、单独的颜色块,是因为三个通道中的最大值索引是有区别的,因此还原回去也不都是同一个位置。
upnet = nn.MaxUnPool2D(2, 2) half_img, indices = F.max_pool2d(to_tensor(img), 2, 2, return_mask=True) # 需要返回索引maxunpool_pred = upnet(half_img, indices) # 根据索引找到原始位置show(img, to_ndarray(half_img), to_ndarray(maxunpool_pred))
<Figure size 1080x360 with 3 Axes>
二维转置卷积层(Convlution2d transpose layer)
该层根据输入(input)、卷积核(kernel)和空洞大小(dilations)、步长(stride)、填充(padding)来计算输出特征层大小或者通过output_size指定输出特征层大小。输入(Input)和输出(Output)为NCHW或NHWC格式,其中N为批尺寸,C为通道数(channel),H为特征层高度,W为特征层宽度。卷积核是MCHW格式,M是输出图像通道数,C是输入图像通道数,H是卷积核高度,W是卷积核宽度。如果组数大于1,C等于输入图像通道数除以组数的结果。转置卷积的计算过程相当于卷积的反向计算。转置卷积又被称为反卷积(但其实并不是真正的反卷积)。欲了解转置卷积层细节,请参考下面的说明和参考文献。如果参数bias_attr不为False, 转置卷积计算会添加偏置项。
不同于上面的上采样操作(如果PixelShuffle没算上前面的卷积步骤),反卷积是一种需要学习的上采样方法,其示意图如下所示。因此反卷积会带来额外的参数,但如果学习的不错,可能会对后续的任务的效果好于不用学习的方法。当然这就见仁见智了。
图源:https://blog.csdn.net/quiet_girl/article/details/84579038
下面为了演示简单训练了100次反卷积的卷积核,当然,直接两个RGB图像的mse_loss和这样搞起来实在是有点坑,效果也不多说,只是表明ConvTranspose是可以训练的。这里的简单训练至少让它看起来像个人了。不过大家也都提到了(也可以看到)反卷积后棋盘格效应 (checkerboard pattern) 出现。
upnet = nn.Conv2DTranspose(3, 3, 4, 2, 1)
half_img = cv2.resize(img, None, None, 0.5, 0.5, cv2.INTER_LINEAR)
upnet.train() # 训练模式opt = paddle.optimizer.AdamW(0.001, parameters=upnet.parameters())for _ in range(100): # 跑100次
upsample_pred = upnet(to_tensor(half_img))
loss = F.mse_loss(upsample_pred, to_tensor(img))
loss.backward()
opt.step()
upnet.eval() # 评估模式convtranspose_pred = upnet(to_tensor(half_img))
show(img, half_img, to_ndarray(convtranspose_pred))<Figure size 1080x360 with 3 Axes>
该OP基于flow field网格的对输入X进行双线性插值采样。网格通常由affine_grid生成, shape为[N, H, W, 2],是shape为[N, H, W]的采样点张量的(x, y)坐标。 其中,x坐标是对输入数据X的第四个维度(宽度维度)的索引,y坐标是第三维度(高维度)的索引,最终输出采样值为采样点的4个最接近的角点的双线性插值结果,输出张量的shape为[N, C, H, W]。
不同于Upsample的规则上采样,grid_sample是一种不规则的上采样,它会依照一个flow-field网格(比如光流,体素流等),根据这个网格中每个位置提供的坐标信息(这里指input中pixel的坐标),将input中对应位置的像素值填充到grid指定的位置,得到最终的输出。在一些有guide结构的网络中,会通过计算边缘流、光流等等,然后使用grid_sample将其他特征按照该网格进行非规则的上采样。grid_sample的示意如下图所示:
图源:https://blog.csdn.net/qq_34914551/article/details/107559031
下面的结果由于直接使用一个上下左右均为-1到1的grid会使得上采样的结果与规则上采样的结果相同,因此为其增加了一点位置的扰动,使得上采样后的结果不相同。
upnet = F.grid_sample# 创建一个水平和竖直方向都是-1到1的格网h, w = img.shape[:2] h_grid = paddle.linspace(-1.0, 1.0, h).reshape([-1, 1]).tile([w]) w_grid = paddle.linspace(-1.0, 1.0, w).reshape([-1, 1]).tile([h]).transpose((1, 0)) grid = paddle.concat([w_grid.unsqueeze(2), h_grid.unsqueeze(2)], axis=2)[None]# 故意对其进行随机扰动,注释这一行恢复的原图将同1grid += (paddle.randn((1, h, w, 2), dtype="float32") / 200) half_img = cv2.resize(img, None, None, 0.5, 0.5, cv2.INTER_LINEAR) gridsample_pred = upnet(to_tensor(half_img), grid) # 以grid来引导图像上采样show(img, half_img, to_ndarray(gridsample_pred))
<Figure size 1080x360 with 3 Axes>
以上就是深度学习中的各类上采样算子的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号