关于SSD,FasterRCNN的重新思考


写在前面:本篇内容是我重新阅读SSD(SSD: Single Shot MultiBox Detector)和FasterRCNN(Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks)后获得的一些新思考,并且这些认知对我而言是更深刻的。

在此,需要感谢来自中科大Bubbliiiing的基础讲解

NMS和Soft-NMS

Hard NMS

非极大抑制(non max suppression,NMS)是目标检测任务中常用的算法,NMS可以筛选出一定区域内属于同一种类得分最大的框。
fig1
左图是经过NMS处理后的结果,右图是没有经过NMS处理的;

对于多分类的非极大抑制,假设输入的形状为: [ b a t c h   s i z e , n u m   a n c h o r , 5 + n u m   c l a s s ] [batch\, size,num\, anchor,5+num\, class] [batchsize,numanchor,5+numclass],第一个维度是图片的数量,第二个维度是所有的先验框anchor,第三个维度是所有先验框的预测结果,5=4+1,4代表框的位置,1代表预测框是否包含物体的置信度。

NMS的过程如下:

  • 1.对所有图片进行循环;
  • 2.找出该图片中得分(是否包含物体的置信度)大于置信度门限的框。在进行重合框筛选前就进行得分的筛选可以大幅度减少框的数量;
  • 3.判断第2步中获得的框的种类与得分。取出预测结果中框的位置。此时最后一维度里面的内容由 5 + n u m   c l a s s 5+num\, class 5+numclass变成了4+1+2,4代表框的位置,1代表预测框是否包含物体的置信度,2分别代表种类的置信度(概率)与种类(从0到 n u m   c l a s s − 1 num\, class-1 numclass1);
  • 4.对种类进行循环,非极大抑制的作用是筛选出一定区域内属于同一种类得分(是否包含物体的置信度)最大的框,对种类进行循环可以帮助我们对每一个类分别进行非极大抑制;
  • 5.根据得分对该种类进行从大到小排序;
  • 6.每次取出得分最大的框,计算其与其它所有预测框的重合程度(IoU),重合程度过大的则剔除;

关于步骤6的进一步说明如下:

  • 取出当前类别box中得分最大的那一个,记为current_box,并保留它;
  • 计算current_box与其余box的IoU,如果其IoU大于设定的门限,就舍弃这些box;
  • 从剩余的box中,再取出最大得分的一个,如此循环往复;

IoU的实现如下:

def iou(b1, b2):
    b1_x1, b1_y1, b1_x2, b1_y2 = b1[0], b1[1], b1[2], b1[3]
    b2_x1, b2_y1, b2_x2, b2_y2 = b2[:, 0], b2[:, 1], b2[:, 2], b2[:, 3]

    inter_rect_x1 = np.maximum(b1_x1, b2_x1)
    inter_rect_y1 = np.maximum(b1_y1, b2_y1)
    inter_rect_x2 = np.minimum(b1_x2, b2_x2)
    inter_rect_y2 = np.minimum(b1_y2, b2_y2)

    inter_area = np.maximum(inter_rect_x2 - inter_rect_x1, 0) * \
                 np.maximum(inter_rect_y2 - inter_rect_y1, 0)

    area_b1 = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
    area_b2 = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)

    iou = inter_area / np.maximum((area_b1 + area_b2 - inter_area), 1e-6)
    return iou

NMS实现如下:

import numpy as np

def non_max_suppression(boxes, num_classes, conf_thres=0.5, nms_thres=0.4):
    bs = np.shape(boxes)[0]
    # 将框转换成左上角右下角的形式
    shape_boxes = np.zeros_like(boxes[:, :, :4])
    shape_boxes[:, :, 0] = boxes[:, :, 0] - boxes[:, :, 2] / 2
    shape_boxes[:, :, 1] = boxes[:, :, 1] - boxes[:, :, 3] / 2
    shape_boxes[:, :, 2] = boxes[:, :, 0] + boxes[:, :, 2] / 2
    shape_boxes[:, :, 3] = boxes[:, :, 1] + boxes[:, :, 3] / 2

    boxes[:, :, :4] = shape_boxes
    output = []
    # 1、对所有图片进行循环。
    for i in range(bs):
        prediction = boxes[i]
        # 2、找出该图片中得分大于门限函数的框。在进行重合框筛选前就进行得分的筛选可以大幅度减少框的数量。
        mask = prediction[:, 4] >= conf_thres
        prediction = prediction[mask]
        if not np.shape(prediction)[0]:
            continue

        # 3、判断第2步中获得的框的种类与得分。
        # 取出预测结果中框的位置与之进行堆叠。
        # 此时最后一维度里面的内容由5+num_classes变成了4+1+2,
        # 四个参数代表框的位置,一个参数代表预测框是否包含物体,两个参数分别代表种类的置信度与种类。
        class_conf = np.expand_dims(np.max(prediction[:, 5:5 + num_classes], 1), -1)
        class_pred = np.expand_dims(np.argmax(prediction[:, 5:5 + num_classes], 1), -1)
        detections = np.concatenate((prediction[:, :5], class_conf, class_pred), 1)
        unique_class = np.unique(detections[:, -1])

        if len(unique_class) == 0:
            continue

        best_box = []
        # 4、对种类进行循环,
        # 非极大抑制的作用是筛选出一定区域内属于同一种类得分最大的框,
        # 对种类进行循环可以帮助我们对每一个类分别进行非极大抑制。
        for c in unique_class:
            cls_mask = detections[:, -1] == c

            detection = detections[cls_mask]
            scores = detection[:, 4]
            # 5、根据得分对该种类进行从大到小排序。
            arg_sort = np.argsort(scores)[::-1]
            detection = detection[arg_sort]
            print(detection)
            while np.shape(detection)[0] > 0:
                # 6、每次取出得分最大的框,计算其与其它所有预测框的重合程度,重合程度过大的则剔除。
                best_box.append(detection[0])
                if len(detection) == 1:
                    break
                # 计算当前得分最高box与剩余box的IoU
                ious = iou(best_box[-1], detection[1:])
                # 保留IoU小于NMS门限的框
                detection = detection[1:][ious < nms_thres]
        output.append(best_box)
    return np.array(output)

Soft NMS

柔性非极大抑制认为不应该只通过 IoU 进行筛选,如图所示,很明显图片中存在两匹马(前面那匹马得分为0.95,后面那匹马得分为0.8),但是此时两匹马的重合程度 IoU 较高,此时我们如果使用 hard nms,后面那匹得分比较低的马会直接被剔除。
fig2

Soft-NMS认为在进行非极大抑制的时候要同时考虑重合程度IoU和得分

Soft NMS相对NMS的改变很小,对于NMS而言,NMS直接将与 得分最大的框 重合程度较高的框 剔除。而Soft-NMS则以一个权重的形式,将获得的 IoU 取高斯指数后乘上原得分,之后重新排序。继续循环。

Soft NMS的实现如下:

def soft_non_max_suppression(boxes, num_classes, conf_thres=0.5, sigma=0.5):
    bs = np.shape(boxes)[0]
    # 将框转换成左上角右下角的形式
    shape_boxes = np.zeros_like(boxes[:, :, :4])
    shape_boxes[:, :, 0] = boxes[:, :, 0] - boxes[:, :, 2] / 2
    shape_boxes[:, :, 1] = boxes[:, :, 1] - boxes[:, :, 3] / 2
    shape_boxes[:, :, 2] = boxes[:, :, 0] + boxes[:, :, 2] / 2
    shape_boxes[:, :, 3] = boxes[:, :, 1] + boxes[:, :, 3] / 2

    boxes[:, :, :4] = shape_boxes
    output = []
    # 1、对所有图片进行循环。
    for i in range(bs):
        prediction = boxes[i]
        # 2、找出该图片中得分大于门限函数的框。在进行重合框筛选前就进行得分的筛选可以大幅度减少框的数量。
        mask = prediction[:, 4] >= conf_thres
        prediction = prediction[mask]
        if not np.shape(prediction)[0]:
            continue

        # 3、判断第2步中获得的框的种类与得分。
        # 取出预测结果中框的位置与之进行堆叠。
        # 此时最后一维度里面的内容由5+num_classes变成了4+1+2,
        # 四个参数代表框的位置,一个参数代表预测框是否包含物体,两个参数分别代表种类的置信度与种类。
        class_conf = np.expand_dims(np.max(prediction[:, 5:5 + num_classes], 1), -1)
        class_pred = np.expand_dims(np.argmax(prediction[:, 5:5 + num_classes], 1), -1)
        detections = np.concatenate((prediction[:, :5], class_conf, class_pred), 1)
        unique_class = np.unique(detections[:, -1])

        if len(unique_class) == 0:
            continue

        best_box = []
        # 4、对种类进行循环,
        # 非极大抑制的作用是筛选出一定区域内属于同一种类得分最大的框,
        # 对种类进行循环可以帮助我们对每一个类分别进行非极大抑制。
        for c in unique_class:
            cls_mask = detections[:, -1] == c

            detection = detections[cls_mask]
            scores = detection[:, 4]
            # 5、根据得分对该种类进行从大到小排序。
            arg_sort = np.argsort(scores)[::-1]
            detection = detection[arg_sort]
            print(detection)
            while np.shape(detection)[0] > 0:
                # 6、Soft NMS 与 NMS 的不同之处如下
                # 取出后验得分最高的box
                best_box.append(detection[0])
                if len(detection) == 1:
                    break
                ious = iou(best_box[-1], detection[1:])
                # 将获得的IOU取高斯指数后乘上原得分,之后重新排序
                """
                np.exp(-(ious * ious) / sigma)相当于一个基于iou计算出的权重, 值在0-1之间
                ious越大, 权重的值越小
                """
                detection[1:, 4] = np.exp(-(ious * ious) / sigma) * detection[1:, 4]
                # 对后验得分去除第一个box后的剩余boxes进行操作
                detection = detection[1:]
                # 按照后验得分(结合了IoU和得分)重新排序
                scores = detection[:, 4]
                arg_sort = np.argsort(scores)[::-1]
                detection = detection[arg_sort]
        output.append(best_box)
    return np.array(output)

可见,Soft NMS 综合考虑了先验框的得分和IoU,一定程度解决了上图中马的目标检测问题。

One Stage与Two Stage

One Stage:SSD

SSD在处理一张图像之前,首先进行以下假设,将图像划分成不同尺度的grid(网格):
fig3
网格中,每个小方块负责检测该位置下对应的对象,显然,密集的网格有利于检测小目标,稀疏的网格有利于检测大目标;

下面回顾SSD的架构:
fig4
输入图像在卷积通路上计算,可以得到6个不同尺寸的特征图(feature map),注意观察它们的尺寸(38x38,19x19,10x10,5x5,3x3,1x1),这正好对应到了前面假设的grid上,特征图的每个点(像素)就对应网格里的一个小方块,于是,接下来我们只需要通过分别处理各个特征图就能实现多尺度目标检测;

现在,一共有六个特征图要进行下一步的处理。为了和普通特征图区分,我们称之为有效特征图

对于每一个有效特征图,都要同时进行两个操作:

  • 一次num_anchors x 4的卷积;
  • 一次num_anchors x num_classes的卷积;

而num_anchors指的是该特征图每一个特征点所拥有的先验框数量。上述提到的六个特征图,每个特征图的每个特征点对应的先验框数量分别为4、6、6、6、4、4。


先验框是事先由我们初始化好的:
fig5
每个小方块(对应着有效特征图上的一个特征点)都联系着几种不同形状的先验框,上图中的每个小方块对应4个先验框(也称为anchor)


为了便于观察one stage的操作,重新回顾SSD的架构,在SSD论文中,模型架构是这样的:
fig6
以有效特征图(512x38x38)为例,将该特征图通过卷积计算,kernel size=3x3,滤波器个数为4x(Classes+4),第一个数字4代表每个特征点对应4个anchor,第二个数字4代表每个anchor的校正信息(左上角坐标和右下角坐标应有的偏移量),Classes代表每个anchor属于类别的概率分布;

等效地,可以将上面的卷积操作分解为以下两个同步操作(相当于是通道的拆分,效果一致,同时便于理解):
fig7
卷积的本质是局部全连接网络作为核在图像上滑动,所以具备全连接网络计算相似度的性质,这对于CNN分类的分支是容易理解的;

对于卷积网络用于回归的理解
输入一个特征图,一层卷积模型中的某一个滤波器在图像上滑动,该滤波器的某个核对应着feature map中的一层二维数组,对于这个二维数组,局部区域上的亮度即代表着某个特征对应此区域的激活程度;
我们的滤波器操作,从表面上看是针对输出通道的特征(与当前滤波器对应的特征),在局部区域上做非线性回归,其实本质也是一种模式的识别,点积的计算帮助我们获得该局部区域内的对象 偏离模板对象中心 的程度信息
如果以一个具体的anchor分析为例,此时卷积对应着4个滤波器,4个滤波器分别负责识别 x 0 , y 0 , x 1 , y 1 x_{0},y_{0},x_{1},y_{1} x0,y0,x1,y1的偏离程度,可以想象,如果 x 0 x_{0} x0相对于ground truth的左上角坐标是右偏的,网络输出一个正数,以纠正 x 0 x_{0} x0以向左边移动;


为什么这样有效
坐标 x x x只能左右移动校正,坐标 y y y只能上下移动校正,而这正好可以通过数值的正负来反映,这使得卷积可以实现"回归"的效果,但本质依然属于某个模式的识别


当得到所有anchor的坐标校正信息和分类概率分布后,先对每个anchor进行位置校正,此时改名为bounding box,使用NMS作为后处理删除冗余的框后,即可输出最终的目标检测结果。

Two Stage:FasterRCNN

对于FasterRCNN,它可以看作是对one stage模型的进一步细致精修,FasterRCNN同样会先经过卷积通路(backbone)获取特征图feature map,只不过这个特征图是比较密集的,同样让每个特征点关联一组不同形状的anchor,然后进行和one satge同样的卷积回归与分类操作(对应下图红色虚线框),只是此时只分类前景和背景(即是否包含物体),然后用回归的位置信息修正前景框,对这些前景框进行NMS,取出框中对应到feature map的特征,分别对这些特征再次进行卷积的分类和回归,此时的回归将作为位置的进一步精修,分类则是将前景对象细分到目标对象的各种类别。
fig8

One Stage与Two Stage的关系

通过上面的描述,可以发现,one stage的特点是模型可以同时计算输出先验框anchor的类别概率分布和其位置校正信息,再利用NMS后处理得到目标检测结果;

two stage的特点是模型先像one stage一样计算出anchor的类别概率和位置校正信息,只是这里的类别概率只区分前景和背景;
然后对包含前景的anchor进行位置修正,再使用NMS做后处理得到建议框proposal box,利用proposal box回到feature map截取对应的特征,分别进行卷积的回归(位置精修)与具体类别分类。

two stage考虑前景和背景的先验条件,帮助模型更准确对具体对象做目标检测,但也很明显地增加了计算开销。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值