Google的Resizer缩放模块个人理解及相关实现代码

论文地址https://arxiv.org/abs/2103.09950

论文中指出,你的模型效果不好可能是因为你的resize方法不行,因而提出了带有学习参数的resizer模块。

本人基于cifar-10数据集做实验,后发现当输入为极小分辨率的图像(32*32)时,采用resizer模块进行上采样可以有较好效果(相比于cv2的resize根据相邻值添加冗余特征,resizer可以获得更多有效的特征)。当图像较大时,效果并不明显。感兴趣的可以自行实验。

个人理解:

由于cifar-10数据集的图像尺寸都是32x32,而ImageNet预训练模型大多都是基于224x224尺寸训练的结果。因此,实验中如果图像尺寸选择32x进行训练,即使训练了很长的迭代数,最终效果还是不如224x的。尽管32x到224x的缩放是通过双线性插值实现的,理论上扩大尺寸的同时添加了很多的冗余特征,但仍然可以获得更高的实验精度。于是开始探索,能否通过包含可学习参数的网络层来实现上采样?效果会不会更好呢?

而谷歌恰好针对resize在视觉任务上的作用发表了一篇论文:Learning to Resize Images for Computer Vision Tasks。里面的做法是把较大的原图先resize到448x448,然后再通过论文提出的resizer模块缩放到224x224,最终的实验效果会比直接把原图resize到224x224好很多。

于是在backbone网络前也加入了论文中的resizer模块,不同的是,论文中用来进行下采样,此处用来进行上采样。实验中测试了多种上采样尺寸,但更大的尺寸意味着只能使用更小的batchsize以及更大的训练开销,因此最终选择了上采样到224x224。

实验精度大概提高了0.5%(97.72%->98.17%)。感兴趣的读者可以将此模块简单地加入到自己的网络中进行测试,resizer的输出结果接上原始模型的backbone即可~

import torch
import torch.nn as nn
import torch.nn.functional as F
from functools import partial


class ResBlock(nn.Module):
    def __init__(self, channel_size: int, negative_slope: float = 0.2):
        super().__init__()
        self.block = nn.Sequential(
            nn.Conv2d(channel_size, channel_size, kernel_size=3, padding=1,
                      bias=False),
            nn.BatchNorm2d(channel_size),
            nn.LeakyReLU(negative_slope, inplace=True),
            nn.Conv2d(channel_size, channel_size, kernel_size=3, padding=1,
                      bias=False),
            nn.BatchNorm2d(channel_size)
        )

    def forward(self, x):
        return x + self.block(x)


class Resizer(nn.Module):
    """
    scale_factor:缩放因子,output_shape = input_shape * scale_factor
    """
    def __init__(self, scale_factor=7):
        super().__init__()
        self.interpolate_mode = "bilinear"
        self.scale_factor = scale_factor

        n = 16  # 卷积通道数
        r = 2  # ResBlock数量,网络深度
        slope = 0.2

        self.module1 = nn.Sequential(
            nn.Conv2d(3, n, kernel_size=7, padding=3),
            nn.LeakyReLU(slope, inplace=True),
            nn.Conv2d(n, n, kernel_size=1),
            nn.LeakyReLU(slope, inplace=True),
            nn.BatchNorm2d(n)
        )

        resblocks = []
        for i in range(r):
            resblocks.append(ResBlock(n, slope))
        self.resblocks = nn.Sequential(*resblocks)

        self.module3 = nn.Sequential(
            nn.Conv2d(n, n, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(n)
        )

        self.module4 = nn.Conv2d(n, 3, kernel_size=7,
                                 padding=3)

        self.interpolate = partial(F.interpolate,
                                   scale_factor=self.scale_factor,
                                   mode=self.interpolate_mode,
                                   align_corners=False,
                                   recompute_scale_factor=False)

    def forward(self, x):
        residual = self.interpolate(x)
        out = self.module1(x)
        out_residual = self.interpolate(out)
        out = self.resblocks(out_residual)
        out = self.module3(out)
        out = out + out_residual
        out = self.module4(out)
        out = out + residual
        return out

下面是一个使用demo~

class ResizerEfficientnet(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.resizer_model = Resizer()
        self.backbone = efficientnet.efficientnetb0()

    def forward(self, x):
        x = self.resizer_model(x)
        x = self.backbone(x)
        return x

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值