【单目3D目标检测】GUPNet论文精读与代码解析
创始人
2024-02-10 20:20:08

文章目录

  • 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边界框的高度,然后通过投影公式depth=h3d⋅f/h2ddepth =h_{3d}·f / h_{2d}depth=h3d​⋅f/h2d​ (fff为摄像机焦距)推断出深度。由该公式推导出的深度与估计的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. 首先假设投影过程中的 h3dh_{3d}h3d​ 是拉普拉斯分布 La(μh,λh)La(\mu_h,\lambda_h)La(μh​,λh​) ,也即X∼La(μ,λ),fX(x)=12λexp⁡(−∣x−μ∣λ)X \sim L a(\mu, \lambda), f_X(x)=\frac{1}{2 \lambda} \exp \left(-\frac{|x-\mu|}{\lambda}\right)X∼La(μ,λ),fX​(x)=2λ1​exp(−λ∣x−μ∣​),此时h3dh_{3d}h3d​的损失函数可定义为:
    Lh3d=2σh∣∣μh−h3dgt∣∣+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​​=σh​2​​∣∣​∣μh​−h3dgt​∣∣∣​+log(σh​)

  2. 将其代入投影模型,可计算出depth为:
    dp=f⋅h3dh2d=f⋅(λh⋅X+μh)h2d=f⋅λhh2d⋅X+f⋅μhh2d\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​​=h2d​f⋅h3d​​=h2d​f⋅(λh​⋅X+μh​)​=h2d​f⋅λh​​⋅X+h2d​f⋅μh​​​
    其中XXX是一个归一化的拉普拉斯分布La(0,1)La(0,1)La(0,1)。可以看到深度估计值dpd_pdp​的期望μp\mu_pμp​和标准差σp\sigma_pσp​分别为f⋅μhh2d\frac{f · \mu_h}{h_{2d}}h2d​f⋅μh​​和f⋅σhh2d\frac{f · \sigma_h}{h_{2d}}h2d​f⋅σh​​,其中σh\sigma_hσh​是拉普拉斯分布La(μh,λh)La(\mu_h,\lambda_h)La(μh​,λh​)对应的标准差(σh2=2λh2\sigma_h^2=2\lambda_h^2σh2​=2λh2​ )

  3. 对于结果dpd_pdp​而言,均值μp\mu_pμp​对应投影depth结果,而σp\sigma_pσp​则反应了投影不确定度。在此基础上,为了更精准的depth输出,本文额外让神经网络预测出一个depth的修正值(depth bias),本文假设该修正值也是拉普拉斯分布La(μb,λb)La(\mu_b,\lambda_b)La(μb​,λb​),因此最终depth则变成:
    d=La(μp,σp)+La(μ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. 为了优化最终深度分布,本文应用了不确定性回归损失:
    Ldepth =2σd∣∣μd−dgt∣∣+log⁡(σd)\mathcal{L}_{\text {depth }}=\frac{\sqrt{2}}{\sigma_d}\left||\mu_d-d^{g t}|\right|+\log \left(\sigma_d\right) Ldepth ​=σd​2​​∣∣​∣μd​−dgt∣∣∣​+log(σd​)
    为了简化起见,假定深度分布属于拉普拉斯分布。整体损失会使投影结果更接近GT,梯度同时影响深度偏差、h2dh_{2d}h2d​和h3dh_{3d}h3d​。此外,在优化过程中还训练了三维高度和深度偏差的不确定性

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

  6. 最终的推理得分可以计算为:
    p3d=p3d∣2d⋅p2d=pdepth ⋅p2dp_{3 d}=p_{3 d \mid 2 d} \cdot p_{2 d}=p_{\text {depth }} \cdot p_{2 d} p3d​=p3d∣2d​⋅p2d​=pdepth ​⋅p2d​
    该评分既代表了二维检测置信度,也代表了深度推理置信度,可以指导更好的可靠性。其计算过程引入了投影模型的先验,因此由投影模型引起的误差放大效应可以被一定程度上解决,因为由h3dh_{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 <=> dgtd^{gt}dgt
  • log_variance <=> log(σd)log(\sigma_d)log(σd​)
    计算过程如下:
  • 原计算公式:Ldepth =2σd∣∣μd−dgt∣∣+log⁡(σd)\mathcal{L}_{\text {depth }}=\frac{\sqrt{2}}{\sigma_d}\left||\mu_d-d^{g t}|\right|+\log \left(\sigma_d\right)Ldepth ​=σd​2​​∣∣μd​−dgt∣∣+log(σd​)
  • 令:v=log(σd2)=2log(σd)v=log(\sigma_d^2)=2log(\sigma_d)v=log(σd2​)=2log(σd​)
  • 则代码中的计算公式为:Ldepth =2exp(−0.5v)∣μd−dgt∣+0.5v\mathcal{L}_{\text {depth }}=\sqrt{2}exp(-0.5v)|\mu_d-d^{gt}|+0.5vLdepth ​=2​exp(−0.5v)∣μd​−dgt∣+0.5v
     

HTL

In Paper

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

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

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

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

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

  1. 为了对每个任务进行充分的训练,目标是随着训练的进行将wi(t)w_i(t)wi​(t)从0逐渐增加到1。因此采用多项式时间调度函数作为加权函数:
    wi(t)=(tT)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]
    其中,TTT为训练总epoch数,归一化时间变量tT\frac{t}{T}Tt​可以自动调整时间刻度。αi(t)\alpha_i(t)αi​(t)是第ttt个epoch的调整参数,对应第iii个任务的所有前置任务。αi\alpha_iαi​越大,wiw_iwi​增加越快,即如果所有的前置任务都得到了很好的训练,那么αi\alpha_iαi​就应该很大,否则就应该很小
  1. αi(⋅)\alpha_i(·)αi​(⋅)与其所有前置任务有关,将所有前置任务的学习情况指标相乘表示为该任务的损失函数权重:
    αi(t)=∏j∈Pilsj(t)\alpha_i(t)=\prod_{j \in \mathcal{P}_i} l s_j(t) αi​(t)=j∈Pi​∏​lsj​(t)
    其中,Pi\mathcal{P}_iPi​表示第iii个任务的所有前置任务集合,lsjls_jlsj​表示这些前置任务中,第jjj的任务的学习情况指标(越好,值越大,取值介于0 ~ 1之间)。这个公式意味着αi\alpha_iαi​只有在所有前置任务都达到高lsjls_jlsj​(训练良好)时才会得到较高的值
  2. 这里作者使用尺度不变因子(Scale-Invariant Factor)来表示每个任务的学习情况:
    lsj(t)=DFj(K)−DFj(t)DFj(K)DFj(t)=1K∑t^=t−Kt−1∣Lj′(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)​=K1​t^=t−K∑t−1​∣∣​Lj′​(t^)∣∣​​
    其中,Lj′(t^)\mathcal{L}_j^{\prime}(\hat{t})Lj′​(t^)为Lj()\mathcal{L}_j()Lj​()在第t^\hat tt^个epoch时的一阶导数,可以表示损失函数的局部变化趋势。DFj(t)\mathcal{D} \mathcal{F}_j(t)DFj​(t)计算第ttt个epoch之前的最近KKK个epoch导数的平均值,用来反映平均值的变化趋势。DFj(K)\mathcal{D} \mathcal{F}_j(K)DFj​(K)表示第jjj个任务训练开始时的前KKK个epoch的变化趋势。如果Lj\mathcal{L}_jLj​在最近的KKK个epoch内快速下降,DFj\mathcal{D} \mathcal{F}_jDFj​将得到较大的值。因此lsjls_jlsj​公式表示比较当前训练的变化趋势与刚开始训练时变化趋势之间的差异。如果当前的损失趋势与开始的趋势相似,则指标会给出一个很小的值,这意味着该任务没有很好地训练。反之,如果任务趋于收敛,则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 weightsloss_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 listif 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 / Ttime_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.0for 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的lossself.past_losses.pop(0)# 添加当前epoch的lossself.past_losses.append(eval_loss_input)return loss_weightsdef update_e0(self, eval_loss):self.epoch0_loss = torch.cat([_.unsqueeze(0) for _ in eval_loss.values()]).unsqueeze(0)

 

Refernece

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

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...