论文模型复现

注:下有视频讲解,可供参考

引入

       在目标检测中,DenseNet表现良好,通过聚合不同感受野特征层的方式,保留了中间特征层的信息。它通过feature reuse 使得模型的大小和flops大大降低,但是,实验证明,DenseNet backbone更加耗时也增加了能耗:dense connection架构使得输入channel线性递增,导致了更多的内存访问消耗,进而导致更多的计算消耗和能耗。因此文章介绍了一种由OSA(one-shot-aggregation)模块组成的叫做VoVNet的高效体系结构。该网络在继承DenseNet的多感受野表示多种特征的优点的情况下,同时解决了密集连接效率低的问题。该网络性能优于DenseNet,而且速度也比DenseNet快2倍。 此外,VoVNet网络速度和效率也都优于ResNet,而且对于小目标检测的性能有了显著提高。

模型介绍

模型重要概念介绍 

(1)rethinking densenet

实际上,DenseNet通过密集连接来交换特征数量和特征质量。尽管DenseNet的表现证明了这种交换是有益的,但从能源和时间的角度来看,这种交换还有其他一些缺点。

  • 首先,密集的连接会导致较高的内存访问成本。因为在固定的计算量或模型参数下,当输入和输出信道尺寸相同时,MAC可以最小化。密集连接增加了输入通道大小,而输出通道大小保持不变,因此,每个层的输入和输出通道大小都不平衡。因此,DenseNet在计算量或参数相同的模型中具有较高的mac值,并且消耗更多的能量和时间。
  • 其次,密集连接引入瓶颈结构,影响了GPU并行计算的效率。当模型尺寸较大时,线性增加的输入尺寸是一个严重的问题,因为它使得整体计算相对于深度呈二次增长。为了抑制这种增长,DenseNet采用了增加1×1卷积层的瓶颈结构来保持3 × 3卷积层的输入大小不变。尽管这种方法可以减少FLOPs和参数,但同时它会损害GPU并行计算的效率。瓶颈结构将一个3 × 3卷积层分成两个较小的层,导致更多的顺序计算,从而降低了推理速度。

(2)OSA模块

  • 文章提出一次性聚合(one-shot aggregation 即OSA)模块,该模块将其特征同时聚合到最后一层。如图1所示。每个卷积层包含双向连接,一个连接到下一层以产生具有更大感受野的特征,而另一个仅聚合到最终输出特征映射中一次。
  • 与DenseNet的不同之处在于,每一层的输出并没有按路线(route)到所有后续的中间层,这使得中间层的输入大小是恒定的。这样就提高了GPU的计算效率。
  • 另外一个不同之处在于没有了密集连接,因此MAC比DenseNet小得多。
  • 此外,由于OSA模块聚集了浅层特征,它包含的层更少。因此,OSA模块被设计成只有几层,可以在GPU中高效计算。

 VoVNet复现

# 复现后的OSA模块:
class _OSA_module(nn.Layer):
    def __init__(self,
                 in_ch,
                 stage_ch,
                 concat_ch,
                 layer_per_block,
                 module_name,
                 identity=False):
        super(_OSA_module, self).__init__()

        self.identity = identity
        self.layers = nn.LayerList()
        in_channel = in_ch
        for i in range(layer_per_block):
            self.layers.append(nn.Sequential(
                *conv3x3(in_channel, stage_ch, module_name, i)))
            in_channel = stage_ch

        # feature aggregation
        in_channel = in_ch + layer_per_block * stage_ch
        self.concat = nn.Sequential(
            *(conv1x1(in_channel, concat_ch, module_name, 'concat')))

    def forward(self, x):
        identity_feat = x
        output = []
        output.append(x)
        for layer in self.layers:
            x = layer(x)
            output.append(x)

        x = paddle.concat(output, axis=1)
        xt = self.concat(x)

        if self.identity:
            xt = xt + identity_feat

        return xt


class _OSA_stage(nn.Sequential):
    def __init__(self,
                 in_ch,
                 stage_ch,
                 concat_ch,
                 block_per_stage,
                 layer_per_block,
                 stage_num):
        super(_OSA_stage, self).__init__()

        if not stage_num == 2:
            self.add_sublayer('Pooling',
                nn.MaxPool2D(kernel_size=3, stride=2, ceil_mode=True))

        module_name = f'OSA{stage_num}_1'
        self.add_sublayer(module_name,
            _OSA_module(in_ch,
                        stage_ch,
                        concat_ch,
                        layer_per_block,
                        module_name))
        for i in range(block_per_stage-1):
            module_name = f'OSA{stage_num}_{i+2}'
            self.add_sublayer(module_name,
                _OSA_module(concat_ch,
                            stage_ch,
                            concat_ch,
                            layer_per_block,
                            module_name,
                            identity=True))
# 复现后VovNet39、VovNet57、VovNet27_slim网络模型
class VoVNet(nn.Layer):
    def __init__(self,
                 config_stage_ch,
                 config_concat_ch,
                 block_per_stage,
                 layer_per_block,
                 class_num=1000):
        super(VoVNet, self).__init__()

        # Stem module
        stem = conv3x3(3,   64, 'stem', '1', 2)
        stem += conv3x3(64,  64, 'stem', '2', 1)
        stem += conv3x3(64, 128, 'stem', '3', 2)
        self.add_sublayer('stem', nn.Sequential(*stem))

        stem_out_ch = [128]
        in_ch_list = stem_out_ch + config_concat_ch[:-1]
        self.stage_names = []
        for i in range(4): #num_stages
            name = 'stage%d' % (i+2)
            self.stage_names.append(name)
            self.add_sublayer(name,
                            _OSA_stage(in_ch_list[i],
                                       config_stage_ch[i],
                                       config_concat_ch[i],
                                       block_per_stage[i],
                                       layer_per_block,
                                       i+2))

        self.classifier = nn.Linear(config_concat_ch[-1], class_num)

        for m in self.sublayers():
            if isinstance(m, nn.Conv2D):
                # nn.init.kaiming_normal_(m.weight)
                kaiming_normal_(m.weight)
            elif isinstance(m, (nn.BatchNorm2D, nn.GroupNorm)):
                # nn.init.constant_(m.weight, 1)
                # nn.init.constant_(m.bias, 0)
                ones_(m.weight)
                zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                # nn.init.constant_(m.bias, 0)
                zeros_(m.bias)

    def forward(self, x):
        x = self.stem(x)
        for name in self.stage_names:
            x = getattr(self, name)(x)
        x = F.adaptive_avg_pool2d(x, (1, 1)).flatten(1)
        x = self.classifier(x)
        return x



def _load_pretrained(pretrained, model, model_url="", use_ssld=False):
    if pretrained is False:
        pass
    elif pretrained is True:
        load_dygraph_pretrain_from_url(model, model_url, use_ssld=use_ssld)
    elif isinstance(pretrained, str):
        load_dygraph_pretrain(model, pretrained)
    else:
        raise RuntimeError(
            "pretrained type is not available. Please use `string` or `boolean` type."
        )

def _vovnet(arch,
            config_stage_ch,
            config_concat_ch,
            block_per_stage,
            layer_per_block,
            pretrained,
            progress,
            **kwargs):
    model = VoVNet(config_stage_ch, config_concat_ch,
                   block_per_stage, layer_per_block,
                   **kwargs)
    if pretrained:
        _load_pretrained(pretrained, model)
    return model


def vovnet57(pretrained=False, progress=True, **kwargs):
    r"""Constructs a VoVNet-57 model as described in
    `"An Energy and GPU-Computation Efficient Backbone Networks"
    <https://arxiv.org/abs/1904.09730>`_.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    return _vovnet('vovnet57', [128, 160, 192, 224], [256, 512, 768, 1024],
                    [1,1,4,3], 5, pretrained, progress, **kwargs)


def vovnet39(pretrained=False, progress=True, **kwargs):
    r"""Constructs a VoVNet-39 model as described in
    `"An Energy and GPU-Computation Efficient Backbone Networks"
    <https://arxiv.org/abs/1904.09730>`_.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    return _vovnet('vovnet39', [128, 160, 192, 224], [256, 512, 768, 1024],
                    [1,1,2,2], 5, pretrained, progress, **kwargs)


def vovnet27_slim(pretrained=False, progress=True, **kwargs):
    r"""Constructs a VoVNet-39 model as described in
    `"An Energy and GPU-Computation Efficient Backbone Networks"
    <https://arxiv.org/abs/1904.09730>`_.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    return _vovnet('vovnet27_slim', [64, 80, 96, 112], [128, 256, 384, 512],
                    [1,1,1,1], 5, pretrained, progress, **kwargs)

 分享视频

论文模型复现

分享人:李龙

分享时间:2022/5/24

分享平台:腾讯会议

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值