Date: 2020/11/25
Coder: CW
Foreword:
本文将要介绍一种为训练样本分配标签的策略,这种策略称作 FreeAnchor,注意不是 anchor free 哦!FreeAnchor 是用于 anchor-based 体系下的策略,那么它到底free在哪里呢?anchor还能玩起freestyle?
是这样的,FreeAnchor 指的是在训练过程中让anchor能够根据模型当前的表现来自由匹配标签,而非基于硬性规则为anchor分配标签。什么叫基于硬性规则?最常见的就是基于IoU的规则,即当anchor和某个gt box的IoU超过预设的阀值时,就将其作为这个gt的正样本(也就是将这个gt的类别分配给该anchor作为标签)。若anchor匹配到了多个gt boxes,那么可以将IoU最大的那个gt分配给anchor。
OK,与基于硬性规则分配标签相比,FreeAnchor的优势在哪里呢?通常情况下,使用硬性规则来分配标签并无大碍,但是在一些场景下,这样训练出来的模型效果并不好,而使用FreeAnchor却能克服这个问题,下面就让CW为大家开启这场知识盛宴吧!
Contents
I. 提出问题:基于硬性规则分配标签带来的问题
II. 设计新方案:怎样才算是好的方案
i). 保证召回率
ii). 提高精度
iii). 兼容NMS
III. 具体方法:极大似然估计
i). 召回率似然函数
ii). 精度似然函数
-Saturated Linear 函数
iii). 从似然函数到损失函数
-Mean-Max 函数
IV. 实施行动:源码实现
基于硬性规则分配标签带来的问题
用前文提到的基于IoU的规则来举例说明,看下面这张图。目标物是月亮,绿色框代表其gt box,红色框代表anchor,直观上来看两者重叠部分还蛮大的,在这种规则下,通常会将这个gt分配给anchor。然而我们可以看到,anchor框内的区域几乎不包含有月亮的部分,大部分都是黑色的背景,从而模型在这个框内看到的也大部分是属于背景的特征,但是在这种规则下却硬要这个anchor去负责预测月亮,岂非“强anchor所难”..
另外,特别是在训练初期,网络接近于随机预测,基于这个anchor回归出来的proposal很可能如下绿色框所示(画工比较有个性别吐槽..):
但是此时的规则却将这个object标签分配给它去计算loss,模型难免会说:太难了吧~!
因此,对于像上述月亮这种“偏心”(即目标物中心不在gt box几何中心)的物体,这种基于IoU来为训练样本分配标签的做法显得很不友好。
怎样才算是好的方案
和谈恋爱一样,强求是不会有幸福的,那么怎样的匹配方式才好呢?我们推崇自由匹(恋)配(爱)。
保证召回率
首先要保证的是召回率(Recall),就像考试做题,不要留空白,能答上来的就写上,不能答上来的也尽量扯。对于每个gt,至少要有一个anchor去匹配,也就是至少要有一个anchor对应的proposal负责预测它。
提高精度
精度(Precision)的计算公式是,要提高精度最直观的方式是尽量减少FP(False-Positive)。FP就是那些定位错误却预测为目标物体的proposals。因此,对于定位错误(不好)的那些proposals,我们要将其分类为背景。
兼容NMS
大部分目标检测的后处理都会用到NMS,它会将预测框排序,使得分类置信度高的排在前面,然后每次都以置信度最高的为基准,去掉与其重叠度(通常用IoU作评判)高的冗余框。因此,FreeAnchor策略希望能够兼容NMS,避免在这个过程中有预测框定位得很好但是由于在分类上的得分不够高而被干掉了,定位好的才是靓仔,这可是看脸的世界。
极大似然估计
OK,想法是有了,那么具体的方法呢?
作者很优秀,将这个问题设计成极大似然估计问题,极大似然估计是用频率来描述概率的一种思想,在这里就是在训练过程中使用模型的预测结果去寻找合适的目标,从而再反过来推断模型的参数。
在anchor-based体系下的目标检测任务中,loss函数通常设计为如下形式:
其中和分别表示正负样本anchor集,表示目标物体集合,分别表示正样本anchor对应的类别预测和回归预测的损失,{0,1},等于1代表anchor j 和object i 匹配,反之亦然,代表负样本anchor对应到背景的分类预测损失。
作者构思的最大似然估计也是从这个公式下手进行设计的:
由于{0,1},因此上式可继续转化为如下:
其中各形式的就代表对应损失的似然概率,似然概率越大,损失函数越小。
到目前为止,以上还是基于硬性规则来分配标签的,就是根据IoU的规则来确定其值是0还是1的,而且是分开优化的,FreeAnchor希望的是,能够在训练过程中让anchor根据模型当前表现自由地匹配相应的标签,同时定位好的proposal在分类上的得分也要高,于是还需要进一步的设计。
召回率似然函数
在上一节说过,首先要保证召回率。对于每个目标物体,我们都为其选择k(原作中k=50)个和其IoU最大的anchor作为候选正样本,然后定义优化召回率对应的似然函数如下:
表示分类置信度,代表候选anchor j 属于object i 的概率;表示定位置信度,代表候选anchor j 定位object i的得分;代表的是,对于每个object i,计算其k个候选anchors的分类置信度与定位置信度乘积,然后取最大值;整体来看,就是所有object所得最大置信度的乘积。
从这个公式可以看出,FreeAnchor将分类置信度与定位置信度放在一起进行优化。
精度似然函数
接着,为了提高精度,定义精度似然函数如下:
代表anchor j(对应的proposal)属于背景的置信度,注意这个置信度是从定位方面去衡量的,反映的是定位的好坏程度,定位得越差(proposal与object的IoU越小),这个值就越大;代表anchor j (对应的预测结果)属于背景的概率,对应背景的分类置信度。
虽然明白了公式中各项的意义,但你可能还是有点懵,为什么优化精度要这样设计呢?
这样来看,若一个anchor定位比较差,明明深圳在广东省你却将bbox定位到北京去了,它对应的值就比较大,那么我们肯定希望将它分类成背(北)景(京),即它对应的要尽可能大,否则它将成为一个FP(定位差却分类成前景),这样才能使乘积降下来,从而让的值变大,而就是对每个anchor j都进行这样的计算,最后乘起来,有联合概率的意思。
总地来说,就是希望定位差的anchor(对应的预测结果)被分类成背景。
Saturated Linear 函数
作者定义的公式如下:
表示anchor j 能匹配object i 的概率,而表示anchor j与每个物体计算匹配概率,取其中的最大值。作者认为,的定义应该满足以下3个性质:
i). anchor j 与 object i 的IoU越大,这个概率值应该越大;
ii). anchor j 与 object i 的IoU小于预设的阀值时,这个值应该被置为0;
iii). 对于每个object i,有且只有一个anchor j 满足=1
前面说到过,反映的是anchor定位的好坏程度,以上性质与这个考虑也是相吻合的。另外,这些性质也能兼容NMS。
最终,其计算方式被设计为一个称作saturated linear的函数,形式如下:
从似然函数到损失函数
最终的似然概率函数是召回率似然概率函数与精度似然概率函数乘积:
讲似然概率函数讲得有点high..我们还是要回到模型训练中来,既然要训练就离不开loss函数,因此需要将这个似然函数转换为loss的形式去优化:
Mean-Max 函数
你有没有发现,对于,每个object只会从其候选anchors中选出最大的参与训练。这样,对于训练初始阶段来说,参与训练的anchors样本貌似就太少了。另外,在训练初期,网络参数随机初始化,所有anchors的置信度都比较低,具有最高置信度的anchor也不一定是匹配程度最好的anchor。因此,需要让更多的anchors参与到训练中来,待训练得差不多(模型已经成为老司机)了,再选max的来训练。
为了解决这个问题,作者机智地设计了一个Mean-Max函数来取代Max函数,公式如下:
在各项值都较小趋近相同时,函数输出值近似于均值;当其中某项值较大,而其它项较小,并且差异明显时,函数输出值就倾向于这个较大的值。相当于从mean过渡到max的效果,正好与训练前期和后期模型输出的置信度对应起来,妙哉妙哉~!
除了使用Mean-Max函数取代Max函数,还对loss中的第二项使用Focal Loss,同时为loss中的每项都加上相应的权重:
源码实现
费了不少口水(抱歉,忘了我是在码字,不需要动嘴..),对于coder来说,只知道理论不会写代码相当于什么都不知道,来吧,看看代码怎么写。哦,对了,添一句,以下实现基于Pytorch框架。
这里我使用一个类来封装,先把需要的参数进行初始化:
然后loss的计算封装在__call__()方法里,这样能将这个类对象当作函数来调用:
这里我的实现兼容了FPN多尺度,classifications和regressions分别代表分类和回归预测,anchors包含了特征金字塔所有层级的anchors,annotations是图像的标注,包括目标物体的bbox和类别。
输入进来的是一个batch的数据,为了思路更清晰,对每张图片依次进行计算:
由于在标签制作时,为了使得一个batch的标注能组成一个tensor,于是我将各张图片的目标物体数目填充到一致,对于填充的物体,其类别标签设置为-1,这样就能将其和真实的物体区分出来。
接下来先计算,也就是候选anchors(对应的proposals)的分类置信度。注意这里要将每个object的候选anchors对应的置信度设置到object的对应类别下(这里使用了Pytorch的gather()方法)。
然后计算,代表候选anchors(对应的proposals)的定位好坏程度。
红框部分与前文所述的公式对应:
顺便贴下smooth l1 loss的实现:
有了以上两个似然概率,我们已经可以将loss公式中的第一项计算出来了,这部分称作positive loss,因为其是针对正样本的,要求它对应的似然概率越大越好(loss当然还是越小越好):
其中positive_bag_loss的实现如下:
先将联合概率输入Mean-Max函数计算,得到转换后的概率,然后使用二元交叉熵计算损失,这里直接将目标值设置为1,因为此处输入到交叉熵函数的预测变量是概率,我们希望这个概率越大越好。
loss中的第一项已经搞定,是时候从第二项下手了,先计算:
注意这里要取消梯度!因为FreeAnchor的思想正是根据网络的预测结果来动态分配标签,所谓似然估计正是如此。另一个红框部分对应的就是saturated linear函数。
有了,计算自然水到渠成:
这里可能有点绕,在实现的时候,这个概率并不是如公式般将anchor与object对应起来,而是将这个值设置在anchor与object对应的类别下,这是因为这个概率需要与网络的分类预测输出乘在一起(element-wise multiply),维度需要对应起来。作者在实现这部分时用了稀疏矩阵(torch.sparse_coo_tensor),没那么好理解,CW改成了这种比较low的形式,虽然计算速度可能稍受影响,但相对来说更直观、易理解。
至此,每张图像需要的计算已完工,下面可以来汇总下结果,还是先处理loss中的第一项:
接着是loss中的第二项,称作negative loss,最后把两项loss乘上对应权重加起来得到最终的loss:
注意这里实现的时候,对应于公式中的部分,我们直接使用网络的分类预测就OK了。哦,放心,CW不会漏了negative_bag_loss的:
别被名字骗了,实质上就是一个focal loss,与positive_bag_loss类似,其中也用到了二元交叉熵,不同的是,这次的目标值设置为0,因为这里针对的是FP,既然是FP当然希望它输出的置信度越小越好。
至于以上box_encode和box_decode的部分,这里就不贴出来了,了解anchor-based算法的应该都知道,随便三两下就码出来了。
End
如今,像FreeAnchor这种在训练过程中引导网络自由学习匹配标签的策略越来越多,突破了传统基于hard规则分配标签的束缚,咋一看倒是越来越智能了,希望这个世界早日从人工弱智进化到人工智能,干巴爹酷纳塞!