大白话分析——SSD目标检测网络从训练到预测(上)

一. 背景

本文档以ssd300作为背景网络进行解读,以Tensorflow,Keras为框架
原始代码:https://github.com/pierluigiferrari/ssd_keras
分析后的代码:https://github.com/Freshield/LEARN_detection/tree/master/a4_github_better_ssd/a2_analyze_model
目前网上基本都网络部分讲的比较多,但是真正训练和预测部分都相对粗略,所以自己网上找了一个相对比较好的ssd检测作为蓝本来分析,然后把相应的过程用大白话给表达出来,方便大家可以更好的理解网络。

二. 整体流程

整个SSD大致可以分为四个部分:模型部分,数据部分,Loss部分以及decode部分

三. 模型部分

模型部分是一般网上文章说的比较多的部分,模型可以分为主干网络,conf预测,loc预测,pirorbox,reshaper这5个部分,下图为总体的架构
大白话分析——SSD目标检测网络从训练到预测(上)

1. 主干网络

大白话分析——SSD目标检测网络从训练到预测(上)
这里是主干网络和yolo的网络的对比,可以看出从不同的地方选择了多尺度的信息。主干网络这例使用的是vgg网络,其实就是从头到尾的正常卷积操作,尺寸的变化如下:
(300,300,3) -> (150,150,64) -> (75,75,128) -> (38,38,256) -> (38,38,512)(conv4_3) -> (19,19,512) -> (19,19,1024)(fc7) -> (10,10,512)(conv6_2) -> (5,5,256)(conv7_2) ->
(3,3,256)(conv8_2) -> (1,1,256)(conv9_2)
这里后边加上名称的都是之后要用的的层。

2. conf预测

这是是conf预测,也就是confidence,其实就是相当于分类网络,把每个priorbox的21个类别都预测出来,就像正常的分类网络一样。各层的变化如下:
conv4_3(38,38,512) -> L2 norm -> (38,38,4*21)
这里要进行L2 norm的原因是conv4_3层比较靠前,它的值相对较大所以使用L2 norm来减小数值。后边的4为每个格的先验框数量,也就是说本层一共有38*38*4个先验框,具体的解释会在priorbox部分介绍,而21则代表了类别数,其中20个类别和1个背景类。
fc7(19,19,1024) -> (19,19,6*21),6代表这层是每个格6个先验框,一共19*19*6个先验框。
conv6_2(10,10,512) -> (10,10,6*21)
conv7_2(5,5,256) -> (5,5,6*21)
conv8_2(3,3,256) -> (3,3,4*21)
conv9_2(1,1,256) -> (1,1,4*21)

3. loc预测

这里是坐标预测的部分,需要特别注意的是,坐标的预测并不是真正的预测检测的目标的位置,而是检验的目标相对于先验框也就是priorbox的相对位置,包括相对的中心点的偏移以及宽高的缩放。
这里之所以不直接预测检验框的位置是因为无法做到。由于使用的卷积神经网络,所以所有的操作都是卷积,而卷积神经网络先验的前提是空间不变性,所以我们会用同一个卷积核按照步长遍历整个图像,这样可以大大减少所需的计算量。但是也由于是同样的卷积核,那么对于同一个特征,也会得出一模一样的结果。我们假设在一张图的左上和右下有一模一样的两辆车,那么同一个卷积核则会得出同样的结果而无法得出不同的车的位置。这也是之所以需要先验框的一部分原因,另一部分原因是我们不知道有多少个物体。
具体的尺寸变化如下:
conv4_3(38,38,512) -> L2 norm -> (38,38,4*4)
我们同样使用L2 norm之后的结果,然后通过卷积得到(38,38,4*4)的conv4_3_loc,这里的第一个4是像conf预测一样,代表着每个格4个先验框,而后边的4则代表预测的相对位置,分别是lx,ly,lw,lh对应是中心点x轴的偏移,中心点y轴的偏移,宽的缩放,高的缩放,具体的相对计算方法会在数据的编码部分说明。
fc7(19,19,1024) -> (19,19,6*4)
conv6_2(10,10,512) -> (10,10,6*4)
conv7_2(5,5,256) -> (5,5,6*4)
conv8_2(3,3,256) -> (3,3,4*4)
conv9_2(1,1,256) -> (1,1,4*4)

4. Priorbox

这里Priorbox,boundingbox,bbox,anchorbox,anchor,先验框都是指的一个东西,就像下图的虚线框所示,对于每张图都生成n个预先设定好大小和长宽比的框框,然后去和label也就是groundtruth进行比较,然后得到lx,ly,lw,lh就可以进行loss的计算,然后反向传播来学习了。
大白话分析——SSD目标检测网络从训练到预测(上)
从图中我们也可以发现,越往后边的层,它的格子数越小,也就是感受野更大,也就是对应到原图的框就更大,也就是检测的物体就更大。这样通过不同的层,得到的结果,就可以检测到不同大小的物体。
直觉上,我们如果想要得到图上的先验框,那么我们需要知道不同特征图上的缩放比例(当前层相对于原始图像的比例),不同先验框的长宽,先验框的中心点位置。
在获得先验框的大小上,这里是先通过计算不同层的缩放比获得一个层的基础sk值,然后通过不同的长宽比和sk计算真实的长宽应该是多少。

a. scale缩放比例

我们要得到bbox在原图的位置,首先要确定不同层的不同缩放比例,也就是scale。
大白话分析——SSD目标检测网络从训练到预测(上)
这里smin为0.2,smax为0.9,但是对于第一个特征图的先验框,设置sk为0.1,对应回原图也就是0.1*300=30,之后的sk都按照上边来计算。我们一共有6个特征图,但是去除第一个特征图后还有5个特征图,所以上面公式这里m为5。smin为0.2,对应回原图也就是0.2*300=60,这里的 大白话分析——SSD目标检测网络从训练到预测(上)相当于增加的步长,也就是每个(sk+1)-sk的值,不过注意这里官方计算方法是先乘100再除5-1=4,然后取整,得到步长为17,还要再除以100=0.17,这就是真正每个sk之间的差别,最终这里的scales列表为[0.1,0.2,0.37,0.54,0.71,0.88,1.05]这里可以发现由于取整的原因,并没有我们设定的0.9这个smax值。而之所以会出现第7个值1.05是为了之后计算长宽比时候的需要。

b. aspect ratio长宽比

从上边的图上可以看出为了适应不同的图像我们需要设置不同的长宽比。在主干网络的时候,我们看到所有层的每个格要不然是4个bbox,或者是6个bbox,这里每层的scale是相同的,不同的便是他们的长宽比不同。总体上来看一共有5中长宽比,也就是aspect ratio也叫ar,[1, 2, 0.5, 3, 1/3],长宽的计算为大白话分析——SSD目标检测网络从训练到预测(上)。那么这里只有5种长宽比,有6个bbox的怎么办?其实看上图也会发现,每个格中还有一个小方块,这是每层都会有的一种特殊的框,也是为何在scale中会有1.05这个值的原因。

i. 特殊sk

每个小格,当ar为1时,会引入一个特殊的sk,也就是一个特殊的尺寸的框。大白话分析——SSD目标检测网络从训练到预测(上),也就是当前的scale乘下一层的scale再开方,这也是为何要引入1.05的原因,因为在第六层的时候,我们需要下一层的这个值去计算。而计算的方法以conv4_3为例,特殊sk=(0.1*0.2)**0.5=0.1414,0.1414*300取整后等于42

所以conv4_3的长宽比为[1,2,0.5],长宽计算如下:
ar=1,特殊sk时:w=h=0.14*300/1**0.5=42
ar=1,正常sk时,w=h=0.1*300/1**0.5=30
ar=2,w=0.1*300/2**0.5=21,h=0.1*300*2**0.5=42
ar=0.5,w=0.1*300/0.5**0.5=42,h=0.1*300*0.5**0.5=21
所以最后bbox的4中长宽为[[30,30],[42,42],[42,21],[21,42]]

c. bbox的中心点

在得到先验框的大小后,那么下一步就是要知道不同的先验框应该放在什么位置上了,这里我们使用两个参数,step和offset

i. step

这里的step相当于是不同的先验框中心点的距离

ii. offset

offset是一个比例值,需要和step一起计算,这个相当于是左上第一个先验框的位置,比如step=8,offset=0.5,那么第一个先验框的中心点的位置为(0.5*8, 0.5*8)=(4, 4)。

这里offset的值为0.5
steps的值为[8, 16, 32, 64, 100, 300]

直觉上,我们会认为先验框的中心点应该就是映射图的相应格的中心点,所以对于conv4_3(38, 38),那么每个格的step应该四舍五入后300//38=8,在前几层step的确是这样设置的。但是我们发现在第三层开始这样计算后的值应该是30而不是32, 64应该是60,这里一个比较好的解释是首先这里的偏移量很小,一个是2一个是4,而且先验框只是一个设置的框,最后会通过lx,ly,lw,lh通过偏移再得到真实的值,所以偏差一点并没有太大的问题,同时这些值为8的倍数,在底层硬件处理上会可以得到更好的优化。而到了更大的尺寸100和300,如果还使用8的倍数,这样会让偏差过大。
其实先验框的设置相对可以比较随意,我在往上找了几种实现,都会有一些小的偏差,但是最终训练完的效果都还可以,证明后期的学习可以去除这些小的偏差影响。

iii. 生成中心点步骤

在得到相应的steps后,遍可以根据这里的step和offset得到所有先验框的中心点了,这里以conv4_3为例。
由于ssd为正方形,x,y轴一致
第一个点为8(step) * 0.5(offset) = 4,也就是(4, 4)
最后一个点为 4(第一个点位置) + (38(格数)-1)×8(step) = 300
通过np.linspace生成cx=cy=[4,12,20,28…,292,300]
通过np.meshgrid把cx,cy放到一起生成网格
cx_grid(38, 38) = [[4,12,20,28,…,300],[4,12,20,28…,300],…]
cy_grid(38, 38) = [[4,4,4,4…,4], [12,12,12,…,12], …, [300,300,…,300]]

d. 生成bbox框

在上边集齐了我们生成bbox所需的所有元素后,这里可以生成我们的先验框了。

i. variance

在最后生成bbox前,需要说明一个variance的概念,这个也是要放到我们的bbox中的。
这个的值为[0.1,0.1,0.2,0.2]。这四个值是之后计算loss时需要使用的,其实个人觉得这个由于所有的框都使用同样的值,其实可以不用放到bbox中。之后当我们生成bbox后,也会发现,其实bbox有点像一个大型的配置字典,这里边的所有东西都是设置好的,而且里边的东西是不会更改或者学习的,是辅助网络训练使用的,这里的variance也是辅助训练用的,方法是计算loss时lx,ly,lw,lh要分别除以这里的variance的值,这样可以增大loss,也就增加了梯度,让网络更好的学习,具体的讲解会再loss部分进行。这里只知道要把这四个值存储到bbox矩阵中即可。

每个bbox包含8个值,分别是(cx,cy,w,h,v0,v1,v2,v3),也就是中心点,长宽和variance。
这里针对conv4_3举例
首先生成零矩阵(38,38,4,4),第一个4代表这里每个格有4个先验框,最后是4是因为最后直接把variance拼接上就好,这里先把中心点和长宽生成好。
放入中心点的坐标,把cx_grid(38,38),cy_grid(38,38)都复制乘38,38,4然后放到零矩阵的第0,1维。
放入长宽,在上边b中得到的wh_list(4, 2),通过广播直接把w,h放到零矩阵的第2,3维。
归一化,目前已经生成了返回的矩阵,但是当前是相对原图的绝对值,这里为了更好的学习,要把值都归一化。cx,w都除以原图的w也就是300,同理cy,h都除以原图的h也就是300,这样得到归一化后的bbox矩阵(38,38,4,4)
加上variance,直接把variance变为矩阵(4,),然后通过广播拼接到bbox矩阵上即可。
加上batch,目前我们的bbox为(38,38,4,8),但是我们训练时候每次有batch_size,所以最后这里再把bbox复制拓展batch次,最后得到bbox(batch,38,38,4,8),然后目前都是numpy矩阵,最后使用constant变成keras的tensor,返回
下图是各个层的先验框的参数图
大白话分析——SSD目标检测网络从训练到预测(上)
最后生成的各层的Priorbox为:
conv4_3(38,38,512) -> L2 norm -> (38,38,4,8)
fc7(19,19,1024) -> (19,19,6,8)
conv6_2(10,10,512) -> (10,10,6,8)
conv7_2(5,5,256) -> (5,5,6,8)
conv8_2(3,3,256) -> (3,3,4,8)
conv9_2(1,1,256) -> (1,1,4,8)
需要注意的是,可以发现Priorbox是直接生成的,主要是为了计算loss和生成预测框时使用,最重要的原因是预测的是相对于priorbox的偏移和缩放量,所以需要这些先验框。

5. reshaper

到现在为止,我们已经得到模型的全部所有模块,最后就是通过reshaper把各个模块拼接到一起,方便之后进行loss的计算以及predict。

a. conf, loc, priorbox转换

首先针对每层把conf,loc和priorbox的层转换下shape,这里以conv4_3为例
conv4_3_conf(38,38,4,21) -> (38*38*4, 21)
conv4_3_loc(38,38,4,4) -> (38*38*4, 4)
conv4_3_priorbox(38,38,4,8) -> (38*38*4, 8)

b. mbox_conf, mbox_loc, mbox_priorbox

在各层的conf, loc,priorbox都转换之后,把相应层的conf,loc,priorbox都放到一起。
38*38*4+19*19*6+10*10*6+5*5*6+3*3*4+1*1*4=8732,代表着我们一共有8732个先验框
mbox_conf(8732, 21)
mbox_loc(8732, 4)
mbox_priorbox(8732, 8)

c. softmax

目前这里的conf依然为原始的预测值,为了之后计算loss方便,这里对m_conf进行softmax计算,其实这里也可以不要,直接最后使用tf的softmax加cross entropy的算子即可。
mbox_conf(8732, 21) -> softmax -> (8732, 21)
整合所有的预测
最后这里把mbox_conf(8732, 21), mbox_loc(8732, 4)和mbox_priorbox(8732, 8)拼接到一起,prediction(8732, 21+4+8),这里的顺序为分类+定位+先验框,这里的顺序需要在数据生成和计算loss时都要保持一致。
从网络整体上看相当于完成了这样的映射:
image(300,300,3) -> prediction(8732, 33)

至此,模型部分全部完成。