【IA-SSD】阅读笔记与代码理解

本文介绍了IA-SSD,一种在单RTX2080Ti上达到85FPS的高效3D点云检测网络,通过极小的内存占用和并行处理实现。研究关注于前景点采样,尤其是行人检测性能的提升,并提出类别感知和质心感知采样策略。然而,网络在处理背景点和类别平衡方面仍有改进空间。
摘要由CSDN通过智能技术生成

结果:

 IA-SSD为目前最快的3d点云目标检测网络,在单个RTX 2080Ti上速度高达 85 FPS!

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

代码:https://github.com/yifanzhang713/IA-SSD            演示视频:  https://www.youtube.com/watch?v=3jP2o9KXunA

85FPS的速度是由低内存占用量和高度并行性实现的,所以不能够用在实时应用中,论文中也有比较:

“Mem”和“Paral”表示推理期间每帧的GPU内存占用量以及可以在一个RTX2080Ti(11GB)上并行化的最大批量数量。”速度⊥”, ”速度⊤” 是处理一帧或全负载GPU存储器时的推理速度,†表示将场景分成四个平行部分以加速第一采样层。

        可以看到,得益于IA-SSD极小的显存占用,在测试时可以并行处理100帧点云图像,从而做到极快的速度,但在实时应用中不会同时得到多帧点云图像,所以这里的高速度具有一定的局限性,不过IA-SSD极小的显存占用仍然是一个优点。

出发点:

通过实验发现,许多重要的前景点在最后的边界框回归步骤之前已经被丢弃了。因此其检测性能,特别是对行人等小物体的检测,受到了根本上的限制

 图示为前景点(即汽车、行人和自行车)的实例召回率

16384个点的输入点云通过4个下采样层逐渐下采样到256个点,可以看到使用D-FPS方法最后256个点的召回率在行人上降至不足70%,而文章中提出的方法仍能保持95%以上。注意,论文所提出的方法,前两层中使用的还是D-FPS,只有最后两层不同。

解决方案:

        为了尽可能地保留前景点,转向利用每个点的潜在语义,因为随着分层聚合在每一层进行操作,学习到的点特征可能包含更丰富的语义信息。基于这一思想,提出了以下两种面向任务的采样方法:

1.Class-aware Sampling  类别感知采样

该采样策略旨在学习每个点的语义,以实现选择性下采样。

两个MLP层附加到编码层以进一步估计每个点的语义类别。从原始边界框注释生成的逐点一热语义标签用于监督。

    for k in range(len(confidence_mlp)):
        shared_mlp.extend([
            nn.Conv1d(out_channels,
                      confidence_mlp[k], kernel_size=1, bias=False),
            nn.BatchNorm1d(confidence_mlp[k]),
            nn.ReLU()
        ])
         out_channels = confidence_mlp[k]
    shared_mlp.append(
         nn.Conv1d(out_channels, num_class, kernel_size=1, bias=True),
    )

(128,1024)--> (128,1024)  --> (3,1024)      1024个点,每个点为3个类别的得分

(256,512)--> (256,512)  --> (3,512)            512个点,每个点为3个类别的得分

    cls_features_max, class_pred = cls_features_tmp.max(dim=-1)
    score_pred = torch.sigmoid(cls_features_max) # B,N
    score_picked, sample_idx = torch.topk(score_pred, npoint, dim=-1)     

取得分高的前n个点

交叉熵损失计算:

2.Centroid-aware Sampling 质心感知采样

赋予更接近实例质心的点更高的权重

 f*,b*,l*,r*,u*,d*分别表示一个点到边界框的6个表面(前,后,左,右,上和下)的距离

在这种情况下,更接近盒子质心的点可能具有更高的掩模得分(最大值为1),而位于表面上的点将具有0的掩模得分。在训练期间,软点掩模将用于基于空间位置为边界框内的点分配不同的权重,因此隐含地将几何先验结合到网络训练中。

加权交叉熵损失计算:

3.Contextual Centroid Prediction  上下文质心预测

仿照VoteNet网络,预测一个与中心的偏移。质心预测损失公式如下:

 

网络结构:

         输入点云首先经过几个SA层,然后是实例感知的下采样,以逐步减少内存和计算成本。保留的代表点进一步输入上下文质心感知模块,例如中心预测和建议生成。最后输出3D边框和相关的类标签。

网络可分为两部分:

  1. backbone_3d:     IASSD_Backbone
  2. point_head :        IASSD_Head

 IASSD_Backbone有六层:

层数

采样方法

Grouping半径

点数

特征

0

D-FPS

[0.2,0.8]

4096

64

1

D-FPS

[0.8,1.6]

1024

128

2

Ctr-aware

[1.6,4.8]

512

256

3

Ctr-aware

256

256

Grouping,只从512个点中选256个前景概率高的

4

Vote

256

非采样,通过256个点的特征投票得到物体中心点,即产生新的256个点

5

[4.8, 6.4]

256

512

无采样,由vote产生的点grouping得到近邻点聚合特征

IASSD_Head:

        center_cls_preds = self.cls_center_layers(center_features)  # (total_centers, num_class)
        center_box_preds = self.box_center_layers(center_features)  # (total_centers, box_code_size)

               输出:      center_cls_preds (256,3)

                                center_box_preds (256,30)

总结思考:

        IA-SSD专注于前景点的采样,设计了一种显存占用很小,效果达到了SOTA的网络,但在每个点的语义预测时方法较简单,可能会受到类别不平衡分布的影响。

        另外,在检测中只考虑了前景点,背景点在类别和回归框预测中难道没有影响吗, 能否将部分背景点也考虑在内呢?

本人小白一枚,有不对的地方望大佬指正。

代码解读:

本文代码是基于OpenPCDet实现的,所以如果你了解OpenPCDet,那么看懂IA-SSD的代码并不难。

贴上整体的网络结构方便理解:

IASSD(

  (backbone_3d): IASSD_Backbone(
    (SA_modules): ModuleList(
      (0): PointnetSAModuleMSG_WithSampling(    #第一层网络,可对照前面的表格
        (groupers): ModuleList(
          (0): QueryAndGroup()            #最远点采样
          (1): QueryAndGroup()
        )
        (mlps): ModuleList(               #grouping后的点送入mlp
          (0): Sequential(
            (0): Conv2d(4, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(16, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(16, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
          (1): Sequential(
            (0): Conv2d(4, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(32, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
        )
        (aggregation_layer): Sequential(  #聚合两个半径grouping得到的特征
          (0): Conv1d(96, 64, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
        )
      )
      (1): PointnetSAModuleMSG_WithSampling(
        (groupers): ModuleList(
          (0): QueryAndGroup()
          (1): QueryAndGroup()
        )
        (mlps): ModuleList(
          (0): Sequential(
            (0): Conv2d(67, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
          (1): Sequential(
            (0): Conv2d(67, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(64, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(96, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
        )
        (aggregation_layer): Sequential(
          (0): Conv1d(256, 128, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
        )
        (confidence_layers): Sequential(       #预测每个点前景的概率
          (0): Conv1d(128, 128, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
          (3): Conv1d(128, 3, kernel_size=(1,), stride=(1,))
        )
      )
      (2): PointnetSAModuleMSG_WithSampling(
        (groupers): ModuleList(
          (0): QueryAndGroup()
          (1): QueryAndGroup()
        )
        (mlps): ModuleList(
          (0): Sequential(
            (0): Conv2d(131, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
          (1): Sequential(
            (0): Conv2d(131, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
        )
        (aggregation_layer): Sequential(
          (0): Conv1d(512, 256, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
        )
        (confidence_layers): Sequential(
          (0): Conv1d(256, 256, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
          (3): Conv1d(256, 3, kernel_size=(1,), stride=(1,))
        )
      )
      (3): PointnetSAModuleMSG_WithSampling(          #这层网络只选前景点概率大的256个点,
        (groupers): ModuleList()                      #不grouping
        (mlps): ModuleList()
      )
      (4): Vote_layer(                                 #预测质心
        (mlp_modules): Sequential(
          (0): Conv1d(256, 128, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
        )
        (ctr_reg): Conv1d(128, 3, kernel_size=(1,), stride=(1,))
      )
      (5): PointnetSAModuleMSG_WithSampling(
        (groupers): ModuleList(
          (0): QueryAndGroup()
          (1): QueryAndGroup()
        )
        (mlps): ModuleList(
          (0): Sequential(
            (0): Conv2d(259, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
          (1): Sequential(
            (0): Conv2d(259, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (2): ReLU()
            (3): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (4): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (5): ReLU()
            (6): Conv2d(512, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (7): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (8): ReLU()
          )
        )
        (aggregation_layer): Sequential(
          (0): Conv1d(1536, 512, kernel_size=(1,), stride=(1,), bias=False)
          (1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (2): ReLU()
        )
      )
    )
  )

  (point_head): IASSD_Head(
    (cls_loss_func): WeightedClassificationLoss()   
    (reg_loss_func): WeightedSmoothL1Loss()
    (ins_loss_func): WeightedClassificationLoss()
    (cls_center_layers): Sequential(
      (0): Linear(in_features=512, out_features=256, bias=False)
      (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU()
      (3): Linear(in_features=256, out_features=256, bias=False)
      (4): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (5): ReLU()
      (6): Linear(in_features=256, out_features=3, bias=True)
    )
    (box_center_layers): Sequential(
      (0): Linear(in_features=512, out_features=256, bias=False)
      (1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU()
      (3): Linear(in_features=256, out_features=256, bias=False)
      (4): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (5): ReLU()
      (6): Linear(in_features=256, out_features=30, bias=True)
    )
  )
)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值