【单目3D目标检测】GUPNet论文精读与代码解析

news2024/12/24 9:09:11

文章目录

  • Preface
  • Abstract
  • Contributions
  • Pipeline
    • Backbone
    • Neck
    • Head
    • Loss
  • GUP
    • In Paper
    • In Code
  • HTL
    • In Paper
    • In Code
  • Refernece

Preface

Lu Y, Ma X, Yang L, et al. Geometry uncertainty projection network for monocular 3d object detection[C]. Proceedings of the IEEE/CVF International Conference on Computer Vision. 2021: 3111-3121.
Paper
Code

Abstract

在单目3D目标检测中,几何先验可以帮助深度推理,其中广泛使用的先验是透视投影模型。现有的投影模型方法通常是先估计2D和3D边界框的高度,然后通过投影公式 d e p t h = h 3 d ⋅ f / h 2 d depth =h_{3d}·f / h_{2d} depth=h3df/h2d f f f为摄像机焦距)推断出深度。由该公式推导出的深度与估计的2D/3D高度高度相关,因此高度估计的误差也会反映在估计的深度上。但是,高度估计的误差是不可避免的,特别是在3D高度估计不佳的情况下。因此,本文更关注由3D高度估计误差引起的深度推断误差。作者通过实验发现,3D高度的轻微偏差(0.1米)可能导致投影深度的显著偏移(甚至4米)。这种误差放大效应使得基于投影的方法的输出难以控制,显著影响了推理的可靠性和训练效率。因此,本文提出了一个包含几何不确定性投影(Geometry Uncertainty Projection,GUP)模块和分层任务学习(Hierarchical Task Learning,HTL)策略的几何不确定性投影网络来处理这些问题

 

Contributions

  • 本文主要针对投影模型中的误差放大(Error Amplification)现象进行了两方面改进:
  • GUP:3D高度估计质量的微小变化会引起深度估计质量的较大变化,这使得模型难以预测可靠的不确定性或置信度,导致输出不可控。为了解决这一问题,本文提出了GUP模块,根据分布形式而不是离散值来推断深度
  • HTL:在训练阶段初期,2D/3D高度估计容易产生噪声,误差会被放大,导致深度估计过高。这样会误导网络的训练过程,导致最终性能的下降。为了解决训练的不稳定性,我们提出了HTL策略,目的是确保只有在所有的前置任务(如3D高度估计是深度估计的前置任务之一)都训练好了的情况下,才能训练每个任务
     

Pipeline

输入图像首先经过backbone(基于CenterNet)提取出2D的bounding box(输出一个2D heatmap、2D box的长宽和位置修正量),然后该bounding box经过ROI Align后提取出ROI特征,该特征会与3D坐标系进行concatenate从而获得最终的ROI特征,所有的3D信息推断均会在此ROI特征上进行。本文首先估计出3D box除了depth以外的所有参数。然后2D与3D bounding box的高度将被输入到GUP模块中提取出最终的depth,训练阶段HTL将会对每个部分进行控制从而实现multi-task learning

完整的Pipeline在gupnet\code\lib\models\gupnet.py函数的GUPNet类中的__init__函数中定义

Backbone

从代码中config.yaml配置文件可以看到,GUPNet默认使用的Backbone为DLA34

model:
  type: 'gupnet'
  backbone: 'dla34'
  neck: 'DLAUp'

我们可以在gupnet\code\lib\models\gupnet.py函数的GUPNet类中的forward函数第一行加断点来进行debug,查看完整的Backbone信息:

在这里插入图片描述
Backbone输出的6层feature map:

LevelOut-ChannelHeightWidth
0163841280
132192640
26496320
312848160
42562480
55121240

Neck

同理,从config.yaml配置文件可以看到,GUPNet默认使用的Neck为DLAUP

model:
  type: 'gupnet'
  backbone: 'dla34'
  neck: 'DLAUp'

同样在debug中查看,注意这里的变量名为self.feat_up

在这里插入图片描述
网络前向推断时,将Backbone输出的后4层feature map喂入Neck,最终输出最后一层feature map作为整个网络的输出:

>> feat.shape
>> torch.Size([B, 64, 96, 320])

Head

gupnet\code\lib\models\gupnet.py函数的GUPNet类中的__init__函数定义了两大类Head:

  • 2D 检测输出,和CenterNet一样,包括三部分:
    • heatmap:所有类别的中心点(默认类别为3)
    • offset_2d:2D框的偏移量
    • size_2d: 2D框的宽高
  • 这一部分直接在Neck输出的feature map上预测得到
# initialize the head of pipeline, according to heads setting.
self.heatmap = nn.Sequential(
    nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
    nn.ReLU(inplace=True),
    nn.Conv2d(self.head_conv, 3, kernel_size=1, stride=1, padding=0, bias=True))
self.offset_2d = nn.Sequential(
    nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
    nn.ReLU(inplace=True),
    nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
self.size_2d = nn.Sequential(
    nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
    nn.ReLU(inplace=True),
    nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
  • 3D 检测输出,包括四部分:
    • depth:输出2列信息,第一列为深度值,第二列为深度学习偏差(对数平方的形式)
    • offset_3d:3D框在2D图像上的偏移量
    • size_3d: 输出4列信息,前三列为3D框的长宽高,第四列为3D高度的偏差(对数平方的形式)
    • heading:角度angle预测输出,将其划分为12份,分别预测12个类别分类输出(是哪一类),和12个回归预测输出(具体是多少)
  • 这一部分不是直接在Neck输出的feature map上预测得到的,而是经过ROI Align后提取出ROI特征,该特征会再与3D coord_ranges坐标系进行concatenate从而获得最终的ROI特征,所有的3D信息推断均会在此ROI特征上进行
self.depth = nn.Sequential(
    nn.Conv2d(channels[self.first_level] + 2 + self.cls_num, self.head_conv, kernel_size=3, padding=1,
              bias=True),
    nn.BatchNorm2d(self.head_conv),
    nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d(1),
    nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
self.offset_3d = nn.Sequential(
    nn.Conv2d(channels[self.first_level] + 2 + self.cls_num, self.head_conv, kernel_size=3, padding=1,
              bias=True),
    nn.BatchNorm2d(self.head_conv),
    nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d(1),
    nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
self.size_3d = nn.Sequential(
    nn.Conv2d(channels[self.first_level] + 2 + self.cls_num, self.head_conv, kernel_size=3, padding=1,
              bias=True),
    nn.BatchNorm2d(self.head_conv),
    nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d(1),
    nn.Conv2d(self.head_conv, 4, kernel_size=1, stride=1, padding=0, bias=True))
self.heading = nn.Sequential(
    nn.Conv2d(channels[self.first_level] + 2 + self.cls_num, self.head_conv, kernel_size=3, padding=1,
              bias=True),
    nn.BatchNorm2d(self.head_conv),
    nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d(1),
    nn.Conv2d(self.head_conv, 24, kernel_size=1, stride=1, padding=0, bias=True))

Loss

各个head的损失函数如下:

  • heatmap:Focal Loss
  • offset2s:L1 Loss
  • size2d:L1 Loss
  • size3d:长和宽为L1 Loss,占2/3,3D 高为laplacian_aleatoric_uncertainty_loss(),占1/3
  • offset3d:L1 Loss
  • depthlaplacian_aleatoric_uncertainty_loss()
  • heading:cls为CE,reg为L1 Loss
     

GUP

In Paper

由于误差放大效应的存在,使得获取高质量得分变得非常困难。其本质就是因为投影过程对于uncertainty regression部分而言是不可知(agnostic)的,其没有直接参与到投影过程的计算中,因此使得不确定度的估计质量不高。为了实现对depth进行更好的uncertainty的估计,本文认为把投影过程体现在uncertainty的计算过程中尤为重要。因此本文采用基于概率模型的方法对uncertainty的估计同样引入投影先验

  1. 首先假设投影过程中的 h 3 d h_{3d} h3d 是拉普拉斯分布 L a ( μ h , λ h ) La(\mu_h,\lambda_h) La(μh,λh) ,也即 X ∼ L a ( μ , λ ) , f X ( x ) = 1 2 λ exp ⁡ ( − ∣ x − μ ∣ λ ) X \sim L a(\mu, \lambda), f_X(x)=\frac{1}{2 \lambda} \exp \left(-\frac{|x-\mu|}{\lambda}\right) XLa(μ,λ),fX(x)=2λ1exp(λxμ),此时 h 3 d h_{3d} h3d的损失函数可定义为:
    L h 3 d = 2 σ h ∣ ∣ μ h − h 3 d g t ∣ ∣ + log ⁡ ( σ h ) \mathcal{L}_{h_{3d}}=\frac{\sqrt{2}}{\sigma_h}\left||\mu_h-h_{3d}^{g t}|\right|+\log \left(\sigma_h\right) Lh3d=σh2 μhh3dgt+log(σh)

  2. 将其代入投影模型,可计算出depth为:
    d p = f ⋅ h 3 d h 2 d = f ⋅ ( λ h ⋅ X + μ h ) h 2 d = f ⋅ λ h h 2 d ⋅ X + f ⋅ μ h h 2 d \begin{aligned} d_p &=\frac{f \cdot h_{3 d}}{h_{2 d}}=\frac{f \cdot\left(\lambda_h \cdot X+\mu_h\right)}{h_{2 d}} \\ &=\frac{f \cdot \lambda_h}{h_{2 d}} \cdot X+\frac{f \cdot \mu_h}{h_{2 d}} \end{aligned} dp=h2dfh3d=h2df(λhX+μh)=h2dfλhX+h2dfμh
    其中 X X X是一个归一化的拉普拉斯分布 L a ( 0 , 1 ) La(0,1) La(0,1)。可以看到深度估计值 d p d_p dp的期望 μ p \mu_p μp和标准差 σ p \sigma_p σp分别为 f ⋅ μ h h 2 d \frac{f · \mu_h}{h_{2d}} h2dfμh f ⋅ σ h h 2 d \frac{f · \sigma_h}{h_{2d}} h2dfσh,其中 σ h \sigma_h σh是拉普拉斯分布 L a ( μ h , λ h ) La(\mu_h,\lambda_h) La(μh,λh)对应的标准差( σ h 2 = 2 λ h 2 \sigma_h^2=2\lambda_h^2 σh2=2λh2

  3. 对于结果 d p d_p dp而言,均值 μ p \mu_p μp对应投影depth结果,而 σ p \sigma_p σp则反应了投影不确定度。在此基础上,为了更精准的depth输出,本文额外让神经网络预测出一个depth的修正值(depth bias),本文假设该修正值也是拉普拉斯分布 L a ( μ b , λ b ) La(\mu_b,\lambda_b) La(μb,λb),因此最终depth则变成:
    d = L a ( μ p , σ p ) + L a ( μ b , σ b ) μ d = μ p + μ b , σ d = ( σ p ) 2 + ( σ b ) 2 . \begin{gathered} d=L a\left(\mu_p, \sigma_p\right)+L a\left(\mu_b, \sigma_b\right) \\ \mu_d=\mu_p+\mu_b, \quad \sigma_d=\sqrt{\left(\sigma_p\right)^2+\left(\sigma_b\right)^2} . \end{gathered} d=La(μp,σp)+La(μb,σb)μd=μp+μb,σd=(σp)2+(σb)2 .
    那么这时输出端的不确定度就同时反应了投影模型放大的输入端的不确定性以及网络bias引入的不确定度,称为基于几何的不确定度(Geometry based Uncertainty,GeU)

  4. 为了优化最终深度分布,本文应用了不确定性回归损失:
    L depth  = 2 σ d ∣ ∣ μ d − d g t ∣ ∣ + log ⁡ ( σ d ) \mathcal{L}_{\text {depth }}=\frac{\sqrt{2}}{\sigma_d}\left||\mu_d-d^{g t}|\right|+\log \left(\sigma_d\right) Ldepth =σd2 μddgt+log(σd)
    为了简化起见,假定深度分布属于拉普拉斯分布。整体损失会使投影结果更接近GT,梯度同时影响深度偏差、 h 2 d h_{2d} h2d h 3 d h_{3d} h3d。此外,在优化过程中还训练了三维高度和深度偏差的不确定性

  5. 为了获得最终得分,本文进一步将深度的不确定性 σ d \sigma_d σd将其映射为0 ~ 1之间的值,通过指数函数表示深度的不确定性-置信度(Uncertainty-Confidence,UnC):
    p d e p t h = exp ⁡ ( − σ d ) p_{depth}=\exp \left(-\sigma_d\right) pdepth=exp(σd)
    它可以为每个投影深度提供更精确的置信度

  6. 最终的推理得分可以计算为:
    p 3 d = p 3 d ∣ 2 d ⋅ p 2 d = p depth  ⋅ p 2 d p_{3 d}=p_{3 d \mid 2 d} \cdot p_{2 d}=p_{\text {depth }} \cdot p_{2 d} p3d=p3d2dp2d=pdepth p2d
    该评分既代表了二维检测置信度,也代表了深度推理置信度,可以指导更好的可靠性。其计算过程引入了投影模型的先验,因此由投影模型引起的误差放大效应可以被一定程度上解决,因为由 h 3 d h_{3d} h3d估计误差引起的放大误差会被很好的反应在计算的不确定度中,所以基于此不确定度得到的得分质量将大幅上升

In Code

在代码中,GUP模块主要对应两部分:

  • 前向传播计算深度值
  • 对应代码:gupnet\code\lib\models\gupnet.py函数中GUPNet类的get_roi_feat_by_mask函数
# compute heights of projected objects
box2d_height = torch.clamp(box2d_masked[:, 4] - box2d_masked[:, 2], min=1.0)
# compute real 3d height
# [B * 4]
size3d_offset = self.size_3d(roi_feature_masked)[:, :, 0, 0]
# [B * 1], 最后一列预测3D 高度的偏差, 实际为log(σ_b^2),即对数方差形式
h3d_log_std = size3d_offset[:, 3:4]
size3d_offset = size3d_offset[:, :3]
# 3D的宽高预测值
size_3d = (self.mean_size[cls_ids[mask].long()] + size3d_offset)
# depth = f * (h_3d / h_2d) 投影转换公式
depth_geo = size_3d[:, 0] / box2d_height.squeeze() * roi_calibs[:, 0, 0]
# 网络预测的depth 其shape: torch.Size([:, 2])
# 第一列:深度值depth
# 第二列:depth的修正值(depth bias), 实际为log(σ_b^2),即对数方差形式
depth_net_out = self.depth(roi_feature_masked)[:, :, 0, 0]
# d_p的方差σ_p^2
depth_geo_log_std = (
        h3d_log_std.squeeze() + 2 * (roi_calibs[:, 0, 0].log() - box2d_height.log())).unsqueeze(-1)
# 最终的depth的方差: log(σ_d^2) = log(σ_p^2 + σ_b^2)
depth_net_log_std = torch.logsumexp(torch.cat([depth_net_out[:, 1:2], depth_geo_log_std], -1), -1,
                                    keepdim=True)
# depth_net_out[:, 0:1].sigmoid() 归一化 (0, 1)
# 最终输出的depth包括两列:
# - 第一列为depth的预测值, 由 网络预测输出 + 投影公式转换 得到
# - 第二列为depth的修正值, 实际为log(σ_b^2),即对数方差形式
depth_net_out = torch.cat(
    [(1. / (depth_net_out[:, 0:1].sigmoid() + 1e-6) - 1.) + depth_geo.unsqueeze(-1), depth_net_log_std], -1)

res['train_tag'] = torch.ones(num_masked_bin).type(torch.bool).to(device_id)
res['heading'] = self.heading(roi_feature_masked)[:, :, 0, 0]
res['depth'] = depth_net_out
res['offset_3d'] = self.offset_3d(roi_feature_masked)[:, :, 0, 0]
res['size_3d'] = size3d_offset
res['h3d_log_variance'] = h3d_log_std
  • 反向传播计算深度值的loss
  • 对应代码:gupnet\code\lib\losses\uncertainty_loss.py中的laplacian_aleatoric_uncertainty_loss函数
# 拉普拉斯任意不确定损失  
def laplacian_aleatoric_uncertainty_loss(input, target, log_variance, reduction='mean'):  
    assert reduction in ['mean', 'sum']  
    loss = 1.4142 * torch.exp(-0.5*log_variance) * torch.abs(input - target) + 0.5*log_variance  
    return loss.mean() if reduction == 'mean' else loss.sum()
  • input <=> μ d \mu_d μd
  • target <=> d g t d^{gt} dgt
  • log_variance <=> l o g ( σ d ) log(\sigma_d) log(σd)
    计算过程如下:
  • 原计算公式: L depth  = 2 σ d ∣ ∣ μ d − d g t ∣ ∣ + log ⁡ ( σ d ) \mathcal{L}_{\text {depth }}=\frac{\sqrt{2}}{\sigma_d}\left||\mu_d-d^{g t}|\right|+\log \left(\sigma_d\right) Ldepth =σd2 μddgt+log(σd)
  • 令: v = l o g ( σ d 2 ) = 2 l o g ( σ d ) v=log(\sigma_d^2)=2log(\sigma_d) v=log(σd2)=2log(σd)
  • 则代码中的计算公式为: L depth  = 2 e x p ( − 0.5 v ) ∣ μ d − d g t ∣ + 0.5 v \mathcal{L}_{\text {depth }}=\sqrt{2}exp(-0.5v)|\mu_d-d^{gt}|+0.5v Ldepth =2 exp(0.5v)μddgt+0.5v
     

HTL

In Paper

这一部分在论文中的思路是:先给出最终的结论,即最终我的损失函数是什么样的,然后一步一步地解释这里面每一项是什么来的,以及具体的含义,相当于一种倒序结构

GUP模块主要解决推理阶段的误差放大效应。然而,这种效应也破坏了训练过程。具体来说,在训练开始时,对 h 2 d h_{2d} h2d h 3 d h_{3d} h3d的预测都很不准确,这将误导整个训练,损害性能。为了解决这个问题,本文设计了一个分层任务学习(HTL)来控制每个阶段每个任务的权重,最终总的损失函数 L total  \mathcal{L}_{\text {total }} Ltotal 如下所示:
L total  = ∑ i ∈ T w i ( t ) ⋅ L i \mathcal{L}_{\text {total }}=\sum_{i \in \mathcal{T}} w_i(t) \cdot \mathcal{L}_i Ltotal =iTwi(t)Li
其中各参数含义如下:

  • T \mathcal{T} T:总的任务集合
  • t t t:当前第几个epoch
  • L i \mathcal{L}_i Li:第 i i i个任务的损失函数
  • w i ( t ) w_i(t) wi(t):第 i i i个任务在第 t t t个epoch阶段的损失函数的权重

首先,本文认为每个任务(task)都应该在它的前任务(pre-task)训练好之后才开始训练,并且将任务划分为不同的阶段,如下图所示。第一阶段是2D检测,包括heatmap、2D 偏移量、2D尺寸。第二阶段是3D head,包含角度、3D偏移量和3D尺寸的3D头。这些3D任务都是建立在ROI特征之上的,所以2D检测阶段的任务是它们的前置任务。同样,最后一个阶段是深度推断,它的前置任务是3D尺寸和2D检测阶段的所有任务,因为深度预测依赖于2D高度和2D高度

所以总得来说,我们需要两个元素实现这件事情:1). 任务学习状态评估:用于评估先制任务的学习状态,2). 当前任务控制器:当先制任务学习达标后,提高当前任务的权重:

  1. 为了对每个任务进行充分的训练,目标是随着训练的进行将 w i ( t ) w_i(t) wi(t)从0逐渐增加到1。因此采用多项式时间调度函数作为加权函数:
    w i ( t ) = ( t T ) 1 − α i ( t ) , α i ( t ) ∈ [ 0 , 1 ] w_i(t)=\left(\frac{t}{T}\right)^{1-\alpha_i(t)}, \quad \alpha_i(t) \in[0,1] wi(t)=(Tt)1αi(t),αi(t)[0,1]
    其中, T T T为训练总epoch数,归一化时间变量 t T \frac{t}{T} Tt可以自动调整时间刻度。 α i ( t ) \alpha_i(t) αi(t)是第 t t t个epoch的调整参数,对应第 i i i个任务的所有前置任务。 α i \alpha_i αi越大, w i w_i wi增加越快,即如果所有的前置任务都得到了很好的训练,那么 α i \alpha_i αi就应该很大,否则就应该很小
  1. α i ( ⋅ ) \alpha_i(·) αi()与其所有前置任务有关,将所有前置任务的学习情况指标相乘表示为该任务的损失函数权重:
    α i ( t ) = ∏ j ∈ P i l s j ( t ) \alpha_i(t)=\prod_{j \in \mathcal{P}_i} l s_j(t) αi(t)=jPilsj(t)
    其中, P i \mathcal{P}_i Pi表示第 i i i个任务的所有前置任务集合, l s j ls_j lsj表示这些前置任务中,第 j j j的任务的学习情况指标(越好,值越大,取值介于0 ~ 1之间)。这个公式意味着 α i \alpha_i αi只有在所有前置任务都达到高 l s j ls_j lsj(训练良好)时才会得到较高的值
  2. 这里作者使用尺度不变因子(Scale-Invariant Factor)来表示每个任务的学习情况:
    l s j ( t ) = D F j ( K ) − D F j ( t ) D F j ( K ) D F j ( t ) = 1 K ∑ t ^ = t − K t − 1 ∣ L j ′ ( t ^ ) ∣ \begin{aligned} l s_j(t) &=\frac{\mathcal{D} \mathcal{F}_j(K)-\mathcal{D} \mathcal{F}_j(t)}{\mathcal{D} \mathcal{F}_j(K)} \\ \mathcal{D} \mathcal{F}_j(t) &=\frac{1}{K} \sum_{\hat{t}=t-K}^{t-1}\left|\mathcal{L}_j^{\prime}(\hat{t})\right| \end{aligned} lsj(t)DFj(t)=DFj(K)DFj(K)DFj(t)=K1t^=tKt1Lj(t^)
    其中, L j ′ ( t ^ ) \mathcal{L}_j^{\prime}(\hat{t}) Lj(t^) L j ( ) \mathcal{L}_j() Lj()在第 t ^ \hat t t^个epoch时的一阶导数,可以表示损失函数的局部变化趋势。 D F j ( t ) \mathcal{D} \mathcal{F}_j(t) DFj(t)计算第 t t t个epoch之前的最近 K K K个epoch导数的平均值,用来反映平均值的变化趋势。 D F j ( K ) \mathcal{D} \mathcal{F}_j(K) DFj(K)表示第 j j j个任务训练开始时的前 K K K个epoch的变化趋势。如果 L j \mathcal{L}_j Lj在最近的 K K K个epoch内快速下降, D F j \mathcal{D} \mathcal{F}_j DFj将得到较大的值。因此 l s j ls_j lsj公式表示比较当前训练的变化趋势与刚开始训练时变化趋势之间的差异。如果当前的损失趋势与开始的趋势相似,则指标会给出一个很小的值,这意味着该任务没有很好地训练。反之,如果任务趋于收敛,则lj值将趋近于1,表示该任务的学习情况是很好地,具体图解如下所示:

在总体设计的基础上,每个项的损失权重可以动态地反映其前置任务的学习情况,使训练更加稳定

In Code

  • 对应代码:gupnet\code\lib\losses\loss_function.py中的Hierarchical_Task_Learning函数
  • 这一部分在代码中的逻辑如下:
    • 首先初始化7个Loss的权重:2D相关的都为1,3D相关的都为0
    • 前5个epoch不做任何处理,权重值不变
    • 5个epoch之后,开始计算当前epoch的局部变化趋势(往前数5个epoch内),并且保存前5个epoch内的变化趋势不变
    • 根据论文中的公式更新权重,直到训练结束
# HTL:Hierarchical Task Learning 分层任务学习
# 2D 损失函数的权重一直不变,都为1
# 3D 损失函数的权重初始化为0,在第5个epoch之后,开始变化
# 具体变化规则取决于之前任务的学习情况
# 任务的学习情况则是通过损失函数的局部变化趋势来判断
class Hierarchical_Task_Learning:
    def __init__(self, epoch0_loss, stat_epoch_nums=5):
        self.index2term = [*epoch0_loss.keys()]
        self.term2index = {term: self.index2term.index(term) for term in self.index2term}  # term2index
        # self.term2index: {
        # 'seg_loss': 0, 'offset2d_loss': 1, 'size2d_loss': 2,
        # 'depth_loss': 3, 'offset3d_loss': 4, 'size3d_loss': 5, 'heading_loss': 6}
        self.stat_epoch_nums = stat_epoch_nums  # 对应论文中的K,即与前K个epoch的loss进行比较
        self.past_losses = []
        self.loss_graph = {'seg_loss': [],
                           'size2d_loss': [],
                           'offset2d_loss': [],
                           'offset3d_loss': ['size2d_loss', 'offset2d_loss'],
                           'size3d_loss': ['size2d_loss', 'offset2d_loss'],
                           'heading_loss': ['size2d_loss', 'offset2d_loss'],
                           'depth_loss': ['size2d_loss', 'size3d_loss', 'offset2d_loss']}

    def compute_weight(self, current_loss, epoch):
        T = 140
        # compute initial weights
        loss_weights = {}
        '''
        current_loss_0:{
        'seg_loss': tensor(75.1708, device='cuda:0'), 
        'offset2d_loss': tensor(1.0958, device='cuda:0'), 
        'size2d_loss': tensor(19.7375, device='cuda:0'), 
        'depth_loss': tensor(8.5812, device='cuda:0'), 
        'offset3d_loss': tensor(0.9910, device='cuda:0'), 
        'size3d_loss': tensor(0.9233, device='cuda:0'), 
        'heading_loss': tensor(3.7395, device='cuda:0')}
        
        eval_loss_input:
        tensor([[75.1708,  1.0958, 19.7375,  8.5812,  0.9910,  0.9233,  3.7395]], device='cuda:0')
        '''
        eval_loss_input = torch.cat([_.unsqueeze(0) for _ in current_loss.values()]).unsqueeze(0)
        for term in self.loss_graph:
            if len(self.loss_graph[term]) == 0:
                loss_weights[term] = torch.tensor(1.0).to(current_loss[term].device)
            else:
                loss_weights[term] = torch.tensor(0.0).to(current_loss[term].device)
                # update losses list
        if len(self.past_losses) == self.stat_epoch_nums:
            past_loss = torch.cat(self.past_losses)  # 5个list -> [5, 7]的tensor
            # 使用差分计算5个epoch内,loss的变化趋势,一阶导数
            mean_diff = (past_loss[:-2] - past_loss[2:]).mean(0)
            # 以下代码只运行一次,即保存前5个epoch的loss变化趋势
            if not hasattr(self, 'init_diff'):
                self.init_diff = mean_diff
            # 表示该epoch下loss的变化趋势
            c_weights = 1 - (mean_diff / self.init_diff).relu().unsqueeze(0)
            print('c_weights: ', c_weights)
            print('mean_diff: ', mean_diff)
            print('self.init_diff: ', self.init_diff)
            # t / T
            time_value = min(((epoch - 5) / (T - 5)), 1.0)
            for current_topic in self.loss_graph:
                # 只有3D 预测信息的loss会变化,2D的不会变('size2d_loss','size3d_loss','offset2d_loss')
                if len(self.loss_graph[current_topic]) != 0:
                    control_weight = 1.0
                    for pre_topic in self.loss_graph[current_topic]:
                        # 该epoch下的调整参数
                        control_weight *= c_weights[0][self.term2index[pre_topic]]
                    # (t / T)^(1 - α)
                    loss_weights[current_topic] = time_value ** (1 - control_weight)
            # pop first list 丢掉第一个epoch的loss
            self.past_losses.pop(0)
        # 添加当前epoch的loss
        self.past_losses.append(eval_loss_input)
        return loss_weights

    def update_e0(self, eval_loss):
        self.epoch0_loss = torch.cat([_.unsqueeze(0) for _ in eval_loss.values()]).unsqueeze(0)

 

Refernece

知乎 - 作者解读
知乎 - 3D Bounding Box Estimation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ActivitiListener

ActivitiListener目录概述需求&#xff1a;设计思路实现思路分析1.ActivitiListener2.Activity3.Gateway5.FieldExtensionIOSpecification参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip ha…

骨传导原理是什么?哪些骨传导耳机值得入手

​骨传导耳机是目前耳机市场比较流行耳机&#xff0c;深受年轻一族和运动达人的喜爱。但尽管这种产品受到很多人的青睐&#xff0c;相较传统耳机&#xff0c;大众对骨传导耳机的认识和程度并不高&#xff0c;也有很多小伙伴不知道骨传导耳机的原理是怎么发声的&#xff0c;骨传…

Vue子组件传自定义属性给父组件

我们知道组件之间是不能够之间进行通信的&#xff0c;都是相互独立的&#xff0c;你用不了我的状态和方法&#xff0c;我也用不了你的&#xff0c;那如何实现通信呢&#xff0c;可以间接实现&#xff1b; 实现父组件和子组件的通信&#xff1a; 子组件想用父组件的状态需要父…

Java并发之线程池

文章目录前言一、Java中线程池概览1.1 类图1.2 内部流程图二、源码探索2.1 构造参数2.2 线程池状态2.3 Worker 的添加和运行2.4 阻塞队列2.5 任务拒绝策略三、实际使用3.1 动态线程池3.2 拓展使用3.3 springboot 中线程池参考前言 在高并发的 Java 程序设计中&#xff0c;编写…

数字化安全生产平台 DPS 重磅发布

11 月 5 日&#xff0c;在 2022 杭州 云栖大会上&#xff0c;数字化安全生产平台 DPS 重磅发布&#xff0c;助力传统运维向 SRE 转型。 阿里巴巴资深技术专家 周洋 十四五规划下&#xff0c;各行各业全面加速数字化转型与升级。随着企业数字化业务规模变大&#xff0c;迭代速…

Dubbo服务远程调用的简介及使用教程

一、Dubbo的简介 Dubbo是阿里巴巴公司开源的一个高性能、轻量级的 Java RPC 框架。 致力于提供高性能和透明化的 RPC 远程服务调用方案&#xff0c;以及 SOA 服务治理方案。 官网&#xff1a;https://dubbo.apache.org/ SOA架构&#xff1a;&#xff08;Service-Oriented Arch…

华为云RDS数据库测评:性能超出预期,双11优惠还在继续

一、前言 作为一名电商行业公司的员工&#xff0c;深刻体会到系统大压力、高并发下保证服务的正常使用是多么严峻的挑战。双11这段时间&#xff0c;因为激增的使用量让我们的数据库服务严重吃紧&#xff0c;压力特别的大&#xff0c;甚至还出现了交易漏单&#xff0c;脏数据等…

【Servlet】3:Servlet 的基本原理、Servlet对象的生命周期

目录 第五章 | 动态资源与Servlet | 章节概述 | Tomcat与Servlet的 原理、关系 Tomcat的基本构成​编辑 Server处理HTTP请求 Connector内部架构分析 Container内部架构分析 Tomcat的执行流程小结 | Servlet 概述、接口实现 Servlet的基本概述 实现Servlet接口并通过U…

LeetCode HOT 100 —— 10.正则表达式匹配

题目 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符 ‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 思路 对于字符串…

11月24日国产蓝牙AOA高精度定位vs国外知名厂家的蓝牙aoa定位效果的展示

11月24日国产蓝牙AOA高精度定位vs国外知名厂家的蓝牙aoa定位效果的展示 11月24日国产蓝牙AOA高精度定位vs国外知名厂家的蓝牙aoa定位效果的展示

操作系统的基本概念

文章目录一、操作系统的概念1.什么是操作系统&#xff1f;2 计算机系统的构成3 系统软件的概念4 操作系统的主要作用二、操作系统目标和功能1. 目标1.1 有效性1.2 方便性1.3 可扩充性1.4 开放性2. 功能2.1 作为系统资源的管理者2.2 作为用户与计算机[硬件系统]之间的接口2.3 实…

Linus 文件处理(一)

目录 一、前言 二、低级文件访问 1、write 2、read 3、open 4、Initial Permissions &#xff08;1&#xff09;umask &#xff08;2&#xff09;close &#xff08;3&#xff09;ioctl &#xff08;4&#xff09;第一个 copy_system.c 程序 &#xff08;5&#xff…

Apache ShardingSphere(一) 基本概念介绍

文章目录一 基本介绍1.1 概述1.2 ShardingSphere JDBC1.3 ShardingSphere Proxy1.4 ShardingSphere Sidecar1.5 数据库的扩展1.5.1 向上扩展1.5.2 横向扩展1.5.2.1 读写分离1.5.2.2 垂直切分1.5.2.3 水平切分1.6 分库与分表1.6.1 水平分库1.6.2 水平分表1.6.3 垂直分库1.6.4 垂…

[iOS]使用MonkeyDev完成Hook

一、确定目标 先定个小目标&#xff0c;使用七猫举个例&#xff0c;去移除小说阅读页底部广告和章节之间的广告。 二、HOOK 1. 创建MonkeyApp项目导入砸壳包 2. 使用Reveal工具确定“底部广告”和“章末广告”的视图名称 底部广告 View Controller: Class: QMReader.YYReade…

Strassen矩阵乘法问题(Java)

Strassen矩阵乘法问题&#xff08;Java&#xff09; 文章目录Strassen矩阵乘法问题&#xff08;Java&#xff09;1、前置介绍3、代码实现4、复杂度分析5、参考资料1、前置介绍 矩阵乘法是线性代数中最常见的问题之一 &#xff0c;它在数值计算中有广泛的应用。 设A和B是2个nXn…

搭建灾情快速分析系统 | Bigemap助力防灾减灾重点工作

Bigemap国产基础软件凭借自身强大的新GIS引擎技术与完善的产品链&#xff0c;为相关部门提供了集"灾情采集-灾情监测-灾害快速评估-应急指挥"于一体的灾害防灾减灾解决方案&#xff0c;搭建了灾情快速分析系统&#xff0c;该系统成为相关部门应对灾情的重要支撑平台。…

虚拟号码认证如何开通?

近年来&#xff0c;经常会接到外卖、房产中介、信用贷款等电话&#xff0c;让顾客不胜其扰。现在电话标记功能使用越来越普遍&#xff0c;可以大概了解电话“来意”&#xff0c;同时也会让误标记、恶意标记很方便。对于开展业务或办公司或企业的人&#xff0c;更加不能让自己的…

排序算法之选择排序

今天来给大家介绍一下排序算法之选择排序 选择排序&#xff1a;&#xff08;Selection sort&#xff09;是一种简单直观的排序算法&#xff0c;也是一种不稳定的排序方法。 选择排序的原理&#xff1a; 一组无序待排数组&#xff0c;做升序排序&#xff0c;我们先假定第一个…

【生成模型】Diffusion Models:概率扩散模型

---前言一、Diffusion Model 基本介绍二、生成模型对比三、直观理解Diffusion model四、形式化解析Diffusion model五、详解 Diffusion Model&#xff08;数学推导&#xff09;1.前向过程(扩散过程)2.逆扩散过程3.逆扩散条件概率推导4.训练损失六、训练、测试伪代码1. 训练2.测…

鲲鹏devkit编译调试工具——《sudoku》作业解析

《sudoku》作业解析 本次实验以sudoku项目为例介绍鲲鹏编译调试插件的基本使用方法 本次实验的步骤主要为 获取源码安装鲲鹏编译调试插件服务器配置进行代码同步配置配置测试任务进行编译调试 接下来我们先获取本次实验所需要的源码 获取源码 sudoku项目已经上传到github使…