(七)转置卷积

本文详细介绍了转置卷积的概念,包括其在语义分割任务中的应用,以及如何通过numpy和PyTorch实现。讨论了stride、padding、dilation和多通道对输出尺寸的影响,并通过实例展示了计算过程。
部署运行你感兴趣的模型镜像

欢迎访问个人网络日志🌹🌹知行空间🌹🌹


1.基础情况介绍

在语义分割任务使用的网络结构中,除了使用卷积神经网络进行降采样,为了恢复图像的大小,还需要使用转置卷积。转置卷积被称为transposed convolutionfractionally-strided convolution也有些地方称为deconvolution。转置卷积只是恢复的feature map的空间尺寸并不等同于卷积的逆运算,所以称之为deconvolution是不合适的。

为了简化介绍,假设通道channle=1,对于 n h × n ω n_h\times n_\omega nh×nω的输入和 k h × k ω k_h \times k_\omega kh×kω的卷积核,对于stride=1, padding=0的转置卷积,其计算过程为,将 k h × k ω k_h \times k_\omega kh×kω的卷积核在的输入的每一行每一列上滑动,得到 n h n ω n_h n_\omega nhnω个中间结果,每个中间结果的空间尺寸为 ( n h + k h − 1 ) × ( n ω + k ω − 1 ) (n_h + k_h - 1) \times (n_\omega + k_\omega - 1) (nh+kh1)×(nω+kω1),其值初时化为0,然后对于输入中的每个元素,与卷积核相乘得到 k h × k ω k_h \times k_\omega kh×kω的结果替换掉 ( n h + k h − 1 ) × ( n ω + k ω − 1 ) (n_h + k_h - 1) \times (n_\omega + k_\omega - 1) (nh+kh1)×(nω+kω1)矩阵中的对应部分,最后将所有的中间结果相加得到最后的输出

在这里插入图片描述

使用numpy数组实现上述过程为:

import numpy as np
X = np.array([[0.0, 1.0], [2.0, 3.0]])
K = np.array([[0.0, 1.0], [2.0, 3.0]])
def trans_conv(X, K):
    K = K.reshape((2,2))
    X = X.reshape((2, 2))
    h, w = X.shape
    kh, kw = K.shape
    o = np.zeros((h + kh - 1, w + kw - 1))
    for i in range(h):
        for j in range(w):
            o[i : i + h, j : j + w] += X[i][j] * K
    return o
    
trans_conv(X, K)
"""output
array([[ 0.,  0.,  1.],
       [ 0.,  4.,  6.],
       [ 4., 12.,  9.]])
"""

可见程序的输出与上图计算过程的结果一致。

借用pytorch中的ConvTranspose2d实现的计算的过程为:

import torch
import torch.nn as nn

X = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
tconv.weight.data = K
tconv(X)
"""output
tensor([[[[ 0.,  0.,  1.],
          [ 0.,  4.,  6.],
          [ 4., 12.,  9.]]]], grad_fn=<SlowConvTranspose2DBackward>)
"""

可见这三个地方计算的结果是一致的。

2.使用paddding/stride/dilation/多通道时

2.1 stride

stride表示的是卷积核在输出张量上滑动的步长,前面的例子中使用的是stride=1的场景,在使用stride时,输出张量的空间尺寸 o h o_h oh o ω o_\omega oω的计算方式为 o h = ( n h − 1 ) × s t r i d e + ( k h − 1 ) + 1 o_h = (n_h - 1)\times stride + (k_h - 1) + 1 oh=(nh1)×stride+(kh1)+1, 如下图:

在这里插入图片描述

如上图中 n h = n ω = 3 , k h = k ω = 2 n_h=n_\omega =3, k_h=k_\omega = 2 nh=nω=3,kh=kω=2,则 o h = o ω = 6 o_h=o_\omega = 6 oh=oω=6

使用pytorch验证上述计算过程,则

X = torch.range(0, 8).reshape((1, 1, 3,3))
K = torch.range(0, 3).reshape((1, 1, 2,2))
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, stride=2, bias=False)
tconv.weight.data = K
Y = tconv(X)
print(Y.shape)
print(Y)

"""output
torch.Size([1, 1, 6, 6])
tensor([[[[ 0.,  0.,  0.,  1.,  0.,  2.],
          [ 0.,  0.,  2.,  3.,  4.,  6.],
          [ 0.,  3.,  0.,  4.,  0.,  5.],
          [ 6.,  9.,  8., 12., 10., 15.],
          [ 0.,  6.,  0.,  7.,  0.,  8.],
          [12., 18., 14., 21., 16., 24.]]]],
       grad_fn=<SlowConvTranspose2DBackward>)
"""

2.2 padding

padding是在transpose convolution计算过程中作用在输出张量上面的,因此计算结束后需要裁剪掉padding的大小。

如 (2.1)[###2.1 stride]中的例子,假设padding=1,则输出张量的最外围一圈将被裁剪掉,如下图:

在这里插入图片描述

此时
o h = ( n h − 1 ) × s t r i d e + ( k h − 1 ) + 1 − 2 × p a d d i n g o_h = (n_h - 1)\times stride + (k_h - 1) + 1 - 2\times padding oh=(nh1)×stride+(kh1)+12×padding
使用pytorch ConvTranspose2d API时,只需要加入padding=1参数即可,

X = torch.range(0, 8).reshape((1, 1, 3,3))
K = torch.range(0, 3).reshape((1, 1, 2,2))
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, padding=1, stride=2, bias=False)
tconv.weight.data = K
Y = tconv(X)
print(Y.shape)
print(Y)
"""output
torch.Size([1, 1, 4, 4])
tensor([[[[ 0.,  2.,  3.,  4.],
          [ 3.,  0.,  4.,  0.],
          [ 9.,  8., 12., 10.],
          [ 6.,  0.,  7.,  0.]]]], grad_fn=<SlowConvTranspose2DBackward>)
"""

此外还有output_padding,这个是对转置卷积后输出的张量进行的扩充,这会增大输出张量的尺寸,不过pytorch ConvTranspose2d API的实现只对输出张量的一边(H,W较大的那一边)进行了扩充,因此其对输出张量空间尺寸的影响为:

o h = ( n h − 1 ) × s t r i d e + ( k h − 1 ) + 1 − 2 × p a d d i n g + o u t p u t _ p a d d i n g o_h = (n_h - 1)\times stride + (k_h - 1) + 1 - 2\times padding + output\_padding oh=(nh1)×stride+(kh1)+12×padding+output_padding

2.3 dilation

dilation即扩张卷积,卷积核中存在空洞,因此也称空洞卷积。如下图是dilation=2dilated convolution

在这里插入图片描述

Transposed Convolution中使用dilation时,输出的空间尺寸计算可参考下图:

在这里插入图片描述

由上图可得:

o h = ( n h − 1 ) × s t r i d e + d i l a t i o n × ( k h − 1 ) + 1 − 2 × p a d d i n g o_h = (n_h - 1)\times stride + dilation\times (k_h - 1) + 1 - 2\times padding oh=(nh1)×stride+dilation×(kh1)+12×padding

o h = 2 × 2 + 2 × 1 + 1 = 7 o_h = 2 \times 2 + 2 \times 1 + 1 = 7 oh=2×2+2×1+1=7

使用pytorch验证上图中的例子,可得

X = torch.range(0, 8).reshape((1, 1, 3,3))
K = torch.range(0, 3).reshape((1, 1, 2,2))
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, dilation=2, stride=2, bias=False)
tconv.weight.data = K
Y = tconv(X)
print(Y.shape)
print(Y)

"""output
torch.Size([1, 1, 7, 7])
tensor([[[[ 0.,  0.,  0.,  0.,  1.,  0.,  2.],
          [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
          [ 0.,  0.,  5.,  0., 11.,  0., 11.],
          [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
          [ 6.,  0., 23.,  0., 29.,  0., 23.],
          [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
          [12.,  0., 32.,  0., 37.,  0., 24.]]]],
       grad_fn=<SlowConvTranspose2DBackward>)
"""

综上,对于由padding/stride/dilation/output_padding时,输出张量的计算方式为:

o h = ( n h − 1 ) × s t r i d e + d i l a t i o n × ( k h − 1 ) + 1 − 2 × p a d d i n g + o u t p u t _ p a d d i n g o_h = (n_h - 1)\times stride + dilation\times(k_h - 1) + 1 - 2\times padding + output\_padding oh=(nh1)×stride+dilation×(kh1)+12×padding+output_padding

2.4 multi-channles

同常规的卷积操作,转置卷积对多通道的处理,同样是将每个卷积核在输入张量的各个维度上进行卷积,然后在将各个维度上的卷积结果相加求和即可。

如下代码示例,可见最后的输出正是两个通道上卷积结果求和所得:

X = torch.tensor([[[0.0, 1.0], [2.0, 3.0]],[[0.0, 1.0], [2.0, 3.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]],[[0.0, 1.0], [2.0, 3.0]]])
X, K = X.reshape(1, 2, 2, 2), K.reshape(2, 1, 2, 2)
tconv = nn.ConvTranspose2d(2, 1, kernel_size=2, bias=False)
tconv.weight.data = K
tconv(X)
"""output
tensor([[[[ 0.,  0.,  2.],
          [ 0.,  8., 12.],
          [ 8., 24., 18.]]]], grad_fn=<SlowConvTranspose2DBackward>)
"""

参考资料


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

<think>好的,我现在需要分析用户提供的第二个U-net代码片段,并判断其是否完整。首先,用户已经发送了两次代码,第一次是数据集的Dataloader实现,第二次是模型结构的一部分。用户要求最终四次代码发送完后整体评估,但当前是第二次,我需要根据已有信息进行分析。 首先,回顾第一次的代码:用户定义了一个RemoteSensingDataset类,处理数据加载,但缺少一些辅助函数如read_tif、resize_pair和binarize_label。这部分属于数据预处理,与模型结构无关,所以可能后续不会影响模型完整性的判断,但需要用户确认这些函数是否存在。 第二次的代码用户发送了DoubleConv类的定义,这是一个包含两次卷积、BN和ReLU的模块。同时,用户提到下采样使用maxpool,上采样用转置卷积和跳跃连接,最后一层用1x1卷积加sigmoid。但代码中只实现了DoubleConv,没有看到下采样、上采样或跳跃连接的具体结构。用户提供的注释说明了下采样和上采样的方式,但没有实际代码。例如,下采样部分可能需要一个包含maxpool和DoubleConv的模块,而解码器部分需要转置卷积和跳跃连接的实现。此外,最后的1x1卷积层也没有在代码中体现。 接下来,我需要确定这些部分是否是U-net的必要组成部分。U-net的标准结构包括编码器(下采样)、解码器(上采样)、跳跃连接以及最后的分类层。当前的DoubleConv是编码器和解码器中重复使用的块,但仅此不足以构成完整的U-net。因此,用户还需要定义下采样模块(如Down类,包含maxpool和DoubleConv)、上采样模块(如Up类,使用转置卷积并处理跳跃连接),以及整个U-net的骨架,将各个部分组合起来,包括跳跃连接的拼接操作。此外,最终的1x1卷积层和激活函数也是必须的,否则模型无法输出正确的概率图。 另外,用户提到的上采样使用转置卷积,这需要注意代码中是否有相应的转置卷积层定义,比如nn.ConvTranspose2d。同时,跳跃连接需要将编码器部分的特征图与解码器部分的上采样结果进行拼接,这通常在forward方法中实现,需要确保通道数匹配,可能需要进行裁剪或填充。 当前存在的缺失包括:下采样模块、上采样模块、跳跃连接的具体实现、U-net的整体结构类,以及最终的输出层。需要用户后续提供这些部分的代码才能判断模型是否完整。此外,损失函数和优化器虽然在模型结构之外,但也是完整训练流程的一部分,可能用户会在后续代码中提供。 总结,当前的代码片段只实现了U-net中的基本卷积块,但缺少了模型的核心结构和连接机制,需要进一步补充才能构成完整的U-net模型。</think>根据第二次发送的代码片段,当前模型组件分析如下: **已实现核心模块**: 1. 基础卷积块 `DoubleConv`(符合U-net设计) $$ \text{结构:Conv2d(3x3)} \rightarrow \text{BN} \rightarrow \text{ReLU} \rightarrow \text{Conv2d(3x3)} \rightarrow \text{BN} \rightarrow \text{ReLU} $$ **关键进展**: - ✅ 完成特征提取基础单元 - ✅ 明确采用maxpool下采样策略(注释说明) - ✅ 声明将使用转置卷积上采样(注释说明) **当前缺失结构**(需后续代码验证): ```python # 下采样模块示例(需实现) class Down(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.mpconv = nn.Sequential( nn.MaxPool2d(2), # 下采样层 DoubleConv(in_ch, out_ch) # 双卷积) # 上采样模块示例(需实现) class Up(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.up = nn.ConvTranspose2d(in_ch, in_ch//2, 2, stride=2) # 转置卷积 self.conv = DoubleConv(in_ch, out_ch) # 需处理跳跃连接拼接 # 模型主体结构(需实现) class UNet(nn.Module): def __init__(self): super().__init__() # 应包含编码器、解码器、跳跃连接的完整拓扑 # 最终输出层(需实现) self.final = nn.Sequential( nn.Conv2d(64, 1, kernel_size=1), # 1x1卷积 nn.Sigmoid() # 二值化输出 ) ``` **待验证机制**: 1. 跳跃连接中特征图拼接(concat操作) 2. 转置卷积与maxpool的尺寸匹配 3. 通道数变化的连贯性(典型U-net通道数变化:64→128→256→512→256→128→64→1) 当前进度:已完成基础构件,但未形成完整编解码结构。建议继续发送后续代码以验证模型拓扑完整性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值