文章内容
论文链接:A Ranking-based, Balanced Loss Function Unifying Classification and Localisation in Object Detection
基于mmdet实现代码:aLRP Loss
相关研究现状
主流损失函数定义为 L = L c + w r L r L=L_c+w_rL_r L=Lc+wrLr,存在如下缺点:
- 定位任务和分类任务之间缺乏关联,难以兼顾定位精度和分类精度
- 存在超参 w r w_r wr,对于需要花较长时间训练的网络来说调参困难
- 受到正负样本不均衡/异常值的影响较大,需要设计合理的采样策略或特殊的损失函数(通常需要调参)
1. 定位任务和分类任务的平衡/耦合
- Classification-aware regression loss 使用锚框的分类得分对定位损失进行加权;
- LapNet 基于任务的同方差不确定性将超参 w r w_r wr定义为一个可学习调优的参数
- 其他方法在NMS步骤中对分类和定位分支的输出进行融合
2. 基于排名的目标检测算法
基于排名的损失计算方法能够一定程度缓解正负样本不平衡问题,但目前的算法大都仅限于分类任务,并没有改变定位分支
本文工作
- 基于AP Loss的误差驱动优化算法,扩展出一个能够优化不可导的排序损失的通用框架
- 证明基于排序的损失函数能够自然平衡正负样本
- 提供aLRP Loss及其梯度传播的实现,能够替换AP loss、SmoothL1 loss等损失函数并提高网络性能
基于排序损失的误差驱动优化方法推广
定理1:基于概率分布的损失函数重定义
在AP Loss排序损失
L
=
1
Z
∑
i
∈
P
l
(
i
)
L=\frac{1}{Z}\sum_{i\in P}l(i)
L=Z1∑i∈Pl(i)中,
Z
Z
Z是标准化常量。
定义
L
i
j
L_{ij}
Lij是对正样本
i
i
i和负样本
j
j
j的损失计算,可以看作正样本
i
i
i经由概率
p
(
j
∣
i
)
p(j|i)
p(j∣i)在负样本
j
j
j上的损失,即
L
i
j
=
{
l
(
i
)
p
(
j
∣
i
)
f
o
r
i
∈
P
,
j
∈
N
0
o
t
h
e
r
w
i
s
e
L_{ij}=\begin{cases} l(i)p(j|i)& for\space i\in P,j\in N\\ 0& otherwise \end{cases}
Lij={l(i)p(j∣i)0for i∈P,j∈Notherwise
因此,AP Loss排序损失可表示为
L
=
1
Z
∑
i
∈
P
l
(
i
)
=
1
Z
∑
i
∈
P
∑
j
∈
N
L
i
j
L=\frac{1}{Z}\sum_{i\in P}l(i)=\frac{1}{Z}\sum_{i\in P}\sum_{j\in N}L_{ij}
L=Z1∑i∈Pl(i)=Z1∑i∈P∑j∈NLij。
这个表达方式更为灵活,可以通过定义
p
(
i
∣
j
)
p(i|j)
p(i∣j)改变损失函数的分布,或使之侧重于困难样本。
定理2:正负样本梯度总和相等
∑ i ∈ P ∣ ∂ L ∂ s i ∣ = ∑ j ∈ N ∣ ∂ L ∂ s j ∣ \sum_{i\in P}|\frac{\partial L}{\partial s_i}|=\sum_{j\in N}|\frac{\partial L}{\partial s_j}| i∈P∑∣∂si∂L∣=j∈N∑∣∂sj∂L∣
aLRP Loss 定义
将AP Loss的扩展成aLRP Loss以解决上述三个不足。参考检测准确度(precision)和AP Loss之间的关联,我们将aLRP Loss定义为PR曲线上正样本的LRP均值:
L
a
L
R
P
:
=
1
∣
P
∣
∑
i
∈
P
l
L
R
P
(
i
)
L^{aLRP}:=\frac{1}{|P|}\sum_{i\in P}l^{LRP}(i)
LaLRP:=∣P∣1i∈P∑lLRP(i)
假定锚框足够密集,能够覆盖所有gt,即
N
F
N
=
0
N_{FN}=0
NFN=0(没有gt被忽略/检测为负样本),正样本集合
P
P
P即
T
P
TP
TP,负样本集合
N
N
N即
F
P
FP
FP,
F
N
FN
FN不参与损失函数的计算。因此参考LRP评价指标的定义:
L
R
P
(
s
)
=
1
N
F
N
+
N
T
P
+
N
F
P
(
N
F
N
+
N
F
P
+
∑
k
∈
T
P
ε
l
o
c
(
k
)
)
LRP(s)=\frac{1}{\cancel{N_{FN}}+N_{TP}+N_{FP}}\left(\cancel{N_{FN}}+N_{FP}+\sum_{k\in TP}\varepsilon_{loc}(k)\right)
LRP(s)=NFN
+NTP+NFP1(NFN
+NFP+k∈TP∑εloc(k))
对于正样本
i
i
i,损失值
l
L
R
P
(
i
)
l^{LRP}(i)
lLRP(i)定义如下,其中
N
F
P
(
i
)
N_{FP}(i)
NFP(i)和
r
a
n
k
(
i
)
=
N
T
P
(
i
)
+
N
F
P
(
i
)
rank(i)=N_{TP}(i)+N_{FP}(i)
rank(i)=NTP(i)+NFP(i)分别表示样本
i
i
i在负样本和全体正负样本中的排名:
l
L
R
P
(
i
)
=
1
r
a
n
k
(
i
)
(
N
F
P
(
i
)
+
ε
l
o
c
(
i
)
+
∑
k
∈
P
,
k
≠
i
ε
l
o
c
(
k
)
H
(
x
i
k
)
)
l^{LRP}(i)=\frac{1}{rank(i)}\left(N_{FP}(i)+\varepsilon_{loc}(i)+\sum_{k\in P,k\neq i}\varepsilon_{loc}(k)H(x_{ik})\right)
lLRP(i)=rank(i)1⎝⎛NFP(i)+εloc(i)+k∈P,k=i∑εloc(k)H(xik)⎠⎞
该式也可以拆分为两部分,分别表示分类损失和定位损失:
l
L
R
P
(
i
)
=
N
F
P
r
a
n
k
(
i
)
+
1
r
a
n
k
(
i
)
(
ε
l
o
c
(
i
)
+
∑
k
∈
P
,
k
≠
i
ε
l
o
c
(
k
)
H
(
x
i
k
)
)
l^{LRP}(i)=\textcolor{orangered}{\frac{N_{FP}}{rank(i)}}+\textcolor{blue}{\frac{1}{rank(i)}\left( \varepsilon_{loc}(i)+\sum_{k\in P,k\neq i}\varepsilon_{loc}(k)H(x_{ik})\right)}
lLRP(i)=rank(i)NFP+rank(i)1⎝⎛εloc(i)+k∈P,k=i∑εloc(k)H(xik)⎠⎞
可以发现,aLRP Loss只对分类正确的样本计算定位损失,因此在网络训练初始阶段,分类效果不佳时,损失函数由分类损失主导,网络很难对定位分支进行优化。为了缓解这个问题,引入一个自平衡参数,参考定理2可知正负样本的梯度贡献相等,对定位框梯度
∂
L
a
L
R
P
∂
B
\frac{\partial L^{aLRP}}{\partial B}
∂B∂LaLRP乘以该epoch的均值
L
a
L
R
P
L
l
o
c
a
L
R
P
\frac{L^{aLRP}}{L_{loc}^{aLRP}}
LlocaLRPLaLRP,从而使得分类得分和定位梯度对aLRP Loss的贡献相近。
- 如图(a)所示,正样本的梯度会受到(1)得分更高的正样本的定位精度和(2)得分更高的负样本的影响,图中 p 1 p_1 p1得分更高但定位精度较差,导致 p 2 p_2 p2的梯度增大以压制 p 1 p_1 p1;
- 如图(b)所示,负样本的梯度会受到比其得分更低的全部正样本的均匀影响,以其总和构成梯度;
- 如图©所示,根据定义,得分最高的正样本定位损失会出现在所有样本的定位损失中。
aLRP的反向传播
与AP Loss同样,aLRP Loss可以定义为
L
i
j
a
L
R
P
=
l
L
R
P
(
i
)
p
(
j
∣
i
)
L_{ij}^{aLRP}=l^{LRP}(i)p(j|i)
LijaLRP=lLRP(i)p(j∣i),其目标值定义如下:
L
i
j
a
L
R
P
∗
=
L
a
L
R
P
(
i
)
∗
⋅
p
(
j
∣
i
)
=
1
r
a
n
k
(
i
)
(
N
F
P
(
i
)
+
ε
l
o
c
(
i
)
+
∑
k
∈
P
,
k
≠
i
ε
l
o
c
(
k
)
H
(
x
i
k
)
)
⋅
p
(
j
∣
i
)
=
ε
l
o
c
(
i
)
r
a
n
k
(
i
)
⋅
p
(
j
∣
i
)
\begin{aligned} L_{ij}^{aLRP^*}&=L^{aLRP}(i)^*·p(j|i)\\ &=\frac{1}{rank(i)}\left( \cancel{N_{FP}(i)}+\varepsilon_{loc}(i)+ \cancel{\sum_{k\in P,k\neq i}\varepsilon_{loc}(k)H(x_{ik})} \right)·p(j|i) \\&=\frac{\varepsilon_{loc}(i)}{rank(i)}·p(j|i) \end{aligned}
LijaLRP∗=LaLRP(i)∗⋅p(j∣i)=rank(i)1⎝⎛NFP(i)
+εloc(i)+k∈P,k=i∑εloc(k)H(xik)
⎠⎞⋅p(j∣i)=rank(i)εloc(i)⋅p(j∣i)
从而有
x
i
j
x_{ij}
xij的误差驱动更新量如下,进而可求
∂
L
a
L
R
P
∂
s
i
\frac{\partial L^{aLRP}}{\partial s_i}
∂si∂LaLRP。
Δ
x
i
j
=
(
l
L
R
P
(
i
)
∗
−
l
L
R
P
(
i
)
)
p
(
j
∣
i
)
=
−
1
r
a
n
k
(
i
)
(
N
F
P
(
i
)
+
∑
k
∈
P
,
k
≠
i
ε
l
o
c
(
k
)
H
(
x
i
k
)
)
H
(
x
i
j
)
N
F
P
(
i
)
\begin{aligned} \Delta x_{ij}&=\left( l^{LRP}(i)^*-l^{LRP}(i) \right)p(j|i) \\&=-\frac{1}{rank(i)}\left( N_{FP}(i)+\sum_{k\in P,k\neq i}\varepsilon_{loc}(k)H(x_{ik}) \right)\frac{H(x_{ij})}{N_{FP}(i)} \end{aligned}
Δxij=(lLRP(i)∗−lLRP(i))p(j∣i)=−rank(i)1⎝⎛NFP(i)+k∈P,k=i∑εloc(k)H(xik)⎠⎞NFP(i)H(xij)
Highlight
- 在分类任务和定位任务中都使用排名策略,即分类和定位分支都用到了检测器的全部输出,而不是像传统方法中彼此独立工作,从而能够兼顾高检测精度和高定位质量
- 只有一个不需要调试的超参数(来自软化的阶跃函数 H ( ⋅ ) H(·) H(⋅)),相比于state-of-the-art方法的六个超参数更容易训练
- 该网络由单个损失函数训练,在正负样本间提供可证明的平衡策略
代码解读
代码结构与AP Loss相似,输出一个 c l a s s i f i c a t i o n _ g r a d s classification\_grads classification_grads作为反向传播的依据。
其中正样本的损失值由该样本的排序质量和回归质量决定,torch.sum(fg_relations * regression_losses)
表示所有相对于该样本排序不正确的正样本的回归损失,FP_num/rank[ii]
表示该正样本的排名损失;
而负样本的损失值由所有与之排序错误的正样本共同决定,relevant_bg_grad += (bg_relations*(-fg_grad[ii]/FP_num))
,其中正样本总损失值越大,排名损失越小,对这个负样本的损失值影响越大。
class aLRPLoss(torch.autograd.Function):
@staticmethod
def forward(ctx, logits, targets, regression_losses, delta=1., eps=1e-5):
classification_grads = torch.zeros(logits.shape).cuda()
# ---------------------#
# Filter fg logits
# ---------------------#
fg_labels = (targets == 1)
fg_logits = logits[fg_labels]
fg_num = len(fg_logits)
rank = torch.zeros(fg_num).cuda()
prec = torch.zeros(fg_num).cuda()
fg_grad = torch.zeros(fg_num).cuda()
# --------------------------------------#
# Filter non-trivial negative samples
# --------------------------------------#
# Do not use bg with scores less than minimum fg logit
# since changing its score does not have an effect on precision
threshold_logit = torch.min(fg_logits)-delta
# Get valid bg logits
relevant_bg_labels = ((targets == 0) & (logits >= threshold_logit))
relevant_bg_logits = logits[relevant_bg_labels]
relevant_bg_grad = torch.zeros(len(relevant_bg_logits)).cuda()
# -----------------------------#
# Loop on posivite indices
# -----------------------------#
# sort the fg logits and loop over each positive following the order
order = torch.argsort(fg_logits)
for ii in order:
# x_ij s as score differences with fgs
fg_relations = fg_logits - fg_logits[ii]
# Apply piecewise linear function and determine relations with fgs (H(x_ij))
fg_relations = torch.clamp(fg_relations/(2*delta)+0.5, min=0, max=1)
# Discard i=j in the summation in rank_pos
fg_relations[ii] = 0
# x_ij s as score differences with bgs
bg_relations = relevant_bg_logits - fg_logits[ii]
# Apply piecewise linear function and determine relations with bgs
bg_relations = torch.clamp(bg_relations / (2 * delta) + 0.5, min=0, max=1)
# Compute the rank of the example within fgs and number of bgs with larger scores rank^+(i)
rank_pos = 1 + torch.sum(fg_relations)
FP_num = torch.sum(bg_relations)
# Store the total since it is normalizer also for aLRP Regression error rank(i)
rank[ii] = rank_pos + FP_num
# Compute precision for this example to compute classification loss
prec[ii] = rank_pos/rank[ii]
# For stability, set eps to an infinite small value (e.g. 1e-6), then compute grads
# no AP interpolation here
if FP_num > eps:
# fg_grad = regression_loss + ranking_loss
fg_grad[ii] = -(torch.sum(fg_relations * regression_losses) + FP_num)/rank[ii]
# bg_grad += fg_grad/N_FP
relevant_bg_grad += (bg_relations*(-fg_grad[ii]/FP_num))
# aLRP with grad formulation
classification_grads[fg_labels] = fg_grad
classification_grads[relevant_bg_labels] = relevant_bg_grad
classification_grads /= fg_num
cls_loss = 1-prec.mean()
ctx.save_for_backward(classification_grads)
return cls_loss, rank, order
@staticmethod
def backward(ctx, out_grad1, out_grad2, out_grad3):
g1, = ctx.saved_tensors
return g1*out_grad1, None, None, None, None