美文网首页
ATSS:一种自适应选择正负训练样本的策略(附源码实现)

ATSS:一种自适应选择正负训练样本的策略(附源码实现)

作者: CW不要无聊的风格 | 来源:发表于2020-12-08 18:03 被阅读0次

    Date: 2020/12/03

    Author: CW

    Foreword:

    当今目标检测算法非 anchor-based 即 anchor-free,那么有哪些因素会造成它们之间的性能差异呢?CW前段时间研读了CVPR 2020的一篇paper——Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection,文中指出,anchor-based 和 anchor-free 的本质区别在于如何定义正负训练样本,这也导致了它们之间的性能差异。于是,作者在这项工作中提出了一种称之为 Adaptive Training Sample Selection (ATSS) 的算法以打破这两者的差异性(或者说缩小两者之间的差异,如论文题目'bridging the gap'),同时还能提升它们各自的性能,该算法能够根据目标物体的统计特征自动选择正负训练样本。另外,作者还通过实验证明了不一定非要在图像上的每个位置铺设多个anchor才能获得好的效果,只要正确选择了正负样本,图像中的每个位置仅铺设一个anchor就够了。


    Contents

    I. 结论与贡献

    II. 研究切入点

    III. 实验与论述

    IV. ATSS算法

    V. 源码实现与实验

    VI. 个人看法(+吐槽)


    结论与贡献

    时间最宝贵,说话、写文章就应该结论先行。这篇paper的结论与贡献主要有以下:

    i). anchor-based与anchor-free的本质区别在于如何定义正负训练样本

    ii). 若正确选择了正负训练样本,则没有必要在图像的每个位置都铺设多个anchor

    iii). 提出ATSS算法,能够自动根据目标物体的统计特征选择正负训练样本,缩小anchor-based和anchor-free之间的性能差异,同时提升它们各自原有的性能


    研究切入点

    作者在研究anchor-based和anchor-free两者的本质差别时,分别选择了两者具有代表性的算法RetinaNet(anchor-based)和FCOS(anchor-free) 作为切入点进行实验。FCOS属于anchor-free 中基于中心的方法,另一类是基于关键点的算法,如CornetNet等。这两类中基于中心的方法与 anchor-based的机制较为相似,相当于将anchor-box的角色替换为中心点(域)。

    因此,作者在anchor-free算法里选择了基于中心的方法FCOS与RetinaNet来进行比较。经过分析,作者认为两者主要有以下差异:

    i). 每个位置铺设anchor/中心的数量:RetinaNet是多个,而FCOS是一个;

    ii). 正负训练样本的定义:RetinaNet基于anchor和gt box的IoU,而FCOS基于gt box中心点与铺设点之间的距离和尺度约束;

    iii). 回归bbox的方法:RetinaNet通过anchor中心点的偏移量和宽高比例,而FCOS则通过铺设中心到gt box四条边和中心的距离

    OK,那么以上哪个才是导致anchor-based和anchor-free之间性能差异的决定性因素呢?一起来看看作者的实验与论述吧!


    实验与论述

    作者选择暂时忽略以上第i)点差异,先研究ii)和iii),这好办,将RetinaNet在每个位置铺设的anchor数量设置为1即搞定。

    另外,由于FCOS中使用了许多训练相关(非模型架构本身特有)的tricks,因此需要将这些tricks也施加到RetinaNet上,以进一步消除其它因素的干扰。

    RetinaNet vs FCOS(i)

    由上表可以看到,在逐步施加FCOS所用的tricks后,RetinaNet不断涨点,最终来AP值来到了37.0%,与FCOS只相差0.8%。

    然后,真正的重头戏来了!作者将RetinaNet和FCOS定义正负训练样本策略设置为一致,也就是都使用RetinaNet的策略或者都用FCOS的策略,另外还将它们回归bbox的方法也设置成相同的,即都用anchor进行回归或都用中心点来回归,最终使用控制变量法进行交叉实验。

    RetinaNet vs FCOS(ii)

    在上表中,每行代表在相同的定义正负样本策略下使用不同的回归bbox方法获得的性能(以AP值作为衡量),每列代表在相同的回归bbox方法下使用不同的定语正负样本策略获得的性能。

    首先按行来看,可以发现,在相同的定义正负样本策略下,无论使用哪种回归bbox的方法,性能都几乎没有差异;接着按列来看,还可以发现,使用FCOS那种基于空间和尺度约束的定义正负样本策略,获得的性能较好(无论使用哪种回归bbox的方法)。

    实验结果告诉我们,anchor-based和anchor-free之间性能差异的根源在于如何定义正负训练样本,而回归bbox的方法相对来说是无关紧要的。

    注意,这里的实验中,将RetinaNet在每个位置上铺设的anchor数量设置为1,而原始是9,结合这个实验结果,其实也可以说明在每个位置上铺设多个anchor并非是一个必要手段(因为在这个实验中,anchor数量少了,反而涨点了)。当然,作者也通过进一步的实验进行了验证。

    RetinaNet的anchor设置比较

    表中第一行代表原始RetinaNet的结果,第二行+Imprs代表使用了FCOS的各种训练tricks之后的改进版对应的结果,从后面行的结果可以看出,使用了作者提出的ATSS策略后,每个位置上anchor铺设的数量已对性能影响不大。


    ATSS算法

    对于作者所提出的ATSS算法,读完下图的伪代码,基本也就懂了。

    ATSS算法的输入输出 ATSS算法流程

    算了,不偷懒,还是概述下吧。

    i). 对于每个gt,在每个金字塔特征层都选出k(默认k=9)个候选anchors,这些候选anchors是中心到gt box中心最近的k个,若有L个金字塔特征层则每个gt都会选出k*L个候选anchors;

    ii). 对于每个gt box,计算其每个候选anchor和它的IoU,从而进一步计算出这些IoU的均值和标准差,然后将IoU阀值设置为均值和标准差之和

    iii). 对于每个gt,在候选anchors中选出IoU不小于阀值并且中心在也这个gt box内的作为正样本,其余则为负样本

    算法本身并不复杂,但有几个点需要特意指明下:

    1. IoU阀值为何这样设置?

    候选anchors的IoU阀值代表了和gt匹配的平均水平,而标准差则反映了这些候选anchors与gt匹配程度的差异性,标准差大代表可能在某个特征层上anchors与gt匹配得较好,这批anchors的质量更高,因此将阀值设置为均值的基础上再加上标准差就能够筛选出更高质量的anchors

    2. 为何将正样本anchors的中心限制在gt box内?

    以我们的直观认知来看,anchor中心离gt box中心越近,通常匹配得更好,从而回归出来的bbox质量更高。另外,在RetinaNet中,bbox中心离gt box中心越近,通常IoU都比较大,代表产生的预测效果好;在FCOS中,预测的定位点离gt box中心越近也将产生更高质量的检测。因此,离gt box中心越近通常能够产生更高质量的检测结果

    3. ATSS能够为小目标也公平地分配正样本,不会倾向于为大目标分配更多的正样本

    RetinaNet和FCOS都会倾向于为尺度较大的目标分配更多的正样本,而ATSS却能够避免这个现象,为何?

    首先,作者认为,anchors与gt box的IoU呈近似正态分布,那么根据统计定律,IoU位于[mean+std, 1]这个区间的大约会有0.16k*L(实际的实验结果显示约为0.2k*L)个anchors,k和L都是定值,因此每个gt分配得到的正样本数量差不多。虽然还会除去中心不在gt box内的那批,可能会觉得小目标的候选anchors会被去掉的多,毕竟小目标的框比较小。但是想象下,对于小目标,若某个候选anchor与其IoU在阀值以上,那么大概率这个候选anchor的中心会位于gt box内。

    4. 超参数k的设置

    作者认为,k不算作超参。

    k值设置对性能的影响

    实验发现,模型性能对算法中k值的设置并不敏感,从7到17对应的AP值几乎没有差异。k太大(如19)由于引入了太多低质量的候选框,从而降低性能;而k太小(如3)则应该是提供的候选样本太少导致统计值不足以代表分布,因而性能明显下降。

    综合来看,参数k的鲁棒性还是较好的,因此不算作一个需要精调的超参。


    源码实现与实验

    根据上一节所述的算法pipeline,ATSS的实现思路主要包括以下几个环节:

    1. 为每个gt在每个特征金字塔层找出k个候选anchors,候选anchors是中心到gt box的中心距离最近的k个。若有L个特征层,那么这一步会为每个gt分配k*L个候选anchors

    2. 计算每个objects的统计特征,从而为每个gt设置一个IoU阀值,以便进一步从候选anchors中筛选出IoU不小于阀值的作为正样本候选。这里所谓的统计特征其实是候选anchors与对应的gt boxes的IoU均值和标准差

    3. 第2步筛选出来的anchors还不是正样本,还要满足中心在对应的gt boxes内的才能作为正样本,其余anchors(包括非候选anchors)都被视作负样本,因此还需要判断下anchors中心是否在对应的gt boxes内;

    4. 由于一个(正样本)anchor可能同时作为多个gt的正样本,因此我们需要找出与anchor有最大IoU的gt,然后分配给这个anchor,使得每个正样本anchor仅对一个gt负责注意,负样本anchors不需要分配gt,为了与正样本区分开来,在实现时可以将负样本的IoU设置为一个无效值(可以用负数,因为IoU的值域在0-1区间)。这样,若某个anchor与gt boxes的最大IoU是这个无效值,则说明其是负样本,直接给它分配背景类标签。

    OK,整体思路过了一遍,现在来看看具体的源码,以下是CW基于Pytorch的实现。

    以下的anchors和targets变量都是list,代表一个batch的数据,list中每个元素对应batch中每张图像的数据。每张图像的anchors包含所有特征金字塔层对应的anchors,num_anchors_per_level用于指示各层分别有多少个anchors。超参k设置为默认值9,代表为每个gt在每个特征层都找出9个候选anchors。

    for循环代表依次处理batch中的各张图像,先考虑例外情况,判断下图像中是否有目标物体,若没有,那么直接将所有anchors作为负样本,即全部都分配背景类标签,搞定!不需要再执行ATSS算法的pipeline,简单省事。

    atss源码实现 (i)

    若图像是有目标物体的,那么接下来就真正开始执行算法的pipeline。

    首先执行上述的第1个环节,也就是找候选anchors。在计算gt boxes中心与anchors中心的l2距离时,是对图像中所有gt boxes和anchors两两之间进行计算的,因此需要用num_anchors_per_level这个变量对计算结果进行划分(这里使用torch.split()方法),从而得到在各个特征金字塔层对应的结果。

    接下来就可以在各特征层选出k个候选anchors(当某层anchors数量不足k个,那么该层所有anchors都会被选中),需要注意的是这里topk()方法返回的anchors索引对应的是在当前特征层的索引,因此需要加上前面所有层的anchors数量才是在全局(所有anchors)中的索引。

    atss源码实现 (ii)

    候选anchors选出来后,开始进入第2个环节,这个环节会在候选anchors中区分出哪些是IoU大于阀值的,以便作为正样本候选(注意此处还不是最终的正样本)。最终得到的是一个mask,,即以下的is_pos_candidates变量。

    atss源码实现 (iii)

    然后,开始实现第3个环节所述的部分。在这里,我先对所有anchors与所有gt boxes两两之间进行判断,判断的是anchor的中心是否在gt box内,从而得到一个mask,也就是以下的is_in_gt_boxes变量。由于前面在实现第1个环节时得到了候选anchors的索引,因此我就可以利用这个索引在这个mask中得到候选anchors对应的mask(这里使用了Pytorch的gather()方法),这样就可以知道候选anchors中哪些anchors的中心是在gt boxes内的了。当然,你也可以直接用候选anchors(而不是所有anchors)与gt boxes两两之间进行判断来得到mask,就看各人风格了。

    最终,将这里得到的mask与实现第2个环节时得到的mask相与(and &),从而得到一个可以将候选anchors区分为正负样本的mask。注意,这个mask对应的是候选anchors而非所有anchors!

    atss源码实现 (iv)

    还没完哦,还有第4个环节提到的部分需要实现,这里先处理负样本,也就是要将它们与gt boxes对应的IoU设置为无效值(这里用-1代替),以便后续与正样本区分开来。以下for循环代表依次对每个gt box与所有anchors的IoU结果进行处理。

    从目标出发,我们需要知道负样本的全局索引(或者说得到一个mask),然后才能将无效值设置上去,那么怎样才能做到呢?

    利用我们的现有条件,在以上实现过程中,我们得到了候选anchors的全局索引以及能够区分候选anchors是否为正样本的mask。因此,不妨先用这个mask在候选anchors的全局索引中取出正样本anchors的全局索引(即以下的pos_anchors_indices变量)。然后,将所有anchors的索引与正样本anchors索引比较,这样就可以得到mask用于区分正负样本了,并且,这个mask对应的是anchors的全局索引!

    但是这里还需要进行一些预处理才能将所有anchors的索引与正样本anchors的索引进行比较,因为两者的shape不一致!我这里是将正样本anchors索引扩展一个维度(在dim1扩展),这样就会由于广播机制使得每个正样本索引都与所有anchors的索引进行比较,得到的每行(有多少个正样本就有多少行)结果仅有一个位置为True,并且各行为True的位置都不会处于同一列(每一列对应每个anchors全局索引)。然后将这个结果在dim0上sum起来(也就是把所有行加起来)同时转换回布尔类型,这样就得到了所需的mask,对其进行取反(not ~),得到以下的is_neg_of_all变量,那么这个mask中True对应的位置即代表负样本。

    有了这个mask,我们就理所当然地可以取出负样本对应的IoU了,然后将原来的值替换为无效值。

    atss源码实现 (v)

    别急别急,还差一步就大功告成了,现在是时候为每个anchor分配标签了。如第4个环节所述,我们要让每个正样本anchor仅对一个gt负责。首先,我们取出各个anchor与gt boxes的最大IoU以及对应的gt(索引)。经过前面的处理,如今只有正样本anchors对应的IoU是真实的值,而负样本的已经被设置为无效值(-1)了。然后,利用这个条件就可以区分出正负样本,从而为各个anchor分配对应的标签和gt box。

    在这里,我的实现是用在Faster R-CNN的RPN上的,因此只需做二分类区分前背景即可,所以我将matched_ious != -1得到的mask直接转换为float32类型就可以得到非0即1的二分类标签值。如果是多分类,则先将各anchor的标签设置为匹配的gt对应的类别,然后再将负样本(可以利用matched_ious == -1这个mask)的标签设置为背景类进行覆盖。

    至于负样本匹配的gt box可以不必太在意,后续计算回归loss时会根据标签是否为前景从而决定是否要参与计算,因此,只要分配的标签对了即可,负样本匹配的gt box根本不会被用到。

    atss源码实现 (vi)

    接下来谈谈CW基于这份源码实现所做的实验。

    这里展示的源码实现是嵌入到Faster R-CNN的RPN中的,由于Faster R-CNN是two-stage架构,因此在第二阶段还有一次划分正负样本的操作(对RPN输出的RoI进行划分),因此CW也试着在第二阶段使用ATSS,这里的样本由于是RPN的预测结果,不像anchor在各特征层有固定数量(在这里相当于将所有RoI视作在同一层),因此不能在各特征层去选择候选,所以对于每个gt在所有RoI样本中选择top k作为候选时,我尝试设置了不同的k值:9、25、45,实验发现k=45时性能最好,这相当于在RPN中使用k=9的情况(5层金字塔总共45)。根据原作的实验结果显示,每个gt最终会选出大约0.2k*L个正样本,也就是若为每个gt分配45个候选的话,最终会有大约9个是正样本,我的实验结果也映证了这一点(无论是在第一阶段还是第二阶段)。

    另外,我尝试了仅在RPN中使用ATSS,而第二阶段保持Faster R-CNN原有的策略,发现模型的性能比在两个阶段都使用ATSS更好,无论是AP值还是AR值。

    综合我的实验,模型评估结果(基于AP值和AR值)如下:

    仅在RPN使用ATSS > 两个阶段都使用ATSS > 不使用ATSS(即原本的Faster R-CNN)

    需要特别指出的是,在我的实验中,使用ATSS后,小目标的AP值都会显著提升!估计是原本基于IoU定义正负样本的策略会倾向于为大目标分配更多正样本,从而让小目标“感到了寂寞”,这也在一定程度上映证了前文所述的“ATSS能够为小目标也公平地分配正样本,不会倾向于为大目标分配更多的正样本”


    个人看法(+吐槽)

    虽然作者声称ATSS是'adaptive',但是细心地看下整个算法流程,不难发现其实质还是基于hard规则来定义正负样本从而分配标签的,只不过其规则由单纯地基于IoU变为基于IoU分布,外加空间约束(中心在gt box内)。

    为何说不是adaptive?因为anchors是不变的,那么anchors与gt的IoU分布(统计的均值与标准差)就不变,从而每个gt设置的IoU阀值在训练过程中是不会变的,进而每个gt筛选出来的正样本在每次训练迭代中也是一样的,哪里adaptive了?估计作者之所以说adaptive可能是因为这种规则是基于统计得到的,然而实质上这个统计结果并不会变..要说adaptive的话,还是FreeAnchor:一种自由匹配标签的策略(附源码实现)比较够意思,算得上是个诚实的孩子。

    另外,作者在paper中说到anchors与gt boxes的IoU呈正态分布这点也有点鬼畜,直接有种让人不禁想怼过去的冲动:凭啥就呈正态分布了!?(不是CW怼的,我只是替想怼的偷偷表个态~)如果有更严格的证明就更好了(可能这个根本就无法证明,不同数据集和不同的anchor设置可能会使得结果根本不会仅呈一种分布)。

    然而CW并不是说ATSS不好使,起码在我的实验中还是能涨点的,特别是对于小目标。另外,ATSS对于代码实现也十分友好,相对于FreeAnchor原作的实现来说简洁易懂很多,看来诚实的孩子内心却可能比较复杂..

    其实,我最欣赏的是作者在此项工作中探究anchor-based和anchor-free区别时所做的实验,首先是为各自选择具有代表性且两者之间又有共性的算法,分别是RetinaNet和FCOS;然后排除一些无关因素(或者说不是实验主要研究对象)的干扰,将两者尽量改造到一致,使得两者的差别仅在于需要探究的方面;接着通过控制变量法进行交叉实验,最后得出结论。整体实验不复杂、不骚气、不难懂却又有一定说服力,配得上三好学生的称号!

    总而言之,还是十分感谢大佬的工作,让CW既能学习又能过把吐槽瘾!

    相关文章

      网友评论

          本文标题:ATSS:一种自适应选择正负训练样本的策略(附源码实现)

          本文链接:https://www.haomeiwen.com/subject/kbnmwktx.html