PyTorch中转置卷积步长大于卷积核时的底层运行机制
转置卷积步长大于卷积核尺寸的运行逻辑
你对“步长大于核尺寸会导致像素不参与运算”的直觉只适用于普通卷积,转置卷积的步长作用逻辑和普通卷积完全不同,不存在输入像素被跳过不处理的情况。
转置卷积的固定运算流程
不管步长和卷积核的尺寸关系如何,nn.ConvTranspose2d的运算始终按3个固定步骤执行:
- 输入插零上采样:对输入特征图的相邻像素之间插入
stride - 1个零值,插零后的特征图尺寸为(H_in - 1) * stride + 1。这一步是转置卷积“上采样”的核心,步长越大,插零后的中间特征图尺寸越大。 - 边缘补零:按照卷积设置的padding参数,对插零后的特征图四周补零,默认padding=0时,会补
kernel_size - 1圈零(保证无padding时转置卷积和对应参数普通卷积的尺寸映射关系严格互逆)。 - 常规卷积运算:用指定的卷积核,以步长1、无额外padding的方式在补零后的中间特征图上滑窗做卷积,得到最终输出。
示例代码的逐过程验证
你设置的参数为:输入尺寸2×2,卷积核尺寸2,步长4,padding=0,output_padding=0,按流程计算:
- 插零阶段:相邻像素插4-1=3个零,得到尺寸为
(2-1)*4 +1 = 5×5的中间特征图,原输入的4个像素分别位于中间图的(0,0)、(0,4)、(4,0)、(4,4)坐标位,其余位置全为0。 - 补零阶段:默认补
2-1=1圈零,中间特征图尺寸变为7×7。 - 卷积阶段:用2×2卷积核做步长1的普通卷积,输出尺寸为
7 - 2 + 1 = 6×6,和代码中断言的输出尺寸完全一致。
你可以直接打印输出张量验证数值:每个原输入像素会在输出上对应生成一个2×2的加权响应块,块和块之间间隔4 - 2 = 2个像素宽度的零值区域——这些零值区域就是你误以为“没参与运算的像素”对应的位置,实际上这些位置是插零生成的,卷积核滑过这些位置时参与计算的都是零值,因此输出为0,不存在任何输入像素被跳过丢弃的情况。
补充说明
普通卷积如果设置步长大于卷积核尺寸,确实会出现部分输入像素永远不会被卷积核覆盖、直接被丢弃的情况,但这属于普通卷积下采样的正常逻辑,API不会主动报错,只是这类参数设置因为会丢失大量信息,实际场景中极少使用。
验证代码如下:
IN = 1 OUT = 1 KERNEL_SIZE = 2 proof_conv = nn.ConvTranspose2d(IN, OUT, kernel_size=KERNEL_SIZE, stride=4) assert proof_conv.weight.shape == (IN, OUT, KERNEL_SIZE, KERNEL_SIZE) FILTER = [ [1., 2.], [0., 1.] ] weights = [ [FILTER] ] weights_as_tensor = torch.from_numpy(np.asarray(weights)).float() assert weights_as_tensor.shape == proof_conv.weight.shape proof_conv.weight = nn.Parameter(weights_as_tensor) img = [[ [1., 2.], [3., 4.] ]] img_as_tensor = torch.from_numpy(np.asarray(img)).float() out_img = proof_conv(img_as_tensor) assert out_img.shape == (OUT, 6, 6)
内容的提问来源于stack exchange,提问作者Foobar
相关产品推荐
相关产品推荐

