Single Shot Multibox Detection (SSD)实战(上)
Single Shot Multibox Detection (SSD)实战(上)
介绍了边界框、锚框、多尺度对象检测和数据集。现在,我们将利用这些背景知识构建一个目标检测模型:单次多盒检测(SSD)。这种快速简便的模式已经被广泛应用。该模型的一些设计思想和实现细节也适用于其他对象检测模型。
1. Model
图1显示了一个SSD模型的设计。该模型的主要组成部分是一个基本网络块和若干个串联的多尺度特征块。在这里,基网络块用于提取原始图像的特征,一般采用深度卷积神经网络的形式。关于SSDs的论文选择在分类层之前放置一个截断的VGG,但现在这通常被ResNet取代。我们可以设计基础网络,使其输出更大的高度和宽度。这样,基于这个特征图生成更多的锚框,允许我们检测更小的对象。接下来,每个多尺度要素块都会减少上一层提供的要素映射的高度和宽度(例如,它可以将尺寸缩小一半)。然后使用特征映射中的每个元素来扩展输入图像上的感受野。这样,多尺度特征块越接近图1顶部,其输出特征图越小,基于特征图生成的锚框越少。此外,特征块越接近顶部,特征图中每个元素的感受野越大,越适合检测较大的物体。由于SSD根据基本网络块和每个多尺度特征块生成不同数量的不同大小的锚盒,然后预测锚盒的类别和偏移量(即预测的边界框),以检测不同大小的对象,因此SSD是一种多尺度目标检测模型。
Fig. 1 The SSD is composed of a base network block and several multiscale feature blocks connected in a series.
接下来,我们将描述图1中模块的实现。首先,我们需要讨论类别预测和包围盒预测的实现。
1.1. Category Prediction Layer
将对象类别的数目设置为问问. 在这种情况下,锚盒类别的数量是q+1,0表示只包含背景的定位框。对于特定比例,将要素映射的高度和宽度分别设置为h和w。如果我们以每个元素为中心来生成锚箱,我们需要对hwa锚箱。如果我们使用全连接层(FCN)作为输出,这可能会导致模型参数过多。如何使用卷积层通道输出类别预测。SSD采用相同的方法来降低模型复杂度。具体地说,类别预测层使用保持输入高度和宽度的卷积层。因此,输出和输入与特征映射的宽度和高度的空间坐标一一对应。假设输出和输入具有相同的空间坐标(x,y),坐标的通道(x,y)在输出特征图上,包含使用输入特性图坐标生成的所有定位框的类别预测(x,y)作为中心。因此,有a(q+1)输出通道,输出通道索引为i(q+1)+ji(q+1)+j(0≤j≤q0≤j≤q)表示类别索引的预测j对于锚箱索引。我们将定义这种类型的类别预测层。在我们指定参数a和q之后,它使用3×3卷积层,填充为1。这个卷积层的输入和输出的高度和宽度保持不变。
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import autograd, gluon, image, init, np, npx
from mxnet.gluon import nn
npx.set_np()
def cls_predictor(num_anchors, num_classes):
return nn.Conv2D(num_anchors * (num_classes + 1), kernel_size=3,
padding=1)
1.2. Bounding Box Prediction Layer
包围盒预测层的设计与类别预测层的设计相似。唯一的区别是,在这里,我们需要为每个锚框预测4个偏移量,而不是q+1类别。
def bbox_predictor(num_anchors):
return nn.Conv2D(num_anchors * 4, kernel_size=3, padding=1)
1.3. Concatenating Predictions for Multiple Scales
如前所述,SSD使用基于多尺度的特征映射来生成锚盒并预测其类别和偏移量。由于不同尺度的特征映射,同一元素中心锚盒的形状和数量不同,不同尺度下的预测输出可能具有不同的形状。
在下面的例子中,我们使用同一批数据构建两个不同比例的特征映射,Y1和Y2。在这里,Y2的高度和宽度是Y1的一半。以类别预测为例,我们假设Y1和Y2特征映射中的每个元素生成五个(Y1)或三个(Y2)锚定框。当有10个对象类别时,类别预测输出通道的数量为 5×(10+1)=55或3×(10+1)=33。预测输出的格式是(批次大小、通道数、高度、宽度)。如您所见,除了批量大小,其他维度的大小是不同的。因此,我们必须将它们转换成一致的格式,并将多个尺度的预测串联起来,以便于后续的计算。
def forward(x, block):
block.initialize()
return block(x)
Y1 = forward(np.zeros((2, 8, 20, 20)), cls_predictor(5, 10))
Y2 = forward(np.zeros((2, 16, 10, 10)), cls_predictor(3, 10))
(Y1.shape, Y2.shape)
((2, 55, 20, 20), (2, 33, 10, 10))
通道尺寸包含具有相同中心的所有锚定盒的预测。我们首先将通道维度移动到最后一个维度。我们可以将批量大小转换为相同的二进制大小(因为批量大小的预测是相同的)(batch size, height ×× width ×× number of channels),以便于后续在第一尺寸。
def flatten_pred(pred):
return npx.batch_flatten(pred.transpose(0, 2, 3, 1))
def concat_preds(preds):
return np.concatenate([flatten_pred(p) for p in preds], axis=1)
因此,尽管Y1和Y2的形态不同,我们仍然可以将同一批次的两个不同尺度的预测结果串联起来。
concat_preds([Y1, Y2]).shape
(2, 25300)
1.4. Height and Width Downsample Block
对于多尺度目标检测,我们定义了下面的下采样块,它将高度和宽度减少了50%。这个街区有两个3×3,填充为1和a的卷积层2×2,以串联方式连接的跨距为2的最大池化层。我们知道,3×3填充为1的卷积层不会改变特征映射的形状。然而,随后的池化层直接将特征图的大小缩小了一半。因为1×2+(3−1)+(3−1)=6,输出特征映射中的每个元素在形状的输入特征映射上都有一个接受域6×6。height和width downsample块放大了输出特征映射中每个元素的感受野。
def down_sample_blk(num_channels):
blk = nn.Sequential()
for _ in range(2):
blk.add(nn.Conv2D(num_channels, kernel_size=3, padding=1),
nn.BatchNorm(in_channels=num_channels),
nn.Activation('relu'))
blk.add(nn.MaxPool2D(2))
return blk
通过在height和width下采样块中测试正向计算,我们可以看到它改变了输入通道的数目,并使高度和宽度减半。
forward(np.zeros((2, 3, 20, 20)), down_sample_blk(10)).shape
(2, 10, 10, 10)
1.5. Base Network Block
基本网络块用于从原始图像中提取特征。为了简化计算,我们将构造一个基础小网络。该网络由三个高度和宽度的下采样块串联而成,因此在每一步中它的通道数加倍。当我们用输入原始256×256图像时,基础网络块输出具有该形状的特征映射32×32。
def base_net():
blk = nn.Sequential()
for num_filters in [16, 32, 64]:
blk.add(down_sample_blk(num_filters))
return blk
forward(np.zeros((2, 3, 256, 256)), base_net()).shape
(2, 64, 32, 32)
1.6. The Complete Model
SSD型号共包含五个模块。每个模块输出一个特征映射,用于生成锚框并预测这些锚框的类别和偏移量。第一个模块是基本网络块,模块2到4个是高度和宽度下采样块,第五个模块是一个全局最大池化层,它将高度和宽度减小到1。因此,模块2到5都是图1所示的多尺度特征块。
def get_blk(i):
if i == 0:
blk = base_net()
elif i == 4:
blk = nn.GlobalMaxPool2D()
else:
blk = down_sample_blk(128)
return blk
定义每个模块的正向计算过程。与前面描述的卷积神经网络相比,该模块不仅返回卷积计算输出的特征映射Y,而且返回由Y生成的当前尺度的锚盒及其预测的类别和偏移量。
def blk_forward(X, blk, size, ratio, cls_predictor, bbox_predictor):
Y = blk(X)
anchors = npx.multibox_prior(Y, sizes=size, ratios=ratio)
cls_preds = cls_predictor(Y)
bbox_preds = bbox_predictor(Y)
return (Y, anchors, cls_preds, bbox_preds)
当我们在图7.1中提到的更大的目标块时,它必须生成一个更大的锚框。在这里,我们首先将0.2到1.05的间隔分成五个相等的部分,以确定不同比例的较小锚箱的尺寸:0.2、0.37、0.54等,然后根据SQRT(0.2×0.37)= 0.272,SQRT(0.37×0.54)= 0.447,
以及类似的公式,确定了不同尺度下较大锚箱的尺寸。
sizes = [[0.2, 0.272], [0.37, 0.447], [0.54, 0.619], [0.71, 0.79],
[0.88, 0.961]]
ratios = [[1, 2, 0.5]] * 5
num_anchors = len(sizes[0]) + len(ratios[0]) – 1
可以定义完整的模型,TinySSD。
class TinySSD(nn.Block):
def __init__(self, num_classes, **kwargs):
super(TinySSD, self).__init__(**kwargs)
self.num_classes = num_classes
for i in range(5):
# The assignment statement is self.blk_i = get_blk(i)
setattr(self, 'blk_%d' % i, get_blk(i))
setattr(self, 'cls_%d' % i, cls_predictor(num_anchors,
num_classes))
setattr(self, 'bbox_%d' % i, bbox_predictor(num_anchors))
def forward(self, X):
anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5
for i in range(5):
# getattr(self, 'blk_%d' % i) accesses self.blk_i
X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward(
X, getattr(self, 'blk_%d' % i), sizes[i], ratios[i],
getattr(self, 'cls_%d' % i), getattr(self, 'bbox_%d' % i))
# In the reshape function, 0 indicates that the batch size remains
# unchanged
anchors = np.concatenate(anchors, axis=1)
cls_preds = concat_preds(cls_preds)
cls_preds = cls_preds.reshape(
cls_preds.shape[0], -1, self.num_classes + 1)
bbox_preds = concat_preds(bbox_preds)
return anchors, cls_preds, bbox_preds
现在,我们创建一个SSD模型实例,并使用它对图像minibatchx执行正向计算,它的高度和宽度为256像素。如前所述,第一个模块输出带有形状的特征映射32×32。因为模块2到4是高度和宽度的下采样块,模块5是一个全局池化层,并且特征图中的每个元素都被用作4个锚框的中心,总共,在五个比例下为每个图像生成锚框。
net = TinySSD(num_classes=1)
net.initialize()
X = np.zeros((32, 3, 256, 256))
anchors, cls_preds, bbox_preds = net(X)
print('output anchors:', anchors.shape)
print('output class preds:', cls_preds.shape)
print('output bbox preds:', bbox_preds.shape)
output anchors: (1, 5444, 4)
output class preds: (32, 5444, 2)
output bbox preds: (32, 21776)
Single Shot Multibox Detection (SSD)实战(上)的更多相关文章
- Single Shot Multibox Detection (SSD)实战(下)
Single Shot Multibox Detection (SSD)实战(下) 2. Training 将逐步解释如何训练SSD模型进行目标检测. 2.1. Data Reading and In ...
- 论文笔记 SSD: Single Shot MultiBox Detector
转载自:https://zhuanlan.zhihu.com/p/33544892 前言 目标检测近年来已经取得了很重要的进展,主流的算法主要分为两个类型(参考RefineDet):(1)two-st ...
- SSD: Single Shot MultiBox Detector
By Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng-Yang Fu, Alexande ...
- 目标检测--SSD: Single Shot MultiBox Detector(2015)
SSD: Single Shot MultiBox Detector 作者: Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, ...
- SSD(single shot multibox detector)算法及Caffe代码详解[转]
转自:AI之路 这篇博客主要介绍SSD算法,该算法是最近一年比较优秀的object detection算法,主要特点在于采用了特征融合. 论文:SSD single shot multibox det ...
- [论文理解]SSD:Single Shot MultiBox Detector
SSD:Single Shot MultiBox Detector Intro SSD是一套one-stage算法实现目标检测的框架,速度很快,在当时速度超过了yolo,精度也可以达到two-stag ...
- 【计算机视觉】目标检测之ECCV2016 - SSD Single Shot MultiBox Detector
本文转载自: http://www.cnblogs.com/lillylin/p/6207292.html SSD论文阅读(Wei Liu--[ECCV2016]SSD Single Shot Mul ...
- SSD(Single Shot MultiBox Detector)的安装配置和运行
下文图文介绍转自watersink的博文SSD(Single Shot MultiBox Detector)不得不说的那些事. 该方法出自2016年的一篇ECCV的oral paper,SSD: Si ...
- SSD(single shot multibox detector)
SSD,全称Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一种目标检测算法,截至目前是主要的检测框架之一,相比Faster RCNN有明显的速 ...
随机推荐
- Android APK程序的smali动态调试
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/71250622 一.Apktool的下载和安装 Apktool是Android逆向分 ...
- hdu2276 矩阵构造
题意: 给了n个灯泡的状态,他们绕成一个环,0是灭,1是亮,每一秒灯泡的状态都会改变,规则是如果当前这个灯泡的左边的灯泡当前是状态1,那么下一秒当前的这个灯泡状态就改变0变1,1变0,最后问 ...
- Python模块化编程
目录 模块化 自定义模块 模块的内置属性 导入模块 安装第三方模块 查看模块的属性和方法 模块化 在Python中,一个.py文件就称之为一个模块(Module),为了避免模块名冲突,Python又引 ...
- Metasploit Framework(MSF)的使用
目录 Metasploit 安装Metasploit 漏洞利用(exploit) 攻击载荷(payload) Meterpreter MS17_010(永恒之蓝) 辅助模块(探测模块) 漏洞利用模块 ...
- Win64 驱动内核编程-4.内核里操作字符串
内核里操作字符串 字符串本质上就是一段内存,之所以和内存使用分开讲,是因为内核里的字符串太有花 样了,细数下来竟然有 4 种字符串!这四种字符串,分别是:CHAR*.WCHAR*.ANSI_STRIN ...
- 【python】Leetcode每日一题-位1的个数
[python]Leetcode每日一题-位1的个数 [题目描述] 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量). 示例1 ...
- SpringBoot 项目 部署 war方式
SpringBoot部署 –war方式 步骤1部署方式 Springboot 和我们之前学习的web 应用程序不一样,其本质上是一个 Java 应用程序,那么又如何部署呢? 通常来说,Springbo ...
- SparkSQL电商用户画像(五)之用户画像开发(客户基本属性表)
7.电商用户画像开发 7.1用户画像--数据开发的步骤 u 数据开发前置依赖 -需求确定 pv uv topn -建模确定表结构 create table t1(pv int,uv int,topn ...
- linux网络编程中INADDR_ANY的含义
INADDR_ANY选项 网络编程中常用到bind函数,需要绑定IP地址,这时可以设置INADDR_ANY INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或&q ...
- 关于__new__和__call__的想法
__new__和__call__很像,两个都是用来产生对象的 __new__用来产生的对象是'类',class 时触发(不是) __call__用来产生的对象是'对象',这种对象无法继续产生对象,但是 ...