PANet(CVPR 2018)原理与代码解析

这篇具有很好参考价值的文章主要介绍了PANet(CVPR 2018)原理与代码解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

paper:Path Aggregation Network for Instance Segmentation

official implementation:GitHub - ShuLiu1993/PANet: PANet for Instance Segmentation and Object Detection 

third party implementation:mmdetection/pafpn.py at master · open-mmlab/mmdetection · GitHub 

前言

信息在神经网络中的传播方式是非常重要的。本文提出的路径聚合网络(Path Aggregation Network, PANet)旨在促进proposal-based实例分割框架中的信息流动。具体来说,通过自底向上的路径增强,利用底层中精确的定位信息来增强整个特征层次,缩短了下层与最上层之间的信息路径。本文还提出了自适应特征池化(adaptive feature pooling),它将特征网格和所有层级的特征连接起来,使每个特征层级上有用的信息直接传播到后面的proposal subnetworks。此外还增加了一个互补分支来为每个proposal捕获不同视野的信息以进一步提升掩膜预测的效果。 

基于上述优化,本文提出的PANet在COCO 2017实例分割任务中获得第一,在目标检测任务中获得第二。

本文的贡献

  1. bottom-up path augmentation
  2. adaptive feature pooling
  3. fully-connected feature fusion

其中前两点既可用在实例分割任务中,也可用在目标检测任务中,对这两个任务的性能提升都有很大的帮助。

方法介绍

Bottom-up Path Aggregation

在神经网络中,高层包含更丰富的语义信息,低层包含更丰富的细节信息,因此通过一个top-down augmenting path将语义较强的特征信息传播到所有特征层级中,从而增强所有特征的语义分类能力是很有必要的,这也是FPN中做的事情。

在实例分割任务中,需要精确的识别出物体的边缘,浅层网络中包含了大量边缘等细节特征,对实例分割任务非常有用。而在传统的CNN和FPN中,浅层的信息传播到顶层需要很长的路径甚至要经过上百层,这会造成很多细节信息的丢失,因此作者新增了一条bottom-up的增强路径,如下图(b)所示,这条路径不到10层,起到shortcut的作用,可以保留更多的细节信息。

panet代码,Object Detection,Instance Segmentation,深度学习,人工智能,目标检测,实例分割,特征融合

具体的实现比较简单和FPN类似,一个大分辨率的特征图 \(N_{i}\) 经过一个stride=2的3x3卷积层减小spatial size后和FPN中的 \(P_{i+1}\) 层的分辨率大小相等,将两者进行element-wise add后再经过一个3x3卷积就得到了 \(N_{i+1}\),如下图所示

panet代码,Object Detection,Instance Segmentation,深度学习,人工智能,目标检测,实例分割,特征融合

Adaptive Feature Pooling

在FPN中,proposals根据大小分配到不同的feature level,将小的proposal分配到较低的特征层,将大的proposal分配到较高的特征层。虽然简单有效,但结果可能不是最优的,比如两个大小只有10个像素差异的proposal有可能被分配到不同的特征层级,即使它们非常相似。

此外,特征的重要性和它所属的层级可能没有很强的关联性。High-level的特征由大的感受野生成并且提取了更丰富的上下文信息,让小的proposal获取这些特征可以更好的利用有用的上下文信息来进行预测。同样,low-level的特征包含丰富的细节信息以及更好的定位精度,让大的proposal获取这些信息显然也是有益的。因此,作者提出对于每个proposal,提取所有层级的特征并进行融合,然后基于此融合特征进行后续的分类和回归。 

实现过程如图(1)(c)所示,对于每个proposal,首先将它映射到所有的特征层级,如图(1)(b)中的灰色区域。然后延续Mask R-CNN的做法,对每个特征层级进行ROIAlign操作,接着对不同层级的feature grid进行融合(element-wise max or sum)。

具体实现中,pooled feature grids首先分别经过一个参数层后,然后再进行融合,使网络能够适应特征。例如FPN中的box分支有两个fc层,我们在第一个fc层后进行融合操作。Mask R-CNN中的mask预测分支有四个卷积层,我们在第一个和第二个卷积层之间进行融合操作。下图是box分支上adaptive feature pooling

panet代码,Object Detection,Instance Segmentation,深度学习,人工智能,目标检测,实例分割,特征融合

Fully-connected Fusion

原始的Mask R-CNN中,mask预测分支是FCN的结构,因为全连接层可以提取到和卷积层不同的信息,作者在mask预测分支新增了一个fc分支,结构如下。

panet代码,Object Detection,Instance Segmentation,深度学习,人工智能,目标检测,实例分割,特征融合

具体而言,在原始的FCN的第三个卷积后,增加了一个分支,首先经过两个3x3卷积,为了减少计算量第二个卷积的输出通道减半,然后经过一个全连接层,这个fc层是用来预测一个class-agnostic前景/背景mask。因为最终mask的大小为28x28,因此这里fc层输出一个784x1x1的向量,然后reshape成28x28大小的特征图。最后,FCN分支预测的每个类别的的mask都与fc分支预测的前景/背景mask相加,得到最终的输出mask。

代码解析

在YOLO v4以及后面的版本中大都用到了PANet,但那里面的PANet特指neck中的bottom-up path augmentation结构,因此这里只解析一下这部分的代码。下面是mmdetection中的实现,其中输入batch_size=2,预处理后input_shape=(2, 3, 300, 300)。backbone为resnet-50,输入图片经过backbone后进入neck也就是这里的PAFPN中,代码进行了一些注释,主要是打印了一些操作以及一些中间输出结果。

bottom-up path和原始的top-down FPN的处理过程比较相似只不过方向相反,但具体实现也有一些区别:

  1. FPN中有lateral_conv,即对于backbone中的C2~C5首先分别经过1x1卷积将输出通道统一为256,然后再进行top-down特征融合。而bottom-up path中没有这个lateral_conv,直接用P2~P5,比如N2就是P2。
  2. FPN中不同层级特征图融合需要先对齐spatial size,具体通过最近邻插值进行上采样。而bottom-up path是通过stride=2的3x3卷积进行下采样。

最后的输出除了N2~N5,上面还多加了一层N6,通过对N5进行stride=2的max pooling得到。而在FPN中增加一个P6,比如在retinanet中是通过stride=2的3x3卷积得到的,并且卷积的输入也不一定是P5,有'on_input', 'on_lateral', 'on_output'这几种选择,见下面的代码。文章来源地址https://www.toymoban.com/news/detail-548048.html

# Copyright (c) OpenMMLab. All rights reserved.
import torch.nn as nn
import torch.nn.functional as F
from mmcv.cnn import ConvModule
from mmcv.runner import auto_fp16

from ..builder import NECKS
from .fpn import FPN


@NECKS.register_module()
class PAFPN(FPN):
    """Path Aggregation Network for Instance Segmentation.

    This is an implementation of the `PAFPN in Path Aggregation Network
    <https://arxiv.org/abs/1803.01534>`_.

    Args:
        in_channels (List[int]): Number of input channels per scale.
        out_channels (int): Number of output channels (used at each scale)
        num_outs (int): Number of output scales.
        start_level (int): Index of the start input backbone level used to
            build the feature pyramid. Default: 0.
        end_level (int): Index of the end input backbone level (exclusive) to
            build the feature pyramid. Default: -1, which means the last level.
        add_extra_convs (bool | str): If bool, it decides whether to add conv
            layers on top of the original feature maps. Default to False.
            If True, it is equivalent to `add_extra_convs='on_input'`.
            If str, it specifies the source feature map of the extra convs.
            Only the following options are allowed

            - 'on_input': Last feat map of neck inputs (i.e. backbone feature).
            - 'on_lateral':  Last feature map after lateral convs.
            - 'on_output': The last output feature map after fpn convs.
        relu_before_extra_convs (bool): Whether to apply relu before the extra
            conv. Default: False.
        no_norm_on_lateral (bool): Whether to apply norm on lateral.
            Default: False.
        conv_cfg (dict): Config dict for convolution layer. Default: None.
        norm_cfg (dict): Config dict for normalization layer. Default: None.
        act_cfg (str): Config dict for activation layer in ConvModule.
            Default: None.
        init_cfg (dict or list[dict], optional): Initialization config dict.
    """

    def __init__(self,
                 in_channels,
                 out_channels,
                 num_outs,
                 start_level=0,
                 end_level=-1,
                 add_extra_convs=False,
                 relu_before_extra_convs=False,
                 no_norm_on_lateral=False,
                 conv_cfg=None,
                 norm_cfg=None,
                 act_cfg=None,
                 init_cfg=dict(
                     type='Xavier', layer='Conv2d', distribution='uniform')):
        super(PAFPN, self).__init__(
            in_channels,
            out_channels,
            num_outs,
            start_level,
            end_level,
            add_extra_convs,
            relu_before_extra_convs,
            no_norm_on_lateral,
            conv_cfg,
            norm_cfg,
            act_cfg,
            init_cfg=init_cfg)
        # add extra bottom up pathway
        self.downsample_convs = nn.ModuleList()
        self.pafpn_convs = nn.ModuleList()
        for i in range(self.start_level + 1, self.backbone_end_level):
            d_conv = ConvModule(
                out_channels,
                out_channels,
                3,
                stride=2,
                padding=1,
                conv_cfg=conv_cfg,
                norm_cfg=norm_cfg,
                act_cfg=act_cfg,
                inplace=False)
            pafpn_conv = ConvModule(
                out_channels,
                out_channels,
                3,
                padding=1,
                conv_cfg=conv_cfg,
                norm_cfg=norm_cfg,
                act_cfg=act_cfg,
                inplace=False)
            self.downsample_convs.append(d_conv)
            self.pafpn_convs.append(pafpn_conv)

    @auto_fp16()
    def forward(self, inputs):
        # [torch.Size([2, 256, 75, 75])
        #  torch.Size([2, 512, 38, 38])
        #  torch.Size([2, 1024, 19, 19])
        #  torch.Size([2, 2048, 10, 10])]
        """Forward function."""
        assert len(inputs) == len(self.in_channels)

        # build laterals
        laterals = [
            lateral_conv(inputs[i + self.start_level])  # 0
            for i, lateral_conv in enumerate(self.lateral_convs)
        ]
        # print(self.lateral_convs)
        # ModuleList(
        #   (0): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1))
        #   )
        #   (1): ConvModule(
        #     (conv): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
        #   )
        #   (2): ConvModule(
        #     (conv): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
        #   )
        #   (3): ConvModule(
        #     (conv): Conv2d(2048, 256, kernel_size=(1, 1), stride=(1, 1))
        #   )
        # )
        # print(laterals)
        # [torch.Size([2, 256, 75, 75])
        #  torch.Size([2, 256, 38, 38])
        #  torch.Size([2, 256, 19, 19])
        #  torch.Size([2, 256, 10, 10])]

        # build top-down path
        used_backbone_levels = len(laterals)  # 4
        for i in range(used_backbone_levels - 1, 0, -1):
            prev_shape = laterals[i - 1].shape[2:]
            # fix runtime error of "+=" inplace operation in PyTorch 1.10
            laterals[i - 1] = laterals[i - 1] + F.interpolate(
                laterals[i], size=prev_shape, mode='nearest')

        # build outputs
        # part 1: from original levels
        inter_outs = [
            self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)
        ]
        # print(self.fpn_convs)
        # ModuleList(
        #   (0): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        #   (1): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        #   (2): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        #   (3): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        # )

        # print(self.downsample_convs)
        # ModuleList(
        #   (0): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        #   )
        #   (1): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        #   )
        #   (2): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        #   )
        # )

        # part 2: add bottom-up path
        for i in range(0, used_backbone_levels - 1):
            inter_outs[i + 1] += self.downsample_convs[i](inter_outs[i])

        # print(self.pafpn_convs)
        # ModuleList(
        #   (0): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        #   (1): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        #   (2): ConvModule(
        #     (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        #   )
        # )
        outs = []
        outs.append(inter_outs[0])
        outs.extend([
            self.pafpn_convs[i - 1](inter_outs[i])
            for i in range(1, used_backbone_levels)
        ])

        # part 3: add extra levels
        if self.num_outs > len(outs):  # 5,4
            # use max pool to get more levels on top of outputs
            # (e.g., Faster R-CNN, Mask R-CNN)
            if not self.add_extra_convs:  # False
                for i in range(self.num_outs - used_backbone_levels):
                    outs.append(F.max_pool2d(outs[-1], 1, stride=2))
            # add conv layers on top of original feature maps (RetinaNet)
            else:
                if self.add_extra_convs == 'on_input':
                    orig = inputs[self.backbone_end_level - 1]
                    outs.append(self.fpn_convs[used_backbone_levels](orig))
                elif self.add_extra_convs == 'on_lateral':
                    outs.append(self.fpn_convs[used_backbone_levels](
                        laterals[-1]))
                elif self.add_extra_convs == 'on_output':
                    outs.append(self.fpn_convs[used_backbone_levels](outs[-1]))
                else:
                    raise NotImplementedError
                for i in range(used_backbone_levels + 1, self.num_outs):
                    if self.relu_before_extra_convs:
                        outs.append(self.fpn_convs[i](F.relu(outs[-1])))
                    else:
                        outs.append(self.fpn_convs[i](outs[-1]))
        # print(outs)
        # [torch.Size([2, 256, 75, 75])
        #  torch.Size([2, 256, 38, 38])
        #  torch.Size([2, 256, 19, 19])
        #  torch.Size([2, 256, 10, 10])]
        return tuple(outs)

到了这里,关于PANet(CVPR 2018)原理与代码解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Incremental Object Detection via Meta-Learning【论文解析】

    摘要:在真实世界的情境中,目标检测器可能会不断遇到来自新类别的物体实例。当现有的目标检测器应用于这种情景时,它们对旧类别的性能会显著下降。已经有一些努力来解决这个限制,它们都应用了知识蒸馏的变体来避免灾难性遗忘。然而,我们注意到,尽管蒸馏有助

    2024年02月06日
    浏览(39)
  • End-to-End Object Detection with Transformers(论文解析)

    我们提出了一种将目标检测视为直接集合预测问题的新方法。我们的方法简化了检测流程,有效地消除了许多手工设计的组件的需求,如显式编码我们关于任务的先验知识的非极大值抑制过程或锚点生成。新框架的主要要素,称为DEtection TRansformer或DETR,包括一个基于集合的全

    2024年02月09日
    浏览(45)
  • 3D Object Detection经典论文整理【分类/下载/代码/笔记】

    3D Object Detection经典论文整理 【分类/下载/代码/笔记】 W.P. Xiao, Visual group, SHUSV 版本 更新时间 更新内容 作者 1 V 1.0 2021.12 论文分类整理 W.P. Xiao 2 V 1.0.1 2022.1.7 更新code链接 Y.Q. Wu 目录   笔者分类整理了与三维目标检测有关的经典论文和部分最新论文的PDF下载链接、开源代码

    2023年04月27日
    浏览(42)
  • 【代码解读】RRNet: A Hybrid Detector for Object Detection in Drone-captured Images

    首先我们将代码从GitHub上下载下来:代码地址 找到程序的主入口train.py这个类,可以看到这个类比较简单,大部分是引用其他类。具体每一个类的定义可以从不同小节中查看 2.1 init函数 首先来看这个类的初始化函数 2.2 train函数 2.3 dist_training_process函数 来看一下 init_operator 函

    2024年02月14日
    浏览(40)
  • 【CVPR2018 3D ResNet】3D ResNet网络结构详解

    3D ResNet系列网络由日本国家工业科学技术研究院的Kensho Hara等人提出。接下来,我将对3D ResNet系列网络做出详细的网络结构解释,欢迎大家补充与提问。 我的github链接主页为XuecWu (Conna) · GitHub

    2024年02月12日
    浏览(42)
  • CVPR 2018 | Spotlight论文:单摄像头数秒构建3D人体模型

    想把自己的身体形象投射进电子游戏里?现在已经是很容易的事了。人工智能算法此前已被广泛应用于虚拟现实头像、监视、服装试穿或电影等多种任务的人体建模上,但大多数方法需要特殊的照相设备来检测景深,或从多个角度探查人体。近日,来自德国布伦瑞克工业大学

    2024年02月06日
    浏览(42)
  • Ultra-Fast-Lane-Detection代码解析

    近期由于学习要求,复现成功Ultra-Fast-Lane-Detection代码后,记录下自己在原理上的学习笔记,本人刚接触这块,有不对的地方欢迎指正。 代码链接:GitHub - cfzd/Ultra-Fast-Lane-Detection: Ultra Fast Structure-aware Deep Lane Detection (ECCV 2020) 论文创新点: 使用全局特征的基于行的选择问题,提

    2024年02月13日
    浏览(35)
  • (CVPR 2018) 3D Semantic Segmentation with Submanifold Sparse Convolutional Networks

    卷积网络是分析图像、视频和3D形状等时空数据的事实标准。虽然其中一些数据自然密集(例如照片),但许多其他数据源本质上是稀疏的。示例包括使用LiDAR扫描仪或RGB-D相机获得的3D点云。当应用于此类稀疏数据时,卷积网络的标准“密集”实现非常低效。我们引入了新的

    2023年04月08日
    浏览(50)
  • 解决 远程 服务器--Microsoft Windows CredSSP 远程执行代码漏洞(CVE-2018-0886)【原理扫描】(KB4103725 或 KB4103715)

    系统: windows server 2012 R2 standard 扫描出漏洞: Microsoft Windows CredSSP 远程执行代码漏洞(CVE-2018-0886)【原理扫描】 按照微软官方给的答案: https://portal.msrc.microsoft.com/zh-CN/security-guidance/advisory/CVE-2018-0886,对应下载安装包是 KB4103725 或 KB4103715 但是下载后安装 报错: 此更新不适用此

    2024年02月06日
    浏览(69)
  • 目标检测【Object Detection】

    目标检测是计算机视觉中的一个重要问题,它的目的是从图像或视频序列中识别出特定的目标,并将其从背景中分离出来。目标检测的任务包括: 检测出图像或视频序列中的目标,例如人、车辆、动物等。 对目标进行分类,例如将人分为人类、车辆、动物等。 确定目标的位

    2023年04月14日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包