目标检测主流的框架主要是faster-rcnn,SSD,YOLO三大模型,本文主要对SSD算法框架进行详解,faster-rcnn详解可参考https://zhuanlan.zhihu.com/p/31426458 ,这是我看过对faster-rcnn解释最清楚的一篇博客,Y0LO_V3网络会在后续进行学习并更新。
不同于faster-rcnn使用rpn网络进行检测和边框回归,SSD网络的主要设计思想是特征分层提取,并依此进行边框回归和分类。具体网络结构如下:
由SSD的网络结构可以看出SSD的网络分为6个stage,每个stage能学习到一个特征图,然后在每个特征图上进行边框回归和分类。SSD使用VGG-16-Atrous作为基础网络(conv4_3, fc_7),随后在VGG-16基础网络上添加的特征提取层(conv8_2, conv9_2, conv10_2, Conv11_2)。
为了保证网络对小目标有很好检测效果,SSD除了在最终特征图上做目标检测之外,还在之前选取的5个特征图上进行预测。SSD网络以VGG16的前5层卷积网络作为第1个stage,然后将VGG16中的fc6和fc7两个全连接层转化为两个卷积层并将fc_7作为网络的第2个stage,接着在此基础上,SSD网络继续增加了Conv8、Conv9、Conv10和Conv11四层网络,用来提取更高层次的语义信息同时作为第3、4、5、6个stage。
首先需要了解一下卷积层和池化层输出图像大小的计算公式:
Featuremap_ h =((image_h + 2*pad_h – kernel_h)/stride_h)+ 1
Featuremap_ w =((image_w +2*pad_w – kernel_w)/stride_w)+ 1
Featuremap_ h:卷积或池化操作后输出特征图的高度
Featuremap_ h:卷积或池化操作后输出特征图的宽度
image_h:输入图像的高度 image_w:输入图像的宽度
pad_h:在输入图像的高度方向两边各增加pad_h个单位长度(因为有两边,所以乘以2)
pad_w:在输入图像的宽度方向两边各增加pad_w个单位长度(因为有两边,所以乘以2)
kernel_h:卷积核的高度 kernel_w:卷积核的宽度
stride_h:高度方向的滑动步长; stride_w:宽度方向的滑动步长。
根据网络结构中每一个卷积层的参数可以得对应每一层feature map的大小,例如:输入图像大小为300*300*3,VGG16中第一个卷积层中设置了64个大小为3*3*3的卷积核,同时在输入图像的上下作用各填充一行数据即pading=1,卷积核滑动步长为1。因此,第一个卷积层feature map 大小为300*300*64。之后的pooling层: 2*2*64 s=2即步长为2,feature map 变为150*150*64。因此,第1个stage对应feature map为38*38*512,第2个stage对应feature map为19*19*1024,第3个为10*10*512, 第4个为5*5*256, 第5个为3*3*256, 第6个stage对应feature map为1*1*256。
在SSD中引入了Prior Box,实际上与Faster RCNN Anchor非常类似,就是一些目标的预选框。与Faster RCNN不同之处在于SSD中每个stage生成不同尺度(scale)和比例(Ratio)的预选框。
假设使用m(SSD中m=6)个不同层次的feature map,则最底层的feature map的scale值为,最高层的为,其它层则通过公式计算得到:
第一层feature map对应的min_size=s1, max_size=s2;第二层min_size=s2, max_size=s3;其它层以此类推。
使用不同的ratio值ar∈{1,2,3,1/2,1/3},计算预选框的宽度和高度:((第K层的min_size=sk), ,另外对于ratio=2的情况,额外再指定scale为。
1.先以 min_size为宽高生成一个正方形的框
2.使用sqrt(min_size* max_size),生成一个正方形的框。
3.然后根据每一层的 aspect_ratio,再去生成。若aspect_ratio=2,那么会自动的再添加一个aspect_ratiod = 1/2,然后根据下面的计算方法:
分别生成两个框,一个对应 ar = 2 一个对应 ar= 1/2。(这是额外增加的aspect_ratio框的计算方式,正常的计算方式如2.2中的介绍方式)。
直观点说,就是min_size和max_size会分别生成一个正方形的框,aspect_ratio参数会生成2个长方形的框。所以输出框的个数 :
prior_box_num = count(min_size)*1+count(max_size)*1+count(aspect_ratio)*2。(when aspect_ratio=2,乘以2),这样就可以跟下表计算对接上了。
Name |
Out_size |
Prior_box_num |
Total_num |
conv4_3 |
38*38 |
4 |
5776 |
fc_7 |
19*19 |
6 |
2166 |
conv8_2 |
10*10 |
6 |
600 |
conv9_2 |
5*5 |
6 |
150 |
conv10_2 |
3*3 |
4 |
36 |
Conv11_2 |
1*1 |
4 |
4 |
|
|
|
8732 |
在学习Prior Box的使用之前,需要先了解边框回归的概念与原理:
边框回归:(这里借鉴了上述链接关于faster-rcnn边框回归的介绍)
如图绿色表示的是飞机的实际框标签(ground truth),红色的表示的其中一个默认框 (default Box或Prior Box),但是由于红色区域定位不准确,这张图相当于没有正确检测出飞机,所以我们希望采用一种方法对红色的框进行微调,使得默认框和实际框更加接近。
对于目标框一般使用四维向量来表示(x,y,w,h),分别表示目标框的中心点坐标、宽、高,我们使用A表示默认框,使用G表示目标的ground truth,我们的目标是寻找一种关系,使得原始的默认框A经过映射到一个和真实框G更接近的回归窗口G′,即:
那么如何去计算F呢?这里我们可以通过平移和缩放实现F(A)=G′:
上面公式中,我们需要学习四个参数,分别是dx(A),dy(A),dw(A),dh(A),其中(Aw⋅dx(A),Aw⋅dy(A))表示的两个框中心距离的偏移量。当默认框 A与G相差较小时,可以认为这种变换是一种线性变换, 那么就可以用线性回归来建模对目标框进行微调(注意,只有当默认框A和G比较接近时,才能使用线性回归模型,否则就是复杂的非线性问题了)。
接下来就是如何通过线性回归获得dx(A),dy(A),dw(A),dh(A)。线性回归就是给定输入的特征向量X(每步卷积池化得到),学习一组参数W,使得线性回归的输出WX和真实值Y的差很小。对于该问题,输入X是特征图,我们使用ϕ表示,同时训练时还需要A到G变换的真实参数值:(tx,ty,tw,th),A与G之间的平移参数(tx,ty)和尺度因子(tw,th)为:
输出是dx(A),dy(A),dw(A),dh(A),那么目标函数可以表示为:
其中ϕ(A)是特征图上默认框对应像素点的特征向量,w是需要学习的参数,d(A)是得到预测值,(*表示x,y,w,h,也就是每一个变换对应一个上述目标函数),为了让预测值dx(A),dy(A),dw(A),dh(A)和真实值差距tx,ty,tw,th最小,代价函数如下:
对于训练边框回归函数时,输入时默认框对应的特征ϕ,监督信号是A到G的变换参数(tx,ty,tw,th),即训练的目标是:输入ϕ的情况下使网络输出与监督信号尽可能接近。那么边框回归函数工作时,再输入ϕ时,回归网络的输出就是每个默认框的平移参数和变换尺度(tx,ty,tw,th),显然即可用来修正默认框的位置了。
接下来开始SSD中Prior Box的使用:
这里以conv4_3为例分析介绍Prior Box的使用,conv4_3对应于SSD中的第一个stage,feature map 大小为300*300*64。
其中SSD 300在conv4_3生成prior box的conv4_3_norm_priorbox层prototxt定义如下:
在conv4_3对应feature map网络被分成了3路:
第1路(softmax分类,得到每个Prior Box对应每个类别的概率):
经过一次batch norm+一次3*3卷积后,生成了[1, num_class*num_priorbox, layer_height, layer_width]大小的feature用于softmax分类目标和非目标(其中num_class是目标类别,SSD300中num_class = 21,即20个类别+1个背景)
第2路(边框回归,得到每个预测框的左上角和左下角对应坐标即Prior Box调整后的位置信息):
经过一次batch norm+一次3*3卷积后,生成了[1, 4*num_priorbox, layer_height, layer_width]大小的feature用于bounding box regression(即每个点生成num_priorbox,个[dxmin,dymin,dxmax,dymax],用于默认框的位置调整)
第3路(PriorBox层,为特征图中每个位置(像素点)生成默认框即计算每个默认框相对于网络输入层输入图像的归一化左上角和右下角坐标以及设置的坐标variance值):
生成了[1, 2, 4*num_priorbox]大小的prior box blob,其中2个channel分别存储prior box相对于网络输入层输入图像的归一化左上角和右下角坐标的4个坐标值和对应的4个variance.
4个variance,这实际上是一种边框位置调整中的权重。在上图线路(2)中,网络输出[dxmin,dymin,dxmax,dymax]即左上角和右下角坐标的平移尺度,即对应下面代码中 bbox.xmin(),bbox.ymin(),bbox.xmax() ,bbox.ymax() ;然后利用如下方法进行prior box的位置进行回归调整:
其中,(prior_bbox.xmin() ,prior_bbox.ymin())为prior box的左上角坐标,(prior_bbox.xmax() ,prior_bbox.ymax())为prior box的右下角坐标。
将预选框(prior box)与真实框按照IOU(JaccardOverlap)进行匹配,匹配成功则这个预选框就是positive example(正样本),如果匹配不上,就是negative example(负样本),显然这样产生的负样本的数量要远远多于正样本。这里将前向loss进行排序,选择最高的num_sel个prior box序号集合 D。那么如果Match成功后的正样本序号集合P。那么最后正样本集为 P与第一步的正样本的和,负样本集为 D - Dcap{P}。同时可以通过规范num_sel的数量(是正样本数量的三倍)来控制使得最后正、负样本的比例在 1:3 左右。
具体过程如下:
1、正样本:
先是从groudtruth box出发给每个groudtruth box找到了最匹配的prior box放入候选正样本集,然后再从prior box出发为prior box集中寻找与groundtruth box满足IOU>0.5的一个IOU最大的prior box(如果有的话)放入候选正样本集,这样显然就增大了候选正样本集的数量。
2、负样本
在生成一系列的预选框之后,会产生很多个符合 真实框的 positive boxes(候选正样本集),但同时,不符合实框的也很多,而且这个 negative boxes(候选负样本集),远多于候选正样本集。这会造成候选正样本集、候选负样本集之间的不均衡。训练时难以收敛。
因此,SSD采取方法为将所有的预选框对应predictions(prior boxes)loss 进行排序。对于候选正样本集:选择最高的几个prior box与正样本集匹配,匹配不成功则删除这个正样本(这里的意思可能是正样本已经很接近ground truth box了,不需要再训练了);对于候选负样本集:选择最高的几个prior box与候选负样本集匹配,匹配成功则作为负样本。
与常见的 Object Detection 的方法损失函数相同,分为两部分:计算相应的 预选框与目标类别的 score(置信度)以及相应的回归结果(位置回归)。置信度是采用 Softmax Loss(Faster R-CNN是log loss),位置回归则是采用 Smooth L1 loss。之前得到的8732个预选框经过Jaccard Overlap筛选后数量减少;其中不满足的框标记为负数,其余留下的标为正数框。紧随其后:
我们令 i 表示第 i个默认框,j表示第 j个真实框,p表示第p个类。那么表示 第 i 个预选框与类别p的第 j 个 ground truth box(真实框) 相匹配的Jaccard系数,若不匹配的话,则。总的目标损失函数(objective loss function)就由位置回归损失(loc)与分类置信度(conf)的加权求和:
分类置信度损失(conf):
位置回归损失(loc):
其中,l 为预测框,g为真实框。(cx,cy)为边框调整后的默认框(d)的中心,(w,h) 为默认框的宽和高。