目标检测问题总结
- 目标检测二阶段和一阶段的核心区别
- 目标检测二阶段比一阶段的算法精度高的原因
-
- [1. 正负样本不平衡](#1. 正负样本不平衡)
- 2.样本的不一致性
- 如何解决目标检测中遮挡问题
- 如何解决动态目标检测
- FPN的作用
- 如何解决训练数据样本过少的问题
- IOU代码实现
- NMS代码实现
- NMS的改进思路
目标检测二阶段和一阶段的核心区别
目标检测技术从阶段上分为两种,一阶段和二阶段。
二阶段的核心思想是:首先提出proposal框 ,通过第一阶段的网络回归出目标框的大致位置 、大小 以及是前景的概率 ,第二阶段是通过另一个网络回归出目标框的位置、大小及类别。
一阶段网络的核心:对于输入图像,通过网络直接回归出目标大小、位置和类别
目标检测二阶段比一阶段的算法精度高的原因
1. 正负样本不平衡
当某个类别的样本数特别多的时候,训练出来的网络对该类的检测精度往往会比较高,而当某一类的训练样本数较少的时候,模型对该类目标的检测精度就会有所下降。
对于一阶段目标检测来说,它既要做定位又要做分类,最后几层中1X1的卷积层的loss都是混合在一起,没有明确的分工。
对于二阶段目标检测来说,在RPN网络结构中进行了前景和背景的分类和检测,这个过程与一阶段的目标检测直接一上来就进行分类和检测要简单的多,有了前景和背景的区分,就可以选择性的挑选样本,这样正负样本就更加的均衡,然后重点对一些参数进行分类训练,训练的分类难度会比一阶段目标检测直接做混合分类和预测框回归要来的简单很多。
但是一阶段检测算法要比二阶段检测算法快。
2.样本的不一致性
RPN获得多个anchors的时候,会使用一个NMS,在进行回归 操作的时候,预测框和标签的IOU同回归后预测框和标签的IOU相比,一般会有较大的变化,但是NMS使用的时候用的是回归前的置信度,这样就会导致一些回归后高IOU的预测框被删除。这就使得回归前的置信度并不能完全表征回归后的IOU大小。这样就会导致算法精度的下降,在第一次使用NMS时候这种情况会比较明显,第二次使用的时候就会好很多。因此,一阶段只使用一次NMS是会对精度有影响的,而二阶段目标检测中会在RPN之后进行一个更为精细的回归,在该处也会用到NMS,此时检测的精度就会好很多。
如何解决目标检测中遮挡问题
遮挡本身也可以分为两种类型,一种是由于非目标造成的遮挡,一种是由于也是需要检测的目标造成的遮挡。这两种遮挡分别被叫做occlusion和crowded。
对于前一种类型遮挡,很难有针对性的办法去解决,最好的办法也就是使用更多的数据和更强的feature。可以从训练数据入手。加掩膜,加扰动,提高算法对遮挡的应对能力。
对于第二种遮挡,提出了Repulsion Loss
如何解决动态目标检测
FPN的作用
FPN是在卷积神经网络中图像金字塔的应用。图像金字塔在多尺度识别中有重要的作用,尤其是小目标检测。顶层特征上采样后和底层特征融合,每层独立预测。
FPN的设计动机:
- 高层特征向低层特征融合,增加低层特征表达能力,提升性能
- 不同尺度的目标可以分配到不同层预测,达到分而治之。
- FPN每层做特征融合的特征图有两个,首先是前向传播,然后取了每个特征图做上采样(最近邻插值),对应前向传播的特征图做融合。融合的方式是:通过1x1卷积调整通道数,然后直接add。之后进行3x3卷积操作,目的是消除上采样的混叠效应。
- 其实,fpn真正起作用的是分而治之的策略,特征融合的作用其实很有限,此外fpn存在消耗大量显存,降低推理速度 。
如何解决训练数据样本过少的问题
利用预训练模型进行迁移微调(fine-tuning),预训练模型通常在特征上拥有很好的语义表达。此时,只需将模型在小数据集上进行微调就能取得不错的效果。这也是目前大部分小数据集常用的训练方式。视觉领域内,通常会ImageNet上训练完成的模型。自然语言处理领域,也有BERT模型等预训练模型可以使用。
单样本或者少样本学习(one-shot,few-shot learning),这种方式适用于样本类别远远大于样本数量的情况等极端数据集。例如有1000个类别,每个类别只提供1-5个样本。少样本学习同样也需要借助预训练模型,但有别于微调的在于,微调通常仍然在学习不同类别的语义,而少样本学习通常需要学习样本之间的距离度量。例如孪生网络(Siamese Neural Networks)就是通过训练两个同种结构的网络来判别输入的两张图片是否属于同一类。3. 以上两种是常用训练小样本数据集的方式。此外,也有些常用的方式:数据集增强、正则或者半监督学习等方式来解决小样本数据集的训练问题。
IOU代码实现
python
import numpy as np
def ComputeIOU(boxA,boxB):
# 计算相交框的坐标
# bbox[0][1] 左上角坐标
# bbox[2][3] 右下角坐标
x1 = np.max([boxA[0],boxB[0]])
y1 = np.max([boxA[1], boxB[1]])
x2 = np.min([boxA[2], boxB[2]])
y2 = np.min([boxA[3], boxB[3]])
# 计算交区域 并区域 及IOU
S_A = (boxA[2]-boxA[0]+1)*(boxA[3]-boxA[1]+1)
S_B = (boxB[2]-boxB[0]+1)*(boxB[3]-boxB[1]+1)
interArea = np.max([x2-x1+1, 0])*np.max([y2-y1+1,0]) ##一定要和0比较大小,如果是负数就说明压根不相交
unionArea = S_A + S_B - interArea
iou = interArea/unionArea
return iou
boxA = [1,1,3,3]
boxB = [2,2,4,4]
IOU = ComputeIOU(boxA, boxB)
NMS代码实现
python
import numpy as np
def nms(dets,iou_thred,cfd_thred):
if len(dets) == 0:
return []
bboxes = np.array(dets)
# 对整个bboxes排序
bboxes = bboxes[np.argsort(bboxes[:,4])]
pick_bboxes = []
while bboxes.shape[0] and bboxes[-1,-1] >= cfd_thred:
bbox = bboxes[-1]
x1 = np.maximum(bbox[0],bboxes[:-1,0])
y1 = np.maximum(bbox[1], bboxes[:-1,1])
x2 = np.minimum(bbox[2], bboxes[:-1,2])
y2 = np.minimum(bbox[3], bboxes[:-1,3])
inters = np.maximum(x2-x1+1, 0) * np.maximum(y2-y1+1, 0)
unions = (bbox[2]-bbox[0]+1)*(bbox[3]-bbox[1]+1) + (bboxes[:-1,2]-bboxes[:-1,0]+1)*(bboxes[:-1,3]-bboxes[:-1,1]+1) - inters
ious = inters/unions
keep_indices = np.where(ious<iou_thred)
bboxes = bboxes[keep_indices] ## indices一定不包括自己
pick_bboxes.append(bbox)
return np.asarray(pick_bboxes)
dets = [[187, 82, 337, 317, 0.9], [150, 67, 305, 282, 0.75], [246, 121, 368, 304, 0.8]]
dets_nms = nms(dets, 0.5, 0.3)
print(dets_nms)
NMS的改进思路
- 根据手动设置阈值的缺陷,通过自适应的方法在目标系数时使用小阈值,目标稠密时使用大阈值。例如Adaptive NMS
- 将低于阈值的直接置为0的做法太hard,通过将其根据IoU大小来进行惩罚衰减,则变得更加soft。例如Soft NMS,Softer NMS。
- 只能在CPU上运行,速度太慢的改进思路有三个,一个是设计在GPU上的NMS,如CUDA NMS,一个是设计更快的NMS,如Fast NMS,最后一个是掀桌子,设计一个神经网络来实现NMS,如 ConvNMS。
- IoU的做法存在一定缺陷,改进思路是将目标尺度、距离引进IoU的考虑中。如DIoU