R-CNN
2014年
SVM:https://blog.csdn.net/lyychlj/article/details/118340030
边界框回归:https://blog.csdn.net/zijin0802034/article/details/77685438
Fast-RCNN
2015年
Faster-RCNN
rpn head的输出是包括分类和回归,分类是二分类,只区分前景和背景;回归是仅仅对于前景样本(正样本)进行基于anchor的变换回归。rpn head的目的是提取roi,然后输入到rcnn head部分进行refine。 rcnn head的输出是包括分类和回归,分类输出是类别数+1(1是考虑背景),回归是仅仅对于前景样本不考虑分类类别进行基于roi的变换回归,rcnn head的目的是对rpn提取的roi特征进行refine,输出精准bbox。
faster rcnn可参考:https://www.cnblogs.com/silence-cho/p/13926742.html
正负样本定义
rpn和rcnn的正负样本定义都是基于MaxIoUAssigner,只不过定义阈值不一样而已。
rpn的assigner:
assigner = dict(
type = 'MaxIouAssigner',
pos_iou_thr = 0.7,
neg_iou_thr = 0.3,
min_pos_iou = 0.3,
ignore_iof_thr = -1),
rcnn的assigner:
assigner = dict(
type = 'MaxIouAssigner',
pos_iou_thr = 0.5,
neg_iou_thr = 0.5,
min_pos_iou = 0.5,
ignore_iof_thr = -1),
正负样本定义非常关键。MaxIoUAssigner的操作包括4个步骤:
- 首先初始化时候假设每个anchor的mask都是-1,表示都是忽略anchor
- 将每个anchor和所有gt的iou的最大Iou小于neg_iou_thr的anchor的mask设置为0,表示是负样本(背景样本)
- 对于每个anchor,计算其和所有gt的iou,选取最大的iou对应的gt位置,如果其最大iou大于等于pos_iou_thr,则设置该anchor的mask设置为1,表示该anchor负责预测该gt bbox,是高质量anchor
- 3的设置可能会出现某些gt没有分配到对应的anchor(由于iou低于pos_iou_thr),故下一步对于每个gt还需要找出和最大iou的anchor位置,如果其iou大于min_pos_iou,将该anchor的mask设置为1,表示该anchor负责预测对应的gt。通过本步骤,可以最大程度保证每个gt都有anchor负责预测,如果还是小于min_pos_iou,那就没办法了,只能当做忽略样本了。从这一步可以看出,3和4有部分anchor重复分配了,即当某个gt和anchor的最大iou大于等于pos_iou_thr,那肯定大于min_pos_iou,此时3和4步骤分配的同一个anchor。
从上面4步分析,可以发现每个gt可能和多个anchor进行匹配,每个anchor不可能存在和多个gt匹配的场景。在第4步中,每个gt最多只会和某一个anchor匹配,但是实际操作时候为了多增加一些正样本,通过参数gt_max_assign_all可以实现某个gt和多个anchor匹配场景。通常第4步引入的都是低质量anchor,网络训练有时候还会带来噪声,可能还会起反作用。
简单总结来说就是:如果anchor和gt的iou低于neg_iou_thr的,那就是负样本,其应该包括大量数目;如果某个anchor和其中一个gt的最大iou大于pos_iou_thr,那么该anchor就负责对应的gt;如果某个gt和所有anchor的iou中最大的iou会小于pos_iou_thr,但是大于min_pos_iou,则依然将该anchor负责对应的gt;其余的anchor全部当做忽略区域,不计算梯度。该最大分配策略,可以尽最大程度的保证每个gt都有合适的高质量anchor进行负责预测。
由于rcnn head预测值是rpn head的refine,故rcnn head面对的anchor(其实就是rpn输出的roi)和gt的iou会高于rpn head部分,anchor质量更高,故min_pos_iou阈值设置的比较高,由于pos_iou_thr和neg_iou_thr设置都是0.5,那么忽略区域那就是没有了,因为rcnn head面对的都是高质量样本,不应该还存在忽略区域。
正负样本采样
正负样本定义可以区分正负和忽略样本,但是依然存在大量的正负样本不平衡问题,解决办法可以通过正负样本采样或者loss上面一定程度解决,faster rcnn默认是需要进行正负样本采样的。 rpn head和rcnn head的采样器都比较简单,就是随机采样,阈值不一样而已。
rpn head采样器:
sampler=dict(
type = 'RandomSampler',
num = 256,
pos_fraction = 0.5,
neg_pos_ub = -1,
add_get_as_proposals = False),
rcnn head采样器:
sampler=dict(
type = 'RandomSampler',
num = 512,
pos_fraction = 0.25,
neg_pos_ub = -1,
add_get_as_proposals = True),
num表示采样后样本总数,包括正负和忽略样本,pos_fraction表示其中的正样本比例。add_gt_as_proposals是为了放在正样本太少而加入的,可以保证前期收敛更快、更稳定,属于技巧。neg_pos_ub表示正负样本比例,用于确定负样本采样个数上界,例如我打算采样1000个样本,正样本打算采样500个,但是可能实际正样本才200个,那么正样本实际上只能采样200个,如果设置neg_pos_ub=-1,那么就会对负样本采样800个,用于凑足1000个,但是如果设置为neg_pos_ub比例,例如1.5,那么负样本最多采样200x1.5=300个,最终返回的样本实际上不够1000个。默认情况neg_pos_ub=-1。 由于rcnn head的输入是rpn head的输出,在网络训练前期,rpn无法输出大量高质量样本,故为了平衡和稳定rcnn训练过程,通常会对rcnn head部分添加gt作为proposal。
对正负样本单独进行随机采样就行,如果不够就全部保留。
由于原始faster rcnn采用的loss是ce和SmoothL1Loss,不存在loss层面解决正负样本不平衡问题,故不需要分析loss。