转置卷积
目前为止,在卷积神经网络层中,通常会减少下采样输入图像的空间维度(高和宽)。然而如果输入和输出图像的空间维度相同,在以像素级分类的语义分割中将会很方便:输出像素所处的通道维可以保有输入像素在同一位置上的分类结果。
为了实现这一点,可以使用另一种类型的卷积神经网络层,它可以增加上采样中间层特征图的空间维度,用于逆转下采样导致的空间尺寸减少。
1.基本操作
假设有一个 n h × n w n_h\times n_w nh×nw的输入张量和一个 k h × k w k_h\times k_w kh×kw的卷积核,一步幅为1滑动卷积核窗口,每行 n w n_w nw次,每列 n h n_h nh次,共产生 n h n w n_hn_w nhnw个结果,每个中间结果都是 ( n h + k h ) − 1 × ( n w + k w − 1 ) (n_h+k_h)-1\times(n_w+k_w-1) (nh+kh)−1×(nw+kw−1)的张量,初始化为0。
输入张量中的每个元素乘以卷积核,得到一个
k
h
×
k
w
k_h\times k_w
kh×kw张量,替换中间张量的一部分。最后所有中间结果相加得到最终结果。
Y
[
i
:
i
+
h
,
j
:
j
+
w
]
+
=
X
[
i
,
j
]
⋅
K
Y[i:i+h,j:j+w] += X[i,j]\cdot K
Y[i:i+h,j:j+w]+=X[i,j]⋅K
为什么称为转置?
对于卷积 Y = X @ W Y =X@ W Y=X@W,可以对 W W W构造一个 V V V,使得卷积等价于矩阵乘法 Y ′ = V X ′ Y' = VX' Y′=VX′,这里 Y ′ , X ′ Y',X' Y′,X′是 Y , X Y,X Y,X对应的向量版本。
也就是说如果 Y ′ Y' Y′长为 n n n, X ′ X' X′长为 m m m,则 V V V的形状为 n × m n\times m n×m,转置之后 V T V^T VT的形状变为 m × n m\times n m×n,则X和Y的形状也交换了。
那么转置卷积等价于 Y ′ = V T X ′ Y'=V^T X' Y′=VTX′,如果卷积将输入从 ( h , w ) (h,w) (h,w)变成了 ( h ′ , w ′ ) (h',w') (h′,w′),同样超参数的转置卷积则从 ( h ′ , w ′ ) (h',w') (h′,w′)变成 ( h , w ) (h,w) (h,w)
import torch
from torch import nn
from d2l import torch as d2l
def trans_conv(X, K):
h, w = K.shape
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))
X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
# nn库自带的转置卷积函数,超参数很明显
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
tconv.weight.data = K
print(tconv(X))
2.填充、步幅和多通道
2.1 填充
与常规卷积不同,在转置卷积中,填充被应用于的输出(常规卷积将填充应用于输入)。 例如,当将高和宽两侧的填充数指定为1时,转置卷积的输出中将删除第一和最后的行与列。
'''填充、步幅和多通道'''
# 填充是对输出进行填充
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, padding=1, bias=False)
tconv.weight.data = K
print('填充:',tconv(X))
输出反而变小了
2.2 步幅
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, stride=2, bias=False)
tconv.weight.data = K
print('步幅为2:',tconv(X))
2.3 多输入输出通道
与常规卷积类似的,假设输入有 c i c_i ci个通道,且转置卷积为每个输入通道分配了一个 k h × k w k_h\times k_w kh×kw的卷积张量,指定多个输出通道时,每个输出通道将有一个 c i × k h × k w c_i\times k_h\times k_w ci×kh×kw的卷积核
如果我们将X带入卷积层f来输出 Y = f ( x ) Y=f(x) Y=f(x),并创建一个与f具有相同的超参数、但输出通道数量是X中通道数的转置卷积层 g g g,那么 g ( Y ) g(Y) g(Y)的形状将与 X X X相同:
'''多输入输出通道'''
X = torch.rand(size=(1, 10, 16, 16))
conv = nn.Conv2d(10, 20, kernel_size=5, padding=2, stride=3)
tconv = nn.ConvTranspose2d(20, 10, kernel_size=5, padding=2, stride=3)
tconv(conv(X)).shape == X.shape # True
3.与矩阵乘法的联系
13.10. 转置卷积 — 动手学深度学习 2.0.0 documentation (d2l.ai)
之前有写过了,大概就是卷积操作可以使用矩阵乘法实现。
4.关于填充、步幅的补充
输入矩阵填充,核矩阵上下左右翻转后做正常卷积(填充0、步幅1)
s=2时,行和列之间插入一行或已列
p=0.则填充k-1=2-1=1。
形状换算
输入高(宽)为n,核k,填充p,步幅s,那么对于转置卷积:
n
′
=
s
n
+
k
−
2
p
−
s
n'=sn +k-2p-s
n′=sn+k−2p−s
如果想让高宽成倍增加,那么
k
=
2
p
+
s
k=2p+s
k=2p+s
回忆一下,对于卷积:
n
′
=
⌊
(
n
−
k
−
2
p
+
s
)
/
s
⌋
→
n
≥
s
n
′
+
k
−
2
p
−
s
n'=\lfloor(n-k-2p+s)/s\rfloor \rightarrow n\ge sn'+k-2p-s
n′=⌊(n−k−2p+s)/s⌋→n≥sn′+k−2p−s
转置卷积不是反卷积,反卷积是指卷积的逆运算:
i
f
Y
=
c
o
n
v
(
X
,
K
)
,
t
h
e
n
X
=
d
e
c
o
n
v
(
Y
,
K
)
if\ Y=conv(X,K), then\ X=deconv(Y,K)
if Y=conv(X,K),then X=deconv(Y,K)
转置卷积本质上依旧是卷积,只是矩阵形式上的变化相反。