GFocal Loss: 为密集目标检测学习合格且分散的边界框.

目前比较强力的单阶段目标检测器最终的输出基本会包含3个表示:

  1. 分类表示:目标的类别得分(classification score
  2. 检测框表示:边界框的回归结果(bbox regression
  3. 检测框的质量估计(IoU/centerness score

1. 目前存在的问题

现有的表示形式存在如下问题:

  1. 分类预测分值和质量评估分值训练测试不一致
  2. bbox回归采用的表示不够灵活,没有办法建模复杂场景下不确定性

① classification score 和 IoU/centerness score 训练测试不一致

  1. 用法不一致。训练的时候,分类和质量估计独立地训练;但测试的时候却又是乘在一起作为NMS score排序的依据;
  2. 对象不一致。分类分支能够使得少量的正样本和大量的负样本一起成功训练,但是质量估计通常就只针对正样本训练。

② bbox regression 采用的表示不够灵活,没有办法建模复杂场景下的uncertainty

在复杂场景中,边界框的表示具有很强的不确定性,而现有的框回归本质都是建模了非常单一的狄拉克分布,应该用一种general的分布去建模边界框的表示。

2. 解决方案

针对目前目标检测存在的两个问题,提出了两个解决办法:

  1. bbox预测质量和分类分支loss联合表示,解决两分支独立训练,联合测试不一致问题
  2. 采用自发学习的灵活分布建模形式来表示bbox不确定性,具体是采用softmax+积分形式,相当于把回归问题转换为分类问题

① 将bbox预测质量和分类分支loss联合表示

对于第一个问题,为了保证训练和测试的一致性,同时还能够兼顾分类score和质量预测score都能够训练到所有的正负样本,需要将两者的表示进行联合。从物理上依然还是保留分类的向量,但是对应类别位置的置信度的物理含义不再是分类的score,而是改为质量预测的score。这样就做到了两者的联合表示。

对于分类-质量联合表示,label变成了0~1之间的连续值。如果要引入Focal Loss平衡正负、难易样本的特性,需要让其支持连续数值的监督,因此需要对Focal Loss在连续label上拓展,称为Quality Focal Loss (QFL)Focal Loss表示为:

\[FL(p) = -(1-p_t)^\gamma \log(p_t) \\ p_t = \begin{cases} p, & y=1 \\ 1-p, & y=0 \end{cases}\]

QFL则将其修改为:

\[QFL(p) = -|y-p|^\beta \left( y\log p + (1-y) \log(1-p) \right)\]

其中$y$为$0$~$1$的质量标签,$\sigma$为预测。注意QFL的全局最小解即是$\sigma = y$,这样交叉熵部分变为完整的交叉熵。调节因子为距离绝对值的幂次函数,实验中发现一般取$\beta = 2$为最优。

def quality_focal_loss(pred, target, beta=2.0):
    """
    Args:
        pred (torch.Tensor): Predicted joint representation of classification
            and quality (IoU) estimation with shape (N, C), C is the number of
            classes.
        target (tuple([torch.Tensor])): Target category label with shape (N,)
            and target quality label with shape (N,).
        beta (float): The beta parameter for calculating the modulating factor.
            Defaults to 2.0.
    Returns:
        torch.Tensor: Loss tensor with shape (N,).
    """
    assert len(target) == 2, """target for QFL must be a tuple of two elements,
        including category label and quality label, respectively"""
    # label denotes the category id, score denotes the quality score
    label, score = target  # label(N) 类别,score(N),预测bbox和gt bbox的iou

    # negatives are supervised by 0 quality score
    pred_sigmoid = pred.sigmoid()  # (N,class_num)
    scale_factor = pred_sigmoid
    zerolabel = scale_factor.new_zeros(pred.shape)
    # 先假设所有label都是负样本,计算bce loss,乘上sigmoid^beta次方,达到focal效应
    loss = F.binary_cross_entropy_with_logits(
        pred, zerolabel, reduction='none') * scale_factor.pow(beta)

    # FG cat_id: [0, num_classes -1], BG cat_id: num_classes
    bg_class_ind = pred.size(1)
    # 找出正样本索引
    pos = ((label >= 0) & (label < bg_class_ind)).nonzero().squeeze(1)
    pos_label = label[pos].long()
    # positives are supervised by bbox quality (IoU) score
    scale_factor = score[pos] - pred_sigmoid[pos, pos_label]  # 正样本focal值
    # 计算正样本处的bce loss,注意label=score,而不是1
    loss[pos, pos_label] = F.binary_cross_entropy_with_logits(
        pred[pos, pos_label], score[pos],
        reduction='none') * scale_factor.abs().pow(beta)

    loss = loss.sum(dim=1, keepdim=False)  # (N,class_num)在1维度直接求和,变成(N,)
    return loss

① 采用自发学习的灵活分布建模形式来表示bbox不确定性

对于第二个问题,选择直接回归一个任意分布来建模框的表示。在连续域上回归是不可能的,所以可以用离散化的方式,通过softmax来实现即可。

首先作者对所有 gt bbox 映射到特征图维度,并计算所有coco数据集中正样本的回归范围,如下所示:

可以发现最大值大概可以设置为$16$,也就是说分布长度可以设置为$16+1$,而且需要特别指出不同的数据集由于分布不一致,需要针对数据特点重新确定这个超参。

对于任意分布来建模框的表示,它可以用积分形式(通过求和实现)直接转化得到浮点坐标,嵌入到任意已有的和框回归相关的损失函数上。不过如果分布过于任意,网络学习的效率可能会不高,原因是一个积分目标可能对应了无穷多种分布模式。

考虑到真实的分布通常不会距离标注的位置太远,所以额外引入Distribution Focal Loss (DFL),希望网络能够快速地聚焦到标注位置附近的数值,使得他们概率尽可能大。其形式上与QFL的右半部分很类似,含义是以类似交叉熵的形式去优化与标签$y$最接近的一左一右两个位置的概率,从而让网络快速地聚焦到目标位置的邻近区域的分布中去。

\[DFL(S_i, S_{i+1}) = -\left( (y_{i+1}-y)\log S_i + (y-y_i) \log S_{i+1} \right)\]

$y_i$和$y_{i+1}$是浮点值$y$的左右整数值,$S$是输出分布,长度为$17$,可以看出这本质就是一个bce loss,通过计算全局最优解是:

\[S_i=\frac{y_{i+1}-y}{y_{i+1}-y_i}, S_{i+1}=\frac{y-y_i}{y_{i+1}-y_i}\]

学出来的分布理论上是在真实浮点坐标的附近,并且以线性插值的模式得到距离左右整数坐标的权重。

def distribution_focal_loss(pred, label):
    """
    Args:
        pred (torch.Tensor): Predicted general distribution of bounding boxes
            (before softmax) with shape (N, n+1), n is the max value of the
            integral set `{0, ..., n}` in paper.
        label (torch.Tensor): Target distance label for bounding boxes with
            shape (N,).
    Returns:
        torch.Tensor: Loss tensor with shape (N,).
    """
    dis_left = label.long()  # 坐标整数值
    dis_right = dis_left + 1  # 右边整数值
    # 线性权重
    weight_left = dis_right.float() - label
    weight_right = label - dis_left.float()
    # 两个bce loss,并且加权,促使学到的分布是双峰分布,提高优化效率
    loss = F.cross_entropy(pred, dis_left, reduction='none') * weight_left \
           + F.cross_entropy(pred, dis_right, reduction='none') * weight_right
    return loss

QFLDFL可以统一地表示为GFL (Generalized Focal Loss):

\[GFL\left(p_{y_l}, p_{y_r}\right)=-\left|y-\left(y_l p_{y_l}+y_r p_{y_r}\right)\right|^\beta\left(\left(y_r-y\right) \log \left(p_{y_l}\right)+\left(y-y_l\right) \log \left(p_{y_r}\right)\right)\]