FCOS:优雅的Anchor-Free目标检测
论文地址代码地址
讲完一篇RetinaNet后,打算切入讲一下Anchor-Free的目标检测算法流程。让大家对目标检测中不同类型的检测方法有一个更深刻的印象。

1. FCOS简介

FCOS是一个非常优秀的单阶段、Anhor-Freed的目标检测算法,也是我比较喜欢的Anchor-Free算法之一,不管是idea、性能、部署可行性上都是非常优秀的一篇论文。

2.论文思路

无论是Anchor-Free和Anchor-based归根到底还是去预测box和类别,只是表达方式不太罢了,首先先确定一点FCOS中是有用到FPN层的,用FPN不仅是为了单纯提高检测精度,也是为了弥补算法上的不足。之后补充。先按照写代码的逻辑那样,讲解下在某一层FPN上,网络的输出表达形式是什么,target是什么,loss是什么。看了这么多检测算法,理解并掌握一个算法的精髓就是这三个要素:输出表示什么Targets是什么损失函数怎么设计。主要把握这三点,就能对一个算法有更深刻的印象,也能把不同的算法进行串联。比如Anchor-Based算法其实就是在更改Targets上做了工作,(网络输出+固定的Anchor)=>target。

2.1回归上的表示

框的表示方式目前来看最多的是(x, y, w, h),RCNN系列,YOLO,Retina,Objects as Points等等都是这样的表示方式,这种是最多的,(x1, y1, x2, y2)左上角和右下角的表示方式,代表作CornerNet,FCOS中采用的是**(x,y)+到上下左右四个边界的距离(l, t, b, r)。其中(x, y)怎么来呢?就是这个pixel的位置+取整带来的offset。offset很好理解,比如一个框中心点(212,212),下采样4次后变成了(13.25,13.25),那么在Featuremap上对应的点就只能是(13, 13),那么少的这(0.25,0.25)的偏移量也是需要网络去拟合的。所以box的表示这块需要两个输出,一个是offset_mp(N, 2, H, W),一个是框的边界输出ltbr_mp(N, 4, H, W)。比如那么其中(1, :, 13, 13)这个元素就表示了一个box,中心点是(13, 13),offset由offset_mp上的输出表示,(l, t, b, r)则由ltbr_mp进行表示。如果一个点落在多个框内,则回归面积更小的那个框,为什么是面积最小的那个而不是面积最大的那个?我的理解是面积大的本身正样本就很多了,所以应该给那个面积小的。如下图所示

回归target选择面积小的

回归使用的LOSS是IOU_LOSS,不太懂IOU系列LOSS的人可以看看这篇文章,我觉得说得蛮好的。

2.2分类

分类可是个重头戏,因为这涉及到一个正负样本均衡性问题以及FCOS算法中的一些细节表示问题,首先在FCOS里面是采用了多个二分类进行多分类的思路,这个思路也是非常普遍了,损失函数用的FocalLoss。比如COCO是有80个类别,那么用于分类的FeatureMap大小尺寸是(N, 80, H, W),在每个channel上进行sigmoid之后,就是一个二分类了。为1则表示是这个类别,为0则表示是背景。这也是在多标签任务里面使用的方式,当然没有多标签任务的话inference的时候就是取分数最高的作为它的预测分数和所属类别。

分类中正负样本的划分是这样:如果这个Pixel在这个框内,则他就所属这个类别,即它的target就是这个类别,这一点是和回归一致的。其实到这一步,这个anchor-free的整体方案就已经浮出水面,接下来就请你把自己代入为作者,去思考下这样的anchor-free方案有什么问题?需要怎么解决。

问题1:如果featuremap上某个点不仅在box1中还在box2中,那么它所属哪个类别?

解:这种点就属于模糊样本,首先FPN可以解决大量这类问题,如果不同大小尺寸的box在不同的FPN层上计算LOSS的话,那么就能大大减少这类情况(一张图上有差不多大小的两个同类别框发生了重叠),确实这个想法听上去也是比较make-sense,事实很多算法里都会采用这样的方式。当然这只是减少了而已,如果真的发生的话,就算做面积小的那个类别。为什么是面积小的呢?我的理解是面积小的其分类产生的loss本身就小一些,如果算做面积大的那个类别,那不同类别loss的不平衡性就更大了

问题2:如果这样的话,一个框的中心点和其边缘上的点LOSS是一样大?

这似乎不太make-sense,因为GT框的边缘往往是背景,而中心点一般都是目标。这一点在FCOS中通过center-ness进行了矫正,center-ness就是输出对loss大小进行权重控制的featuremap。思路也很好理解。

centerness计算

就是这样的一个东西,写了段粗糙的代码可视化看下这是个什么玩意:

import cv2
import numpy as np
def draw_centerness(box):
    x1, y1, x2, y2 = box
    w, h = x2 - x1, y2 - y1
    xs = np.arange(w).repeat(h).reshape(h, w).transpose(1, 0).reshape(-1, 1)
    ys = np.arange(h).repeat(w).reshape(h, w).reshape(-1, 1)
    left = xs - x1
    right = x2 - xs
    top = ys - y1
    bottom = y2 -ys
    hm = np.sqrt(np.minimum(left, right)/np.maximum(left, right) * np.minimum(top, bottom)/np.maximum(top, bottom))
    hm = hm.reshape(h, w)
    return hm
hm =draw_centerness([0, 0, 200, 200])
cv2.imshow('hm', hm)
cv2.waitKey()
centerness可视化

就这个一个玩意,看上去像是个耶稣祈福,越亮说明值越接近1,看上去还不错,确实能有边缘抑制的作用。

问题3:怎么用好FPN?

FPN在检测任务中几乎是必备的,一般用法就是说高层检测大目标低层检测小目标,但是在FCOS中加了一个trick处理,上面已经说了每个点的回归LOSS和分类LOSS,CenterNess抑制。那么还有一点就是在FPN结构下的一个样本分配问题,之前已经说明了FCOS中有冲突问题,一个点可能落在不同的GT框中。在FCOS里面是这么做的,不同层集的FPN只对其对应尺寸的框进行拟合.比如一共有5层FPN,那就设置为(0, 64), (64, 128),(128, 256),(256,512),(512,无穷大)这样5个尺度,这么操作之后就大大减少了冲突问题。

你以为这么就完了?注意我们回归的目标是什么(left, top,right, bottom)是中心点到框边缘的距离,那明显就是越大的框回归值越大,这就带来一个回归一致的问题,因此还加了一个Scale参数来进行修正。即回归的预测会乘上一个可学习的Scale参数,这个细节确实说明作者的sense还是相当厉害的。

理解完这些之后再看这张图,就非常清晰明了了.

损失函数就是一个分类+回归损失+centerness损失,下面是分类+回归的损失,centerness损失就是BCELOSS.

分类回归loss

其他Trick:Group Normalization在FCOS中提升效果明显

实验:比较也不是最新的论文,就截几张图吧,不解读了.

centerness热力图显示

和Retina对比以及FPN减少的混淆样本比例

模块的消融实验

CenterNess实验

和其他论文对比

centerness对误检样本的抑制分析

总体来说,实验上也做的很充分,对每个细节都进行了详细的消融实验和分析,学术上的严谨值得学习,论文的idea也非常有意思,精度也很高,也用到了很多常用的优化策略,包括Iouloss,GN等等,细节设置上,不同层的FPN区分混淆样本,Scale矫正不同层的回归值,提高回归精度,centerness抑制误检测框等等一整套思路都非常make sense,也很优雅。还有一个细节就是为什用left-top-right-bottom这样的方式来预测一个Box呢?预测box的方式很多啊,cx, cy,w,h是最常用的,为什么不是预测cx,cy,w,h呢?我想是因为一个Box中内每一个点要回归的box都是它,但是他们在featuremap上的位置不是杨的,cx,cy,w,h不符合这样的方式。其他比如objects as points,retinanet,faster rcnn, yolo等等,在分配target上要么是计算IOU要么是计算中心点落在的位置。所以他们适合用cx, cy, w,h这样的方式。不能看了之后就说只是知道了FCOS中的算法流程,更重要的是为什么这么做?为什么?为什么?不断追问自己,不行就邮件问作者,这样才有所收获。

写完之后感觉脑子思路清晰了好多,希望能对大家有所帮助,写文章不易,如果觉得文章对你有帮助,麻烦给一个免费一键三连,以后会输出更多的干货,如果有什么疑问也留下评论,感谢~