pytorch 上采样 upsample 时align_corners 设为true 还是false

本文通过示例对比了PyTorch中上采样(Upsampling)使用align_corners参数为True与False的区别。展示了不同设置下bilinear模式的具体输出,并解释了两种情况下像素插值的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

今天遇到了一个问题, 在写上采样 upsample的时候到底 align_corners 设为true 还是false

talk is cheap

按照pytorch doc 中的示例:

>>> input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
>>> input
tensor([[[[ 1.,  2.],
          [ 3.,  4.]]]])

>>> m = nn.Upsample(scale_factor=2, mode='nearest')
>>> m(input)
tensor([[[[ 1.,  1.,  2.,  2.],
          [ 1.,  1.,  2.,  2.],
          [ 3.,  3.,  4.,  4.],
          [ 3.,  3.,  4.,  4.]]]])

>>> m = nn.Upsample(scale_factor=2, mode='bilinear')  # align_corners=False
>>> m(input)
tensor([[[[ 1.0000,  1.2500,  1.7500,  2.0000],
          [ 1.5000,  1.7500,  2.2500,  2.5000],
          [ 2.5000,  2.7500,  3.2500,  3.5000],
          [ 3.0000,  3.2500,  3.7500,  4.0000]]]])

>>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
>>> m(input)
tensor([[[[ 1.0000,  1.3333,  1.6667,  2.0000],
          [ 1.6667,  2.0000,  2.3333,  2.6667],
          [ 2.3333,  2.6667,  3.0000,  3.3333],
          [ 3.0000,  3.3333,  3.6667,  4.0000]]]])

>>> # Try scaling the same data in a larger tensor
>>>
>>> input_3x3 = torch.zeros(3, 3).view(1, 1, 3, 3)
>>> input_3x3[:, :, :2, :2].copy_(input)
tensor([[[[ 1.,  2.],
          [ 3.,  4.]]]])
>>> input_3x3
tensor([[[[ 1.,  2.,  0.],
          [ 3.,  4.,  0.],
          [ 0.,  0.,  0.]]]])

>>> m = nn.Upsample(scale_factor=2, mode='bilinear')  # align_corners=False
>>> # Notice that values in top left corner are the same with the small input (except at boundary)
>>> m(input_3x3)
tensor([[[[ 1.0000,  1.2500,  1.7500,  1.5000,  0.5000,  0.0000],
          [ 1.5000,  1.7500,  2.2500,  1.8750,  0.6250,  0.0000],
          [ 2.5000,  2.7500,  3.2500,  2.6250,  0.8750,  0.0000],
          [ 2.2500,  2.4375,  2.8125,  2.2500,  0.7500,  0.0000],
          [ 0.7500,  0.8125,  0.9375,  0.7500,  0.2500,  0.0000],
          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]]]])

>>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
>>> # Notice that values in top left corner are now changed
>>> m(input_3x3)
tensor([[[[ 1.0000,  1.4000,  1.8000,  1.6000,  0.8000,  0.0000],
          [ 1.8000,  2.2000,  2.6000,  2.2400,  1.1200,  0.0000],
          [ 2.6000,  3.0000,  3.4000,  2.8800,  1.4400,  0.0000],
          [ 2.4000,  2.7200,  3.0400,  2.5600,  1.2800,  0.0000],
          [ 1.2000,  1.3600,  1.5200,  1.2800,  0.6400,  0.0000],
          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]]]])

具体区别呢?
查找pytorch论坛得图
在这里插入图片描述

  • 当**align_corners = True**时,像素被视为网格的格子上的点,拐角处的像素对齐.可知是点之间是等间距的
  • 当**align_corners = False**时, 像素被视为网格的交叉线上的点, 拐角处的点依然是原图像的拐角像素,但是差值的点间却按照上图的取法取,导致点与点之间是不等距的
<think>嗯,用户让我帮忙修改CycleGAN解码器中的上采样部分,把原来的转置卷积换成双线性上采样加普通卷积的组合。之前用户提到过生成的图像边缘有黑色边框和角落的黑点,这可能是因为转置卷积导致的棋盘效应。现在用户想用双线性上采样来替代,应该怎么做呢? 首先,我需要理解用户提供的代码结构。解码器的每个上采样层(up1到up4)目前使用的是转置卷积(ConvTranspose2d),而用户希望改用Upsample层和普通卷积。用户还给出了一些被注释掉的代码,其中原本用的是Upsample和Conv2d的组合,但后来换成了转置卷积。现在需要恢复或调整这些部分。 我需要检查每个上采样层的输入输出通道数是否正确。比如,在up1中,原来的转置卷积是输入512,输出256,kernel_size=4,stride=2,padding=1。如果换成双线性上采样,那么上采样的倍数是2倍,所以Upsample的scale_factor应该是2,mode='bilinear',然后接一个普通卷积层,输入512,输出256,kernel_size=3,padding=1。不过用户原来的注释里,up1的Conv2d是输入512到256,但后面可能还有跳跃连接,需要确保通道数匹配。 接下来,需要确保每个上采样步骤后的通道数正确,特别是在有跳跃连接的情况下。例如,up3的输入是256,输出是64,但注释里提到输入256=128(来自up2的输出)加上128(来自d2的跳跃连接)。因此,在替换为双线性上采样和卷积,可能需要调整输入通道数,确保跳跃连接后的拼接操作正确。 另外,用户之前的回答中提到过转置卷积可能导致棋盘效应,而双线性上采样加卷积可以减少这种问题。因此,修改后的代码应该避免使用转置卷积,转而使用Upsample和Conv2d的组合,同保持InstanceNorm和ReLU层不变。 需要注意每个上采样层的输入和输出通道是否正确,特别是当有跳跃连接,输入的通道数可能因为拼接而翻倍。例如,up2原来的转置卷积输入是512,输出128,但如果有跳跃连接的话,可能需要调整输入通道数为拼接后的结果。比如,如果up1的输出是256,然后与编码器中对应层的特征图拼接,假设编码器中的特征图是256通道,那么up2的输入应该是256(来自up1) + 256(跳跃连接)=512,这候up2的卷积层输入应该是512,输出128。但用户原来的代码中,up2的转置卷积输入是512,输出128,所以可能跳跃连接部分在代码的其他地方处理了通道数,需要确保修改后的代码也处理了这一点。 同样地,up3的转置卷积输入是256,输出64,而注释里提到输入是128(up2的输出)+128(d2)=256,所以修改后的up3的卷积层输入应该是256,输出64。而up4的输入是128(up3的输出)+64(d1)=192?但原代码中的转置卷积输入是128,输出3。这里可能需要进一步确认,但根据用户提供的代码,up4的转置卷积输入是128,输出3,所以如果用双线性上采样和卷积的话,输入应该是128,输出3,kernel_size=3,padding=1,最后接Tanh激活函数。 另外,用户提到在替换转置卷积需要注意反卷积的参数是否和双线性上采样加卷积的参数一致。例如,转置卷积的kernel_size=4,stride=2,padding=1,这样的参数在转置卷积中会导致输出尺寸翻倍。而双线性上采样的scale_factor=2也会将尺寸翻倍,之后的卷积使用kernel_size=3,padding=1,stride=1,这样整体的尺寸变化和转置卷积是相同的。 最后,要确保修改后的代码中所有上采样层都正确替换,并且通道数和跳跃连接正确拼接。可能需要逐层检查每个up模块的输入输出,确保与跳跃连接的特征图拼接后的通道数正确。</think>以下是针对解码器中上采样部分的修改建议,将原本的转置卷积(`ConvTranspose2d`)替换为**双线性上采样+普通卷积**组合,并确保通道数适配跳跃连接(Skip Connection): --- ### **修改后的解码器代码(PyTorch实现)** ```python # 解码器(调整通道数适应拼接) self.up1 = nn.Sequential( # 替换转置卷积:上采样2倍 + 普通卷积 nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False), nn.Conv2d(512, 256, kernel_size=3, padding=1), # 输入512 → 输出256 nn.InstanceNorm2d(256), nn.ReLU(inplace=True) ) self.up2 = nn.Sequential( nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False), nn.Conv2d(512, 128, kernel_size=3, padding=1), # 输入512 = 256(up1输出) + 256(跳跃连接) nn.InstanceNorm2d(128), nn.ReLU(inplace=True) ) self.up3 = nn.Sequential( nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False), nn.Conv2d(256, 64, kernel_size=3, padding=1), # 输入256 = 128(up2输出) + 128(跳跃连接) nn.InstanceNorm2d(64), nn.ReLU(inplace=True) ) self.up4 = nn.Sequential( nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False), nn.Conv2d(128, 3, kernel_size=3, padding=1), # 输入128 = 64(up3输出) + 64(跳跃连接) nn.Tanh() ) ``` --- ### **关键修改说明** #### **1. 替换转置卷积为双线性上采样** - **原始问题**:`ConvTranspose2d`容易产生棋盘效应(规律性黑点),尤其是当输出通道数不是输入通道数的整数倍。 - **修改方案**: - 使用`nn.Upsample(scale_factor=2, mode='bilinear')`实现2倍上采样。 - 紧跟普通卷积`nn.Conv2d`调整通道数(例如`512→256`)。 #### **2. 适配跳跃连接的通道数** - **跳跃连接拼接规则**: - `up2`输入通道数应为`256(up1输出) + 256(编码器对应层特征) = 512`,因此`up2`的卷积层输入设为512。 - 同理,`up3`输入为`128(up2输出) + 128(编码器特征) = 256`,`up4`输入为`64(up3输出) + 64(编码器特征) = 128`。 - **注意**:若编码器特征通道数与解码器不一致,需通过`1x1卷积`调整通道数后再拼接。 #### **3. 保持归一化和激活函数** - 保留`InstanceNorm2d`和`ReLU`,确保与原始模型训练稳定性一致。 - 最后一层使用`Tanh`将输出值约束到[-1,1],避免溢出导致的异常像素。 --- ### **修改前后对比** | 层名 | 原方案(转置卷积) | 新方案(双线性上采样+卷积) | 输入→输出通道 | |------|--------------------|---------------------------|-------------| | `up1` | `ConvTranspose2d(512→256)` | `Upsample + Conv2d(512→256)` | 512 → 256 | | `up2` | `ConvTranspose2d(512→128)` | `Upsample + Conv2d(512→128)` | 512 → 128 | | `up3` | `ConvTranspose2d(256→64)` | `Upsample + Conv2d(256→64)` | 256 → 64 | | `up4` | `ConvTranspose2d(128→3)` | `Upsample + Conv2d(128→3)` | 128 → 3 | --- ### **补充建议** 1. **对齐跳跃连接特征**: - 若编码器特征图通道数与解码器不匹配,可在拼接前添加`1x1卷积`对齐通道: ```python # 示例:对齐编码器特征通道数 self.skip_conv = nn.Conv2d(encoder_channels, decoder_channels, kernel_size=1) ``` 2. **减少上采样伪影**: - 在`Upsample`前加入反射填充(`nn.ReflectionPad2d(1)`),进一步平滑边缘。 3. **验证尺寸一致性**: - 使用`print(x.shape)`逐层检查特征图尺寸,确保上采样后与跳跃连接的特征图尺寸一致。 通过以上修改,可显著减少因转置卷积导致的边缘黑边和角落黑点问题,同保持生成图像的自然过渡。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值