1. 什么是正负样本?
- 对于像YOLO系列的结构,正负样本就是feature map上的每一个grid cell(或者说对应的anchor)。
- 对于像RCNN系列的结构,RPN阶段定义的正负样本其实和YOLO系列一样,也是每一个grid cell。RCNN阶段定义的正负样本是RPN模块输出的一个个proposals,即感兴趣区域(region of interesting,roi),最后会用RoIPooling或者RoIAlign对每一个proposal提取特征,变成区域特征,这和grid cell中的特征是不一样的。
- 对于DETR系列,正负样本就是Object Queries,与gt是严格的一对一匹配。而YOLO,RCNN是可以多对一的匹配。
通常情况下,检测问题会涉及到3种不同性质的样本:
- 正样本(positive)
对于positive,一旦判定某个grid cell或者proposal是正样本,你就需要对其负责cls+bbox的训练。 - 忽略样本(ignore)
ignore最大的用处就是可以处理模棱两可的样本,以及影响模型训练的样本。所以对于ignore,对其不负责任何训练,或者对其负责bbox的训练,但是不负责cls的训练。 - 负样本(negative)
对于negative,只负责cls的训练,不负责bbox的训练。
2. 怎么定义哪些是正样本/ignore/负样本
常规使用的方法:
- 借助每个grid cell中人为设置的anchor,计算其与所有gt(ground truth)的iou,通过iou的信息来判定每个grid cell属于positive/ignore/negative哪种。
- 以当前gt为中心的一定范围内,去判定每个grid cell属于哪种样本。
在具体的自动驾驶量产项目中,往往会根据实际需求,比如对precision和recall的要求,在与gt匹配的逻辑中,会从类别、大小等角度去考虑,另外还会考虑特殊标记的gt框(hard、dontcare)。
有以下几个原则:
- 数量少的类别A,为其尽可能匹配适当多一点的anchor,数量多的类别B,为其匹配少量且高质量的anchor。这样做目的是提高A的recall,提高B的precision,保证每个batch中,各类别间生成的正样本数量趋于1:1
- 为小目标匹配高质量的anchor,忽略其周围低质量的anchor。这样做是为了减少小目标的误检,可能在一定程度上牺牲了召回。
- 对于中大目标,就要考虑具体那个类别的数量了,数量少的类别匹配多一点,数量多就少匹配。
- 对于特殊标记的gt框,如hard、dontcare, 如果一些负样本和这些hard、dontcare强相关,那么把这些负样本变成ignore,避免让样本间产生歧义。
正负样本的定义过程是一个迭代的过程,会根据模型的实际训练过程以及测试效果来动态调整,比如模型对某个类recall偏低,那么此时我们就要增加该类生成正样本的数量了。
定义的过程就是将正负样本严格区分开,为后续的采样提供方便,如下图,将从正样本过渡到负样本的这些样本归入ignore。
3. 采样哪些正负样本参与训练
个人认为:该部分是训练检测模型最为核心的部分,直接决定模型最后的性能。理解正负样本的训练,实质是理解正负样本的变化是如何影响precision和recall的。
我们先考虑3个基本问题,对于某个类别gt:
- 假设我们希望precision=1,不考虑recall,那么属于该gt的并且参与训练的正负样本理想情况会是什么样的?
正样本:数量越多越好,并且质量越高越好。
负样本:多样性越丰富越好,数量越多越好(实际已经满足数量多的情况)。
2. 假设我们希望recall=1,不考虑precision呢?
正样本:数量越多越好。
负样本:数量为0最好。
3. 现在我们希望precision=1, recall=1呢?
正样本:数量越多越好,并且质量越高越好。
负样本:多样性越丰富越好,并且数量越多越好。
从以上3个问题分析得到,对于某个类别的gt,属于该gt的正样本中,数量和质量是矛盾的。数量越多,那么质量必然下降,recall会偏高,precision会偏低。反之,数量越少,质量会高,但是recall会偏低,precision会偏高。对于负样本来说,要求它数量越多,并且多样性越丰富,这并不矛盾,实际是可以做到这点。
有人会问,不看mAP吗?
mAP是综合衡量了recall从0到1变化的过程中(实际recall达不到1),precision的变化曲线,mAP并不直观,实际把mAP当做其中一个衡量指标而已。
所以,我们采样的目标就是:
- 正样本:质量高,数量适当
- 负样本:多样性越丰富,数量适当(或者说是正样本数量的n倍,n一般取值[3,10])
一般情况下,定义的那些正样本都会采样参与训练,负样本就随机采样一些去训练。但在训练的过程中你需要考虑几点:
1. 定义的那些正样本,模型真的都能搞定吗?
在量产级的数据集中,往往会有百千万量级的目标,虽然在定义正样本的时候考虑到了很多因素,但是面对百千万量级的目标,往往会存在一定比例的正样本,模型压根就学不会,训练后期模型loss就在一个小区间里震荡,所以我们就要对这些样本做进一步处理,把其归为ignore,减少他们对模型训练的影响。
对于FN(漏检),我们就要根据具体的需求分析这些FN到底是否需要检出,如果需要检出,就需要调整定义这些FN的正样本的匹配逻辑,让其产生适合训练的正样本。
2. 面对数量众多的负样本,怎么针对性的采样(适应自己的项目)。
其实在项目前期,负样本的采样可以选择随机,但当你进行大量路采数据测试后,总结发现模型输出的FP,比如,发现模型输出大框背景的频次偏高,那么这个时候我们就要改变随机采样负样本的策略,就要针对性的增加小分辨率feature map上的负样本的采样。如果模型经常把特定背景(树尖,房屋)检测为目标,那么我们需要1. 检查gt的标注质量。2. 想办法采样到这类的负样本参与训练。
3. 尽可能保证每个batch中,类别间采样的正样本比例为1:1。
在量产级数据中,因为是实车采集,往往会出现类别不均衡现象,随着数据量的不断增加,这种不均衡会被严重放大,如果直接采样全部正样本采样训练,模型很可能出现precision和recall偏向类别多的那个类,比如类A,这个时候就需要考虑适当降低类A的采样,同时考虑适当增加类B类C的采样训练,来达成类别间正样本的比例接近1:1。
所以,正负样本的采样是根据当前模型的检测效果来动态改变优化的,但是不管怎么改变,对正负样本的采样不会偏离理想状态的,只不过离理想状态的距离由自己手头的数据集标注质量决定。
如有错误,请指正。