Single Shot Multibox Detection (SSD)实战(上)
Single Shot Multibox Detection (SSD)實(shí)戰(zhàn)(上)
介紹了邊界框、錨框、多尺度對(duì)象檢測(cè)和數(shù)據(jù)集。現(xiàn)在,我們將利用這些背景知識(shí)構(gòu)建一個(gè)目標(biāo)檢測(cè)模型:單次多盒檢測(cè)(SSD)。這種快速簡便的模式已經(jīng)被廣泛應(yīng)用。該模型的一些設(shè)計(jì)思想和實(shí)現(xiàn)細(xì)節(jié)也適用于其他對(duì)象檢測(cè)模型。
- Model
圖1顯示了一個(gè)SSD模型的設(shè)計(jì)。該模型的主要組成部分是一個(gè)基本網(wǎng)絡(luò)塊和若干個(gè)串聯(lián)的多尺度特征塊。在這里,基網(wǎng)絡(luò)塊用于提取原始圖像的特征,一般采用深度卷積神經(jīng)網(wǎng)絡(luò)的形式。關(guān)于SSDs的論文選擇在分類層之前放置一個(gè)截?cái)嗟腣GG,但現(xiàn)在這通常被ResNet取代。我們可以設(shè)計(jì)基礎(chǔ)網(wǎng)絡(luò),使其輸出更大的高度和寬度。這樣,基于這個(gè)特征圖生成更多的錨框,允許我們檢測(cè)更小的對(duì)象。接下來,每個(gè)多尺度要素塊都會(huì)減少上一層提供的要素映射的高度和寬度(例如,它可以將尺寸縮小一半)。然后使用特征映射中的每個(gè)元素來擴(kuò)展輸入圖像上的感受野。這樣,多尺度特征塊越接近圖1頂部,其輸出特征圖越小,基于特征圖生成的錨框越少。此外,特征塊越接近頂部,特征圖中每個(gè)元素的感受野越大,越適合檢測(cè)較大的物體。由于SSD根據(jù)基本網(wǎng)絡(luò)塊和每個(gè)多尺度特征塊生成不同數(shù)量的不同大小的錨盒,然后預(yù)測(cè)錨盒的類別和偏移量(即預(yù)測(cè)的邊界框),以檢測(cè)不同大小的對(duì)象,因此SSD是一種多尺度目標(biāo)檢測(cè)模型。
Fig. 1 The SSD is composed of a base network block and several multiscale feature blocks connected in a series.
接下來,我們將描述圖1中模塊的實(shí)現(xiàn)。首先,我們需要討論類別預(yù)測(cè)和包圍盒預(yù)測(cè)的實(shí)現(xiàn)。
1.1. Category Prediction Layer
將對(duì)象類別的數(shù)目設(shè)置為問問. 在這種情況下,錨盒類別的數(shù)量是q+1,0表示只包含背景的定位框。對(duì)于特定比例,將要素映射的高度和寬度分別設(shè)置為h和w。如果我們以每個(gè)元素為中心來生成錨箱,我們需要對(duì)hwa錨箱。如果我們使用全連接層(FCN)作為輸出,這可能會(huì)導(dǎo)致模型參數(shù)過多。如何使用卷積層通道輸出類別預(yù)測(cè)。SSD采用相同的方法來降低模型復(fù)雜度。具體地說,類別預(yù)測(cè)層使用保持輸入高度和寬度的卷積層。因此,輸出和輸入與特征映射的寬度和高度的空間坐標(biāo)一一對(duì)應(yīng)。假設(shè)輸出和輸入具有相同的空間坐標(biāo)(x,y),坐標(biāo)的通道(x,y)在輸出特征圖上,包含使用輸入特性圖坐標(biāo)生成的所有定位框的類別預(yù)測(cè)(x,y)作為中心。因此,有a(q+1)輸出通道,輸出通道索引為i(q+1)+ji(q+1)+j(0≤j≤q0≤j≤q)表示類別索引的預(yù)測(cè)j對(duì)于錨箱索引。我們將定義這種類型的類別預(yù)測(cè)層。在我們指定參數(shù)a和q之后,它使用3×3卷積層,填充為1。這個(gè)卷積層的輸入和輸出的高度和寬度保持不變。
%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
包圍盒預(yù)測(cè)層的設(shè)計(jì)與類別預(yù)測(cè)層的設(shè)計(jì)相似。唯一的區(qū)別是,在這里,我們需要為每個(gè)錨框預(yù)測(cè)4個(gè)偏移量,而不是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使用基于多尺度的特征映射來生成錨盒并預(yù)測(cè)其類別和偏移量。由于不同尺度的特征映射,同一元素中心錨盒的形狀和數(shù)量不同,不同尺度下的預(yù)測(cè)輸出可能具有不同的形狀。
在下面的例子中,我們使用同一批數(shù)據(jù)構(gòu)建兩個(gè)不同比例的特征映射,Y1和Y2。在這里,Y2的高度和寬度是Y1的一半。以類別預(yù)測(cè)為例,我們假設(shè)Y1和Y2特征映射中的每個(gè)元素生成五個(gè)(Y1)或三個(gè)(Y2)錨定框。當(dāng)有10個(gè)對(duì)象類別時(shí),類別預(yù)測(cè)輸出通道的數(shù)量為 5×(10+1)=55或3×(10+1)=33。預(yù)測(cè)輸出的格式是(批次大小、通道數(shù)、高度、寬度)。如您所見,除了批量大小,其他維度的大小是不同的。因此,我們必須將它們轉(zhuǎn)換成一致的格式,并將多個(gè)尺度的預(yù)測(cè)串聯(lián)起來,以便于后續(xù)的計(jì)算。
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))
通道尺寸包含具有相同中心的所有錨定盒的預(yù)測(cè)。我們首先將通道維度移動(dòng)到最后一個(gè)維度。我們可以將批量大小轉(zhuǎn)換為相同的二進(jìn)制大小(因?yàn)榕看笮〉念A(yù)測(cè)是相同的)(batch size, height ×× width ×× number of channels),以便于后續(xù)在第一尺寸。
def flatten_pred(pred):
return
npx.batch_flatten(pred.transpose(0, 2, 3, 1))
def concat_preds(preds):
return np.concatenate([flatten_pred§ for
p in preds], axis=1)
因此,盡管Y1和Y2的形態(tài)不同,我們?nèi)匀豢梢詫⑼慌蔚膬蓚€(gè)不同尺度的預(yù)測(cè)結(jié)果串聯(lián)起來。
concat_preds([Y1, Y2]).shape
(2, 25300)
1.4. Height and Width Downsample Block
對(duì)于多尺度目標(biāo)檢測(cè),我們定義了下面的下采樣塊,它將高度和寬度減少了50%。這個(gè)街區(qū)有兩個(gè)3×3,填充為1和a的卷積層2×2,以串聯(lián)方式連接的跨距為2的最大池化層。我們知道,3×3填充為1的卷積層不會(huì)改變特征映射的形狀。然而,隨后的池化層直接將特征圖的大小縮小了一半。因?yàn)?×2+(3?1)+(3?1)=6,輸出特征映射中的每個(gè)元素在形狀的輸入特征映射上都有一個(gè)接受域6×6。height和width downsample塊放大了輸出特征映射中每個(gè)元素的感受野。
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下采樣塊中測(cè)試正向計(jì)算,我們可以看到它改變了輸入通道的數(shù)目,并使高度和寬度減半。
forward(np.zeros((2, 3, 20, 20)), down_sample_blk(10)).shape
(2, 10, 10, 10)
1.5. Base Network Block
基本網(wǎng)絡(luò)塊用于從原始圖像中提取特征。為了簡化計(jì)算,我們將構(gòu)造一個(gè)基礎(chǔ)小網(wǎng)絡(luò)。該網(wǎng)絡(luò)由三個(gè)高度和寬度的下采樣塊串聯(lián)而成,因此在每一步中它的通道數(shù)加倍。當(dāng)我們用輸入原始256×256圖像時(shí),基礎(chǔ)網(wǎng)絡(luò)塊輸出具有該形狀的特征映射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型號(hào)共包含五個(gè)模塊。每個(gè)模塊輸出一個(gè)特征映射,用于生成錨框并預(yù)測(cè)這些錨框的類別和偏移量。第一個(gè)模塊是基本網(wǎng)絡(luò)塊,模塊2到4個(gè)是高度和寬度下采樣塊,第五個(gè)模塊是一個(gè)全局最大池化層,它將高度和寬度減小到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
定義每個(gè)模塊的正向計(jì)算過程。與前面描述的卷積神經(jīng)網(wǎng)絡(luò)相比,該模塊不僅返回卷積計(jì)算輸出的特征映射Y,而且返回由Y生成的當(dāng)前尺度的錨盒及其預(yù)測(cè)的類別和偏移量。
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)
當(dāng)我們?cè)趫D7.1中提到的更大的目標(biāo)塊時(shí),它必須生成一個(gè)更大的錨框。在這里,我們首先將0.2到1.05的間隔分成五個(gè)相等的部分,以確定不同比例的較小錨箱的尺寸:0.2、0.37、0.54等,然后根據(jù)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_classesfor 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] * 5for i in range(5):# getattr(self, 'blk_%d' % i) accesses self.blk_iX, 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# unchangedanchors = 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
現(xiàn)在,我們創(chuàng)建一個(gè)SSD模型實(shí)例,并使用它對(duì)圖像minibatchx執(zhí)行正向計(jì)算,它的高度和寬度為256像素。如前所述,第一個(gè)模塊輸出帶有形狀的特征映射32×32。因?yàn)槟K2到4是高度和寬度的下采樣塊,模塊5是一個(gè)全局池化層,并且特征圖中的每個(gè)元素都被用作4個(gè)錨框的中心,總共,在五個(gè)比例下為每個(gè)圖像生成錨框。
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)
總結(jié)
以上是生活随笔為你收集整理的Single Shot Multibox Detection (SSD)实战(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Anchor Boxes示例实战
- 下一篇: Single Shot Multibox