反卷积是一种特殊的正向卷积操作, 通过补零的方式扩大输入图像的尺寸, 接着翻转卷积核, 和普通卷积一样进行正向卷积, 由于前期补充了大量的零, 即便进行了卷积运算, 输出图像的尺寸依然比输入图像大, 这样就达到了向上采样的目的
下面展示一些实例, 使用 PyTorch 计算反卷积
示例-1: 输入输出通道数都是1, 步长也为1
输入数据 1*1*3*3(Batch 和 Channel 均为 1)
In [1]: import torch
In [2]: from torch import nn
In [3]: torch.manual_seed(0)
Out[3]: <torch._C.Generator at 0x7f17986464b0>
In [4]: x = torch.randint(5, size=(1,1,3,3), dtype=torch.float)
In [5]: x
Out[5]:
tensor([[[[4., 4., 3.],
[0., 3., 4.],
[2., 3., 2.]]]])
卷积核 1*1*2*2
In [6]: w = torch.tensor([[1,2],[0,1]], dtype=torch.float).view(1,1,2,2)
In [7]: w
Out[7]:
tensor([[[[1., 2.],
[0., 1.]]]])
创建反卷积层, 不使用偏置(方便计算)
In [8]: layer = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=0, bias=False)
In [9]: layer.weight = nn.Parameter(w)
In [10]: layer.eval()
Out[10]: ConvTranspose2d(1, 1, kernel_size=(2, 2), stride=(1, 1), bias=False)
手动计算
- 翻转卷积核
- 填充零
对于正常卷积 oup = (inp + 2*padding - kernel_size) / stride + 1
, 反卷积可以看做普通卷积的镜像操作, 反卷积的输入对应正向卷积的输出, 令 oup=3
, 得 inp=4
(padding=0, kernel_size=2, stride=1), 所以输出尺寸为 4*4
步长为 1 时, 仅在四周补零, 为了得到 4*4 的输出, 需要补一圈零
- 正向卷积
使用 PyTorch 计算
# 无需正向传播
In [17]: with torch.no_grad():
...: y = layer(x)
...:
In [18]: y
Out[18]:
tensor([[[[ 4., 12., 11., 6.],
[ 0., 7., 14., 11.],
[ 2., 7., 11., 8.],
[ 0., 2., 3., 2.]]]])
修改反卷积层, 额外设置 padding=1
# padding=1
In [30]: layer_2 = nn.ConvTranspose2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=1, bias=False)
In [31]: layer_2.weight = nn.Parameter(w)
In [32]: layer_2.eval()
Out[32]: ConvTranspose2d(1, 1, kernel_size=(2, 2), stride=(1, 1), padding=(1, 1), bias=False)
根据 oup = (inp + 2*padding - kernel_size) / stride + 1
, 代入 oup=3, padding=1, kernel_size=2, stride=1, 得 inp=2, 反卷积后特征图尺寸为 2*2, 此时无需填充零, 计算过程为
使用 PyTorch 验证
In [33]: with torch.no_grad():
...: y = layer_2(x)
...:
In [34]: y
Out[34]:
tensor([[[[ 7., 14.],
[ 7., 11.]]]])