理解ConvTranspose2d操作

理解ConvTranspose2d操作

ConvTranspose2d是一种常用的可以对图像进行上采样的方法,可以用于扩大图像尺寸。其本质上也是一个卷积操作,目的是恢复对应的卷积参数下,卷积前的原始图像大小。文章ConvTranspose2d原理,深度网络如何进行上采样?搭配了动图介绍了其计算过程,比较直观。这里通过代码的方式对该模块的输入输出以及计算过程进行解释。 首先,文章分析了该模块的输入输出,然后通过自定义的ConvTranspose2d模块,解释了算法的计算过程。

输入与输出

对于输入通道为 ( N , C i n , H i n , W i n ) (N, C_{in}, H_{in}, W_{in}) (N,Cin,Hin,Win)的图像,模型的输出为 ( N , C o u t , H o u t , W o u t ) (N, C_{out}, H_{out}, W_{out}) (N,Cout,Hout,Wout)

其中:
H o u t = ( H i n − 1 ) × stride [ 0 ] − 2 × padding [ 0 ] + dilation [ 0 ] × ( kernel_size [ 0 ] − 1 ) + output_padding [ 0 ] + 1 H_{out} = (H_{in} - 1) \times \text{stride}[0] - 2 \times \text{padding}[0] + \text{dilation}[0] \times (\text{kernel\_size}[0] - 1) + \text{output\_padding}[0] + 1 Hout=(Hin1)×stride[0]2×padding[0]+dilation[0]×(kernel_size[0]1)+output_padding[0]+1

W o u t = ( W i n − 1 ) × stride [ 1 ] − 2 × padding [ 1 ] + dilation [ 1 ] × ( kernel_size [ 1 ] − 1 ) + output_padding [ 1 ] + 1 W_{out} = (W_{in} - 1) \times \text{stride}[1] - 2 \times \text{padding}[1] + \text{dilation}[1] \times (\text{kernel\_size}[1] - 1) + \text{output\_padding}[1] + 1 Wout=(Win1)×stride[1]2×padding[1]+dilation[1]×(kernel_size[1]1)+output_padding[1]+1

这里,为了使得模型更加简洁,我们不关心dialationoutput_shadding参数。因而可以写作
H o u t = ( H i n − 1 ) × stride [ 0 ] − 2 × padding [ 0 ] + kernel_size [ 0 ] H_{out} = (H_{in} - 1) \times \text{stride}[0] - 2 \times \text{padding}[0] + \text{kernel\_size}[0] Hout=(Hin1)×stride[0]2×padding[0]+kernel_size[0]

W o u t = ( W i n − 1 ) × stride [ 1 ] − 2 × padding [ 1 ] + kernel_size [ 1 ] W_{out} = (W_{in} - 1) \times \text{stride}[1] - 2 \times \text{padding}[1] + \text{kernel\_size}[1] Wout=(Win1)×stride[1]2×padding[1]+kernel_size[1]

可以联想一下,卷积操作的输入和输出关系
H o u t = [ H i n − k e r n e l _ s i z e [ 0 ] + 2 ∗ p a d d l i n g [ 0 ] s t r i d e [ 0 ] ] + 1 H_{out} = \left[\frac{H_{in}-kernel\_size[0] + 2*paddling[0]}{stride[0]}\right] + 1 Hout=[stride[0]Hinkernel_size[0]+2paddling[0]]+1
可以看出,二者的大小形状完全是可逆的关系,通过代码进行说明

import torch 
from torch import nn 

conv = nn.Conv2d(3, 5, 5, padding=1)
tconv = nn.ConvTranspose2d(5, 3, 5, padding=1)

input = torch.randn((1, 3, 7, 9))
output = conv(input)

print('输入的维度', input.shape) # (1, 3, 7, 9)
print('卷积后的维度', output.shape) # 进行变换 (1, 5, 5, 7)

tinput = tconv(output)

print('经过逆卷积后的维度', tinput.shape) #->(1, 3, 7, 9) 与原始图像input的维度相同

计算过程详解

ConvTranspose2d原理,深度网络如何进行上采样?搭配了动图解释计算过程,比较直观。这里通过自写的模块Mytranspose2d来具体说明计算过程,并与标准模块的计算结果进行了对比,可以搭配着看,更好的理解。

模块定义如下:

class MyTranspose2d(nn.Module):
    # 这里为了简单起见,kernel_size限制为int 类型,对应的kernel大小为(kernel_size, kernel_size)
    # 模型只是为了说明前馈计算的流程,不考虑效率和易用性
    def __init__(self, inchannel, outchannel, kernel_size, padding=0, stride=1):
        super().__init__()
        self.weight = nn.Parameter(torch.zeros((inchannel, outchannel, kernel_size, kernel_size)))
        self.bias = nn.Parameter(torch.zeros((outchannel, )))
        self.padding = padding
        self.stride = stride
        self.outchannel = outchannel
        self.inchannel = inchannel
        self.F = kernel_size

    def forward(self, input):
        # input的维度为 N, C, H, W
        N, C, H, W = input.shape 
        assert(C == self.inchannel)
        Co, Ho, Wo = self.outchannel, (H-1)*self.stride-2*self.padding + self.F, (W-1)*self.stride-2*self.padding + self.F

        output = torch.zeros((N, Co, Ho, Wo))

        # 对输入进行补0,方便后续进行卷积操作
        padding_input = torch.zeros((N, C, (H-1)*self.stride-1+2*self.F-2*self.padding, (W-1)*self.stride-1+2*self.F-2*self.padding))
        for i in range(N):
            for c in range(C):
                for j in range(H):
                    for k in range(W):
                        jp = self.F-1-self.padding + j * self.stride
                        kp = self.F-1-self.padding + k * self.stride
                        padding_input[i, c, jp, kp] = input[i, c, j, k]
        

        for i in range(N):
            for c in range(Co):
                for j in range(Ho):
                    for k in range(Wo):
                        jf = j + self.F
                        kf = k + self.F
                        # 由于是卷积,所以这里要Flip一下
                        output[i, c, j, k] = (self.weight[:, c, :, :].flip([1, 2]) * padding_input[i, :, j:jf, k:kf]).sum() + self.bias[c]
        return output 

计算结果的比较


# 标准的数据输入

inchannel = 1
outchanel = 1
kernel_size = 5
padding = 2
stride = 2

x = torch.randn((2, inchannel, 2, 2)) # 输入

convt = nn.ConvTranspose2d(inchannel, outchanel, kernel_size, padding=padding, stride=stride)
convm = MyTranspose2d(inchannel, outchanel, kernel_size, padding=padding, stride=stride)

# 确保二者的权重参数一致
convm.weight.data = convt.weight.data.clone().detach()
convm.bias.data = convt.bias.data.clone().detach()

# 计算模型的输出
out1 = convt(x)
out2 = convm(x)


err = out1 - out2
print(err.abs().max()) # 2.98e-8

与标准库模型的计算结果最大误差为2.98e-8,说明计算结果是正确的。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值