SSD: Single Shot MultiBox Detector解读

2024-08-24 18:08

本文主要是介绍SSD: Single Shot MultiBox Detector解读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

此SSD非彼SSD,不过都有一个特点快,我之前读过了这篇,这次算是重温,而且前面介绍了很多检测网络,尤其是FPN时更是对SSD有一个很根本的解读,所以这篇博客算是一个SSD精华介绍,哈哈。

贡献和特点

SSD最大的贡献,就是在多个feature map上进行预测,这点我在上一篇FPN也说过它的好处,可以适应更多的scale。第二个是用小的卷积进行分类回归,区别于YOLO及其faster rcnn的fc,大大降低参数和提速。

最大的特点就是不同于region-based的方法如faster rcnn等,ssd是没有region proposal这个过程的,follow了YOLO的工作,把检测的任务转化为了一个回归任务,直接对default box进行暴力回归。

论文中对不同scale的feature map进行预测,主要是conv4_3(之前在问过作者为什么选用conv4_3,这是作者在另一篇parsing的文章ParseNet得到的经验,这一层和特别,具体是什么就是实验证明这个层好)以及多加了一些更强语义的feature。注意 SSD的分类和坐标回归都是全卷积的,不需要roipooling,因为faster rcnn是两步先有rpn对anchor进行2分类和回归然后修正anchor后的proposal需要重新映射到feature上取feature进行后面的细分类和回归,而SSD是一步到位的,直接对default box进行细分类和一次暴力回归。以以C=21类,Conv4_3,分类为例,用3*3的卷积对feature上每个点进行分类,得到84*h*w的结果,类似分割正常应该是21channel的,由于Conv4_3feature上每个点是对应4个default(prior) box的,所以是84channel

# conv4_3上设置的4个不同的比例,feature上每个点对应4个default(prior) box
layer {name: "conv4_3_norm_mbox_conf"type: "Convolution"bottom: "conv4_3_norm"top: "conv4_3_norm_mbox_conf"convolution_param {num_output: 84  # 21*4 feature上每个点对应4个default(prior) box, 21个类别pad: 1kernel_size: 3stride: 1}
}
layer {name: "conv4_3_norm_mbox_loc"type: "Convolution"bottom: "conv4_3_norm"top: "conv4_3_norm_mbox_loc"convolution_param {num_output: 16  # 4*4 feature上每个点对应4个default(prior) box, 4个坐标pad: 1kernel_size: 3stride: 1}
}

这里引用一张别人解读的文章Single Shot MultiBox Detector(SSD)的图片,我们刚刚也说了得到feature上每个点对应boxes(4/6)的回归值16*h*w,然后作者把这个拉平成一个向量(红线处flat),然后和gt做loss。就这样简单用一个3*3的卷积得到回归值然后做loss 佩服 网络这能都学会(其实faster rcnn也差不多是用1*1做的,任务简单些)。另一部分就正常softmax分类。

这里写图片描述

Default box

之前说fpn(发表晚于ssd)时也讲到,解决不同scale物体的检测,通常的做法就是放缩图片送到网络然后融合结果,但是费时,而ssd就是在不同scale的feature上预测不同大小的物体来解决这个问题,不同scale的feature视野域不同,刚好大物体在后面的feature预测,小物体在前面预测。说到底这个default box是十分类似于faster rcnn的anchor的,只是这个box是在不同scale的feature上取的。

不同层default box的大小公式如下,注意每个feature上的点虽然对应4或6个box,但是box并不是整个点对回去的原图,看公式最后的层最大也就0.9。feature上每个点的中心即是其对应box的中心,scale为1时再附带一个S’k的scale。

# SSD300 6个预测的层,他们的步长和比例设置 最后一个feature大小1*1大小步长为300
# 比例为1的其实两个,每个层都有忽略不写了 2是2和1/2 3是3和1/3
mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
steps = [8, 16, 32, 64, 100, 300]
aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]

这里写图片描述
这里写图片描述

mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
# in percent %
min_ratio = 20
max_ratio = 90
step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - 2)))
min_sizes = []
max_sizes = []
for ratio in xrange(min_ratio, max_ratio + 1, step):min_sizes.append(min_dim * ratio / 100.)max_sizes.append(min_dim * (ratio + step) / 100.)
min_sizes = [min_dim * 10 / 100.] + min_sizes
max_sizes = [min_dim * 20 / 100.] + max_sizes
print min_sizes
print max_sizes
'''
conv4_3特殊对待了
[30.0, 60.0, 111.0, 162.0, 213.0, 264.0]
[60.0, 111.0, 162.0, 213.0, 264.0, 315.0]
'''
'''default box的生成'feature_maps' : [38, 19, 10, 5, 3, 1],'min_dim' : 300,'steps' : [8, 16, 32, 64, 100, 300],'min_sizes' : [30, 60, 111, 162, 213, 264],'max_sizes' : [60, 111, 162, 213, 264, 315],'aspect_ratios' : [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
'''
for k, f in enumerate(self.feature_maps):for i, j in product(range(f), repeat=2):f_k = self.image_size / self.steps[k]# unit center x,ycx = (j + 0.5) / f_kcy = (i + 0.5) / f_k# aspect_ratio: 1# rel size: min_sizes_k = self.min_sizes[k] / self.image_sizemean += [cx, cy, s_k, s_k]# aspect_ratio: 1# rel size: sqrt(s_k * s_(k+1))s_k_prime = sqrt(s_k * (self.max_sizes[k] / self.image_size))mean += [cx, cy, s_k_prime, s_k_prime]# rest of aspect ratiosfor ar in self.aspect_ratios[k]:mean += [cx, cy, s_k * sqrt(ar), s_k / sqrt(ar)]mean += [cx, cy, s_k / sqrt(ar), s_k * sqrt(ar)]

Conv4_3 38*38的feature map通过3*3的卷积层预测4个不同宽高比的box,Conv7 19*19预测6个,Conv8_2, Conv9_2预测6个不同长宽比的box,Conv 10_2,Conv11_2预测4个。所以38*38*4+19*19*6+10*10*6+5*5*6+3*3*4+4 = 8732,所有不同scale feature map上的点对应8732个box。可能大物体和小物体不占大多数,所以Conv4_3 Conv 10_2 Conv11_2都只有4个scale,而且Conv4_3太大了,scale太多box太多了。

这里写图片描述

gt匹配策略

还有一个就是给这些box指定gt的策略,基本和faster rcnn类似
1. 对于每个gt,将其指定给与它iou最大的box,保证每个gt都有box去回归
2. 对于剩下的box,只要与任一gt iou>0.5(faster rcnn是0.7),都将这个gt指定给它
3. 貌似剩下的都是负样本了(faster rcnn是与任一gt的iou<0.3才为负样本,其他非正非负忽略掉),这点文中没有直接说,等我看下代码吧

难样本挖掘和数据增强

按照 default boxes 的 confidence 的大小。 选择最高的几个,保持最后的正负样本为1:3,文中训练时没说用nms哎,只有在inference时说了用nms,具体还要我再刷一遍代码。

By using a confidence threshold of 0.01, we can filter out most boxes. We then apply nms with jaccard overlap of 0.45 per class and keep the top 200 detections per image.

数据增强:
1. 使用原始的图像
2. 从原图随机采样一个 patch,但是采样后的patch中与原图所有gt boxes iou的最小值要满足 >0.1 0.3…0.9
3. 从原图随机采样 patch没有任何限制
采样从以上3种形式中随机选取,在这些采样步骤之后,每一个采样的patch被resize到固定的大小,并且以0.5的概率随机的 水平翻转(horizontally flipped)。除此之外,SSD还有一些提前的数据增强S光照啊旋转等等,这也是它最被argue的一点了。

def _crop(image, boxes, labels):height, width, _ = image.shapeif len(boxes)== 0:return image, boxes, labelswhile True:'''1. 使用原始的图像  对应mode: None  if mode is None:return image, boxes, labels2. 从原图随机采样一个 patch,但是采样后的patch中与原图所有gt boxes iou的最小值要满足 >0.1 0.3...0.9对应mode: (0.1, None),(0.3, None),等iou = matrix_iou(boxes, roi[np.newaxis])if not (min_iou <= iou.min() and iou.max() <= max_iou):continue3. 从原图随机采样 patch没有任何限制 对应mode: (None, None)if min_iou is None:min_iou = float('-inf')if max_iou is None:max_iou = float('inf')
'''mode = random.choice((None,(0.1, None),(0.3, None),(0.5, None),(0.7, None),(0.9, None),(None, None),))if mode is None:return image, boxes, labelsmin_iou, max_iou = modeif min_iou is None:min_iou = float('-inf')if max_iou is None:max_iou = float('inf')for _ in range(50):scale = random.uniform(0.3,1.)min_ratio = max(0.5, scale*scale)max_ratio = min(2, 1. / scale / scale)ratio = math.sqrt(random.uniform(min_ratio, max_ratio))w = int(scale * ratio * width)h = int((scale / ratio) * height)l = random.randrange(width - w)t = random.randrange(height - h)roi = np.array((l, t, l + w, t + h))iou = matrix_iou(boxes, roi[np.newaxis])if not (min_iou <= iou.min() and iou.max() <= max_iou):continueimage_t = image[roi[1]:roi[3], roi[0]:roi[2]]centers = (boxes[:, :2] + boxes[:, 2:]) / 2mask = np.logical_and(roi[:2] < centers, centers < roi[2:]) \.all(axis=1)boxes_t = boxes[mask].copy()labels_t = labels[mask].copy()if len(boxes_t) == 0:continueboxes_t[:, :2] = np.maximum(boxes_t[:, :2], roi[:2])boxes_t[:, :2] -= roi[:2]boxes_t[:, 2:] = np.minimum(boxes_t[:, 2:], roi[2:])boxes_t[:, 2:] -= roi[:2]return image_t, boxes_t,labels_t

实验结果

效果真的很赞,又快又好(和作者强大的码力也有很大关系的,膜拜中),一些消融实验就不一一介绍了。

这里写图片描述

不足之处

  1. default box的min_size,max_size和aspect_ratio值这些设置很吃经验的,不好调节
  2. 对小物体依然不ok,预测是从conv4_3开始的,一是有些小物体在这都看不到了,二是浅层feature来预测的话语义又不够强,很难分类,而小物体又很依赖上下文信息来识别,这也是之前讲的fpn的改进之处了,把强语义的feature给融合过来了 (图片第3-6列)
  3. 因为ssd从多个level的feature进行预测,所以很容易重复地检测到一个物体的多个部件或者把多个物体合并到一个物体里(第一列多个狗头,第二列把两个狗合并成一个狗),具体原因不是特别清楚???(图片第1、2列)

这里写图片描述


参考了Single Shot MultiBox Detector(SSD)
faster rcnn和ssd我都参看了知乎专栏:机器学习随笔,收益匪浅,ps这个作者之前在csdn现在所有csdn文章都删掉挂到知乎了,特别感谢。

这篇关于SSD: Single Shot MultiBox Detector解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1103217

相关文章

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文:Improving Language Understanding by Generative Pre-Training 链接:https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点:生成loss和微调loss同时作用,让下游任务来适应预训

SAM2POINT:以zero-shot且快速的方式将任何 3D 视频分割为视频

摘要 我们介绍 SAM2POINT,这是一种采用 Segment Anything Model 2 (SAM 2) 进行零样本和快速 3D 分割的初步探索。 SAM2POINT 将任何 3D 数据解释为一系列多向视频,并利用 SAM 2 进行 3D 空间分割,无需进一步训练或 2D-3D 投影。 我们的框架支持各种提示类型,包括 3D 点、框和掩模,并且可以泛化到不同的场景,例如 3D 对象、室

LLM系列 | 38:解读阿里开源语音多模态模型Qwen2-Audio

引言 模型概述 模型架构 训练方法 性能评估 实战演示 总结 引言 金山挂月窥禅径,沙鸟听经恋法门。 小伙伴们好,我是微信公众号《小窗幽记机器学习》的小编:卖铁观音的小男孩,今天这篇小作文主要是介绍阿里巴巴的语音多模态大模型Qwen2-Audio。近日,阿里巴巴Qwen团队发布了最新的大规模音频-语言模型Qwen2-Audio及其技术报告。该模型在音频理解和多模态交互

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python

速通GPT-3:Language Models are Few-Shot Learners全文解读

文章目录 论文实验总览1. 任务设置与测试策略2. 任务类别3. 关键实验结果4. 数据污染与实验局限性5. 总结与贡献 Abstract1. 概括2. 具体分析3. 摘要全文翻译4. 为什么不需要梯度更新或微调⭐ Introduction1. 概括2. 具体分析3. 进一步分析 Approach1. 概括2. 具体分析3. 进一步分析 Results1. 概括2. 具体分析2.1 语言模型

Open-Sora代码详细解读(1):解读DiT结构

Diffusion Models专栏文章汇总:入门与实战 前言:目前开源的DiT视频生成模型不是很多,Open-Sora是开发者生态最好的一个,涵盖了DiT、时空DiT、3D VAE、Rectified Flow、因果卷积等Diffusion视频生成的经典知识点。本篇博客从Open-Sora的代码出发,深入解读背后的原理。 目录 DiT相比于Unet的关键改进点 Token化方

Transformer从零详细解读

Transformer从零详细解读 一、从全局角度概况Transformer ​ 我们把TRM想象为一个黑盒,我们的任务是一个翻译任务,那么我们的输入是中文的“我爱你”,输入经过TRM得到的结果为英文的“I LOVE YOU” ​ 接下来我们对TRM进行细化,我们将TRM分为两个部分,分别为Encoders(编码器)和Decoders(解码器) ​ 在此基础上我们再进一步细化TRM的