一、反卷积(转置卷积)
反卷积通常用于将低维特征映射成高维输入,与卷积操作的作用相反。一般反卷积只能还原图片的大小,不能将图片还原为原来的图片。反卷积操作的时候有两种情况,分为步长为1和步长大于1。
1. stride = 1
- stride=1时,反卷积操作时在特征图周围填充0,至于添加多少padding,就要看原图和特征图的大小关系。比如原图大小为4 * 4 ,卷积核大小为3 * 3,步长为1,卷积后的特征图大小为:2 * 2,那么现在要将这个尺寸的特征图反卷积成4 * 4 大小,那么先填充padding=2的0,也即添加两圈。然后用3 * 3 卷积核进行卷积,得到了5 * 5 大小的图片。如图所示:
卷积操作:
反卷积操作:
2.stride > 1
- 当步长大于1时,先在周围添加0, 然后按照步长等于1时的操作进行,如下图所示的操作:
- 步长大于2时的反卷积操作:
3. 反卷积操作实现
from torchvision.transforms import ToTensor, ToPILImage
import torch.nn as nn
import matplotlib.pyplot as plt
from PIL import Image
# 显示原始图像
orginal_img = Image.open("yellow/dog.jpg")
plt.subplot(1, 3, 1)
plt.axis('off')
plt.title("orginal pic")
plt.imshow(orginal_img)
# 显示卷积后的图像
img = ToTensor()(orginal_img)
conv = nn.Conv2d(3, 3, 3, 1)
y = conv(img.unsqueeze(0))
conv_img = ToPILImage("RGB")(y.squeeze(0))
plt.subplot(1, 3, 2)
plt.axis('off')
plt.title("conv pic")
plt.imshow(conv_img)
#显示反卷积后的图像
transconv = nn.ConvTranspose2d(3, 3, 3, 1)
trans_img = transconv(y)
trans_img = ToPILImage("RGB")(trans_img.squeeze(0))
plt.subplot(1, 3, 3)
plt.axis('off')
plt.title("transconv pic")
plt.imshow(trans_img)
plt.show()
上述代码只是简单的展示了反卷积如何实现,它与卷积的实现方法很相似。
二、反池化
1. 反池化操作
反池化过程主要介绍两种池化:最大池化的反池化和平均池化的反池化。
其中最大池化在反池化时要记录最大值所在的位置,然后其余位置用0补充,平均池化一般用邻近元素作填充。如下图所示:
3. 反池化操作的实现
from torchvision.transforms import ToTensor, ToPILImage
import torch.nn as nn
import matplotlib.pyplot as plt
from PIL import Image
# 显示原始图像
orginal_img = Image.open("yellow/dog.jpg")
plt.subplot(1, 3, 1)
plt.axis('off')
plt.title("orginal pic")
plt.imshow(orginal_img)
# 卷积层
img = ToTensor()(orginal_img)
conv = nn.Conv2d(3, 3, 3, 1)
y = conv(img.unsqueeze(0))
# 池化层,显示池化后的图片
"""
参数return_indices:表示返回最大值所在的位置,最大池化在进行反池化操作哦时要记录最大值所在的位置。
"""
pool = nn.MaxPool2d(2, 2, return_indices=True)
pool_y, index = pool(y)
pool_img = ToPILImage("RGB")(pool_y.squeeze())
plt.subplot(1, 3, 2)
plt.axis('off')
plt.title("pool pic")
plt.imshow(pool_img)
# 反池化操作并显示反池化后的图片
unpool = nn.MaxUnpool2d(2, 2)
y = unpool(pool_y, index)
# 对比池化操作输出的值和反池化操作输出值的大小
print("池化后的值:", pool_y)
print("反池化后的值:", y)
unpool_img = ToPILImage("RGB")(y.squeeze())
plt.subplot(1, 3, 3)
plt.axis('off')
plt.title("unpool pic")
plt.imshow(unpool_img)
plt.show()
池化的作用:
- 减小特征图大小
- 降低过拟合,降低过拟合是减小输出大小的结果,它同样也减少了后续层中的参数的数量。
- 池化操作很暴力的丢弃了一些图片的特征,可以用大步长的卷积代替池化操作,这样就可以保证图片特征不丢失。
三、普通上采样
(1)最邻近插值
最邻近插值算法的原理:在原图像中找到最邻近的一个点,然后把这个点的像素值插入到目标图像中,最近临插值算法优点是算法简单易于实现,但是缺点是由于相邻像素点的像素值相同,容易出现色块现象。
(2)双线性插值
- 双线性插值原理解释
首先在Q11Q21、Q12Q22方向上进行两次线性插值,得到R1、R2;然后在R1R2方向再做一次线性插值得到P点,这就是双线性插值。
双线性插值原理动图:
图片来源:https://www.cnblogs.com/SpiritAmos/p/11991528.html
(3) 最邻近插值和双线性插值的实现:
from torchvision.transforms import ToTensor, ToPILImage
import torch.nn as nn
import matplotlib.pyplot as plt
from PIL import Image
import torch.nn.functional as F
orginal_img = Image.open("yellow/dog.jpg")
plt.subplot(1, 3, 1)
plt.axis('off')
plt.title("orginal pic")
plt.imshow(orginal_img)
img = ToTensor()(orginal_img)
conv = nn.Conv2d(3, 3, 3, 1)
y = conv(img.unsqueeze(0))
trans_conv = nn.ConvTranspose2d(3, 3, 3, 1)
y = trans_conv(y)
# pool = nn.MaxPool2d(2, 2)
# pool_y = pool(y)
# 最邻近插值,参数scale_factor 表示放大或缩小倍数
"""
以下两种写法都可以用F.interpolate()函数代替
upsample = nn.UpsamplingNearest2d(scale_factor=2)
upsample = nn.Upsample(scale_factor=2, mode='nearest')
"""
z1 = F.interpolate(y, scale_factor=2, mode='nearest')
near_conv_img = ToPILImage("RGB")(z1.squeeze(0))
plt.subplot(1, 3, 2)
plt.axis('off')
plt.title("nearest")
plt.imshow(near_conv_img)
# 双线性插值,双线性插值中参数align_corners是对齐角的意思
"""
以下两种写法都可以用F.interpolate()函数代替
upsample = nn.UpsamplingBilinear2d(scale_factor=2)
upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
"""
z2 = F.interpolate(y, scale_factor=2, mode='bilinear', align_corners=True)
linear_conv_img = ToPILImage("RGB")(z2.squeeze(0))
plt.subplot(1, 3, 3)
plt.axis('off')
plt.title("bilinear")
plt.imshow(linear_conv_img)
plt.show()