🧠 具体讲解神经网络中的转置卷积(Transposed Convolution)
🧭 1. 转置卷积的动机:为什么我们需要它?
标准卷积通常会降低特征图的空间尺寸(比如从 64x64 → 32x32),这对于提取高级特征很有用。但在某些任务中,比如:
- 图像生成(如 GAN)
- 图像分割(如 U-Net 的解码器)
- 上采样(upsampling)
我们需要把特征图的尺寸还原回来(比如从 32x32 → 64x64),而且希望这个还原是可学习的,也就是说参数可训练。
这时候就需要 转置卷积(也叫反卷积、fractionally strided convolution)。
🧱 2. 什么是转置卷积?直觉理解
我们先从标准卷积的矩阵乘法视角出发来看。
标准卷积可以被视为:
y = K x y = Kx y=Kx
其中 K K K 是一个稀疏的矩阵(由卷积核扩展而来)。
那么转置卷积就是这个线性变换的转置矩阵乘法:
转置卷积是:
x = K T y x = K^T y x=KTy
直觉上:
- 标准卷积:压缩/整合信息
- 转置卷积:扩展/重建信息
🧮 3. 数学公式和计算流程
设输入特征图大小为 C i n × H i n × W i n C_{in} \times H_{in} \times W_{in} Cin×Hin×Win,
卷积核大小为 K × K K \times K K×K,步长为 S S S,padding 为 P P P,输出通道数为 C o u t C_{out} Cout。
标准卷积输出尺寸计算:
H o u t = ⌊ H i n + 2 P − K S ⌋ + 1 H_{out} = \left\lfloor \frac{H_{in} + 2P - K}{S} \right\rfloor + 1 Hout=⌊SHin+2P−K⌋+1
转置卷积输出尺寸计算:
H o u t = ( H i n − 1 ) ⋅ S − 2 P + K H_{out} = (H_{in} - 1) \cdot S - 2P + K Hout=(Hin−1)⋅S−2P+K
你可以看到,转置卷积公式中是乘而不是除,所以它有上采样的效果。
🔄 4. 转置卷积是怎么运作的?图示直觉(描述)
- 在输入之间插入空格(步长为 2 就在像素间插入 1 个零)
- 在这个“稀疏图像”上进行普通卷积
- 因为插入了零,卷积核扫描时会“扩大视野”,从而生成更大的输出
这种方式又叫 “fractionally strided convolution”(因为它“模拟”了小于 1 的步长)
🧠 5. 与标准卷积的反向传播的关系
这其实是转置卷积名字的由来!
- 在实现标准卷积的 反向传播 时,梯度传播过程中的某一步就需要一个类似转置卷积的操作来“还原”梯度。
- 这也是为什么 PyTorch 中
ConvTranspose2d
是Conv2d
的“梯度反向过程”的实现。
所以:
转置卷积并不是“把卷积反过来”,而是在反向传播中自然出现的操作。
📦 6. PyTorch 实现
import torch
import torch.nn as nn# 转置卷积层:输入通道3,输出通道16,kernel_size=3,stride=2,padding=1
conv_transpose = nn.ConvTranspose2d(in_channels=3, out_channels=16, kernel_size=3, stride=2, padding=1, output_padding=1)x = torch.randn(1, 3, 32, 32)
y = conv_transpose(x)
print(y.shape) # torch.Size([1, 16, 64, 64])
注意:
output_padding 是为了调节输出大小(因为整数除法/乘法可能不是精确反转)
转置卷积参数量和普通卷积一样: C i n × C o u t × K 2 C_{in} \times C_{out} \times K^2 Cin×Cout×K2
🔁 7. 参数与输出尺寸公式总结
对于 ConvTranspose2d
,输出大小计算公式为:
H o u t = ( H i n − 1 ) ⋅ S − 2 P + K + output_padding H_{out} = (H_{in} - 1) \cdot S - 2P + K + \text{output\_padding} Hout=(Hin−1)⋅S−2P+K+output_padding
这里:
- H i n H_{in} Hin:输入高度
- S S S:stride(步长)
- P P P:padding
- K K K:kernel size(卷积核大小)
output_padding
:用于解决无法精确还原的边界问题
🔬 8. 转置卷积 vs 上采样+卷积
我们也可以用非转置卷积实现上采样 + 卷积操作:
import torch.nn.functional as Fx = torch.randn(1, 3, 32, 32)
x_up = F.interpolate(x, scale_factor=2, mode='nearest') # 最近邻上采样
conv = nn.Conv2d(3, 16, kernel_size=3, padding=1)
y = conv(x_up)
区别在于:
方法 | 可学习参数 | 是否精确上采样 | 计算效率 |
---|---|---|---|
转置卷积 | ✅ 是 | ✅ 是 | ✅ 快 |
上采样 + 卷积 | ✅ 是 | ✅ 是 | 较慢,但更可控 |
💣 9. 转置卷积的常见问题:Checkerboard Artifacts
因为卷积核在插值时可能导致像素间不均匀的覆盖区域,转置卷积有时会出现“棋盘格伪影”。
🛠️ 解决方式:
- 使用
上采样 + 卷积
替代 - 使用更小的 kernel 和 stride
- 加强正则化,或使用 PixelShuffle 等替代方式
🧪 10. 实际应用:U-Net 解码器
在 U-Net 的上采样阶段,我们希望将低分辨率特征图还原为高分辨率(最终恢复为原图大小)。
self.upconv = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2)
然后 concat encoder 输出 + decoder 输出,再继续卷积。
转置卷积在这一步:
- 学习“如何恢复图像细节”
- 与 skip connection 保持 spatial 对齐
- 输出最终的分割图像或生成图像