【深度学习】【CVPR2020】Revisiting the Sibling Head in Object Detector(TSD)

TSD : task-aware spatial disentanglement
代码地址 : https://github.com/Sense-X/TSD

简要

该论文提出的方法取得OpenImage Object Detection Challenge 2019 冠军。

  • 数据集
    OpenImages Challenge 2019 目标检测数据集,是 OpenImages V5数据集的一个子集,有174万图片,1460万个bbox和500个类别(5个level,每个level下的类别都有隶属关系)。

  • 出发点
    相比于分类任务,目标检测任务多了一个回归的分支,这两分支几乎共享相同的参数,但其实是有冲突的。IoU-Net里面发现“高的分类分数的location经常预测出不好的bbox”。为了解决这个问题,就再多加一个分支来预测IOU作为定位置信度,但是不对齐的问题仍然存在。(因为提取的特征都是对于一个点,这个点对应到原图不一定就符合“显著的地区用来分类,边缘的地区用来回归”)。Double-Head R-CNN也可以被认为是多加了一个分支,但是因为两个分支的特征是同一个proposal的RoI特征,问题依然存在。所以需要在空间上分解分类和局部化的梯度流(spatially disentangle the gradient flows of classification and localization.)

本文结合代码来阅读(实际上是看了论文我也不太懂)

代码运行

预备(可跳过)

遇到很多问题应该是版本不兼容,按照https://github.com/Sense-X/TSD/blob/master/docs/INSTALL.md最终Python==3.7,PyTorch1.1错误就消失了。

先复习一下RPN的结构。RPN的提出代替了SS,使候选区域提取的时间开销几乎降为0。/mmdet/models/anchor_heads/rpn_head.py

在这里插入图片描述
由于使用了FPN(c2,c3,c4,c5 — p2,p3,p4,p5,p6,采样率分别是4,8,16,32,64)。这里不使用stage1的输出是因为占内存。ResNet-FPN作为RPN输入的feature map是p2,p3,p4,p5,p6,而作为后续Fast RCNN的输入则是p2,p3,p4,p5,使用p6是因为想获得更大的anchor尺度512×512。接下来的问题就是为生成的proposals(Fast RCNN的输入)选择哪层feature map来得到ROI区域。
在这里插入图片描述
k0是scale=224的ROI所选取的层,RetinaNet论文设为4。也就是224尺度的大小属于p4。

而在实际代码里(mmdet/models/roi_extractors/single_level.py)做了一点变化:

def map_roi_levels(self, rois, num_levels):
        """Map rois to corresponding feature levels by scales.

        - scale < finest_scale * 2: level 0
        - finest_scale * 2 <= scale < finest_scale * 4: level 1
        - finest_scale * 4 <= scale < finest_scale * 8: level 2
        - scale >= finest_scale * 8: level 3

        Args:
            rois (Tensor): Input RoIs of all batch, shape (k, 5). index = 0 which batch_img
            num_levels (int): Total level number.

        Returns:
            Tensor: Level index (0-based) of each RoI, shape (k, )
        """
        scale = torch.sqrt(
            (rois[:, 3] - rois[:, 1] + 1) * (rois[:, 4] - rois[:, 2] + 1))
        target_lvls = torch.floor(torch.log2(scale / self.finest_scale + 1e-6))
        target_lvls = target_lvls.clamp(min=0, max=num_levels - 1).long()
        return target_lvls

self.finest_scale = 56,也就是p2的尺度是56左右,假设生成的ROI尺度分别有32, 64, 128, 256, 512,那么分配的level分别是0,0,1,2,3,也就是p2,p2,p3,p4,p5。

cls分支如果损失函数使用sigmoid的话,通道数要除去背景,通道就是1,也就是binary_cross_entropy,先经过sigmoid函数在进行BCE损失计算。否则就是cross_entroy,此时标签代表属于0或1哪一类,通道就是2。

生成anchor的过程先生成该层特征图对应stride,对应scale(实际上乘以8,这样每个特征图预测的大小scale分别是32, 64, 128, 256, 512)的三个ratio对应的偏差坐标,再加上特征图上个点对应原图的坐标。

# 生成一层的anchor
def grid_anchors(self, featmap_size, stride=16, device='cuda'):
        base_anchors = self.base_anchors.to(device) 
        # 三个ratio下的base anchor(求出该特征图特定stride下, 之后再乘以8)的xmin,ymin,xmax, ymax 
        # 这样每个特征图预测的大小scale分别是32, 64, 128, 256, 512
        feat_h, feat_w = featmap_size
        shift_x = torch.arange(0, feat_w, device=device) * stride # 对应到原图 
        shift_y = torch.arange(0, feat_h, device=device) * stride
        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) 
        shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1)
        '''
        tensor([[   0,    0,    0,    0],
        [   4,    0,    4,    0],
        [   8,    0,    8,    0],
        ...,
        [ 788, 1212,  788, 1212],
        [ 792, 1212,  792, 1212],
        [ 796, 1212,  796, 1212]], device='cuda:0')

        '''
        shifts &#
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值