了解 Conv2DTranspose 的 PyTorch 实现
Understanding the PyTorch implementation of Conv2DTranspose
我试图理解一个使用 PyTorch 转置卷积函数的示例片段,以及文档 here,作者在文档中写道:
"The padding argument effectively adds dilation * (kernel_size - 1) -
padding amount of zero padding to both sizes of the input."
考虑下面的代码片段,其中 [1, 1, 4, 4]
个样本图像被输入到 ConvTranspose2D
操作,参数为 stride=2
和 padding=1
,权重矩阵为形状(1, 1, 4, 4)
具有介于 1
和 16
之间的条目(在本例中为 dilation=1
和 added_padding = 1*(4-1)-1 = 2
)
sample_im = torch.ones(1, 1, 4, 4).cuda()
sample_deconv2 = nn.ConvTranspose2d(1, 1, 4, 2, 1, bias=False).cuda()
sample_deconv2.weight = torch.nn.Parameter(
torch.tensor([[[[ 1., 2., 3., 4.],
[ 5., 6., 7., 8.],
[ 9., 10., 11., 12.],
[13., 14., 15., 16.]]]]).cuda())
产生:
>>> sample_deconv2(sample_im)
tensor([[[[ 6., 12., 14., 12., 14., 12., 14., 7.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[10., 20., 22., 20., 22., 20., 22., 11.]]]], device='cuda:0',
grad_fn=<CudnnConvolutionTransposeBackward>)
现在我看到了没有步长和填充的转置卷积的简单例子。例如,如果输入是 2x2
图像 [[2, 4], [0, 1]]
,并且具有一个输出通道的卷积滤波器是 [[3, 1], [1, 5]]
,那么生成的形状为 (1, 1, 3, 3)
的张量可以看作是下图中最右边四个矩阵的和:
问题是我似乎无法在同一可视化中找到使用步长 and/or 填充的示例。根据我的代码片段,我很难理解填充是如何应用于样本图像的,或者步幅是如何工作来获得这个输出的。任何见解表示赞赏,即使只是了解如何计算结果矩阵的 (0,0)
条目中的 6
或 (0,1)
条目中的 12
也会非常有帮助。
nn.ConvTranspose2d
的输出空间维度由下式给出:
out = (x - 1)s - 2p + d(k - 1) + op + 1
其中x
是输入空间维度和out
相应的输出大小,s
是步幅,d
扩张,p
填充,k
内核大小,op
输出填充。
如果我们保留以下操作数:
对于输入的每个值,我们通过计算内核的每个元素的乘积来计算一个缓冲区(具有相应的颜色)。
以下是 s=1, p=0
、s=1, p=1
、s=2, p=0
和 s=2, p=1
的可视化:
s=1, p=0
:输出为3x3
对于蓝色缓冲区,我们有 (1) 2*k_top-left = 2*3 = 6
; (2) 2*k_top-right = 2*1 = 2
; (3) 2*k_bottom-left = 2*1 = 2
; (4) 2*k_bottom-right = 2*5 = 10
.
s=1, p=1
:输出为1x1
s=2, p=0
:输出为4x4
s=2, p=2
:输出为2x2
我认为让事情变得混乱的原因是他们对文档中“输入”或“输出”的含义以及术语“步幅”和“填充”的重载不是很在意。
我发现通过问自己以下问题更容易理解 PyTorch 中的转置卷积:我会给一个正常的前向卷积层提供什么参数,这样它就会给出手头的张量,我正在将其输入转置卷积层?
比如“stride”应该理解为forwardconv中的“stride”,即滑动核的移动步长。
在转置转换中,“步幅”实际上意味着不同的东西:stride-1
是进入 转置 转换的输入单元之间的交错空槽的数量层。那是因为 forward 转换中的 greater-than-1 “步幅”造成了这样的漏洞。请参见下图的说明:
该图还表明,转置卷积层中的内核移动步长始终为 1,而与“步幅”的值无关。我发现牢记这一点非常重要。
与 padding
参数类似。应该理解为应用于 forward conv 的 0-padding。由于这种填充,我们在 forward conv 的输出中得到了一些额外的单元。因此,如果我们随后将此输出输入到转置转换中,为了回到原来的 non-padded 长度,那些额外的东西应该被删除,因此等式中的 -2p
项。
请参见下图。
总之,在张量形状变换的意义上,它们的设计使得普通 conv 和转置 conv 是彼此的“逆”操作。 (但我确实认为应该改进文档。)
有了这个原则,dilation
和 output_padding
的论点也可以相对容易地计算出来。我已经写了一个blog,以防有人感兴趣。
我试图理解一个使用 PyTorch 转置卷积函数的示例片段,以及文档 here,作者在文档中写道:
"The padding argument effectively adds dilation * (kernel_size - 1) - padding amount of zero padding to both sizes of the input."
考虑下面的代码片段,其中 [1, 1, 4, 4]
个样本图像被输入到 ConvTranspose2D
操作,参数为 stride=2
和 padding=1
,权重矩阵为形状(1, 1, 4, 4)
具有介于 1
和 16
之间的条目(在本例中为 dilation=1
和 added_padding = 1*(4-1)-1 = 2
)
sample_im = torch.ones(1, 1, 4, 4).cuda()
sample_deconv2 = nn.ConvTranspose2d(1, 1, 4, 2, 1, bias=False).cuda()
sample_deconv2.weight = torch.nn.Parameter(
torch.tensor([[[[ 1., 2., 3., 4.],
[ 5., 6., 7., 8.],
[ 9., 10., 11., 12.],
[13., 14., 15., 16.]]]]).cuda())
产生:
>>> sample_deconv2(sample_im)
tensor([[[[ 6., 12., 14., 12., 14., 12., 14., 7.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[12., 24., 28., 24., 28., 24., 28., 14.],
[20., 40., 44., 40., 44., 40., 44., 22.],
[10., 20., 22., 20., 22., 20., 22., 11.]]]], device='cuda:0',
grad_fn=<CudnnConvolutionTransposeBackward>)
现在我看到了没有步长和填充的转置卷积的简单例子。例如,如果输入是 2x2
图像 [[2, 4], [0, 1]]
,并且具有一个输出通道的卷积滤波器是 [[3, 1], [1, 5]]
,那么生成的形状为 (1, 1, 3, 3)
的张量可以看作是下图中最右边四个矩阵的和:
问题是我似乎无法在同一可视化中找到使用步长 and/or 填充的示例。根据我的代码片段,我很难理解填充是如何应用于样本图像的,或者步幅是如何工作来获得这个输出的。任何见解表示赞赏,即使只是了解如何计算结果矩阵的 (0,0)
条目中的 6
或 (0,1)
条目中的 12
也会非常有帮助。
nn.ConvTranspose2d
的输出空间维度由下式给出:
out = (x - 1)s - 2p + d(k - 1) + op + 1
其中x
是输入空间维度和out
相应的输出大小,s
是步幅,d
扩张,p
填充,k
内核大小,op
输出填充。
如果我们保留以下操作数:
对于输入的每个值,我们通过计算内核的每个元素的乘积来计算一个缓冲区(具有相应的颜色)。
以下是 s=1, p=0
、s=1, p=1
、s=2, p=0
和 s=2, p=1
的可视化:
s=1, p=0
:输出为3x3
对于蓝色缓冲区,我们有 (1) 2*k_top-left = 2*3 = 6
; (2) 2*k_top-right = 2*1 = 2
; (3) 2*k_bottom-left = 2*1 = 2
; (4) 2*k_bottom-right = 2*5 = 10
.
s=1, p=1
:输出为1x1
s=2, p=0
:输出为4x4
s=2, p=2
:输出为2x2
我认为让事情变得混乱的原因是他们对文档中“输入”或“输出”的含义以及术语“步幅”和“填充”的重载不是很在意。
我发现通过问自己以下问题更容易理解 PyTorch 中的转置卷积:我会给一个正常的前向卷积层提供什么参数,这样它就会给出手头的张量,我正在将其输入转置卷积层?
比如“stride”应该理解为forwardconv中的“stride”,即滑动核的移动步长。
在转置转换中,“步幅”实际上意味着不同的东西:stride-1
是进入 转置 转换的输入单元之间的交错空槽的数量层。那是因为 forward 转换中的 greater-than-1 “步幅”造成了这样的漏洞。请参见下图的说明:
该图还表明,转置卷积层中的内核移动步长始终为 1,而与“步幅”的值无关。我发现牢记这一点非常重要。
与 padding
参数类似。应该理解为应用于 forward conv 的 0-padding。由于这种填充,我们在 forward conv 的输出中得到了一些额外的单元。因此,如果我们随后将此输出输入到转置转换中,为了回到原来的 non-padded 长度,那些额外的东西应该被删除,因此等式中的 -2p
项。
请参见下图。
总之,在张量形状变换的意义上,它们的设计使得普通 conv 和转置 conv 是彼此的“逆”操作。 (但我确实认为应该改进文档。)
有了这个原则,dilation
和 output_padding
的论点也可以相对容易地计算出来。我已经写了一个blog,以防有人感兴趣。