在第 14.3 節(jié)到第 14.6 節(jié)中,我們介紹了邊界框、錨框、多尺度目標(biāo)檢測(cè)和目標(biāo)檢測(cè)數(shù)據(jù)集?,F(xiàn)在我們準(zhǔn)備使用這些背景知識(shí)來(lái)設(shè)計(jì)一個(gè)目標(biāo)檢測(cè)模型:?jiǎn)未味嗫驒z測(cè)(SSD)(Liu et al. , 2016)。該模型簡(jiǎn)單、快速、應(yīng)用廣泛。雖然這只是大量目標(biāo)檢測(cè)模型中的一種,但本節(jié)中的一些設(shè)計(jì)原則和實(shí)現(xiàn)細(xì)節(jié)也適用于其他模型。
14.7.1。模型
圖 14.7.1提供了單次多框檢測(cè)設(shè)計(jì)的概述。該模型主要由一個(gè)基礎(chǔ)網(wǎng)絡(luò)和幾個(gè)多尺度特征圖塊組成。基礎(chǔ)網(wǎng)絡(luò)用于從輸入圖像中提取特征,因此可以使用深度 CNN。例如,原始的單次多框檢測(cè)論文采用在分類層之前截?cái)嗟腣GG網(wǎng)絡(luò) (Liu et al. , 2016),而 ResNet 也被普遍使用。通過(guò)我們的設(shè)計(jì),我們可以讓基礎(chǔ)網(wǎng)絡(luò)輸出更大的特征圖,從而生成更多的錨框來(lái)檢測(cè)更小的物體。隨后,每個(gè)多尺度特征圖塊從前一個(gè)塊減少(例如,減半)特征圖的高度和寬度,并使特征圖的每個(gè)單元增加其在輸入圖像上的感受野。
回想一下14.5 節(jié)中深度神經(jīng)網(wǎng)絡(luò)通過(guò)圖像的分層表示進(jìn)行多尺度目標(biāo)檢測(cè)的設(shè)計(jì) 。由于靠近圖 14.7.1頂部的多尺度特征圖較小但具有較大的感受野,因此它們適用于檢測(cè)較少但較大的對(duì)象。
簡(jiǎn)而言之,通過(guò)其基礎(chǔ)網(wǎng)絡(luò)和多個(gè)多尺度特征圖塊,單次多框檢測(cè)生成不同數(shù)量的不同大小的錨框,并通過(guò)預(yù)測(cè)這些錨框的類別和偏移量(因此邊界盒);因此,這是一個(gè)多尺度目標(biāo)檢測(cè)模型。
圖 14.7.1作為多尺度目標(biāo)檢測(cè)模型,單次多框檢測(cè)主要由一個(gè)基礎(chǔ)網(wǎng)絡(luò)和幾個(gè)多尺度特征圖塊組成。
下面,我們將描述圖14.7.1中不同塊的實(shí)現(xiàn)細(xì)節(jié)。首先,我們討論如何實(shí)現(xiàn)類和邊界框預(yù)測(cè)。
14.7.1.1。類別預(yù)測(cè)層
讓對(duì)象類的數(shù)量為q. 然后anchor boxes有 q+1類,其中類 0 是背景。在某種程度上,假設(shè)特征圖的高度和寬度是h和w, 分別。什么時(shí)候a以這些特征圖的每個(gè)空間位置為中心生成anchor boxes,一共 hwaanchor boxes需要分類。由于參數(shù)化成本可能很高,這通常會(huì)使完全連接層的分類變得不可行?;叵胍幌挛覀?cè)?.3 節(jié)中如何使用卷積層的通道來(lái)預(yù)測(cè)類別。單次多框檢測(cè)使用相同的技術(shù)來(lái)降低模型的復(fù)雜性。
具體來(lái)說(shuō),類預(yù)測(cè)層使用卷積層而不改變特征圖的寬度或高度。這樣,在特征圖的相同空間維度(寬度和高度)下,輸出和輸入之間可以存在一一對(duì)應(yīng)關(guān)系。更具體地說(shuō),輸出特征映射的通道在任何空間位置(x, y) 表示以 (x,y) 輸入特征圖。為了產(chǎn)生有效的預(yù)測(cè),必須有a(q+1)輸出通道,其中對(duì)于相同的空間位置,具有索引的輸出通道i(q+1)+j 代表類別的預(yù)測(cè)j (0≤j≤q) 對(duì)于錨框i (0≤i
下面我們定義這樣一個(gè)類預(yù)測(cè)層,指定a和 q分別通過(guò)參數(shù)num_anchors和num_classes。該層使用了3×3填充為1的卷積層。該卷積層的輸入和輸出的寬度和高度保持不變。
14.7.1.2。邊界框預(yù)測(cè)層
邊界框預(yù)測(cè)層的設(shè)計(jì)與類預(yù)測(cè)層的設(shè)計(jì)類似。唯一的區(qū)別在于每個(gè)錨框的輸出數(shù)量:這里我們需要預(yù)測(cè)四個(gè)偏移量而不是q+1類。
14.7.1.3。連接多個(gè)尺度的預(yù)測(cè)
正如我們提到的,單次多框檢測(cè)使用多尺度特征圖來(lái)生成錨框并預(yù)測(cè)它們的類別和偏移量。在不同的尺度下,特征圖的形狀或以同一單元為中心的錨框數(shù)量可能會(huì)有所不同。因此,不同尺度的預(yù)測(cè)輸出的形狀可能會(huì)有所不同。
在下面的例子中,我們構(gòu)建了兩種不同比例的特征圖,Y1并且Y2,對(duì)于同一個(gè)小批量,其中 的高度和寬度Y2是 的一半Y1。讓我們以類別預(yù)測(cè)為例。Y1假設(shè)分別為和中的每個(gè)單元生成 5 個(gè)和 3 個(gè)錨框Y2。進(jìn)一步假設(shè)對(duì)象類的數(shù)量為 10。對(duì)于特征圖Y1和Y2類預(yù)測(cè)輸出中的通道數(shù)為5×(10+1)=55 和3×(10+1)=33,其中任一輸出形狀為(批量大小、通道數(shù)、高度、寬度)。
我們可以看到,除了 batch size 維度,其他三個(gè)維度都有不同的大小。為了連接這兩個(gè)預(yù)測(cè)輸出以實(shí)現(xiàn)更高效的計(jì)算,我們將把這些張量轉(zhuǎn)換為更一致的格式。
請(qǐng)注意,通道維度包含具有相同中心的錨框的預(yù)測(cè)。我們先把這個(gè)維度移到最里面。由于批量大小對(duì)于不同的尺度保持不變,我們可以將預(yù)測(cè)輸出轉(zhuǎn)換為具有形狀的二維張量(批量大小,高度×寬度×通道數(shù))。然后我們可以沿著維度 1 以不同的比例連接這些輸出。
通過(guò)這種方式,即使 和Y1在Y2通道、高度和寬度方面具有不同的大小,我們?nèi)匀豢梢詫⑦@兩個(gè)預(yù)測(cè)輸出以兩個(gè)不同的尺度連接起來(lái),用于同一個(gè)小批量。
14.7.1.4。下采樣塊
為了檢測(cè)多個(gè)尺度的對(duì)象,我們定義了以下下采樣塊down_sample_blk,將輸入特征圖的高度和寬度減半。實(shí)際上,該塊應(yīng)用了8.2.1 節(jié)中 VGG 塊的設(shè)計(jì)。更具體地說(shuō),每個(gè)下采樣塊由兩個(gè)3×3填充為 1 的卷積層后跟一個(gè)2×2步幅為 2 的最大池化層。我們知道,3×3填充為 1 的卷積層不會(huì)改變特征圖的形狀。然而,隨后的2×2max-pooling 將輸入特征圖的高度和寬度減半。對(duì)于這個(gè)下采樣塊的輸入和輸出特征圖,因?yàn)?×2+(3?1)+(3?1)=6,輸出中的每個(gè)單元都有一個(gè)6×6輸入的感受野。因此,下采樣塊在其輸出特征圖中擴(kuò)大了每個(gè)單元的感受野。
在下面的例子中,我們構(gòu)建的下采樣塊改變了輸入通道的數(shù)量,并將輸入特征圖的高度和寬度減半。
14.7.1.5?;A(chǔ)網(wǎng)絡(luò)塊
基礎(chǔ)網(wǎng)絡(luò)塊用于從輸入圖像中提取特征。為簡(jiǎn)單起見,我們構(gòu)建了一個(gè)由三個(gè)下采樣塊組成的小型基礎(chǔ)網(wǎng)絡(luò),每個(gè)塊的通道數(shù)加倍。給定一個(gè)256×256輸入圖像,這個(gè)基礎(chǔ)網(wǎng)絡(luò)塊輸出32×32特征圖(256/23=32).
14.7.1.6。完整模型
完整的單次多框檢測(cè)模型由五個(gè)塊組成。每個(gè)塊生成的特征圖用于 (i) 生成錨框和 (ii) 預(yù)測(cè)這些錨框的類別和偏移量。在這五個(gè)塊中,第一個(gè)是基礎(chǔ)網(wǎng)絡(luò)塊,第二到第四個(gè)是下采樣塊,最后一個(gè)塊使用全局最大池化將高度和寬度都降低到1。從技術(shù)上講,第二到第五個(gè)塊是圖 14.7.1中的所有那些多尺度特征圖塊。
現(xiàn)在我們?yōu)槊總€(gè)塊定義前向傳播。與圖像分類任務(wù)不同,此處的輸出包括 (i) CNN 特征圖 Y,(ii) 在當(dāng)前尺度下使用生成的錨框,以及 (iii)為這些錨框Y預(yù)測(cè)的類別和偏移量(基于)。Y
回想一下,在圖 14.7.1中,靠近頂部的多尺度特征圖塊用于檢測(cè)較大的對(duì)象;因此,它需要生成更大的錨框。sizes在上面的前向傳播中,在每個(gè)多尺度特征圖塊中,我們通過(guò)調(diào)用函數(shù)的參數(shù)(在第 14.4 節(jié)multibox_prior中描述)傳入一個(gè)包含兩個(gè)尺度值的列表。下面將0.2和1.05之間的區(qū)間平均分為五個(gè)部分,以確定五個(gè)塊處較小的刻度值:0.2、0.37、0.54、0.71和0.88。然后它們的較大比例值由下式給出 0.2×0.37=0.272, 0.37×0.54=0.447, 等等。
現(xiàn)在我們可以定義完整的模型TinySSD如下。
我們創(chuàng)建一個(gè)模型實(shí)例并使用它對(duì)小批量數(shù)據(jù)執(zhí)行前向傳播256×256圖片X。
如本節(jié)前面所示,第一個(gè)塊輸出 32×32特征圖?;叵胍幌拢诙降谒膫€(gè)下采樣塊將高度和寬度減半,第五個(gè)塊使用全局池化。由于沿特征圖的空間維度為每個(gè)單元生成 4 個(gè)錨框,因此在所有五個(gè)尺度上總共 (322+162+82+42+1)×4=5444為每個(gè)圖像生成錨框。
14.7.2。訓(xùn)練
現(xiàn)在我們將解釋如何訓(xùn)練用于目標(biāo)檢測(cè)的單發(fā)多框檢測(cè)模型。
14.7.2.1。讀取數(shù)據(jù)集并初始化模型
首先,讓我們閱讀 第 14.6 節(jié)中描述的香蕉檢測(cè)數(shù)據(jù)集。
香蕉檢測(cè)數(shù)據(jù)集中只有一類。定義模型后,我們需要初始化其參數(shù)并定義優(yōu)化算法。
14.7.2.2。定義損失函數(shù)和評(píng)估函數(shù)
物體檢測(cè)有兩種類型的損失。第一個(gè)損失涉及錨框的類別:它的計(jì)算可以簡(jiǎn)單地重用我們用于圖像分類的交叉熵?fù)p失函數(shù)。第二個(gè)損失涉及正(非背景)錨框的偏移:這是一個(gè)回歸問(wèn)題。然而,對(duì)于這個(gè)回歸問(wèn)題,這里我們不使用 第 3.1.3 節(jié)中描述的平方損失。相反,我們使用?1范數(shù)損失,預(yù)測(cè)值與真實(shí)值之間差異的絕對(duì)值。掩碼變量 bbox_masks在損失計(jì)算中過(guò)濾掉負(fù)錨框和非法(填充)錨框。最后,我們將anchor box class loss和anchor box offset loss相加得到模型的損失函數(shù)。
我們可以使用準(zhǔn)確性來(lái)評(píng)估分類結(jié)果。由于使用?1對(duì)于偏移量的范數(shù)損失,我們使用平均絕對(duì)誤差來(lái)評(píng)估預(yù)測(cè)的邊界框。這些預(yù)測(cè)結(jié)果是從生成的錨框和它們的預(yù)測(cè)偏移量中獲得的。
14.7.2.3。訓(xùn)練模型
在訓(xùn)練模型時(shí),我們需要生成多尺度錨框 ( anchors) 并在前向傳播中預(yù)測(cè)它們的類別 ( cls_preds) 和偏移量 ( )。然后我們根據(jù)標(biāo)簽信息對(duì)生成的anchor boxes的bbox_predsclasses( cls_labels)和offsets( )進(jìn)行標(biāo)注。最后,我們使用類別和偏移量的預(yù)測(cè)值和標(biāo)記值來(lái)計(jì)算損失函數(shù)。為了簡(jiǎn)潔的實(shí)現(xiàn),這里省略了測(cè)試數(shù)據(jù)集的評(píng)估。bbox_labelsY
14.7.3。預(yù)言
在預(yù)測(cè)期間,目標(biāo)是檢測(cè)圖像上所有感興趣的對(duì)象。下面我們讀取并調(diào)整測(cè)試圖像的大小,將其轉(zhuǎn)換為卷積層所需的四維張量。
使用multibox_detection下面的函數(shù),預(yù)測(cè)的邊界框是從錨框及其預(yù)測(cè)的偏移量中獲得的。然后使用非最大抑制來(lái)去除相似的預(yù)測(cè)邊界框。
最后,我們將所有置信度為 0.9 或更高的預(yù)測(cè)邊界框顯示為輸出。
14.7.4。概括
Single shot multibox detection是一種多尺度目標(biāo)檢測(cè)模型。通過(guò)其基礎(chǔ)網(wǎng)絡(luò)和多個(gè)多尺度特征圖塊,單次多框檢測(cè)生成不同數(shù)量的不同大小的錨框,并通過(guò)預(yù)測(cè)這些錨框(即邊界框)的類別和偏移量來(lái)檢測(cè)不同大小的對(duì)象。
在訓(xùn)練單次多框檢測(cè)模型時(shí),損失函數(shù)是根據(jù)錨框類別和偏移量的預(yù)測(cè)值和標(biāo)記值計(jì)算的。
14.7.5。練習(xí)
你能通過(guò)改進(jìn)損失函數(shù)來(lái)改進(jìn)單次多框檢測(cè)嗎?例如,替換?1平滑的范數(shù)損失?1預(yù)測(cè)偏移量的標(biāo)準(zhǔn)損失。此損失函數(shù)使用圍繞零的平方函數(shù)來(lái)實(shí)現(xiàn)平滑度,它由超參數(shù)控制σ:
什么時(shí)候σ非常大,這個(gè)損失類似于 ?1規(guī)范損失。當(dāng)它的值越小,損失函數(shù)越平滑。
此外,在實(shí)驗(yàn)中我們使用交叉熵?fù)p失進(jìn)行類別預(yù)測(cè):表示為pj真實(shí)類別的預(yù)測(cè)概率j,交叉熵?fù)p失是 ?log?pj. 我們還可以使用焦點(diǎn)損失 (Lin等人,2017 年):給定超參數(shù) γ>0和α>0,此損失定義為:
正如我們所見,增加γ可以有效地減少分類良好的例子的相對(duì)損失(例如,pj>0.5) 因此訓(xùn)練可以更多地關(guān)注那些被錯(cuò)誤分類的困難示例。
由于篇幅限制,我們?cè)诒竟?jié)中省略了單次多框檢測(cè)模型的一些實(shí)現(xiàn)細(xì)節(jié)。能否在以下幾個(gè)方面進(jìn)一步改進(jìn)模型:
當(dāng)一個(gè)對(duì)象與圖像相比小得多時(shí),模型可以將輸入圖像調(diào)整得更大。
通常有大量的負(fù)錨框。為了使類別分布更加平衡,我們可以對(duì)負(fù)錨框進(jìn)行下采樣。
在損失函數(shù)中,為類損失和偏移損失分配不同的權(quán)重超參數(shù)。
使用其他方法來(lái)評(píng)估對(duì)象檢測(cè)模型,例如單發(fā)多框檢測(cè)論文 (Liu et al. , 2016)中的方法。
%matplotlib inline
import torch
import torchvision
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
def cls_predictor(num_inputs, num_anchors, num_classes):
return nn.Conv2d(num_inputs, num_anchors * (num_classes + 1),
kernel_size=3, padding=1)
%matplotlib inline
from mxnet import autograd, gluon, image, init, np, npx
from mxnet.gluon import nn
from d2l import mxnet as d2l
npx.set_np()
def cls_predictor(num_anchors, num_classes):
return nn.Conv2D(num_anchors * (num_classes + 1), kernel_size=3,
padding=1)
def bbox_predictor(num_inputs, num_anchors):
return nn.Conv2d(num_inputs, num_anchors * 4, kernel_size=3, padding=1)
def bbox_predictor(num_anchors):
return nn.Conv2D(num_anchors * 4, kernel_size=3, padding=1)
def forward(x, block):
return block(x)
Y1 = forward(torch.zeros((2, 8, 20, 20)), cls_predictor(8, 5, 10))
Y2 = forward(torch.zeros((2, 16, 10, 10)), cls_predictor(16, 3, 10))
Y1.shape, Y2.shape
(torch.Size([2, 55, 20, 20]), torch.Size([2, 33, 10, 10]))
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))
def flatten_pred(pred):
return torch.flatten(pred.permute(0, 2, 3, 1), start_dim=1)
def concat_preds(preds):
return torch.cat([flatten_pred(p) for p in preds], dim=1)
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)
concat_preds([Y1, Y2]).shape
torch.Size([2, 25300])
concat_preds([Y1, Y2]).shape
(2, 25300)
def down_sample_blk(in_channels, out_channels):
blk = []
for _ in range(2):
blk.append(nn.Conv2d(in_channels, out_channels,
kernel_size=3, padding=1))
blk.append(nn.BatchNorm2d(out_channels))
blk.append(nn.ReLU())
in_channels = out_channels
blk.append(nn.MaxPool2d(2))
return nn.Sequential(*blk)
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
forward(torch.zeros((2, 3, 20, 20)), down_sample_blk(3, 10)).shape
torch.Size([2, 10, 10, 10])
forward(np.zeros((2, 3, 20, 20)), down_sample_blk(10)).shape
(2, 10, 10, 10)
def base_net():
blk = []
num_filters = [3, 16, 32, 64]
for i in range(len(num_filters) - 1):
blk.append(down_sample_blk(num_filters[i], num_filters[i+1]))
return nn.Sequential(*blk)
forward(torch.zeros((2, 3, 256, 256)), base_net()).shape
torch.Size([2, 64, 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)
def get_blk(i):
if i == 0:
blk = base_net()
elif i == 1:
blk = down_sample_blk(64, 128)
elif i == 4:
blk = nn.AdaptiveMaxPool2d((1,1))
else:
blk = down_sample_blk(128, 128)
return blk
def get_blk(i):
if i == 0:
blk = base_net()
elif i == 4:
blk = nn.GlobalMaxPool2D()
else:
blk = down_sample_blk(128)
return blk
def blk_forward(X, blk, size, ratio, cls_predictor, bbox_predictor):
Y = blk(X)
anchors = d2l.multibox_prior(Y, sizes=size, ratios=ratio)
cls_preds = cls_predictor(Y)
bbox_preds = bbox_predictor(Y)
return (Y, anchors, cls_preds, bbox_preds)
def blk_forward(X, blk, size, ratio, cls_predictor, bbox_predictor):
Y = blk(X)
anchors = d2l.multibox_prior(Y, sizes=size, ratios=ratio)
cls_preds = cls_predictor(Y)
bbox_preds = bbox_predictor(Y)
return (Y, anchors, cls_preds, bbox_preds)
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
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
class TinySSD(nn.Module):
def __init__(self, num_classes, **kwargs):
super(TinySSD, self).__init__(**kwargs)
self.num_classes = num_classes
idx_to_in_channels = [64, 128, 128, 128, 128]
for i in range(5):
# Equivalent to the assignment statement `self.blk_i = get_blk(i)`
setattr(self, f'blk_{i}', get_blk(i))
setattr(self, f'cls_{i}', cls_predictor(idx_to_in_channels[i],
num_anchors, num_classes))
setattr(self, f'bbox_{i}', bbox_predictor(idx_to_in_channels[i],
num_anchors))
def forward(self, X):
anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5
for i in range(5):
# Here `getattr(self, 'blk_%d' % i)` accesses `self.blk_i`
X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward(
X, getattr(self, f'blk_{i}'), sizes[i], ratios[i],
getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}'))
anchors = torch.cat(anchors, dim=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
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):
# Equivalent to the assignment statement `self.blk_i = get_blk(i)`
setattr(self, f'blk_{i}', get_blk(i))
setattr(self, f'cls_{i}', cls_predictor(num_anchors, num_classes))
setattr(self, f'bbox_{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):
# Here `getattr(self, 'blk_%d' % i)` accesses `self.blk_i`
X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward(
X, getattr(self, f'blk_{i}'), sizes[i], ratios[i],
getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}'))
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
net = TinySSD(num_classes=1)
X = torch.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: torch.Size([1, 5444, 4])
output class preds: torch.Size([32, 5444, 2])
output bbox preds: torch.Size([32, 21776])
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)
batch_size = 32
train_iter, _ = d2l.load_data_bananas(batch_size)
Downloading ../data/banana-detection.zip from http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zip...
read 1000 training examples
read 100 validation examples
batch_size = 32
train_iter, _ = d2l.load_data_bananas(batch_size)
Downloading ../data/banana-detection.zip from http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zip...
read 1000 training examples
read 100 validation examples
device, net = d2l.try_gpu(), TinySSD(num_classes=1)
trainer = torch.optim.SGD(net.parameters(), lr=0.2, weight_decay=5e-4)
device, net = d2l.try_gpu(), TinySSD(num_classes=1)
net.initialize(init=init.Xavier(), ctx=device)
trainer = gluon.Trainer(net.collect_params(), 'sgd',
{'learning_rate': 0.2, 'wd': 5e-4})
cls_loss = nn.CrossEntropyLoss(reduction='none')
bbox_loss = nn.L1Loss(reduction='none')
def calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels, bbox_masks):
batch_size, num_classes = cls_preds.shape[0], cls_preds.shape[2]
cls = cls_loss(cls_preds.reshape(-1, num_classes),
cls_labels.reshape(-1)).reshape(batch_size, -1).mean(dim=1)
bbox = bbox_loss(bbox_preds * bbox_masks,
bbox_labels * bbox_masks).mean(dim=1)
return cls + bbox
cls_loss = gluon.loss.SoftmaxCrossEntropyLoss()
bbox_loss = gluon.loss.L1Loss()
def calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels, bbox_masks):
cls = cls_loss(cls_preds, cls_labels)
bbox = bbox_loss(bbox_preds * bbox_masks, bbox_labels * bbox_masks)
return cls + bbox
def cls_eval(cls_preds, cls_labels):
# Because the class prediction results are on the final dimension,
# `argmax` needs to specify this dimension
return float((cls_preds.argmax(dim=-1).type(
cls_labels.dtype) == cls_labels).sum())
def bbox_eval(bbox_preds, bbox_labels, bbox_masks):
return float((torch.abs((bbox_labels - bbox_preds) * bbox_masks)).sum())
def cls_eval(cls_preds, cls_labels):
# Because the class prediction results are on the final dimension,
# `argmax` needs to specify this dimension
return float((cls_preds.argmax(axis=-1).astype(
cls_labels.dtype) == cls_labels).sum())
def bbox_eval(bbox_preds, bbox_labels, bbox_masks):
return float((np.abs((bbox_labels - bbox_preds) * bbox_masks)).sum())
num_epochs, timer = 20, d2l.Timer()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['class error', 'bbox mae'])
net = net.to(device)
for epoch in range(num_epochs):
# Sum of training accuracy, no. of examples in sum of training accuracy,
# Sum of absolute error, no. of examples in sum of absolute error
metric = d2l.Accumulator(4)
net.train()
for features, target in train_iter:
timer.start()
trainer.zero_grad()
X, Y = features.to(device), target.to(device)
# Generate multiscale anchor boxes and predict their classes and
# offsets
anchors, cls_preds, bbox_preds = net(X)
# Label the classes and offsets of these anchor boxes
bbox_labels, bbox_masks, cls_labels = d2l.multibox_target(anchors, Y)
# Calculate the loss function using the predicted and labeled values
# of the classes and offsets
l = calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels,
bbox_masks)
l.mean().backward()
trainer.step()
metric.add(cls_eval(cls_preds, cls_labels), cls_labels.numel(),
bbox_eval(bbox_preds, bbox_labels, bbox_masks),
bbox_labels.numel())
cls_err, bbox_mae = 1 - metric[0] / metric[1], metric[2] / metric[3]
animator.add(epoch + 1, (cls_err, bbox_mae))
print(f'class err {cls_err:.2e}, bbox mae {bbox_mae:.2e}')
print(f'{len(train_iter.dataset) / timer.stop():.1f} examples/sec on '
f'{str(device)}')
class err 3.29e-03, bbox mae 3.08e-03
4339.3 examples/sec on cuda:0
num_epochs, timer = 20, d2l.Timer()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['class error', 'bbox mae'])
for epoch in range(num_epochs):
# Sum of training accuracy, no. of examples in sum of training accuracy,
# Sum of absolute error, no. of examples in sum of absolute error
metric = d2l.Accumulator(4)
for features, target in train_iter:
timer.start()
X = features.as_in_ctx(device)
Y = target.as_in_ctx(device)
with autograd.record():
# Generate multiscale anchor boxes and predict their classes and
# offsets
anchors, cls_preds, bbox_preds = net(X)
# Label the classes and offsets of these anchor boxes
bbox_labels, bbox_masks, cls_labels = d2l.multibox_target(anchors,
Y)
# Calculate the loss function using the predicted and labeled
# values of the classes and offsets
l = calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels,
bbox_masks)
l.backward()
trainer.step(batch_size)
metric.add(cls_eval(cls_preds, cls_labels), cls_labels.size,
bbox_eval(bbox_preds, bbox_labels, bbox_masks),
bbox_labels.size)
cls_err, bbox_mae = 1 - metric[0] / metric[1], metric[2] / metric[3]
animator.add(epoch + 1, (cls_err, bbox_mae))
print(f'class err {cls_err:.2e}, bbox mae {bbox_mae:.2e}')
print(f'{len(train_iter._dataset) / timer.stop():.1f} examples/sec on '
f'{str(device)}')
class err 3.56e-03, bbox mae 3.78e-03
966.8 examples/sec on gpu(0)
X = torchvision.io.read_image('../img/banana.jpg').unsqueeze(0).float()
img = X.squeeze(0).permute(1, 2, 0).long()
img = image.imread('../img/banana.jpg')
feature = image.imresize(img, 256, 256).astype('float32')
X = np.expand_dims(feature.transpose(2, 0, 1), axis=0)
def predict(X):
net.eval()
anchors, cls_preds, bbox_preds = net(X.to(device))
cls_probs = F.softmax(cls_preds, dim=2).permute(0, 2, 1)
output = d2l.multibox_detection(cls_probs, bbox_preds, anchors)
idx = [i for i, row in enumerate(output[0]) if row[0] != -1]
return output[0, idx]
output = predict(X)
def predict(X):
anchors, cls_preds, bbox_preds = net(X.as_in_ctx(device))
cls_probs = npx.softmax(cls_preds).transpose(0, 2, 1)
output = d2l.multibox_detection(cls_probs, bbox_preds, anchors)
idx = [i for i, row in enumerate(output[0]) if row[0] != -1]
return output[0, idx]
output = predict(X)
[09:37:33] src/operator/nn/./cudnn/./cudnn_algoreg-inl.h:97: Running performance tests to find the best convolution algorithm, this can take a while... (set the environment variable MXNET_CUDNN_AUTOTUNE_DEFAULT to 0 to disable)
def display(img, output, threshold):
d2l.set_figsize((5, 5))
fig = d2l.plt.imshow(img)
for row in output:
score = float(row[1])
if score < threshold:
continue
h, w = img.shape[:2]
bbox = [row[2:6] * torch.tensor((w, h, w, h), device=row.device)]
d2l.show_bboxes(fig.axes, bbox, '%.2f' % score, 'w')
display(img, output.cpu(), threshold=0.9)
def display(img, output, threshold):
d2l.set_figsize((5, 5))
fig = d2l.plt.imshow(img.asnumpy())
for row in output:
score = float(row[1])
if score < threshold:
continue
h, w = img.shape[:2]
bbox = [row[2:6] * np.array((w, h, w, h), ctx=row.ctx)]
d2l.show_bboxes(fig.axes, bbox, '%.2f' % score, 'w')
display(img, output, threshold=0.9)
-
檢測(cè)
+關(guān)注
關(guān)注
5文章
4520瀏覽量
91798 -
pytorch
+關(guān)注
關(guān)注
2文章
808瀏覽量
13383
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
關(guān)于多列列表框的應(yīng)用
多列列表框分頁(yè)顯示
UCGUI多文本框
多列列表框與組合框配合使用
新樹型框擴(kuò)展模塊+例程(iouioupp要的支持多樹型框)
干貨 | 單鏡頭視覺系統(tǒng)檢測(cè)車輛的測(cè)距方法
![干貨 | <b class='flag-5'>單</b>鏡頭視覺系統(tǒng)<b class='flag-5'>檢測(cè)</b>車輛的測(cè)距方法](https://file.elecfans.com/web1/M00/95/88/o4YBAF0CFh-ALXjKAAAKhGWDYms738.jpg)
基于PyTorch的深度學(xué)習(xí)入門教程之DataParallel使用多GPU
解讀目標(biāo)檢測(cè)中的框位置優(yōu)化
![解讀目標(biāo)<b class='flag-5'>檢測(cè)</b>中的<b class='flag-5'>框</b>位置優(yōu)化](https://file.elecfans.com/web2/M00/02/7E/poYBAGDQX5aALjyCAAAk32rJRIQ894.png)
PyTorch教程14.6之對(duì)象檢測(cè)數(shù)據(jù)集
![<b class='flag-5'>PyTorch</b>教程14.6之對(duì)象<b class='flag-5'>檢測(cè)</b>數(shù)據(jù)集](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程14.7之單發(fā)多框檢測(cè)
![<b class='flag-5'>PyTorch</b>教程<b class='flag-5'>14.7</b>之<b class='flag-5'>單</b><b class='flag-5'>發(fā)</b><b class='flag-5'>多</b><b class='flag-5'>框</b><b class='flag-5'>檢測(cè)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
labview多列列表框寫入數(shù)據(jù)
氣密性檢測(cè)儀:單工位與多工位之間的區(qū)別
![氣密性<b class='flag-5'>檢測(cè)</b>儀:<b class='flag-5'>單</b>工位與<b class='flag-5'>多</b>工位之間的區(qū)別](https://file1.elecfans.com/web2/M00/ED/91/wKgaomZn9gOAT9EGAACpyYfGGN0096.png)
評(píng)論