论文国内地址
这是第一篇将anchor-free
的mAP
值刷入COCO
榜单的论文,主要贡献是将keypoints
的估计方式引入目标检测之中。
主要创新点:
Heatmap
表示目标的坐标left-top、right-bottom
Offset
使得定位更加精确Embeddings
使得两个关键点匹配left、right、top、bottom pooling
层增加目标边缘的定位准确度 使用两个Heatmaps
表示一个目标的左上角和右下角点,例如: L e f t _ t o p = B × C × W × H Left\_top=B \times C \times W \times H Left_top=B×C×W×H ,其中 C C C 表示目标类别,同理右下角点完全相同。对于每一个像素,这是一个分类问题,使用focal-loss
去除类别不均衡问题。对于focal-loss
而言,类别非0即1,然后Heatmap
是使用Gaussian-map
生成的,周围的点都是 v a l u e ∈ [ 0 − 1 ] value \in [0-1] value∈[0−1]。下图展示了,label
周围的点实际也是较好的定位点,不应该直接归结为背景,而且给予一定的权重,基于此得重新设计loss
函数
其中 y c i j = 1 y_{cij}=1 ycij=1 的时候和focal-loss
相同, y c i j < 1 y_{cij}<1 ycij<1 的时候使用 1 − y c i j 1-y_{cij} 1−ycij 作为减少惩罚,如下公式(1)所示
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲L_{d e t}=\frac…
当前的网络都会进行Downsample or Upsample
的操作,使用heatmap
最明显的两个缺点:1)计算量比较大,2)精度不准确。对于前者,这里不讨论,可以参考人体关键点期望分布进行解决。后者是这里解决的方案,直接学习一个offset
参数去解决
o k = ( x k n − ⌊ x k n ⌋ , y k n − ⌊ y k n ⌋ ) \boldsymbol{o}_{k}=\left(\frac{x_{k}}{n}-\left\lfloor\frac{x_{k}}{n}\right\rfloor, \frac{y_{k}}{n}-\left\lfloor\frac{y_{k}}{n}\right\rfloor\right) ok=(nxk−⌊nxk⌋,nyk−⌊nyk⌋)
这里比较简单,不再赘述直接使用SmoothL1-Loss
计算即可
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲L_{o f f}=\frac…
此处方法参考论文:Associative Embedding
已经学习到多组两个角点的位置,如何将其对应?和Offset
处理方式类似,直接使用一个参数(一组参数)去编码当前关键点的组ID
信息
比如:Left-top
点的heatmap
维度为 B × C × W × H B\times C \times W \times H B×C×W×H,Embedding
的维度为 B × W × H × N B\times W \times H \times N B×W×H×N ,其中 C C C为种类信息, N N N为维度信息,这样就为每个目标设定了一个长度为 N N N的vector
信息。当然可以使用 N × M × K . . . N\times M \times K... N×M×K... 等多维度信息去表示。
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲L_{\text {pull …
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲L_{p u s h}=\fr…
#https://github.com/zzzxxxttt/pytorch_simple_CornerNet/blob/767bf0af3229d9ffc1679aebdbf5eb05671bbc75/utils/losses.py#L34
def _ae_loss(embd0s, embd1s, mask):
num = mask.sum(dim=1, keepdim=True).float() # [B, 1]
pull, push = 0, 0
for embd0, embd1 in zip(embd0s, embd1s):
embd0 = embd0.squeeze() # [B, num_obj]
embd1 = embd1.squeeze() # [B, num_obj]
embd_mean = (embd0 + embd1) / 2
embd0 = torch.pow(embd0 - embd_mean, 2) / (num + 1e-4)
embd0 = embd0[mask].sum()
embd1 = torch.pow(embd1 - embd_mean, 2) / (num + 1e-4)
embd1 = embd1[mask].sum()
pull += embd0 + embd1
push_mask = (mask[:, None, :] + mask[:, :, None]) == 2 # [B, num_obj, num_obj]
dist = F.relu(1 - (embd_mean[:, None, :] - embd_mean[:, :, None]).abs(), inplace=True)
dist = dist - 1 / (num[:, :, None] + 1e-4) # substract diagonal elements
dist = dist / ((num - 1) * num + 1e-4)[:, :, None] # total num element is n*n-n
push += dist[push_mask].sum()
return pull / len(embd0s), push / len(embd0s)
由于两个角点光靠局部位置很难确定,对比mask
和bbox
的区别。这里使用创新的pooling
层去解决这个问题。**其实按照现在流行的做法,使用Non-local
、SE
模块去处理可能会更好。**做法非常简单,但需要自己写cuda层实现。
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲t_{i j}=\left\{…
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲l_{i j}=\left\{…
此论文是开创性的,位置毋庸置疑。
缺点也是一目了然–>>
基于CornerNet
的改进版本,主要贡献是速度快精度准,当时是用在移动端利器
主要创新点:
CornerNet
已经存在)depth
、direction
…对于CornerNet
来说,回归两个角点+回归Offset+回归分组信息+NMS,显得特别繁琐,而且计算很慢!这里对其进行如下改进:
Offset
对位置精度弥补由于其核心是使用目标的中心点进行的操作,所以添加其它属性非常方便,如上图中的方向、关键点、深度…
主要做的贡献如下(可能之前有人已提出):
- FPN分阶段回归
- Center-ness Loss
论文整体比较简单,直接从头读到尾没有什么障碍,好像Anchor-free的文章都比较简单。下面直接以模块介绍。
文章中 l ∗ 、 b ∗ 、 r ∗ 、 t ∗ l^*、b^*、r^*、t^* l∗、b∗、r∗、t∗ 表示label, l 、 b 、 r 、 t l、b、r、t l、b、r、t 表示predict
文章直接回归 l 、 r 、 b 、 t 、 c l、r、b、t、c l、r、b、t、c 其中 c c c 表示种类,前面四个在上图中有表示。
回归采用正负样本形式:
文章采用FPN结构,用于提高召回率和精确度。参考Anchor-based(不同尺度的Anchor负责不同大小的目标),文章对不同的层进行限制目标大小:其中 M 1 、 M 2 、 . . . M 6 = 0 、 64 、 128 、 256 、 512 M_{1}、M_{2}、...M_{6} = 0、64、128、256、 512 M1、M2、...M6=0、64、128、256、512,按照 M i < ( l ∗ 、 b ∗ 、 r ∗ 、 t ∗ ) < M i − 1 M_{i}<(l^*、b^*、r^*、t^*)
最后文章发现一个问题,NMS时候出现很多和最终目标接近的框,我们希望的是:负样本和正样本区分明显,而不是很多接近正样本的框(比如分类,虽然可以正确分类,但是出现很多 c o n f = 0.45 conf=0.45 conf=0.45 的目标,我们希望出现 c o n f p o s = 0.99 , c o n f n e g = 0.11 conf_{pos}=0.99,conf_{neg}=0.11 confpos=0.99,confneg=0.11)。
文章通过设置 c e n t e r center center 进行控制,对于那些中心偏离的目标进行抑制。我们不仅仅要IOU好,也要center好。文章通过新建一个新的分支进行center-ness进行回归。
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲enterness $^{*}…
此论文对比anchor-free
和anchor-base
的区别,从而在anchor-base上提出一套自动计算anchor的工具。使用较少,这里略过。
将目标检测Loss和评价指标统一,提升检测精度。这是一篇挺好的论文,下面会将其拓展到其它领域。
主要做的贡献如下(可能之前有人已提出):
- 分类Loss+评价指标
- Regression分布推广到一般性
笔者给出简短说明:
- 先去看一下FCOS论文,其中使用 c e n t e r − n e s s center-ness center−ness 计算预测框质量,两个作用:1)训练时抑制质量较差的框。2)前向计算时用于NMS操作指标。
- 问题来了。。。训练阶段、前向计算、评价指标没有统一?
- 论文魔改一下Focal-Loss、center-ness统一为一个Loss
此部分比较简单,基本和FCOS类似
# 代码出自mmdetection
@weighted_loss
def quality_focal_loss(pred, target, beta=2.0):
"""Quality Focal Loss (QFL) is from
Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes
for Dense Object Detection
https://arxiv.org/abs/2006.04388
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.
Return:
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
# negatives are supervised by 0 quality score
pred_sigmoid = pred.sigmoid()
scale_factor = pred_sigmoid
zerolabel = scale_factor.new_zeros(pred.shape)
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]
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)
return loss
主要包括两个部分:
D e l t a Delta Delta 分布推广到任意分布
限制任意分布
# 代码出自mmdetection
@weighted_loss
def distribution_focal_loss(pred, label):
"""Distribution Focal Loss (DFL) is from
Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes
for Dense Object Detection
https://arxiv.org/abs/2006.04388
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,).
Return:
torch.Tensor: Loss tensor with shape (N,).
"""
# 完全按照论文公式(6)所示,label是真实值(目标框和anchor之间的偏差,参考FCOS)
# pred的shape(偏差*分布),如果没有后面的分布,那就变成delta分布
dis_left = label.long() # label范围[0,正无穷],感觉这里应该-1然后限制一下范围最好。作者说long()向下取整,但是这解决不了对称问题。
dis_right = dis_left + 1
weight_left = dis_right.float() - label
weight_right = label - dis_left.float()
loss = F.cross_entropy(pred, dis_left, reduction='none') * weight_left \
+ F.cross_entropy(pred, dis_right, reduction='none') * weight_right
return loss
这篇论文非常非常的简单,类似加入了一个全局信息的SENet
模块、或者说Non-Local
模块,读懂GFLV1之后,马上解决V2的问题。
此方法在小模型上不适合,在大模型上涨点明显。可以进一步推广,此方案用在大模型non-share Head
中,而小模型都是共享Head
的。