经典目标检测
RCNN
首先,使用Selective Search算法,从图片中裁出来N个小区域图像
然后将者N个小区域图像分别使用CNN提取得到D维的特征向量。至此得到了NxD的矩阵,该矩阵包含每个小区域对应的D维特征向量
接着,将NxD的矩阵分别使用k个SVM分类器(Dxk)进行分类,得到Nxk的矩阵,即每个小区域图像的分类结果(k个类别,取概率最大的作为预测类别)。在得到每个小区域图像的预测类别后,可能存在同一个目标被多个小区域图片同时预测到的情况,因此还需要针对每个类别对应的小区域图像做一个NMS后处理。
最后,对于NMS后保留下来M(M<=N)个的小区域图像,先按照这些建议框(小区域图像)与GT之间的IoU做一个过滤,保留IoU大于阈值的建议框,然后将每个建议框对应的D维特征向量输入回归器,得到修正后的建议框位置。这里,和分类器个数一样,总共设置了k个回归器,各自负责不同类别(上一步预测得到的类别)的建议框位置修正。
RCNN存在的问题:
- 测试速度慢(SS算法提取候选框很慢)
- 训练速度慢(CNN,SVM分类器,回归器)
- 训练所需空间大(需要将目标框的小区域提取特征并写入磁盘)
FastRCNN
首先,使用Selective Search算法,从图片中定位得到N个小区域图像,不同于RCNN,这里只需要记录这些建议框的位置信息,不需要单独裁剪出来
然后,将整张图片输入CNN中得到特征图,并根据原图和特征图之间的映射关系(等比例缩放),得到每个建议框对应的特征图(不需要将每个建议框对应的小区域图片单独裁剪出来并分别使用CNN提取特征,因而速度快了很多)
接着,将每个建议框的特征图使用ROI pooling层映射到7x7的小特征图,然后加入全连接层做进一步的特征提取,得到ROI特征向量
最后,每个建议框的ROI特征向量分别输入到分类层和回归层(都是全连接层),得到预测的类别以及边界框回归参数。 这里,对于每一个建议框的ROI特征向量,分类层会输出k+1个节点,k是总类别数,1对应背景类别;边界框参数回归器输出(k+1)x4个节点,即针对每个类别的边界框回归参数,其中的边界框回归参数是$(d_x,d_y,d_w,d_h)$,分别对应将通过SS得到建议框转换到预测的边界框时,建议框的中心点坐标和建议框的宽高的变换尺度,具体计算公式如下:
上述所讲内容,用一张整体网络架构图表示如下:
FastRCNN的损失函数包含分类损失和定位损失两部分:
其中,p是预测的类别向量,假设k类,那么p就是k+1维的类别概率向量,u是真实类别,$t^u=(t^u_x,t^u_y,t^u_w,t^u_h)$是预测的边界框回归参数,$v=(v_x,v_y,v_w,v_h)$是真实的边界框回归参数(数据标定可以得到中心点和宽高,于是可以根据尺度变换公式反推$v$)
分类损失是交叉熵,定位损失如下:
只有预测的正样本边界框才有对应的真实边界框,那些被预测为背景类别的建议框是负样本,负样本是没有真实的边界框与之对应的。所以,只有满足$[u\gt=1]$,即类别不被预测为背景(u=0)的边界框才会计算定位损失,这里的$[u\gt=1]$可以看作是一个逻辑函数,满足$u\gt=1$的条件则函数值为1,否则为0。
Faster R_CNN
首先,将图像输入网络得到相应的特征图
接着,使用RPN生成候选框,并将这些候选框映射到特征图上以获得相应的特征矩阵
最后,将每个特征矩阵通过roi pooling层缩放到7x7大小的特征图,并展平,后面通过一系列全连接层得到预测结果。
因此,Faster R_CNN相当于在Fast R_CNN的基础上引入了RPN,用来替代Fast R_CNN中的SS算法。
现在来介绍RPN。
对于通过主干网络提取得到的卷积特征图,RPN会设置一个3x3(stride=paddding=1,因此可以覆盖特征图上的每一个像素点)的滑动窗口,在整张特征图上进行滑动,并记录下每个时刻滑动窗口的中心点对应原始图像上的中心点,从原图的这个中心点出发可以设置不同比例和尺度的anchor(论文中设置了3个比例,包括1:1,1:2,2:1,和3种尺度,包括128x128,256x256,512x512,因此每个中心点对应有9个anchor)。
以下是一个RPN的简易代码实现,在实际应用时,还会对得到的所有anchor进行后处理,比如去掉超出图像边界的,基于cls得分采用NMS进行过滤等操作。
1 | import torch |
可以看到,RPN有两个head,分别用于分类和bbox参数回归。
其中,分类是一个二分类,只用于区分对应原图上的anchor是前景还是背景,若是前景,则对其进行进一步的分类(目标类别)和bbox参数回归。
通过生成anchor的顺序,可以知道每个anchor的具体尺度和纵横比。生成的anchor是按照尺度和纵横比的组合顺序排列的,因此根据索引可以直接确定每个anchor的参数。
1 | def generate_anchors(base_size=16, ratios=[0.5, 1, 2], scales=[8, 16, 32]): |
将图片的特征图经过RPN之后,会得到许多anchor,需要从这些anchor中确定正负样本。
正样本:anchor与GT框的IoU大于0.7(多对一),或者当没有一个anchor满足IoU与GT框大于0.7时,选择IoU最大的anchor(s)
负样本:anchor与GT框的IoU小于0.3
其他的anchor则直接不对训练做贡献。
RPN的损失函数包含两部分,分类损失和边界框参数回归损失:
分类损失$L_{cls}$是二值交叉熵,$N_{cls}$是一张图片(或一个batch)筛选出来的anchor的数量,原论文中为256,即正负样本的总数。
边界框参数回归损失$L_{reg}$是Smooth L1损失,$N_{reg}$是anchor位置的个数(记得特征图上滑动窗口每一个中心点对应一个anchor位置,每个位置对应9个不同大小比例的anchor),因此也就是特征图的高x宽得到的结果,原论文中大概为2400。负样本不参与边界框参数回归损失的计算,因此$P^*_i$起到了过滤作用,因为只有当anchor为正样本时,$P^*_i=1$,否则取0。