Skip to content

转置卷积

一、什么是转置卷积

之前我们学的卷积层和汇聚层,都是用来缩小输入图像的空间维度的,比如把大图片变成小图片。但在语义分割这类任务里,我们需要把缩小后的特征图恢复到和输入图片一样的尺寸,这样才能每个像素都对应一个分类结果。转置卷积就是用来做这个的,它可以把小的特征图上采样,变成更大的尺寸,相当于逆转了普通卷积的下采样操作。

二、转置卷积的基本操作

我们先从简单的情况说起,不考虑通道,步幅为 1,也没有填充。假设我们有一个 2×2 的输入张量,和一个 2×2 的卷积核。转置卷积的操作是这样的:

  1. 先创建一个中间张量,尺寸是 (输入高度 + 卷积核高度 -1) × (输入宽度 + 卷积核宽度 -1),也就是 (2+2-1)×(2+2-1)=3×3,初始化为 0。
  2. 把输入里的每个元素,乘以卷积核,然后把得到的 2×2 的张量,放到中间张量对应的位置上。比如输入里的 (0,0) 位置的元素,乘以卷积核后,放到中间张量的 (0:2, 0:2) 位置。
  3. 最后把所有中间结果加起来,就得到了转置卷积的输出。

转置卷积的基本操作

简单来说,普通卷积是用卷积核 “压缩” 输入,转置卷积是用卷积核 “放大” 输入,把每个输入元素都变成一个卷积核大小的区域,然后叠加起来。

代码实现(PyTorch 版)

我们可以用简单的代码实现这个基本操作:

python
import torch

def trans_conv(X, K):
    h, w = K.shape
    # 创建中间张量,初始化为0
    Y = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1))
    for i in range(X.shape[0]):
        for j in range(X.shape[1]):
            # 把输入元素乘以卷积核,放到中间张量的对应位置
            Y[i: i + h, j: j + w] += X[i, j] * K
    return Y

# 测试一下
X = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
print(trans_conv(X, K))

运行结果会是一个 3×3 的张量,和我们手动计算的结果一样。

我们也可以用 PyTorch 的高级 API 来实现:

python
# 把输入和卷积核变成四维张量(批量大小,通道数,高度,宽度)
X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
# 创建转置卷积层
tconv = torch.nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
# 设置卷积核参数
tconv.weight.data = K
# 计算转置卷积
print(tconv(X))

这个结果和我们手动实现的结果是一样的。

三、填充、步幅和多通道

1. 填充

和普通卷积不一样,转置卷积的填充是应用在输出上的。比如我们设置填充为 1,就是把输出的第一行、最后一行、第一列、最后一列都去掉。

python
# 创建填充为1的转置卷积层
tconv = torch.nn.ConvTranspose2d(1, 1, kernel_size=2, padding=1, bias=False)
tconv.weight.data = K
print(tconv(X))

这个结果会是一个 1×1 的张量,因为原来的 3×3 输出,去掉了一圈,就变成 1×1 了。

2. 步幅

转置卷积的步幅是设置在输出上的,步幅为 2 的话,就是在中间张量里每隔一个位置放一个结果,这样输出的尺寸会更大。

python
# 创建步幅为2的转置卷积层
tconv = torch.nn.ConvTranspose2d(1, 1, kernel_size=2, stride=2, bias=False)
tconv.weight.data = K
print(tconv(X))

这个结果会是一个 4×4 的张量,比步幅为 1 的时候更大。 转置卷积的步幅为2

3. 多通道

转置卷积的多通道和普通卷积是一样的。如果输入有 c_i 个通道,每个通道都有一个卷积核;如果输出有 c_o 个通道,每个输出通道都有一个 c_i×k_h×k_w 的卷积核。

还有一个很有用的性质:如果我们用一个卷积层把 X 变成 Y,然后用一个和这个卷积层超参数一样的转置卷积层,把 Y 变回原来的尺寸,那么转置卷积的输出形状会和 X 一样。比如:

python
# 随机生成一个输入
X = torch.rand(size=(1, 10, 16, 16))
# 创建卷积层
conv = torch.nn.Conv2d(10, 20, kernel_size=5, padding=2, stride=3)
# 创建转置卷积层,超参数和卷积层一样,输出通道数是输入的通道数
tconv = torch.nn.ConvTranspose2d(20, 10, kernel_size=5, padding=2, stride=3)
# 测试一下形状
print(tconv(conv(X)).shape == X.shape)  # 结果是True

四、转置卷积和矩阵变换的联系

其实转置卷积可以用矩阵乘法来理解。我们可以把普通卷积的卷积核变成一个稀疏的权重矩阵 W,普通卷积的前向传播就是把输入变成向量,然后和 W 相乘。而转置卷积的前向传播,就是把输入向量和 W 的转置相乘。

反过来,普通卷积的反向传播是和 W 的转置相乘,转置卷积的反向传播是和 W 相乘。所以转置卷积相当于交换了普通卷积的前向传播和反向传播。

五、小结

  1. 转置卷积是用来上采样的,可以把小的特征图恢复到更大的尺寸,常用于语义分割等任务。

  2. 转置卷积的基本操作是把每个输入元素乘以卷积核,然后叠加到中间张量的对应位置。

  3. 转置卷积的填充是应用在输出上的,步幅是设置在输出上的,和普通卷积不一样。

  4. 转置卷积可以用矩阵乘法来理解,它的前向传播相当于普通卷积的反向传播,反向传播相当于普通卷积的前向传播。

(注:文档部分内容可能由 AI 生成) 源地址

京ICP备2024093538号-1