0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

PyTorch教程-14.7。單發(fā)多框檢測(cè)

jf_pJlTbmA9 ? 來(lái)源:PyTorch ? 作者:PyTorch ? 2023-06-05 15:44 ? 次閱讀

在第 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è)多尺度特征圖塊組成?;A(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è)模型。

poYBAGR9O62ARusYAAorLEsLQmk559.svg

圖 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的卷積層。該卷積層的輸入和輸出的寬度和高度保持不變。

%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)

14.7.1.2。邊界框預(yù)測(cè)層

邊界框預(yù)測(cè)層的設(shè)計(jì)與類預(yù)測(cè)層的設(shè)計(jì)類似。唯一的區(qū)別在于每個(gè)錨框的輸出數(shù)量:這里我們需要預(yù)測(cè)四個(gè)偏移量而不是q+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)

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ù)、高度、寬度)。

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))

我們可以看到,除了 batch size 維度,其他三個(gè)維度都有不同的大小。為了連接這兩個(gè)預(yù)測(cè)輸出以實(shí)現(xiàn)更高效的計(jì)算,我們將把這些張量轉(zhuǎn)換為更一致的格式。

請(qǐng)注意,通道維度包含具有相同中心的錨框的預(yù)測(cè)。我們先把這個(gè)維度移到最里面。由于批量大小對(duì)于不同的尺度保持不變,我們可以將預(yù)測(cè)輸出轉(zhuǎn)換為具有形狀的二維張量(批量大小,高度×寬度×通道數(shù))。然后我們可以沿著維度 1 以不同的比例連接這些輸出。

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)

通過(guò)這種方式,即使 和Y1在Y2通道、高度和寬度方面具有不同的大小,我們?nèi)匀豢梢詫⑦@兩個(gè)預(yù)測(cè)輸出以兩個(gè)不同的尺度連接起來(lái),用于同一個(gè)小批量。

concat_preds([Y1, Y2]).shape

torch.Size([2, 25300])

concat_preds([Y1, Y2]).shape

(2, 25300)

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è)單元的感受野。

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

在下面的例子中,我們構(gòu)建的下采樣塊改變了輸入通道的數(shù)量,并將輸入特征圖的高度和寬度減半。

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)

14.7.1.5?;A(chǔ)網(wǎng)絡(luò)塊

基礎(chǔ)網(wǎng)絡(luò)塊用于從輸入圖像中提取特征。為簡(jiǎn)單起見(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).

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)

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中的所有那些多尺度特征圖塊。

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

現(xiàn)在我們?yōu)槊總€(gè)塊定義前向傳播。與圖像分類任務(wù)不同,此處的輸出包括 (i) CNN 特征圖 Y,(ii) 在當(dāng)前尺度下使用生成的錨框,以及 (iii)為這些錨框Y預(yù)測(cè)的類別和偏移量(基于)。Y

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)

回想一下,在圖 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, 等等。

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

現(xiàn)在我們可以定義完整的模型TinySSD如下。

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

我們創(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è)圖像生成錨框。

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)

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ù)集。

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

香蕉檢測(cè)數(shù)據(jù)集中只有一類。定義模型后,我們需要初始化其參數(shù)并定義優(yōu)化算法。

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})

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ù)。

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

我們可以使用準(zhǔn)確性來(lái)評(píng)估分類結(jié)果。由于使用?1對(duì)于偏移量的范數(shù)損失,我們使用平均絕對(duì)誤差來(lái)評(píng)估預(yù)測(cè)的邊界框。這些預(yù)測(cè)結(jié)果是從生成的錨框和它們的預(yù)測(cè)偏移量中獲得的。

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())

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

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

poYBAGR9O6-AW7FVAADseBOpZnE997.svg

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)

poYBAGR9O7GABD8UAADtVeSfxzM776.svg

14.7.3。預(yù)言

在預(yù)測(cè)期間,目標(biāo)是檢測(cè)圖像上所有感興趣的對(duì)象。下面我們讀取并調(diào)整測(cè)試圖像的大小,將其轉(zhuǎn)換為卷積層所需的四維張量。

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)

使用multibox_detection下面的函數(shù),預(yù)測(cè)的邊界框是從錨框及其預(yù)測(cè)的偏移量中獲得的。然后使用非最大抑制來(lái)去除相似的預(yù)測(cè)邊界框。

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)

最后,我們將所有置信度為 0.9 或更高的預(yù)測(cè)邊界框顯示為輸出。

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)

pYYBAGR9O7SAdMQ0AAZ8pxcKS-w622.svg

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)

poYBAGR9O7yALdrgAAaOIun_DSk121.svg

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,此損失定義為:

正如我們所見(jiàn),增加γ可以有效地減少分類良好的例子的相對(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)中的方法。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 檢測(cè)
    +關(guān)注

    關(guān)注

    5

    文章

    4348

    瀏覽量

    91101
  • pytorch
    +關(guān)注

    關(guān)注

    2

    文章

    794

    瀏覽量

    13009
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    關(guān)于列列表的應(yīng)用

    前面板建了列列表,添加了水平和垂直分隔欄,首先先確定列列表的行列數(shù),其次根據(jù)窗格的大小改變列寬,最后設(shè)置活動(dòng)單元格的背景色,不知道怎么寫(xiě)代碼,求大神賜教!謝謝?。?!
    發(fā)表于 08-06 21:46

    列列表分頁(yè)顯示

    現(xiàn)在希望對(duì)列列表做如下操作:從數(shù)據(jù)庫(kù)中輸入10000行數(shù)據(jù)導(dǎo)入列列表,要求列列表能夠
    發(fā)表于 11-04 11:15

    基于labview列表的設(shè)計(jì)

    基于labview列表的設(shè)計(jì)
    發(fā)表于 02-21 20:51

    列表的設(shè)計(jì)與操作

    列表的設(shè)計(jì)與操作
    發(fā)表于 02-22 10:14

    UCGUI文本

    3個(gè)文本,按鍵輸入。我也是剛學(xué)UCGUI,并不是什么很高級(jí),因?yàn)楣倦娔X不能現(xiàn)場(chǎng)轉(zhuǎn)送文件,應(yīng)論壇別人的要求,所以上傳到論壇給需要的人。17.ucos&ucgui_文本.rar (4.62 MB )
    發(fā)表于 06-13 04:35

    PyTorch如何入門

    PyTorch 入門實(shí)戰(zhàn)(一)——Tensor
    發(fā)表于 06-01 09:58

    列列表與組合配合使用

    本帖最后由 dk1997 于 2021-11-29 20:54 編輯 使用了組合列列表,實(shí)現(xiàn)類似表格中出現(xiàn)可以選擇預(yù)先定義的內(nèi)容。實(shí)現(xiàn)思路: 1、首先獲取鼠標(biāo)在列列表
    發(fā)表于 11-29 20:49

    新樹(shù)型擴(kuò)展模塊+例程(iouioupp要的支持樹(shù)型

    易語(yǔ)言是一門以中文作為程序代碼編程語(yǔ)言學(xué)習(xí)例程:易語(yǔ)言-新樹(shù)型擴(kuò)展模塊+例程(iouioupp要的支持樹(shù)型
    發(fā)表于 06-06 17:43 ?12次下載

    干貨 | 鏡頭視覺(jué)系統(tǒng)檢測(cè)車輛的測(cè)距方法

    以前提過(guò)目測(cè)距的問(wèn)題,檢測(cè)的障礙物2-D加上攝像頭的姿態(tài)和路面假設(shè)。
    的頭像 發(fā)表于 06-13 17:19 ?7946次閱讀
    干貨 | <b class='flag-5'>單</b>鏡頭視覺(jué)系統(tǒng)<b class='flag-5'>檢測(cè)</b>車輛的測(cè)距方法

    基于PyTorch的深度學(xué)習(xí)入門教程之DataParallel使用GPU

    講到DataParallel使用GPU。 在PyTorch中使用GPU比較簡(jiǎn)單,可以這樣把模型放到GPU上。 model.gpu() 還可以復(fù)制所有的tensors到GPU上。 mytensor = my_ten
    的頭像 發(fā)表于 02-15 09:55 ?3992次閱讀

    解讀目標(biāo)檢測(cè)中的位置優(yōu)化

    為anchor-based(Faster RCNN)、anchor-free(CornerNet)的方法。 本文主要從目標(biāo)位置優(yōu)化的角度來(lái)介紹目標(biāo)檢測(cè)領(lǐng)域的相關(guān)工作。位置優(yōu)化主要可以分為以下幾個(gè)
    的頭像 發(fā)表于 06-21 17:40 ?2430次閱讀
    解讀目標(biāo)<b class='flag-5'>檢測(cè)</b>中的<b class='flag-5'>框</b>位置優(yōu)化

    PyTorch教程14.6之對(duì)象檢測(cè)數(shù)據(jù)集

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程14.6之對(duì)象檢測(cè)數(shù)據(jù)集.pdf》資料免費(fèi)下載
    發(fā)表于 06-05 11:23 ?0次下載
    <b class='flag-5'>PyTorch</b>教程14.6之對(duì)象<b class='flag-5'>檢測(cè)</b>數(shù)據(jù)集

    PyTorch教程14.7發(fā)檢測(cè)

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程14.7發(fā)檢測(cè)
    發(fā)表于 06-05 14:17 ?0次下載
    <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>

    labview列列表寫(xiě)入數(shù)據(jù)

    LabVIEW是一種圖形化編程環(huán)境,廣泛應(yīng)用于工業(yè)自動(dòng)化領(lǐng)域。列列表是LabVIEW中常用的界面控件,可用于顯示和編輯多個(gè)列的數(shù)據(jù)。本文將詳細(xì)介紹如何通過(guò)LabVIEW實(shí)現(xiàn)列列表
    的頭像 發(fā)表于 12-26 13:49 ?4206次閱讀

    氣密性檢測(cè)儀:工位與工位之間的區(qū)別

    在現(xiàn)代工業(yè)生產(chǎn)中,氣密性檢測(cè)儀起著至關(guān)重要的作用。能有效檢測(cè)產(chǎn)品的密封性能,保證產(chǎn)品質(zhì)量符合標(biāo)準(zhǔn)。在氣密性檢測(cè)儀的選擇過(guò)程中,工位與工位
    的頭像 發(fā)表于 06-11 15:00 ?159次閱讀
    氣密性<b class='flag-5'>檢測(cè)</b>儀:<b class='flag-5'>單</b>工位與<b class='flag-5'>多</b>工位之間的區(qū)別