Single Shot MultiBox Detector 论文理解 [author: linkrain]

Single Shot MultiBox Detector 论文理解 [author: linkrain]

最早接触目标检测是通过YOLOv2,但SSD在同一时期也是非常优秀的算法,之后的经历中也确实使用过很多次SSD,但通过代码和博客学习居多,论文一直没有仔细研读,最近突然有个想法,想要把目标检测和图像分类领域的“古老”算法(网络结构)都回顾一遍,以系统化自己脑海中存在的太多杂乱分散的知识。感叹一下,连接(关系)真的非常重要,独立性和关系化可能也是一对值得trade-off的东西喔。

提前讲一句,我知识还比较浅薄,很可能有理解错误或者完全不合理的思考结论,本文中的内容请谨慎参考,不要让我荼毒了你们的思想。

SSD是一种单阶段的方法

什么是单阶段的方法呢,首先和单阶段的方法对应的是两阶段方法,出自深度学习方法应用到目标检测领域的开山之作----RCNN。

两阶段方法的第一阶段是在整张图像上生成预选区域,第二阶段是在每个预选区域上进行类别和框坐标的预测。这种方法需要保证第一阶段的预选区域生成能够覆盖到绝大多数目标区域,因此在第一阶段会生成大量候选区域,而每个候选区域都需要送入后续的检测器去进行分类和坐标预测,这就造成速度成为一个瓶颈,但这种拆分问题渐次逼近的思想降低了每一阶段所需解决的问题难度,整体检测的效果通常来说是优于单阶段的目标检测算法的。

那么单阶段的算法是怎么从一张图的输入中预测出多个结果的呢,本质上,单阶段的方法也是应用了一种问题拆分的思想,但它拆分问题的方式和两阶段算法有所不同,直观上看,两阶段算法更像是串行拆分,拆分出的两个子问题有前后依赖关系,其实就是预选区域分类定位问题依赖于预选区域生成问题;而单阶段问题更像是并行拆分,它把整张图像上的多个目标的检测分类问题拆分为在图像上的每个规则的小方格上进行一个目标或少量定量的几个目标的分类和定位问题,各个子问题之间没有依赖关系,因此可以高效地进行并行处理。因此,一般来说,单阶段的方法在速度上比较有优势。

这里其实想展开来说一下单阶段算法的问题:

  • 通常图像中存在目标的小方格相比于非目标的小方格数量要少得多,而单阶段方法在同一个特征图上是通过同一组卷积核来进行分类和坐标预测的,如果把这张特征图上的像素看成是训练样本,而把这组卷积核看作是分类器的话,那么很明显,训练样本类别非常不平衡,这不利于训练。解决这个问题的方案有一些,例如Focal-Loss,通过动态的样本loss赋权来平衡不同样本的权值更新幅度;还有直接选择和图像中目标数量比较接近或者成固定比例的非目标输出来进行反向传播;嗯,这里应该还有更多的方案,才疏学浅,就先不多列了。

SSD中的前向计算

嗯,先回到正题,讲SSD。那么就先看一下一张图片送入网络后它的图生经历。

  1. 先过骨干网络,论文中作者用的是VGG-16(去除用于分类的网络尾部),同时也说明了换成其它的网络同样也能够得到很好的结果,稍微回顾一下VGG-16的网络结构,以便后续的算法细节理解。VGG-16通过一系列3×3×channels的卷积核进行特征提取,通过5次max-pooling来进行下采样,最终通过多层全连接得到one-hot的类别预测向量。SSD中仅仅只用到第五个max-pooling层前的网络结构来进行特征提取(不包括第五个max-pooling层),如果输入图像尺寸为(224×224×3),那么提取出的特征图形状为(14×14×512),SSD中采用的输入图像尺寸为(300×300×3), 几次下采样后通过卷积得到的特征图尺寸分别为(150×150×64)->(75×75×128)->(38×38×256)->(19×19×512),关于(75×75×128)下采样到(38×38×128),需要在使用max_pooling层的时候选用ceiling方式进行计算,直观来说就是如果边界元素无法填满max_pooling的核,那么就仅仅取核能够覆盖的边界元素计算max值。
  2. 通过3×3卷积扩张特征图通道数,通过1×1卷积压缩特征图通道数,再通过stride=2的3×3卷积扩张特征图通道数的同时下采样,反复多次后得到多种尺度的特征图(这里反复多次并非是同样模式的反复,具体可以查找其他文章中的SSD网络结构图)。
  3. 对于每种尺度的特征图,SSD在特征图的每个cell,也就是特征图的每个像素上进行预测,这里引入了一些不同尺寸和比例的先验框(anchor box),假设先验框的数量是k个,那么对于每一个cell中的每一个先验框都需要预测一个one-hot的c维向量,c是总类别数,同时还需要预测一个4维向量用于目标框坐标回归,所以对于每个cell中的每个先验框,SSD需要预测一个c+4维的向量,进一步,对于特征图的每个cell,SSD需要预测一个k*(c+4)维的向量,于是在这个特征图上预测使用的卷积核形状就确定为 33[k*(c+4)] 。
  4. 将所有尺度的预测结果全部进行NMS处理,去除多余的高重合度低分框,输出最终的预测结果。

骨干网络没什么好说的,这个可以根据需要随意替换;扩张通道再压缩通道这个操作,我自己感觉上是高维空间中更容易学习复杂模式(复杂分布在高维空间中可分度更高),但关于这部分的严谨理论我并没有深入了解;通过下采样得到多种尺度的特征图,并且在每种尺度的特征图上基于不同尺寸和比例的先验框进行预测,这个操作让各种尺寸的目标都能够被网络覆盖到,同时总计8000+的预测框数量能够很好的保证召回率,这是SSD中重要的操作。

网络训练

对网络进行端到端的训练,你需要首先明白网络的输出是什么,前文已经介绍了在一个特定的特征图上网络是如何进行预测的:每个cell预测得到k*(c+4)个值,用于表示k个先验框的c个类别概率以及4个box位置表示。SSD中总共在6个尺度的特征图上进行预测,这6个特征图的尺度(只看高宽)分别为:(38×38),(19×19),(10×10),(5×5),(3×3),(1×1);对应的k值分别为:4,6,6,6,4,4。计算可得最终预测得到的box个数有8732个,每个box需要预测c+4个值,最终网络一共需要预测8732×4×c个值(c包括背景类)。

知道了网络的输出量,那么也就知道了网络的监督量,同样,网络需要8732×4×c个值来进行监督。类别相关的监督量不用多说,就是one-hot的ground-truth类别概率,box相关的监督量需要根据先验框的设置和原始ground-truth-box进行匹配(Matching),论文中介绍了匹配的策略,简单说就是计算每个先验框对于每个ground-truth-box的jaccard-overlap=交并比=IoU,只要大于0.5,那么这个先验框就与这个ground-truth-box匹配,那么该先验框的监督量就是匹配的ground-truth-box的中心点坐标相对于该先验框的中心点坐标的偏移量,以及ground-truth-box的高宽(这里的偏移量和高宽只是指监督量的物理意义,不是指最终监督量的值)。论文中讲这种允许多对一匹配的方式比只匹配IoU最大的目标框的效果要好。

中心偏移监督量计算方式:
delta(cx) = (cx(gt) - cx(anchor)) / w(anchor)
delta(cy) = (cy(gt) - cy(anchor)) / h(anchor)
宽高监督量计算方式:
s(w) = log(w(gt) / w(anchor))
s(h) = log(h(gt) / h(anchor))
其实这里可以计算一下偏移和宽高监督量的取值范围,这个以后再说吧。

有了监督量的表示,就可以开始应用Loss函数来定义优化目标了。SSD中,定位误差是采用以上四个监督量和对应的网络输出结果之间的SmoothL1值作为Loss,仅仅只在匹配到gt-box的先验框上计算后求平均;分类Loss就是多类交叉熵损失函数。

从上述描述可以看出,监督量的计算,进一步推到Loss函数的计算,都需要每个先验框有固定的中心点坐标和高宽值,那么接下来需要考虑的问题就是如何设置先验框。SSD设置了一套计算方法,通过设定先验框的最大最小scale以及一共有几层特征图用于预测box,就可以算出每一层对应的先验框scale,再通过固定的5个高宽比计算出对应特征图上的6种先验框,具体计算方式:

以下代码段只是用代码说明计算方式,不能直接拿去用哈,当然改下可能可以用:)
我也不知道是公式好理解还是代码好理解,但是我是懒得贴公式了……

#以下为计算各个不同尺度特征图的先验框scale方法:
#假定网络采用m个不同尺度的特征图进行box预测
#假定最小的单位先验框scale为s_min
#假定最大的单位先验框scale为s_max
scale = []
for i in range(1,m+1):
  scale.append(s_min + ((s_max - s_min) / (m - 1) * (i - 1)))
  print('scale-{} = {}'.format(i, scale[i-1]))
  #实际上就是在区间[s_min, s_max]上均匀取m个值。

#以下为计算某个尺度特征图上的先验框坐标及高宽方法:
#假定此特征图上设置6种先验框,对应的宽高比存放在列表aspect_ratios中
#假定此特征图上的先验框scale为s[k], 下一尺度特征图上先验框scale为s[k+1]
#假定此特征图的高宽分别为fh和fw,当然通常情况下 fh == fw
import numpy as np
w = np.zeros((fh, fw, 6), dtype=np.float32)
h = np.zeros((fh, fw, 6), dtype=np.float32)
cx = np.zeros((fh, fw, 6), dtype=np.float32)
cy = np.zeros((fh, fw, 6), dtype=np.float32)
aspect_ratios = [1., 2., 3., 0.5, 1./3.]
for raw in range(fh):
  for col in range(fw):
    for i in range(len(aspect_ratios)):
      w[raw, col, i] = s[k] * np.power(aspect_ratios[i], 0.5)
      h[raw, col, i] = s[k] / np.power(aspect_ratios[i], 0.5)
      cx[raw, col, i] = (col + 0.5) / fw
      cy[raw, col, i] = (raw + 0.5) / fh
    w[raw, col, 5] = np.power(s[k] * s[k+1], 0.5) * np.power(aspect_ratios[0], 0.5)
    h[raw, col, 5] = np.power(s[k] * s[k+1], 0.5) / np.power(aspect_ratios[0], 0.5)
    cx[raw, col, 5] = (col + 0.5) / fw
    cy[raw, col, 5] = (raw + 0.5) / fh

还是稍微解释一下,想象在输入图像上有一个边长为a的正方形, 输入图像尺寸为(size, size),这里的输入图像是指resize过后输入网络的图像。则此正方形的scale = a / size。SSD中,每个尺度的特征图上都有一个单位正方形,这个正方形的aspect_ratio = 1.,此正方形的scale即是该特征图的scale,那么通过每个尺度特征图的scale和网络的输入图片尺寸就能够知道每个尺度特征图上的单位正方形在输入图像上的大小,换种角度看,其实scale就是单位正方形在输入图像上的归一化边长。论文中取了6个不同尺度的特征图,其scale分别为[0.2, 0.34, 0.48, 0.62, 0.76, 0.9]。

代码里能看出aspect_ratios一共有5个值,因此至少会生成5种先验框,而aspect_ratios[0]在循环结束后又用来生成了一种先验框,因此最终生成了6种先验框,前文有提到某些层只有4种先验框,设置为只生成4个先验框的层上,计算时忽略掉两个aspect_ratio,它们是3.和1./3.。

前面提到过,单阶段检测存在目标类别不平衡的问题,SSD中采用Hard negative mining的方法来缓解这个问题,具体做法是每次前向传播后将所有非目标的先验框预测的结果按照loss值降序排列,取头部的先验框来进行反向传播,具体取多少取决于目标匹配到的先验框数量,3倍取之。论文中描述这样做能够有比较快的收敛速度,同时也能提升训练的稳定性。

SSD的数据增强策略:
1.在目标周围进行crop采样,分别以最低IoU为[0.1, 0.3, 0.5, 0.7, 0.9]来进行采样
2.随机crop采样
3.采样后以0.5的概率进行水平翻转
4.some photo-metric distortions,主要是随机顺序随机强度([0.5, 1.5])的亮度调整、对比度调整、色彩变换,之后再加入随机光照噪声。这个地方,论文中写方法是类似AlexNet论文中的方法,但我粗略看了下,AlexNet论文中貌似提到的是颜色变换,此处先搁置。

这里的crop采样有一定规则,0.1 <= crop_size / ori_img_size <= 1.0,0.5 <= aspect_ratio <= 2.0,并且如果目标框的中心点在crop区域中的话,保留crop出的这部分目标框内容作为新的目标框。
以何种方式应用crop或者不进行crop可以用随机数控制执行概率,这部分自由度比较高。

暂时先写到这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值