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

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

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

PyTorch教程-14.4. 錨箱

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

物體檢測(cè)算法通常在輸入圖像中采樣大量區(qū)域,判斷這些區(qū)域是否包含感興趣的物體,并調(diào)整區(qū)域的邊界,從而更準(zhǔn)確地預(yù)測(cè)物體的真實(shí)邊界 框。不同的模型可能采用不同的區(qū)域采樣方案。在這里,我們介紹其中一種方法:它生成多個(gè)以每個(gè)像素為中心的具有不同比例和縱橫比的邊界框。這些邊界框稱為錨框。我們將在14.7 節(jié)設(shè)計(jì)一個(gè)基于錨框的目標(biāo)檢測(cè)模型。

首先,讓我們修改打印精度以獲得更簡(jiǎn)潔的輸出。

%matplotlib inline
import torch
from d2l import torch as d2l

torch.set_printoptions(2) # Simplify printing accuracy

%matplotlib inline
from mxnet import gluon, image, np, npx
from d2l import mxnet as d2l

np.set_printoptions(2) # Simplify printing accuracy
npx.set_np()

14.4.1。生成多個(gè)錨框

假設(shè)輸入圖像的高度為h和寬度 w. 我們以圖像的每個(gè)像素為中心生成具有不同形狀的錨框。讓規(guī)模成為s∈(0,1]縱橫比(寬高比)為 r>0. 那么anchor box的寬高分別是hsr和 hs/r, 分別。請(qǐng)注意,當(dāng)中心位置給定時(shí),將確定一個(gè)已知寬度和高度的錨框。

為了生成多個(gè)不同形狀的錨框,讓我們?cè)O(shè)置一系列尺度s1,…,sn和一系列縱橫比 r1,…,rm. 當(dāng)以每個(gè)像素為中心使用這些尺度和縱橫比的所有組合時(shí),輸入圖像將總共有whnm錨箱。雖然這些anchor boxes可能會(huì)覆蓋所有的ground-truth bounding boxes,但是計(jì)算復(fù)雜度很容易過(guò)高。在實(shí)踐中,我們只能考慮那些包含s1或者r1:

(14.4.1)(s1,r1),(s1,r2),…,(s1,rm),(s2,r1),(s3,r1),…,(sn,r1).

也就是說(shuō),以同一個(gè)像素為中心的anchor boxes的個(gè)數(shù)為 n+m?1. 對(duì)于整個(gè)輸入圖像,我們將生成總共 wh(n+m?1)錨箱。

上面生成anchor boxes的方法是在下面的multibox_prior函數(shù)中實(shí)現(xiàn)的。我們指定輸入圖像、比例列表和縱橫比列表,然后此函數(shù)將返回所有錨框。

#@save
def multibox_prior(data, sizes, ratios):
  """Generate anchor boxes with different shapes centered on each pixel."""
  in_height, in_width = data.shape[-2:]
  device, num_sizes, num_ratios = data.device, len(sizes), len(ratios)
  boxes_per_pixel = (num_sizes + num_ratios - 1)
  size_tensor = torch.tensor(sizes, device=device)
  ratio_tensor = torch.tensor(ratios, device=device)
  # Offsets are required to move the anchor to the center of a pixel. Since
  # a pixel has height=1 and width=1, we choose to offset our centers by 0.5
  offset_h, offset_w = 0.5, 0.5
  steps_h = 1.0 / in_height # Scaled steps in y axis
  steps_w = 1.0 / in_width # Scaled steps in x axis

  # Generate all center points for the anchor boxes
  center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h
  center_w = (torch.arange(in_width, device=device) + offset_w) * steps_w
  shift_y, shift_x = torch.meshgrid(center_h, center_w, indexing='ij')
  shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)

  # Generate `boxes_per_pixel` number of heights and widths that are later
  # used to create anchor box corner coordinates (xmin, xmax, ymin, ymax)
  w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]),
          sizes[0] * torch.sqrt(ratio_tensor[1:])))
          * in_height / in_width # Handle rectangular inputs
  h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]),
          sizes[0] / torch.sqrt(ratio_tensor[1:])))
  # Divide by 2 to get half height and half width
  anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(
                    in_height * in_width, 1) / 2

  # Each center point will have `boxes_per_pixel` number of anchor boxes, so
  # generate a grid of all anchor box centers with `boxes_per_pixel` repeats
  out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
        dim=1).repeat_interleave(boxes_per_pixel, dim=0)
  output = out_grid + anchor_manipulations
  return output.unsqueeze(0)

#@save
def multibox_prior(data, sizes, ratios):
  """Generate anchor boxes with different shapes centered on each pixel."""
  in_height, in_width = data.shape[-2:]
  device, num_sizes, num_ratios = data.ctx, len(sizes), len(ratios)
  boxes_per_pixel = (num_sizes + num_ratios - 1)
  size_tensor = np.array(sizes, ctx=device)
  ratio_tensor = np.array(ratios, ctx=device)
  # Offsets are required to move the anchor to the center of a pixel. Since
  # a pixel has height=1 and width=1, we choose to offset our centers by 0.5
  offset_h, offset_w = 0.5, 0.5
  steps_h = 1.0 / in_height # Scaled steps in y-axis
  steps_w = 1.0 / in_width # Scaled steps in x-axis

  # Generate all center points for the anchor boxes
  center_h = (np.arange(in_height, ctx=device) + offset_h) * steps_h
  center_w = (np.arange(in_width, ctx=device) + offset_w) * steps_w
  shift_x, shift_y = np.meshgrid(center_w, center_h)
  shift_x, shift_y = shift_x.reshape(-1), shift_y.reshape(-1)

  # Generate `boxes_per_pixel` number of heights and widths that are later
  # used to create anchor box corner coordinates (xmin, xmax, ymin, ymax)
  w = np.concatenate((size_tensor * np.sqrt(ratio_tensor[0]),
            sizes[0] * np.sqrt(ratio_tensor[1:]))) 
            * in_height / in_width # Handle rectangular inputs
  h = np.concatenate((size_tensor / np.sqrt(ratio_tensor[0]),
            sizes[0] / np.sqrt(ratio_tensor[1:])))
  # Divide by 2 to get half height and half width
  anchor_manipulations = np.tile(np.stack((-w, -h, w, h)).T,
                  (in_height * in_width, 1)) / 2

  # Each center point will have `boxes_per_pixel` number of anchor boxes, so
  # generate a grid of all anchor box centers with `boxes_per_pixel` repeats
  out_grid = np.stack([shift_x, shift_y, shift_x, shift_y],
             axis=1).repeat(boxes_per_pixel, axis=0)
  output = out_grid + anchor_manipulations
  return np.expand_dims(output, axis=0)

我們可以看到返回的anchor box變量的shapeY為(batch size, number of anchor boxes, 4)。

img = d2l.plt.imread('../img/catdog.jpg')
h, w = img.shape[:2]

print(h, w)
X = torch.rand(size=(1, 3, h, w)) # Construct input data
Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
Y.shape

561 728

torch.Size([1, 2042040, 4])

img = image.imread('../img/catdog.jpg').asnumpy()
h, w = img.shape[:2]

print(h, w)
X = np.random.uniform(size=(1, 3, h, w)) # Construct input data
Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
Y.shape

561 728

(1, 2042040, 4)

將anchor box變量的shape修改Y為(圖像高度,圖像寬度,以同一像素為中心的anchor boxes個(gè)數(shù),4),我們就可以得到以指定像素位置為中心的所有anchor boxes。在下文中,我們?cè)L問(wèn)以 (250, 250) 為中心的第一個(gè)錨框。它有四個(gè)要素:(x,y)- 軸坐標(biāo)在左上角和(x,y)錨框右下角的軸坐標(biāo)。兩個(gè)軸的坐標(biāo)值分別除以圖像的寬度和高度。

boxes = Y.reshape(h, w, 5, 4)
boxes[250, 250, 0, :]

tensor([0.06, 0.07, 0.63, 0.82])

boxes = Y.reshape(h, w, 5, 4)
boxes[250, 250, 0, :]

array([0.06, 0.07, 0.63, 0.82])

為了顯示圖像中以一個(gè)像素為中心的所有錨框,我們定義以下show_bboxes函數(shù)在圖像上繪制多個(gè)邊界框。

#@save
def show_bboxes(axes, bboxes, labels=None, colors=None):
  """Show bounding boxes."""

  def make_list(obj, default_values=None):
    if obj is None:
      obj = default_values
    elif not isinstance(obj, (list, tuple)):
      obj = [obj]
    return obj

  labels = make_list(labels)
  colors = make_list(colors, ['b', 'g', 'r', 'm', 'c'])
  for i, bbox in enumerate(bboxes):
    color = colors[i % len(colors)]
    rect = d2l.bbox_to_rect(bbox.detach().numpy(), color)
    axes.add_patch(rect)
    if labels and len(labels) > i:
      text_color = 'k' if color == 'w' else 'w'
      axes.text(rect.xy[0], rect.xy[1], labels[i],
           va='center', ha='center', fontsize=9, color=text_color,
           bbox=dict(facecolor=color, lw=0))

#@save
def show_bboxes(axes, bboxes, labels=None, colors=None):
  """Show bounding boxes."""

  def make_list(obj, default_values=None):
    if obj is None:
      obj = default_values
    elif not isinstance(obj, (list, tuple)):
      obj = [obj]
    return obj

  labels = make_list(labels)
  colors = make_list(colors, ['b', 'g', 'r', 'm', 'c'])
  for i, bbox in enumerate(bboxes):
    color = colors[i % len(colors)]
    rect = d2l.bbox_to_rect(bbox.asnumpy(), color)
    axes.add_patch(rect)
    if labels and len(labels) > i:
      text_color = 'k' if color == 'w' else 'w'
      axes.text(rect.xy[0], rect.xy[1], labels[i],
           va='center', ha='center', fontsize=9, color=text_color,
           bbox=dict(facecolor=color, lw=0))

正如我們剛剛看到的,x和y 變量中的軸boxes分別除以圖像的寬度和高度。在繪制anchor boxes時(shí),我們需要恢復(fù)它們?cè)瓉?lái)的坐標(biāo)值;因此,我們?cè)谙旅娑x變量 bbox_scale?,F(xiàn)在,我們可以繪制圖像中所有以 (250, 250) 為中心的錨框。如您所見(jiàn),比例為 0.75、縱橫比為 1 的藍(lán)色錨框很好地包圍了圖像中的狗。

d2l.set_figsize()
bbox_scale = torch.tensor((w, h, w, h))
fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,
      ['s=0.75, r=1', 's=0.5, r=1', 's=0.25, r=1', 's=0.75, r=2',
       's=0.75, r=0.5'])

pYYBAGR9O3CAJB22AAIlZ__gMTE999.svg

d2l.set_figsize()
bbox_scale = np.array((w, h, w, h))
fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,
      ['s=0.75, r=1', 's=0.5, r=1', 's=0.25, r=1', 's=0.75, r=2',
       's=0.75, r=0.5'])

poYBAGR9O3KAGppVAAImAn42V94682.svg

14.4.2。并集交集 (IoU)

我們剛剛提到圖像中的狗周圍有一個(gè)錨框“井”。如果物體的ground-truth bounding box是已知的,那么這里的“well”怎么量化呢?直觀上,我們可以衡量錨框和真實(shí)邊界框之間的相似度。我們知道杰卡德指數(shù)可以衡量?jī)蓚€(gè)集合之間的相似度。給定的集合A和B,它們的 Jaccard 指數(shù)是交集的大小除以并集的大?。?/p>

(14.4.2)J(A,B)=|A∩B||A∪B|.

事實(shí)上,我們可以將任何邊界框的像素區(qū)域視為一組像素。這樣,我們就可以通過(guò)它們像素集的 Jaccard 指數(shù)來(lái)衡量?jī)蓚€(gè)邊界框的相似度。對(duì)于兩個(gè)邊界框,我們通常將它們的 Jaccard 指數(shù)稱為intersection over union ( IoU ),即它們的交集面積與它們的并集面積之比,如圖14.4.1所示。IoU 的范圍在 0 到 1 之間:0 表示兩個(gè)邊界框完全不重疊,而 1 表示兩個(gè)邊界框相等。

poYBAGR9O3SASbQsAABRYAYXhyk178.svg

圖 14.4.1 IoU 是兩個(gè)邊界框的交集面積與并集面積之比。

對(duì)于本節(jié)的其余部分,我們將使用 IoU 來(lái)衡量錨框和真實(shí)邊界框之間以及不同錨框之間的相似性。給定兩個(gè)錨點(diǎn)或邊界框列表,以下box_iou計(jì)算它們?cè)谶@兩個(gè)列表中的成對(duì) IoU。

#@save
def box_iou(boxes1, boxes2):
  """Compute pairwise IoU across two lists of anchor or bounding boxes."""
  box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) *
               (boxes[:, 3] - boxes[:, 1]))
  # Shape of `boxes1`, `boxes2`, `areas1`, `areas2`: (no. of boxes1, 4),
  # (no. of boxes2, 4), (no. of boxes1,), (no. of boxes2,)
  areas1 = box_area(boxes1)
  areas2 = box_area(boxes2)
  # Shape of `inter_upperlefts`, `inter_lowerrights`, `inters`: (no. of
  # boxes1, no. of boxes2, 2)
  inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2])
  inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])
  inters = (inter_lowerrights - inter_upperlefts).clamp(min=0)
  # Shape of `inter_areas` and `union_areas`: (no. of boxes1, no. of boxes2)
  inter_areas = inters[:, :, 0] * inters[:, :, 1]
  union_areas = areas1[:, None] + areas2 - inter_areas
  return inter_areas / union_areas

#@save
def box_iou(boxes1, boxes2):
  """Compute pairwise IoU across two lists of anchor or bounding boxes."""
  box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) *
               (boxes[:, 3] - boxes[:, 1]))
  # Shape of `boxes1`, `boxes2`, `areas1`, `areas2`: (no. of boxes1, 4),
  # (no. of boxes2, 4), (no. of boxes1,), (no. of boxes2,)
  areas1 = box_area(boxes1)
  areas2 = box_area(boxes2)
  # Shape of `inter_upperlefts`, `inter_lowerrights`, `inters`: (no. of
  # boxes1, no. of boxes2, 2)
  inter_upperlefts = np.maximum(boxes1[:, None, :2], boxes2[:, :2])
  inter_lowerrights = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:])
  inters = (inter_lowerrights - inter_upperlefts).clip(min=0)
  # Shape of `inter_areas` and `union_areas`: (no. of boxes1, no. of boxes2)
  inter_areas = inters[:, :, 0] * inters[:, :, 1]
  union_areas = areas1[:, None] + areas2 - inter_areas
  return inter_areas / union_areas

14.4.3。在訓(xùn)練數(shù)據(jù)中標(biāo)記錨框

在訓(xùn)練數(shù)據(jù)集中,我們將每個(gè)錨框視為訓(xùn)練示例。為了訓(xùn)練目標(biāo)檢測(cè)模型,我們需要每個(gè)錨框的類 和偏移標(biāo)簽,其中前者是與錨框相關(guān)的對(duì)象的類別,后者是真實(shí)邊界框相對(duì)于錨箱。在預(yù)測(cè)過(guò)程中,我們?yōu)槊繌垐D像生成多個(gè)anchor boxes,為所有anchor boxes預(yù)測(cè)類別和偏移量,根據(jù)預(yù)測(cè)的偏移量調(diào)整它們的位置以獲得預(yù)測(cè)的bounding boxes,最后只輸出那些滿足一定條件的預(yù)測(cè)bounding boxes .

正如我們所知,對(duì)象檢測(cè)訓(xùn)練集帶有用于真實(shí)邊界框位置及其周圍對(duì)象類別的標(biāo)簽。為了標(biāo)記任何生成的錨框,我們參考其分配的最接近錨框的地面實(shí)況邊界框的標(biāo)記位置和類別。在下文中,我們描述了一種將最接近的地面實(shí)況邊界框分配給錨框的算法。

14.4.3.1。將真實(shí)邊界框分配給錨框

給定一張圖像,假設(shè)錨框是 A1,A2,…,Ana真實(shí)邊界框是B1,B2,…,Bnb, 在哪里na≥nb. 讓我們定義一個(gè)矩陣X∈Rna×nb, 其元素xij在里面ith行和 jth列是anchor box的IoUAi 和真實(shí)邊界框Bj. 該算法包括以下步驟:

找到矩陣中的最大元素X并將其行和列索引表示為i1和j1, 分別。然后是真實(shí)邊界框Bj1被分配到anchor boxAi1. 這是非常直觀的,因?yàn)?Ai1和Bj1是所有成對(duì)的錨框和真實(shí)邊界框中最接近的。第一次賦值后,丟棄所有元素 i1th行和j1th 矩陣中的列X.

在矩陣中找到最大的剩余元素 X并將其行和列索引表示為 i2和j2, 分別。我們分配地面實(shí)況邊界框Bj2錨框Ai2并丟棄其中的所有元素i2th行和 j2th矩陣中的列X.

此時(shí),矩陣中兩行兩列的元素 X已被丟棄。我們繼續(xù)進(jìn)行,直到所有元素都在nb矩陣中的列X被丟棄。這時(shí)候,我們已經(jīng)為每一個(gè)都分配了一個(gè)ground-truth bounding box nb錨箱。

只遍歷剩下的na?nb錨箱。例如,給定任何錨框Ai, 找到真實(shí)邊界框Bj具有最大的 IoUAi 在整個(gè)ith矩陣行 X, 并賦值Bj到Ai僅當(dāng)此 IoU 大于預(yù)定義閾值時(shí)。

讓我們用一個(gè)具體的例子來(lái)說(shuō)明上述算法。如圖 14.4.2 (左)所示,假設(shè)矩陣中的最大值X是x23,我們分配地面實(shí)況邊界框B3到錨箱A2. 然后,我們舍棄矩陣第2行第3列的所有元素,找到最大的x71在剩余的元素(陰影區(qū)域)中,并分配地面實(shí)況邊界框B1到錨箱 A7. 接下來(lái),如圖14.4.2 (中)所示,舍棄矩陣第7行第1列的所有元素,找出最大的x54在剩余的元素(陰影區(qū)域)中,并分配地面實(shí)況邊界框B4到錨箱 A5. 最后,如圖14.4.2 (右)所示,舍去矩陣第5行第4列的所有元素,找到最大的x92在剩余的元素(陰影區(qū)域)中,并分配地面實(shí)況邊界框B2到錨箱 A9. 之后我們只需要遍歷剩下的anchor boxesA1,A3,A4,A6,A8并根據(jù)閾值決定是否給它們分配ground-truth邊界框。

pYYBAGR9O3aAdT1GAAIUZ8eGjXI700.svg

圖 14.4.2將真實(shí)邊界框分配給錨框。

該算法在以下函數(shù)中實(shí)現(xiàn)assign_anchor_to_bbox 。

#@save
def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
  """Assign closest ground-truth bounding boxes to anchor boxes."""
  num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0]
  # Element x_ij in the i-th row and j-th column is the IoU of the anchor
  # box i and the ground-truth bounding box j
  jaccard = box_iou(anchors, ground_truth)
  # Initialize the tensor to hold the assigned ground-truth bounding box for
  # each anchor
  anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long,
                 device=device)
  # Assign ground-truth bounding boxes according to the threshold
  max_ious, indices = torch.max(jaccard, dim=1)
  anc_i = torch.nonzero(max_ious >= iou_threshold).reshape(-1)
  box_j = indices[max_ious >= iou_threshold]
  anchors_bbox_map[anc_i] = box_j
  col_discard = torch.full((num_anchors,), -1)
  row_discard = torch.full((num_gt_boxes,), -1)
  for _ in range(num_gt_boxes):
    max_idx = torch.argmax(jaccard) # Find the largest IoU
    box_idx = (max_idx % num_gt_boxes).long()
    anc_idx = (max_idx / num_gt_boxes).long()
    anchors_bbox_map[anc_idx] = box_idx
    jaccard[:, box_idx] = col_discard
    jaccard[anc_idx, :] = row_discard
  return anchors_bbox_map

#@save
def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
  """Assign closest ground-truth bounding boxes to anchor boxes."""
  num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0]
  # Element x_ij in the i-th row and j-th column is the IoU of the anchor
  # box i and the ground-truth bounding box j
  jaccard = box_iou(anchors, ground_truth)
  # Initialize the tensor to hold the assigned ground-truth bounding box for
  # each anchor
  anchors_bbox_map = np.full((num_anchors,), -1, dtype=np.int32, ctx=device)
  # Assign ground-truth bounding boxes according to the threshold
  max_ious, indices = np.max(jaccard, axis=1), np.argmax(jaccard, axis=1)
  anc_i = np.nonzero(max_ious >= iou_threshold)[0]
  box_j = indices[max_ious >= iou_threshold]
  anchors_bbox_map[anc_i] = box_j
  col_discard = np.full((num_anchors,), -1)
  row_discard = np.full((num_gt_boxes,), -1)
  for _ in range(num_gt_boxes):
    max_idx = np.argmax(jaccard) # Find the largest IoU
    box_idx = (max_idx % num_gt_boxes).astype('int32')
    anc_idx = (max_idx / num_gt_boxes).astype('int32')
    anchors_bbox_map[anc_idx] = box_idx
    jaccard[:, box_idx] = col_discard
    jaccard[anc_idx, :] = row_discard
  return anchors_bbox_map

14.4.3.2。標(biāo)注類別和偏移量

現(xiàn)在我們可以為每個(gè)錨框標(biāo)記類別和偏移量。假設(shè)一個(gè)錨框A被分配了一個(gè)真實(shí)邊界框 B. 一方面,anchor box的類A將被標(biāo)記為B. 另一方面,anchor box的偏移量A會(huì)根據(jù)中心坐標(biāo)之間的相對(duì)位置進(jìn)行標(biāo)注B和A以及這兩個(gè)框之間的相對(duì)大小。給定數(shù)據(jù)集中不同框的不同位置和大小,我們可以對(duì)那些可能導(dǎo)致更容易擬合的更均勻分布的偏移量應(yīng)用轉(zhuǎn)換到那些相對(duì)位置和大小。這里我們描述一個(gè)常見(jiàn)的轉(zhuǎn)換。給定中心坐標(biāo)A和B 作為(xa,ya)和(xb,yb), 它們的寬度為 wa和wb, 他們的身高為ha和 hb, 分別。我們可以標(biāo)記偏移量A作為

(14.4.3)(xb?xawa?μxσx,yb?yaha?μyσy,log?wbwa?μwσw,log?hbha?μhσh),

其中常量的默認(rèn)值是 μx=μy=μw=μh=0,σx=σy=0.1, 和 σw=σh=0.2. 此轉(zhuǎn)換在下面的函數(shù)中實(shí)現(xiàn)offset_boxes。

#@save
def offset_boxes(anchors, assigned_bb, eps=1e-6):
  """Transform for anchor box offsets."""
  c_anc = d2l.box_corner_to_center(anchors)
  c_assigned_bb = d2l.box_corner_to_center(assigned_bb)
  offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:]
  offset_wh = 5 * torch.log(eps + c_assigned_bb[:, 2:] / c_anc[:, 2:])
  offset = torch.cat([offset_xy, offset_wh], axis=1)
  return offset

#@save
def offset_boxes(anchors, assigned_bb, eps=1e-6):
  """Transform for anchor box offsets."""
  c_anc = d2l.box_corner_to_center(anchors)
  c_assigned_bb = d2l.box_corner_to_center(assigned_bb)
  offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:]
  offset_wh = 5 * np.log(eps + c_assigned_bb[:, 2:] / c_anc[:, 2:])
  offset = np.concatenate([offset_xy, offset_wh], axis=1)
  return offset

如果沒(méi)有為錨框分配真實(shí)邊界框,我們只需將錨框的類別標(biāo)記為“背景”。類別為背景的錨框通常稱為負(fù)錨框,其余稱為正錨框。我們實(shí)現(xiàn)了以下函數(shù),使用真實(shí)邊界框(參數(shù))來(lái)標(biāo)記錨框(參數(shù))multibox_target的類和偏移量。此函數(shù)將背景類設(shè)置為零,并將新類的整數(shù)索引遞增 1。anchorslabels

#@save
def multibox_target(anchors, labels):
  """Label anchor boxes using ground-truth bounding boxes."""
  batch_size, anchors = labels.shape[0], anchors.squeeze(0)
  batch_offset, batch_mask, batch_class_labels = [], [], []
  device, num_anchors = anchors.device, anchors.shape[0]
  for i in range(batch_size):
    label = labels[i, :, :]
    anchors_bbox_map = assign_anchor_to_bbox(
      label[:, 1:], anchors, device)
    bbox_mask = ((anchors_bbox_map >= 0).float().unsqueeze(-1)).repeat(
      1, 4)
    # Initialize class labels and assigned bounding box coordinates with
    # zeros
    class_labels = torch.zeros(num_anchors, dtype=torch.long,
                  device=device)
    assigned_bb = torch.zeros((num_anchors, 4), dtype=torch.float32,
                 device=device)
    # Label classes of anchor boxes using their assigned ground-truth
    # bounding boxes. If an anchor box is not assigned any, we label its
    # class as background (the value remains zero)
    indices_true = torch.nonzero(anchors_bbox_map >= 0)
    bb_idx = anchors_bbox_map[indices_true]
    class_labels[indices_true] = label[bb_idx, 0].long() + 1
    assigned_bb[indices_true] = label[bb_idx, 1:]
    # Offset transformation
    offset = offset_boxes(anchors, assigned_bb) * bbox_mask
    batch_offset.append(offset.reshape(-1))
    batch_mask.append(bbox_mask.reshape(-1))
    batch_class_labels.append(class_labels)
  bbox_offset = torch.stack(batch_offset)
  bbox_mask = torch.stack(batch_mask)
  class_labels = torch.stack(batch_class_labels)
  return (bbox_offset, bbox_mask, class_labels)

#@save
def multibox_target(anchors, labels):
  """Label anchor boxes using ground-truth bounding boxes."""
  batch_size, anchors = labels.shape[0], anchors.squeeze(0)
  batch_offset, batch_mask, batch_class_labels = [], [], []
  device, num_anchors = anchors.ctx, anchors.shape[0]
  for i in range(batch_size):
    label = labels[i, :, :]
    anchors_bbox_map = assign_anchor_to_bbox(
      label[:, 1:], anchors, device)
    bbox_mask = np.tile((np.expand_dims((anchors_bbox_map >= 0),
                      axis=-1)), (1, 4)).astype('int32')
    # Initialize class labels and assigned bounding box coordinates with
    # zeros
    class_labels = np.zeros(num_anchors, dtype=np.int32, ctx=device)
    assigned_bb = np.zeros((num_anchors, 4), dtype=np.float32,
                ctx=device)
    # Label classes of anchor boxes using their assigned ground-truth
    # bounding boxes. If an anchor box is not assigned any, we label its
    # class as background (the value remains zero)
    indices_true = np.nonzero(anchors_bbox_map >= 0)[0]
    bb_idx = anchors_bbox_map[indices_true]
    class_labels[indices_true] = label[bb_idx, 0].astype('int32') + 1
    assigned_bb[indices_true] = label[bb_idx, 1:]
    # Offset transformation
    offset = offset_boxes(anchors, assigned_bb) * bbox_mask
    batch_offset.append(offset.reshape(-1))
    batch_mask.append(bbox_mask.reshape(-1))
    batch_class_labels.append(class_labels)
  bbox_offset = np.stack(batch_offset)
  bbox_mask = np.stack(batch_mask)
  class_labels = np.stack(batch_class_labels)
  return (bbox_offset, bbox_mask, class_labels)

14.4.3.3。一個(gè)例子

讓我們通過(guò)一個(gè)具體的例子來(lái)說(shuō)明錨框標(biāo)記。我們?yōu)榧虞d圖像中的狗和貓定義地面真實(shí)邊界框,其中第一個(gè)元素是類(0 代表狗,1 代表貓),其余四個(gè)元素是(x,y)- 左上角和右下角的軸坐標(biāo)(范圍在 0 和 1 之間)。我們還使用左上角和右下角的坐標(biāo)構(gòu)造了五個(gè)要標(biāo)記的錨框: A0,…,A4(索引從0開(kāi)始)。然后我們?cè)趫D像中繪制這些真實(shí)邊界框和錨框。

ground_truth = torch.tensor([[0, 0.1, 0.08, 0.52, 0.92],
             [1, 0.55, 0.2, 0.9, 0.88]])
anchors = torch.tensor([[0, 0.1, 0.2, 0.3], [0.15, 0.2, 0.4, 0.4],
          [0.63, 0.05, 0.88, 0.98], [0.66, 0.45, 0.8, 0.8],
          [0.57, 0.3, 0.92, 0.9]])

fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, ground_truth[:, 1:] * bbox_scale, ['dog', 'cat'], 'k')
show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']);

poYBAGR9O3mAQHxfAAHMH0ocJS0617.svg

ground_truth = np.array([[0, 0.1, 0.08, 0.52, 0.92],
             [1, 0.55, 0.2, 0.9, 0.88]])
anchors = np.array([[0, 0.1, 0.2, 0.3], [0.15, 0.2, 0.4, 0.4],
          [0.63, 0.05, 0.88, 0.98], [0.66, 0.45, 0.8, 0.8],
          [0.57, 0.3, 0.92, 0.9]])

fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, ground_truth[:, 1:] * bbox_scale, ['dog', 'cat'], 'k')
show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']);

poYBAGR9O3mAQHxfAAHMH0ocJS0617.svg

使用multibox_target上面定義的函數(shù),我們可以根據(jù)狗和貓的真實(shí)邊界框來(lái)標(biāo)記這些錨框的類別和偏移量。在此示例中,背景、狗和貓類的索引分別為 0、1 和 2。下面我們?yōu)閍nchor boxes和ground-truth bounding boxes的例子添加一個(gè)維度。

labels = multibox_target(anchors.unsqueeze(dim=0),
             ground_truth.unsqueeze(dim=0))

labels = multibox_target(np.expand_dims(anchors, axis=0),
             np.expand_dims(ground_truth, axis=0))

返回結(jié)果中有三項(xiàng),都是張量格式。第三項(xiàng)包含輸入錨框的標(biāo)記類。

讓我們根據(jù)圖像中的錨框和真實(shí)邊界框位置分析下面返回的類標(biāo)簽。首先,在所有的anchor boxes和ground-truth bounding boxes對(duì)中,anchor boxes的IoUA4貓的真實(shí)邊界框是最大的。因此,類A4被標(biāo)記為貓。取出包含的對(duì)A4或貓的真實(shí)邊界框,其余的一對(duì)錨框A1狗的真實(shí)邊界框具有最大的 IoU。所以類A1被標(biāo)記為狗。接下來(lái),我們需要遍歷剩下的三個(gè)未標(biāo)記的anchor boxes:A0, A2, 和A3. 為了A0,具有最大IoU的ground-truth邊界框的類別是狗,但I(xiàn)oU低于預(yù)定義的閾值(0.5),因此該類別被標(biāo)記為背景;為了A2,具有最大IoU的ground-truth bounding box的類別是貓,并且IoU超過(guò)閾值,因此該類別被標(biāo)記為貓;為了A3,具有最大IoU的ground-truth bounding box的類別是貓,但該值低于閾值,因此該類別被標(biāo)記為背景。

labels[2]

tensor([[0, 1, 2, 0, 2]])

labels[2]

array([[0, 1, 2, 0, 2]], dtype=int32)

第二個(gè)返回項(xiàng)是形狀的掩碼變量(批量大小,錨框數(shù)量的四倍)。掩碼變量中每四個(gè)元素對(duì)應(yīng)每個(gè)錨框的四個(gè)偏移值。由于我們不關(guān)心背景檢測(cè),這個(gè)負(fù)類的偏移量不應(yīng)該影響目標(biāo)函數(shù)。通過(guò)逐元素乘法,掩碼變量中的零將在計(jì)算目標(biāo)函數(shù)之前過(guò)濾掉負(fù)類偏移。

labels[1]

tensor([[0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 1., 1.,
     1., 1.]])

labels[1]

array([[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]],
   dtype=int32)

第一個(gè)返回的項(xiàng)目包含為每個(gè)錨框標(biāo)記的四個(gè)偏移值。請(qǐng)注意,負(fù)類錨框的偏移量標(biāo)記為零。

labels[0]

tensor([[-0.00e+00, -0.00e+00, -0.00e+00, -0.00e+00, 1.40e+00, 1.00e+01,
     2.59e+00, 7.18e+00, -1.20e+00, 2.69e-01, 1.68e+00, -1.57e+00,
     -0.00e+00, -0.00e+00, -0.00e+00, -0.00e+00, -5.71e-01, -1.00e+00,
     4.17e-06, 6.26e-01]])

labels[0]

array([[-0.00e+00, -0.00e+00, -0.00e+00, -0.00e+00, 1.40e+00, 1.00e+01,
     2.59e+00, 7.18e+00, -1.20e+00, 2.69e-01, 1.68e+00, -1.57e+00,
    -0.00e+00, -0.00e+00, -0.00e+00, -0.00e+00, -5.71e-01, -1.00e+00,
     4.17e-06, 6.26e-01]])

14.4.4。預(yù)測(cè)具有非最大抑制的邊界框

在預(yù)測(cè)期間,我們?yōu)閳D像生成多個(gè)錨框,并為每個(gè)錨框預(yù)測(cè)類別和偏移量。 因此根據(jù)具有預(yù)測(cè)偏移量的錨框獲得預(yù)測(cè)邊界框。下面我們實(shí)現(xiàn)offset_inverse將錨點(diǎn)和偏移預(yù)測(cè)作為輸入并應(yīng)用逆偏移變換以返回預(yù)測(cè)的邊界框坐標(biāo)的函數(shù)。

#@save
def offset_inverse(anchors, offset_preds):
  """Predict bounding boxes based on anchor boxes with predicted offsets."""
  anc = d2l.box_corner_to_center(anchors)
  pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2]
  pred_bbox_wh = torch.exp(offset_preds[:, 2:] / 5) * anc[:, 2:]
  pred_bbox = torch.cat((pred_bbox_xy, pred_bbox_wh), axis=1)
  predicted_bbox = d2l.box_center_to_corner(pred_bbox)
  return predicted_bbox

#@save
def offset_inverse(anchors, offset_preds):
  """Predict bounding boxes based on anchor boxes with predicted offsets."""
  anc = d2l.box_corner_to_center(anchors)
  pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2]
  pred_bbox_wh = np.exp(offset_preds[:, 2:] / 5) * anc[:, 2:]
  pred_bbox = np.concatenate((pred_bbox_xy, pred_bbox_wh), axis=1)
  predicted_bbox = d2l.box_center_to_corner(pred_bbox)
  return predicted_bbox

當(dāng)有很多錨框時(shí),可能會(huì)輸出許多相似(具有顯著重疊)的預(yù)測(cè)邊界框來(lái)包圍同一對(duì)象。為了簡(jiǎn)化輸出,我們可以使用非最大抑制(NMS)合并屬于同一對(duì)象的相似預(yù)測(cè)邊界框 。

以下是非極大值抑制的工作原理。對(duì)于預(yù)測(cè)的邊界框 B,對(duì)象檢測(cè)模型計(jì)算每個(gè)類別的預(yù)測(cè)可能性。表示為p最大的預(yù)測(cè)似然,對(duì)應(yīng)于這個(gè)概率的類就是預(yù)測(cè)的類B. 具體來(lái)說(shuō),我們參考p作為 預(yù)測(cè)邊界框的置信度(分?jǐn)?shù))B. 在同一張圖片上,將所有預(yù)測(cè)的非背景邊界框按照置信度降序排序,生成列表L. 然后我們操作排序列表L在以下步驟中:

選擇預(yù)測(cè)的邊界框B1以最高的信心L作為基礎(chǔ)并刪除所有非基礎(chǔ)預(yù)測(cè)邊界框,其 IoU 為B1超過(guò)預(yù)定義的閾值?從L. 在此刻, L保留具有最高置信度的預(yù)測(cè)邊界框,但丟棄與它太相似的其他邊界框。簡(jiǎn)而言之,那些具有非最大置信度分?jǐn)?shù)的被 抑制。

選擇預(yù)測(cè)的邊界框B2具有第二高的置信度L作為另一個(gè)基礎(chǔ)并刪除所有非基礎(chǔ)預(yù)測(cè)邊界框,其 IoU 與B2超過(guò) ?從L.

重復(fù)上述過(guò)程,直到所有預(yù)測(cè)的邊界框在 L已被用作基礎(chǔ)。此時(shí),任意一對(duì)預(yù)測(cè)邊界框的IoU在L低于閾值 ?; 因此,沒(méi)有一對(duì)彼此太相似。

輸出列表中所有預(yù)測(cè)的邊界框L.

以下nms函數(shù)按降序?qū)χ眯哦鹊梅诌M(jìn)行排序并返回它們的索引。

#@save
def nms(boxes, scores, iou_threshold):
  """Sort confidence scores of predicted bounding boxes."""
  B = torch.argsort(scores, dim=-1, descending=True)
  keep = [] # Indices of predicted bounding boxes that will be kept
  while B.numel() > 0:
    i = B[0]
    keep.append(i)
    if B.numel() == 1: break
    iou = box_iou(boxes[i, :].reshape(-1, 4),
           boxes[B[1:], :].reshape(-1, 4)).reshape(-1)
    inds = torch.nonzero(iou <= iou_threshold).reshape(-1)
    B = B[inds + 1]
  return torch.tensor(keep, device=boxes.device)

#@save
def nms(boxes, scores, iou_threshold):
  """Sort confidence scores of predicted bounding boxes."""
  B = scores.argsort()[::-1]
  keep = [] # Indices of predicted bounding boxes that will be kept
  while B.size > 0:
    i = B[0]
    keep.append(i)
    if B.size == 1: break
    iou = box_iou(boxes[i, :].reshape(-1, 4),
           boxes[B[1:], :].reshape(-1, 4)).reshape(-1)
    inds = np.nonzero(iou <= iou_threshold)[0]
    B = B[inds + 1]
  return np.array(keep, dtype=np.int32, ctx=boxes.ctx)

我們定義以下內(nèi)容multibox_detection以將非最大抑制應(yīng)用于預(yù)測(cè)邊界框。如果您發(fā)現(xiàn)實(shí)現(xiàn)有點(diǎn)復(fù)雜,請(qǐng)不要擔(dān)心:我們將在實(shí)現(xiàn)后立即通過(guò)具體示例展示它是如何工作的。

#@save
def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
            pos_threshold=0.009999999):
  """Predict bounding boxes using non-maximum suppression."""
  device, batch_size = cls_probs.device, cls_probs.shape[0]
  anchors = anchors.squeeze(0)
  num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2]
  out = []
  for i in range(batch_size):
    cls_prob, offset_pred = cls_probs[i], offset_preds[i].reshape(-1, 4)
    conf, class_id = torch.max(cls_prob[1:], 0)
    predicted_bb = offset_inverse(anchors, offset_pred)
    keep = nms(predicted_bb, conf, nms_threshold)
    # Find all non-`keep` indices and set the class to background
    all_idx = torch.arange(num_anchors, dtype=torch.long, device=device)
    combined = torch.cat((keep, all_idx))
    uniques, counts = combined.unique(return_counts=True)
    non_keep = uniques[counts == 1]
    all_id_sorted = torch.cat((keep, non_keep))
    class_id[non_keep] = -1
    class_id = class_id[all_id_sorted]
    conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted]
    # Here `pos_threshold` is a threshold for positive (non-background)
    # predictions
    below_min_idx = (conf < pos_threshold)
    class_id[below_min_idx] = -1
    conf[below_min_idx] = 1 - conf[below_min_idx]
    pred_info = torch.cat((class_id.unsqueeze(1),
                conf.unsqueeze(1),
                predicted_bb), dim=1)
    out.append(pred_info)
  return torch.stack(out)

#@save
def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
            pos_threshold=0.009999999):
  """Predict bounding boxes using non-maximum suppression."""
  device, batch_size = cls_probs.ctx, cls_probs.shape[0]
  anchors = np.squeeze(anchors, axis=0)
  num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2]
  out = []
  for i in range(batch_size):
    cls_prob, offset_pred = cls_probs[i], offset_preds[i].reshape(-1, 4)
    conf, class_id = np.max(cls_prob[1:], 0), np.argmax(cls_prob[1:], 0)
    predicted_bb = offset_inverse(anchors, offset_pred)
    keep = nms(predicted_bb, conf, nms_threshold)
    # Find all non-`keep` indices and set the class to background
    all_idx = np.arange(num_anchors, dtype=np.int32, ctx=device)
    combined = np.concatenate((keep, all_idx))
    unique, counts = np.unique(combined, return_counts=True)
    non_keep = unique[counts == 1]
    all_id_sorted = np.concatenate((keep, non_keep))
    class_id[non_keep] = -1
    class_id = class_id[all_id_sorted].astype('float32')
    conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted]
    # Here `pos_threshold` is a threshold for positive (non-background)
    # predictions
    below_min_idx = (conf < pos_threshold)
    class_id[below_min_idx] = -1
    conf[below_min_idx] = 1 - conf[below_min_idx]
    pred_info = np.concatenate((np.expand_dims(class_id, axis=1),
                np.expand_dims(conf, axis=1),
                predicted_bb), axis=1)
    out.append(pred_info)
  return np.stack(out)

現(xiàn)在讓我們將上述實(shí)現(xiàn)應(yīng)用到一個(gè)有四個(gè)錨框的具體例子中。為簡(jiǎn)單起見(jiàn),我們假設(shè)預(yù)測(cè)的偏移量全為零。這意味著預(yù)測(cè)的邊界框是錨框。對(duì)于背景、狗和貓中的每個(gè)類別,我們還定義了它的預(yù)測(cè)可能性。

anchors = torch.tensor([[0.1, 0.08, 0.52, 0.92], [0.08, 0.2, 0.56, 0.95],
           [0.15, 0.3, 0.62, 0.91], [0.55, 0.2, 0.9, 0.88]])
offset_preds = torch.tensor([0] * anchors.numel())
cls_probs = torch.tensor([[0] * 4, # Predicted background likelihood
           [0.9, 0.8, 0.7, 0.1], # Predicted dog likelihood
           [0.1, 0.2, 0.3, 0.9]]) # Predicted cat likelihood

anchors = np.array([[0.1, 0.08, 0.52, 0.92], [0.08, 0.2, 0.56, 0.95],
           [0.15, 0.3, 0.62, 0.91], [0.55, 0.2, 0.9, 0.88]])
offset_preds = np.array([0] * d2l.size(anchors))
cls_probs = np.array([[0] * 4, # Predicted background likelihood
           [0.9, 0.8, 0.7, 0.1], # Predicted dog likelihood
           [0.1, 0.2, 0.3, 0.9]]) # Predicted cat likelihood

我們可以繪制這些預(yù)測(cè)的邊界框及其對(duì)圖像的置信度。

fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, anchors * bbox_scale,
      ['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9'])

poYBAGR9O36ANQ5IAAIBpW2Sdv8877.svg

fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, anchors * bbox_scale,
      ['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9'])

poYBAGR9O36ANQ5IAAIBpW2Sdv8877.svg

現(xiàn)在我們可以調(diào)用該multibox_detection函數(shù)來(lái)執(zhí)行非極大值抑制,其中閾值設(shè)置為 0.5。請(qǐng)注意,我們?cè)趶埩枯斎胫袨槭纠砑恿艘粋€(gè)維度。

我們可以看到返回結(jié)果的shape為(batch size, anchor boxes number, 6)。最里面維度的六個(gè)元素給出了相同預(yù)測(cè)邊界框的輸出信息。第一個(gè)元素是預(yù)測(cè)的類別索引,它從 0 開(kāi)始(0 是狗,1 是貓)。值 -1 表示背景或非最大抑制中的去除。第二個(gè)元素是預(yù)測(cè)邊界框的置信度。剩下的四個(gè)元素是(x,y)分別為預(yù)測(cè)邊界框的左上角和右下角的軸坐標(biāo)(范圍在 0 和 1 之間)。

output = multibox_detection(cls_probs.unsqueeze(dim=0),
              offset_preds.unsqueeze(dim=0),
              anchors.unsqueeze(dim=0),
              nms_threshold=0.5)
output

tensor([[[ 0.00, 0.90, 0.10, 0.08, 0.52, 0.92],
     [ 1.00, 0.90, 0.55, 0.20, 0.90, 0.88],
     [-1.00, 0.80, 0.08, 0.20, 0.56, 0.95],
     [-1.00, 0.70, 0.15, 0.30, 0.62, 0.91]]])

output = multibox_detection(np.expand_dims(cls_probs, axis=0),
              np.expand_dims(offset_preds, axis=0),
              np.expand_dims(anchors, axis=0),
              nms_threshold=0.5)
output

array([[[ 1. , 0.9 , 0.55, 0.2 , 0.9 , 0.88],
    [ 0. , 0.9 , 0.1 , 0.08, 0.52, 0.92],
    [-1. , 0.8 , 0.08, 0.2 , 0.56, 0.95],
    [-1. , 0.7 , 0.15, 0.3 , 0.62, 0.91]]])

去除那些-1類的預(yù)測(cè)邊界框后,我們可以輸出非最大抑制保留的最終預(yù)測(cè)邊界框。

fig = d2l.plt.imshow(img)
for i in output[0].detach().numpy():
  if i[0] == -1:
    continue
  label = ('dog=', 'cat=')[int(i[0])] + str(i[1])
  show_bboxes(fig.axes, [torch.tensor(i[2:]) * bbox_scale], label)

poYBAGR9O4OAR-stAAHHqnsavPs544.svg

fig = d2l.plt.imshow(img)
for i in output[0].asnumpy():
  if i[0] == -1:
    continue
  label = ('dog=', 'cat=')[int(i[0])] + str(i[1])
  show_bboxes(fig.axes, [np.array(i[2:]) * bbox_scale], label)

poYBAGR9O4aAVgxBAAHHqsy74TQ168.svg

在實(shí)踐中,我們甚至可以在執(zhí)行非最大抑制之前刪除具有較低置信度的預(yù)測(cè)邊界框,從而減少該算法的計(jì)算量。我們還可以對(duì)非最大抑制的輸出進(jìn)行后處理,例如,只保留對(duì)最終輸出具有更高置信度的結(jié)果。

14.4.5。概括

我們以圖像的每個(gè)像素為中心生成具有不同形狀的錨框。

Intersection over union (IoU),也稱為 Jaccard 指數(shù),衡量?jī)蓚€(gè)邊界框的相似性。它是它們的交集面積與聯(lián)合面積的比率。

在訓(xùn)練集中,我們需要為每個(gè)錨框提供兩種類型的標(biāo)簽。一個(gè)是與anchor box相關(guān)的對(duì)象的類別,另一個(gè)是ground-truth bounding box相對(duì)于anchor box的偏移量。

在預(yù)測(cè)過(guò)程中,我們可以使用非最大抑制(NMS)來(lái)去除相似的預(yù)測(cè)邊界框,從而簡(jiǎn)化輸出。

14.4.6。練習(xí)

更改函數(shù)中的sizes和的值 。生成的anchor boxes有什么變化?ratiosmultibox_prior

構(gòu)造和可視化兩個(gè) IoU 為 0.5 的邊界框。它們?nèi)绾蜗嗷ブ丿B?

修改14.4.3 節(jié)和 14.4.4 節(jié)anchors中的 變量。結(jié)果如何變化?

非極大值抑制是一種貪心算法,它通過(guò)移除預(yù)測(cè)的邊界框來(lái)抑制它們。有沒(méi)有可能其中一些被刪除的實(shí)際上有用?如何修改此算法以軟抑制?你可以參考 Soft-NMS ( Bodla et al. , 2017 )。

與其手工制作,不如學(xué)習(xí)非極大值抑制?

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(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)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4628

    瀏覽量

    93183
  • pytorch
    +關(guān)注

    關(guān)注

    2

    文章

    808

    瀏覽量

    13322
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    使用Virtex 6和14.4映射錯(cuò)誤

    理這個(gè)問(wèn)題。任何幫助,將不勝感激。以上來(lái)自于谷歌翻譯以下為原文I'm targetting a design for a Virtex 6 using EDK 14.4. I was receiving
    發(fā)表于 10-19 14:33

    Web ISE 14.4更改重新編譯得到不適合到期

    else has experienced this. I downloaded/installed WebISE 14.4. Everything seems to work
    發(fā)表于 02-19 07:03

    PyTorch如何入門

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

    空間雙索面自式懸索橋總體布置

    受力均勻,結(jié)合面設(shè)于距墩中心16m。跨梁跨中斷面跨梁采用全預(yù)應(yīng)力結(jié)構(gòu),跨徑50m,單六室斷面,標(biāo)準(zhǔn)梁高3m,在室位置梁高加高至5m
    發(fā)表于 10-31 07:35

    Pytorch AI語(yǔ)音助手

    想做一個(gè)Pytorch AI語(yǔ)音助手,有沒(méi)有好的思路呀?
    發(fā)表于 03-06 13:00

    如何安裝TensorFlow2 Pytorch

    如何安裝TensorFlow2 Pytorch?
    發(fā)表于 03-07 07:32

    怎樣使用PyTorch Hub去加載YOLOv5模型

    在Python>=3.7.0環(huán)境中安裝requirements.txt,包括PyTorch>=1.7。模型和數(shù)據(jù)集從最新的 YOLOv5版本自動(dòng)下載。簡(jiǎn)單示例此示例從
    發(fā)表于 07-22 16:02

    如何往星光2板子里裝pytorch

    如題,想先gpu版本的pytorch只安裝cpu版本的pytorch,pytorch官網(wǎng)提供了基于conda和pip兩種安裝方式。因?yàn)樵凼莚isc架構(gòu)沒(méi)對(duì)應(yīng)的conda,而使用pip安裝提示也沒(méi)有
    發(fā)表于 09-12 06:30

    預(yù)應(yīng)力具之具連接器的知識(shí)

    預(yù)應(yīng)力具應(yīng)考慮等級(jí)分類張拉、補(bǔ)張拉和釋放壓力預(yù)應(yīng)力的規(guī)定。鋼筋錨固好幾根預(yù)應(yīng)力筋的預(yù)應(yīng)力具,除需有整束張拉的特性外,尚宜具備單條張拉的概率。
    發(fā)表于 08-13 11:10 ?6277次閱讀

    桿內(nèi)外徑及螺距如何在線檢測(cè)

    桿作用很大,為了確保產(chǎn)品質(zhì)量,保證井下使用安全,需要對(duì)桿的質(zhì)量嚴(yán)格把關(guān)。本文主要研發(fā)了桿幾何尺寸的檢測(cè)設(shè)備,幫助生產(chǎn)高質(zhì)量的桿。
    的頭像 發(fā)表于 12-26 21:08 ?804次閱讀

    蘋果發(fā)布iOS14.4與 iPadOS14.4 RC版更新

    蘋果面向開(kāi)發(fā)人員發(fā)布了 iOS 14.4 與 iPadOS 14.4 RC(Release Candidate) 版更新。 在 iOS 14.4 中,iPhone 相機(jī)可以識(shí)別較小的 QR 碼,并可
    的頭像 發(fā)表于 01-22 09:17 ?2122次閱讀

    蘋果重磅推送iOS 14.4系統(tǒng)更新

    蘋果公司早在去年底就推出了全新的iOS 14.4開(kāi)發(fā)者預(yù)覽版系統(tǒng),并在前幾天面向開(kāi)發(fā)人員發(fā)布了iOS 14.4版本固件。今天(1月27日),蘋果正式面向用戶推送了iOS 14.4系統(tǒng)更新。iOS
    的頭像 發(fā)表于 01-27 11:01 ?3457次閱讀

    蘋果iOS14.4和iPadOS14.4正式版發(fā)布

    今日凌晨,蘋果更新發(fā)布了 iOS 14.4 和 iPadOS 14.4 正式版系統(tǒng),本次更新的版本號(hào)為(18D52),與上周發(fā)布的 iOS 14.4 RC 候選版版本號(hào)一致。
    的頭像 發(fā)表于 01-28 12:10 ?6911次閱讀

    PyTorch教程14.4

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程14.4.pdf》資料免費(fèi)下載
    發(fā)表于 06-05 11:22 ?0次下載
    <b class='flag-5'>PyTorch</b>教程<b class='flag-5'>14.4</b>之<b class='flag-5'>錨</b><b class='flag-5'>箱</b>

    案例分享: OFDR用于桿應(yīng)力測(cè)試

    近日昊衡科技分享測(cè)試案例OFDR用于桿應(yīng)力測(cè)試。桿工程在邊坡、基坑、橋梁與隧道等支護(hù)工程中有廣泛應(yīng)用,文章借助昊衡科技的OSI分布式光纖傳感系統(tǒng)對(duì)桿的受力機(jī)理進(jìn)行應(yīng)用研究,為土木結(jié)構(gòu)健康監(jiān)測(cè)
    的頭像 發(fā)表于 05-13 16:01 ?691次閱讀
    案例分享: OFDR用于<b class='flag-5'>錨</b>桿應(yīng)力測(cè)試