转置卷积(Transposed Convolution),又叫反卷积(Deconvolution),是深度学习中用于进行反卷积操作的核心组件之一。它的作用是在卷积神经网络中对输入的特征图进行上采样的操作。与常规卷积操作不同,转置卷积用于将输入特征图的空间维度(如宽度和高度)扩展为更大的特征图。
简言之:转置卷积核作用是将低分辨率图像转换为高分辨率图像。
再次强调:转置卷积操作并不是真正的卷积的逆运算,因为信息的损失是不可逆的。逆卷积核的权重在训练过程中需要学习,类似于正常卷积操作中的卷积核。
参考资料:
1. 卷积核计算
在 PyTorch 中,我们可以使用以下 API 进行转置卷积计算:
nn.ConvTranspose2d(
in_channels: int,
out_channels: int,
kernel_size: _size_2_t,
stride: _size_2_t = 1,
padding: _size_2_t = 0,
bias: bool = True,
dilation: _size_2_t = 1,
padding_mode: str = 'zeros',
)
参数含义如下:
- in_channels 表示输入图像的通道数
- out_channels 表示输出图像的通道数
- kernel_size 表示每个通道卷积核大小
- stride 表示每个像素点之间填充多少
(stride-1)个 0 - padding 表示在特征图周围填充
(kernel-padding-1)行或列 - bias 表示卷积核的偏置参数是否需要
- padding_mode 在输入数据周围填充的数据,默认填充0
转置卷积计算的过程如下:
- 首先,在输入图像每个像素之间填充
(stride-1)行或列 0 - 其次,在输入图像边缘填充
(kernel-padding-1)行或列 0 - 然后,将卷积核参数由上至下,从左向右翻转
- 最后,在填充和翻转卷积核之后,使用翻转后的卷积核对填充后的图像进行卷积计算。




注意: 进行转置卷积操作时,padding 和 stride 只参与对输入图像的填充处理,后续只需要对处理后的图像进行正常的步长为 1 的卷积操作。
import torch
import torch.nn as nn
import torch.nn.functional as F
def test01():
s = 2
p = 1
k = 3
# 固定随机数
torch.manual_seed(0)
# 输入数据
inputs = torch.randint(1, 10, size=(1, 1, 3, 3), dtype=torch.float32)
# 卷积核参数: (每个卷积核的通道数,输出通道数量,卷积核宽高)
weight = torch.randint(1, 5, size=(1, 1, 3, 3), dtype=torch.float32)
# 偏置形状要和输出通道数一样
bias = torch.zeros(size=(1,), dtype=torch.float32)
outputs = F.conv_transpose2d(inputs, weight, bias, padding=p, stride=s)
print('输出结果:\n', outputs)
# 分解动作
def test02():
s = 2
p = 1
k = 3
# 固定随机数
torch.manual_seed(0)
# 输入数据
inputs = torch.randint(1, 10, size=(1, 1, 3, 3), dtype=torch.float32)
weight = torch.randint(1, 5, size=(1, 1, k, k), dtype=torch.float32)
# 1. 元素之间填充0
new_inputs = torch.zeros(1, 1, 5, 5)
new_inputs[:, :, ::s, ::s] = inputs
print('元素填充:\n', new_inputs)
# 2. 矩阵周围填充0
inputs = F.pad(new_inputs, (k - p - 1,) * 4, value=0.0)
print('周围填充:\n', inputs)
# 3. 卷积核参数向下左右翻转
weight = torch.flip(weight, dims=[2, 3])
print('参数翻转:\n', weight)
# 4. 正常进行卷积操作
outputs = F.conv2d(inputs, weight=weight, padding=0, stride=1)
print('输出结果:\n', outputs)
if __name__ == '__main__':
test01()
print('-' * 50)
test02()
输出结果:
tensor([[[[36., 28., 4., 6., 12.],
[23., 63., 17., 56., 17.],
[28., 29., 32., 31., 28.],
[23., 47., 12., 29., 11.],
[32., 26., 8., 8., 8.]]]])
--------------------------------------------------
元素填充:
tensor([[[[9., 0., 1., 0., 3.],
[0., 0., 0., 0., 0.],
[7., 0., 8., 0., 7.],
[0., 0., 0., 0., 0.],
[8., 0., 2., 0., 2.]]]])
周围填充:
tensor([[[[0., 0., 0., 0., 0., 0., 0.],
[0., 9., 0., 1., 0., 3., 0.],
[0., 0., 0., 0., 0., 0., 0.],
[0., 7., 0., 8., 0., 7., 0.],
[0., 0., 0., 0., 0., 0., 0.],
[0., 8., 0., 2., 0., 2., 0.],
[0., 0., 0., 0., 0., 0., 0.]]]])
参数翻转:
tensor([[[[1., 1., 1.],
[3., 4., 1.],
[3., 2., 4.]]]])
输出结果:
tensor([[[[36., 28., 4., 6., 12.],
[23., 63., 17., 56., 17.],
[28., 29., 32., 31., 28.],
[23., 47., 12., 29., 11.],
[32., 26., 8., 8., 8.]]]])
2. 特征图大小
转置卷积后的特征图形状的计算通常基于以下几个参数:
- \( H_{in} x W_{in} \) 表示输入特征图的形状
- \( K_{h} x K_{w} \) 表示卷积核的大小
- \( S \) 表示 stride 大小
- \( P \) 表示输入图形周围填充的 0

import torch.nn as nn
import torch
def demo():
transpose_conv = nn.ConvTranspose2d(3, 6, kernel_size=3, stride=2, padding=2, bias=False)
intputs = torch.randn(1, 3, 32, 32)
# H_in = 32 W_in = 64
# stride = 2
# kenerl_size = 3
# padding = 2
# H_out = stride * (H_in - 1) + kenerl_size - 2 * padding
# H_out = 2 * (32 - 1) + 3 - 2 * 2 = 61
outputs = transpose_conv(intputs)
print(outputs.shape)
if __name__ == '__main__':
demo()
torch.Size([1, 6, 61, 61])

冀公网安备13050302001966号