.. _sec_anchor: Çapa Kutuları ============= Nesne algılama algoritmaları genellikle girdi imgesinde çok sayıda bölgeyi örnekler, bu bölgelerin ilgilenilen nesneleri içerip içermediğini belirler ve nesnelerin *gerçek referans değeri kuşatan kutularını* daha doğru bir şekilde tahmin etmek için bölgelerin sınırlarını ayarlar. Farklı modeller farklı bölge örnekleme düzenleri benimseyebilir. Burada bu tür yöntemlerden birini sunuyoruz: Her pikselde ortalanmış değişen ölçeklere ve en-boy oranlarına sahip birden çok kuşatan kutu oluşturur. Bu kuşatan kutulara *çapa kutuları* denir. :numref:`sec_ssd` içinde çapa kutularına dayalı bir nesne algılama modeli tasarlayacağız. Öncelikle, sadece daha özlü çıktılar için doğruluk yazdırmayı değiştirelim. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline from d2l import mxnet as d2l from mxnet import gluon, image, np, npx np.set_printoptions(2) # Doğruluğunu basmayı basitleştirin npx.set_np() .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline import torch from d2l import torch as d2l torch.set_printoptions(2) # Doğruluğunu basmayı basitleştirin .. raw:: html
.. raw:: html
Çoklu Çapa Kutuları Oluşturma ----------------------------- Girdi imgesinin :math:`h` yüksekliğine ve :math:`w` genişliğine sahip olduğunu varsayalım. İmgenin her pikselinde ortalanmış farklı şekillere sahip çapa kutuları oluşturuyoruz. *Ölçek* :math:`s\in (0, 1]` ve *en-boy oranı* (genişliğin yüksekliğe oranı) :math:`r > 0` olsun. O zaman çapa kutusunun genişliği ve yüksekliği sırasıyla :math:`ws\sqrt{r}` ve :math:`hs/\sqrt{r}`'dür. Merkez konumu verildiğinde, bilinen genişlik ve yüksekliğe sahip bir çapa kutusu belirlendiğini unutmayın. Farklı şekillerde birden çok çapa kutusu oluşturmak için, bir dizi ölçek :math:`s_1,\ldots, s_n` ve bir dizi en boy oranı :math:`r_1,\ldots, r_m` ayarlayalım. Bu ölçeklerin ve en boy oranlarının tüm kombinasyonlarını merkez olarak her pikselle birlikte kullanırken, girdi imgesinde toplam :math:`whnm` çapa kutusu bulunur. Bu çapa kutuları gerçek referans değerleri kuşatan kutuları kapsayabilmesine rağmen, hesaplama karmaşıklığı basitçe çok yüksektir. Uygulamada, yalnızca :math:`s_1` veya :math:`r_1` içeren kombinasyonları dikkate alalım: .. math:: (s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s_n, r_1). Yani, aynı pikselde ortalanmış çapa kutularının sayısı :math:`n+m-1`'dir. Tüm girdi imgesi için toplam :math:`wh(n+m-1)` çapa kutusu oluşturacağız. Yukarıdaki çapa kutuları oluşturma yöntemi aşağıdaki ``multibox_prior`` işlevinde uygulanır. Girdi imgesini, ölçeklerin bir listesini ve en-boy oranlarının bir listesini belirleriz, daha sonra bu işlev tüm çapa kutularını döndürür. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_prior(data, sizes, ratios): """Her pikselde ortalanmış farklı şekillere sahip çapa kutuları oluşturun.""" 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) # Bağlantıyı bir pikselin merkezine taşımak için kaydırmalar gereklidir. # Bir pikselin yüksekliği=1 ve genişliği=1 olduğundan, merkezlerimizi 0.5 # ile kaydırmayı seçiyoruz. offset_h, offset_w = 0.5, 0.5 steps_h = 1.0 / in_height # y ekseninde ölçeklenmiş adımlar steps_w = 1.0 / in_width # x ekseninde ölçeklenmiş adımlar # Çapa kutuları için tüm merkez noktalarını oluştur 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) # Daha sonra çapa kutusu köşe koordinatları (xmin, xmax, ymin, ymax) # oluşturmak için kullanılacak yükseklik ve genişliklerin `boxes_per_pixel` # sayısını oluşturun w = np.concatenate((size_tensor * np.sqrt(ratio_tensor[0]), sizes[0] * np.sqrt(ratio_tensor[1:]))) \ * in_height / in_width # Dikdörtgen girdileri yönet h = np.concatenate((size_tensor / np.sqrt(ratio_tensor[0]), sizes[0] / np.sqrt(ratio_tensor[1:]))) # Yarım yükseklik ve yarım genişlik elde etmek için 2'ye bölün anchor_manipulations = np.tile(np.stack((-w, -h, w, h)).T, (in_height * in_width, 1)) / 2 # Her merkez noktasında `boxes_per_pixel` sayısı çapa kutusu olacaktır, # bu nedenle `boxes_per_pixel` tekrarlarıyla tüm çapa kutusu # merkezlerinin bir ızgarasını oluşturun 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_prior(data, sizes, ratios): """Her pikselde ortalanmış farklı şekillere sahip çapa kutuları oluşturun.""" 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) # Bağlantıyı bir pikselin merkezine taşımak için kaydırmalar gereklidir. # Bir pikselin yüksekliği=1 ve genişliği=1 olduğundan, merkezlerimizi 0.5 # ile kaydırmayı seçiyoruz. offset_h, offset_w = 0.5, 0.5 steps_h = 1.0 / in_height # y ekseninde ölçeklenmiş adımlar steps_w = 1.0 / in_width # x ekseninde ölçeklenmiş adımlar # Çapa kutuları için tüm merkez noktalarını oluştur 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) shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1) # Daha sonra çapa kutusu köşe koordinatları (xmin, xmax, ymin, ymax) # oluşturmak için kullanılacak yükseklik ve genişliklerin `boxes_per_pixel` # sayısını oluşturun w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]), sizes[0] * torch.sqrt(ratio_tensor[1:])))\ * in_height / in_width # Dikdörtgen girdileri yönet h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]), sizes[0] / torch.sqrt(ratio_tensor[1:]))) # Yarım yükseklik ve yarım genişlik elde etmek için 2'ye bölün anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat( in_height * in_width, 1) / 2 # Her merkez noktasında `boxes_per_pixel` sayısı çapa kutusu olacaktır, # bu nedenle `boxes_per_pixel` tekrarlarıyla tüm çapa kutusu # merkezlerinin bir ızgarasını oluşturun 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) .. raw:: html
.. raw:: html
Döndürülen çapa kutusu değişkeni ``Y``'nin şeklinin (parti boyutu, çapa kutusu sayısı, 4) olduğunu görebiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python img = image.imread('../img/catdog.jpg').asnumpy() h, w = img.shape[:2] print(h, w) X = np.random.uniform(size=(1, 3, h, w)) # Girdi verisi oluştur Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]) Y.shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 561 728 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (1, 2042040, 4) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python img = d2l.plt.imread('../img/catdog.jpg') h, w = img.shape[:2] print(h, w) X = torch.rand(size=(1, 3, h, w)) # Girdi verisi oluştur Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]) Y.shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 561 728 /home/d2l-worker/miniconda3/envs/d2l-tr-release-0/lib/python3.9/site-packages/torch/functional.py:478: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at ../aten/src/ATen/native/TensorShape.cpp:2895.) return _VF.meshgrid(tensors, **kwargs) # type: ignore[attr-defined] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output torch.Size([1, 2042040, 4]) .. raw:: html
.. raw:: html
Çapa kutusu değişkeninin şeklini değiştirdikten sonra ``Y`` olarak (imge yüksekliği, imge genişliği, aynı piksel üzerinde ortalanmış çapa kutularının sayısı, 4), belirtilen piksel konumuna ortalanmış tüm çapa kutularını elde edebiliriz. Aşağıda (250, 250) merkezli ilk çapa kutusuna erişiyoruz. Dört öğeye sahiptir: Sol üst köşedeki :math:`(x, y)` eksen koordinatları ve çapa kutusunun sağ alt köşesindeki :math:`(x, y)` eksen koordinatları. Her iki eksenin koordinat değerleri sırasıyla imgenin genişliği ve yüksekliğine bölünür; böylece değer aralığı 0 ile 1 arasındadır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python boxes = Y.reshape(h, w, 5, 4) boxes[250, 250, 0, :] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([0.06, 0.07, 0.63, 0.82]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python boxes = Y.reshape(h, w, 5, 4) boxes[250, 250, 0, :] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([0.06, 0.07, 0.63, 0.82]) .. raw:: html
.. raw:: html
İmgedeki tüm çapa kutularını bir pikselde ortalanmış olarak gösterirken, imge üzerinde birden çok kuşatan kutu çizmek için aşağıdaki ``show_bboxes`` işlevini tanımlarız. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def show_bboxes(axes, bboxes, labels=None, colors=None): """Çapa kutularını göster.""" 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)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def show_bboxes(axes, bboxes, labels=None, colors=None): """Çapa kutularını göster.""" 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)) .. raw:: html
.. raw:: html
Gördüğümüz gibi, ``boxes`` değişkenindeki :math:`x` ve :math:`y` eksenlerinin koordinat değerleri sırasıyla imgenin genişliği ve yüksekliğine bölünmüştür. Çapa kutularını çizerken, orijinal koordinat değerlerini geri yüklememiz gerekir; bu nedenle, aşağıda ``bbox_scale`` değişkeni tanımlarız. Şimdi, resimde (250, 250) merkezli tüm çapa kutularını çizebiliriz. Gördüğünüz gibi, 0.75 ölçekli ve 1 en boy oranına sahip mavi çapa kutusu, imgedeki köpeği iyi çevreler. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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']) .. figure:: output_anchor_f592d1_48_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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']) .. figure:: output_anchor_f592d1_51_0.svg .. raw:: html
.. raw:: html
Birleşim (IoU) üzerinde Kesişim ------------------------------- Sadece bir çapa kutusunun köpeği imgede “iyi” çevrelediğini belirttik. Nesnenin gerçek referans değeri kuşatan kutusu biliniyorsa, burada “iyi” nasıl ölçülebilir? Sezgisel olarak, çapa kutusu ile gerçek referans değeri kuşatan kutu arasındaki benzerliği ölçebiliriz. *Jaccard indeksi*\ nin iki küme arasındaki benzerliği ölçebileceğini biliyoruz. :math:`\mathcal{A}` ve :math:`\mathcal{B}` kümeleri verildiğinde, bunların Jaccard indeksi kesişimlerinin boyutunun birleşimlerinin boyutuna bölünmesidir: .. math:: J(\mathcal{A},\mathcal{B}) = \frac{\left|\mathcal{A} \cap \mathcal{B}\right|}{\left| \mathcal{A} \cup \mathcal{B}\right|}. Aslında, herhangi bir kuşatan kutunun piksel alanını bir piksel kümesi olarak düşünebiliriz. Bu şekilde, iki kuşatan kutunun benzerliğini piksel kümelerinin Jaccard indeksi ile ölçebiliriz. İki kuşatan kutu için, Jaccard indeksini genellikle :numref:`fig_iou` içinde gösterildiği gibi, kesişme alanlarının birleşme alanlarına oranı olan *bileşim üzerinden kesişme* (*IoU*) olarak adlandırırız. Bir IoU aralığı 0 ile 1 arasındadır: 0 iki kuşatan kutunun hiç örtüşmediği anlamına gelirken 1, iki kuşatan kutunun eşit olduğunu gösterir. .. _fig_iou: .. figure:: ../img/iou.svg IoU, kesişim alanının iki kuşatan kutunun birleşim alanına oranıdır. Bu bölümün geri kalanında, çapa kutuları ile gerçek referans değeri kuşatan kutular ve farklı çapa kutuları arasındaki benzerliği ölçmek için IoU kullanacağız. İki çapa veya kuşatan kutular listesi göz önüne alındığında, aşağıdaki ``box_iou``, bu iki listede çift yönlü IoU hesaplar. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def box_iou(boxes1, boxes2): """İki çapa veya kuşatan kutu listesinde ikili IoU hesaplayın.""" 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def box_iou(boxes1, boxes2): """İki çapa veya kuşatan kutu listesinde ikili IoU hesaplayın.""" 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 .. raw:: html
.. raw:: html
.. _subsec_labeling-anchor-boxes: Eğitim Verilerinde Çapa Kutuları Etiketleme ------------------------------------------- Bir eğitim veri kümesinde, her çapa kutusunu bir eğitim örneği olarak değerlendiririz. Bir nesne algılama modelini eğitmek için, her bir çapa kutusu için *sınıf* ve *uzaklık* etiketlerine ihtiyacımız var; burada birincisi, çapa kutusuyla ilgili nesnenin sınıfıdır ve ikincisi ise çapa kutusuna göre gerçek referans değeri kuşatan kutunun uzaklığıdır. Tahmin sırasında, her imge için birden çok çapa kutusu oluşturur, tüm bağlantı kutuları için sınıfları ve uzaklıkları tahmin eder, tahmini kuşatan kutuları elde etmek için konumlarını tahmin edilen uzaklıklara göre ayarlarız ve son olarak yalnızca belirli kriterleri karşılayan tahmini kuşatan kutuları çıktılarız. Bildiğimiz gibi, bir nesne algılama eğitim kümesi, *gerçek referans değeri kuşatan kutular* ve çevrelenmiş nesnelerin sınıfları için etiketlerle birlikte gelir. Oluşturulan herhangi bir *çapa kutusu* etiketlemek için, çapa kutusuna en yakın olan *atanmış* gerçek referans değeri kuşatan kutunun etiketlenmiş konumuna ve sınıfına başvururuz. Aşağıda, çapa kutularına en yakın gerçek referans değeri kuşatan kutuları atamak için bir algoritma tanımlıyoruz. Çapa Kutularına Gerçek Referans Değeri Kuşatan Kutuları Atama ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Bir imge göz önüne alındığında, çapa kutularının :math:`A_1, A_2, \ldots, A_{n_a}` ve gerçek referans değeri kuşatan kutuların :math:`B_1, B_2, \ldots, B_{n_b}` olduğunu varsayalım, burada :math:`n_a \geq n_b`. :math:`i.` satırında ve :math:`j.` sütununda :math:`x_{ij}` elemanı, :math:`A_i` çapa kutusunun ve :math:`B_j` gerçek referans değer kuşatan kutusunun IoU değeri olan bir :math:`\mathbf{X} \in \mathbb{R}^{n_a \times n_b}` matrisini tanımlayalım. Algoritma aşağıdaki adımlardan oluşur: 1. :math:`\mathbf{X}` matrisindeki en büyük elemanı bulun ve satır ve sütun indekslerini sırasıyla :math:`i_1` ve :math:`j_1` olarak belirtin. Daha sonra :math:`B_{j_1}` gerçek referans değeri kuşatan kutu :math:`A_{i_1}` çapa kutusuna atanır. Bu oldukça sezgiseldir, çünkü :math:`A_{i_1}` ve :math:`B_{j_1}`, tüm çapa kutuları ve gerçek referans değeri kuşatan kutular arasında en yakın olanlardır. İlk atamadan sonra, :math:`\mathbf{X}` matrisindeki :math:`{i_1}.` satırındaki ve :math:`{j_1}.` sütunundaki tüm öğeleri atın. 2. :math:`\mathbf{X}` matrisinde kalan elemanların en büyüğünü bulun ve satır ve sütun indekslerini sırasıyla :math:`i_2` ve :math:`j_2` olarak belirtin. :math:`B_{j_2}` gerçek referans değeri kuşatan kutusunu :math:`A_{i_2}` çapa kutusuna atarız ve :math:`\mathbf{X}` matrisindeki :math:`{i_2}.` satırındaki ve :math:`{j_2}.` sütunundaki tüm öğeleri atarız. 3. Bu noktada, :math:`\mathbf{X}` matrisindeki iki satır ve iki sütundaki öğeler atılmıştır. :math:`\mathbf{X}` matrisindeki :math:`n_b` sütunundaki tüm öğeler atılana kadar devam ederiz. Şu anda, :math:`n_b` çapa kutusunun her birine bir gerçek referans değeri kuşatan kutu atadık. 4. Sadece kalan :math:`n_a - n_b` tane çapa kutusundan geçiş yapın. Örneğin, herhangi bir :math:`A_i` çapa kutusu verildiğinde, :math:`i.` matrisinin :math:`\mathbf{X}` satırı boyunca :math:`A_i` ile en büyük IoU'ya sahip :math:`B_j` gerçek referans değeri kuşatan kutusunu bulun ve yalnızca bu IoU önceden tanımlanmış bir eşikten büyükse :math:`B_j` öğesini :math:`A_i` öğesine atayın. Yukarıdaki algoritmayı somut bir örnek kullanarak gösterelim. :numref:`fig_anchor_label` içinde(solda) gösterildiği gibi, :math:`\mathbf{X}` matrisindeki maksimum değerin :math:`x_{23}` olduğunu varsayarak :math:`x_{23}`, :math:`B_3` numaralı gerçek referans değeri kuşatan kutusunu :math:`A_2` numaralı çapa kutusuna atarız. Daha sonra, matrisin satır 2 ve sütun 3'teki tüm elemanlarını atırız, kalan elemanlarda (gölgeli alan) en büyük :math:`x_{71}`'i buluruz ve :math:`A_7` çapa kutusuna :math:`B_1` numaralı gerçek referans değeri kuşatan kutuyu atarız. Daha sonra, :numref:`fig_anchor_label` (ortada) içinde gösterildiği gibi, matrisin 7. satırı ve 1. sütunundaki tüm öğeleri atın, kalan elemanlardaki (gölgeli alan) en büyük :math:`x_{54}` öğesini bulun ve :math:`B_4` gerçek referans değeri kuşatan kutusunu :math:`A_5` çapa kutusuna atayın. Son olarak, :numref:`fig_anchor_label` içinde (sağda) gösterildiği gibi, matrisin 5. satırı ve 4. sütunundaki tüm elemanları atın, kalan elemanlardaki (gölgeli alan) en büyük :math:`x_{92}` değerini bulun ve :math:`B_2` gerçek referans değeri kuşatan kutusunu :math:`A_9` çapa kutusuna atayın. Bundan sonra, yalnızca geri kalan :math:`A_1, A_3, A_4, A_6, A_8` çapa kutularından geçmemiz ve eşiğe göre gerçek referans değeri kuşatan kutular atanıp atanmayacağına karar vermemiz gerekiyor. .. _fig_anchor_label: .. figure:: ../img/anchor-label.svg Çapa kutularına gerçek referans değeri kuşatan kutular atama. Bu algoritma aşağıdaki ``assign_anchor_to_bbox`` işlevinde uygulanır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): """En yakın gerçek referans değeri kuşatan kutuları çapa kutularına atayın.""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] # i. satır ve j. sütundaki x_ij öğesi, i çapa kutusunun ve j gerçek referans değeri kuşatan kutusunu IoU'sudur. jaccard = box_iou(anchors, ground_truth) # Her çapa için atanmış gerçek referans değeri kuşatan kutuyu tutmak için # tensörü ilklet anchors_bbox_map = np.full((num_anchors,), -1, dtype=np.int32, ctx=device) # Eşiğe göre gerçek referans değeri kuşatan kutuları atayın max_ious, indices = np.max(jaccard, axis=1), np.argmax(jaccard, axis=1) anc_i = np.nonzero(max_ious >= 0.5)[0] box_j = indices[max_ious >= 0.5] 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) # En büyük IoU'yu bul 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): """En yakın gerçek referans değeri kuşatan kutuları çapa kutularına atayın.""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] # i. satır ve j. sütundaki x_ij öğesi, i çapa kutusunun ve j gerçek referans değeri kuşatan kutusunu IoU'sudur. jaccard = box_iou(anchors, ground_truth) # Her çapa için atanmış gerçek referans değeri kuşatan kutuyu tutmak için # tensörü ilklet anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long, device=device) # Eşiğe göre gerçek referans değeri kuşatan kutuları atayın max_ious, indices = torch.max(jaccard, dim=1) anc_i = torch.nonzero(max_ious >= 0.5).reshape(-1) box_j = indices[max_ious >= 0.5] 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) # En büyük IoU'yu bul 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 .. raw:: html
.. raw:: html
Etiketleme Sınıfları ve Uzaklıklar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Artık her çapa kutusu için sınıfı ve uzaklığı etiketleyebiliriz. Bir :math:`A` çapa kutusunun bir :math:`B` gerçek referans değer kuşatan kutusuna atandığını varsayalım. Bir yandan, :math:`A` çapa kutusu sınıfı :math:`B`'ninkiyle aynı olarak etiketlenecektir. Öte yandan, :math:`A` çapa kutusunun uzaklığı, :math:`B` ve :math:`A` arasındaki merkezi koordinatlar arasındaki göreli konuma göre bu iki kutu arasındaki göreli boyutla birlikte etiketlenecektir. Veri kümesindeki farklı kutuların değişen konumları ve boyutları verildiğinde, sığdırılması daha kolay olan daha düzgün dağıtılmış ofsetlere yol açabilecek bu göreli konumlara ve boyutlara dönüşümler uygulayabiliriz. Burada genel bir dönüşümü tanımlıyoruz. :math:`A` ve :math:`B`'nin merkezi koordinatları :math:`(x_a, y_a)` ve :math:`(x_b, y_b)` olarak verildiğinde, sırasıyla genişlikleri :math:`w_a` ve :math:`w_b` ve yükseklikleri :math:`h_a` ve :math:`h_b` olarak verilir. :math:`A` ofsetini şu şekilde etiketleyebiliriz: .. math:: \left( \frac{ \frac{x_b - x_a}{w_a} - \mu_x }{\sigma_x}, \frac{ \frac{y_b - y_a}{h_a} - \mu_y }{\sigma_y}, \frac{ \log \frac{w_b}{w_a} - \mu_w }{\sigma_w}, \frac{ \log \frac{h_b}{h_a} - \mu_h }{\sigma_h}\right), burada sabitlerin varsayılan değerleri :math:`\mu_x = \mu_y = \mu_w = \mu_h = 0, \sigma_x=\sigma_y=0.1` ve :math:`\sigma_w=\sigma_h=0.2`'dır. Bu dönüştürme, aşağıda ``offset_boxes`` işlevinde uygulanmaktadır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def offset_boxes(anchors, assigned_bb, eps=1e-6): """Çapa kutu ofsetlerini dönüştür""" 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def offset_boxes(anchors, assigned_bb, eps=1e-6): """Çapa kutu ofsetlerini dönüştür""" 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 .. raw:: html
.. raw:: html
Bir çapa kutusuna bir gerçek referans değeri kuşatan kutu atanmamışsa, sadece çapa kutusunun sınıfını "arka plan" olarak etiketleriz. Sınıfları arka plan olan çapa kutuları genellikle *negatif* çapa kutuları olarak adlandırılır ve geri kalanı ise *pozitif* çapa kutuları olarak adlandırılır. Aşağıdaki ``multibox_target`` işlevini gerçek referans değeri kuşatan kutuları (``labels`` bağımsız değişkeni) kullanarak sınıfları ve çapa kutuları (``anchors`` bağımsız değişkeni) için uzaklıkları etiketlemek için uyguluyoruz. Bu işlev, arka plan sınıfını sıfıra ayarlar ve yeni bir sınıfın tamsayı dizinini bir artırır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_target(anchors, labels): """Gerçeği referans değeri kuşatan kutuları kullanarak çapa kutularını etiketleyin.""" 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') # Sınıf etiketlerini ve atanmış kuşatan kutu koordinatlarını sıfırlarla ilklet class_labels = np.zeros(num_anchors, dtype=np.int32, ctx=device) assigned_bb = np.zeros((num_anchors, 4), dtype=np.float32, ctx=device) # Çapa kutularının sınıflarını kendilerine atanan gerçek referans değeri # kuşatan kutularını kullanarak etiketleyin. Bir çapa kutusu # atanmamışsa, sınıfını arka plan olarak etiketliyoruz (değer sıfır # olarak kalıyor) 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:] # Ofset dönüşümü 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_target(anchors, labels): """Gerçeği referans değeri kuşatan kutuları kullanarak çapa kutularını etiketleyin.""" 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) # Sınıf etiketlerini ve atanmış kuşatan kutu koordinatlarını sıfırlarla ilklet class_labels = torch.zeros(num_anchors, dtype=torch.long, device=device) assigned_bb = torch.zeros((num_anchors, 4), dtype=torch.float32, device=device) # Çapa kutularının sınıflarını kendilerine atanan gerçek referans değeri # kuşatan kutularını kullanarak etiketleyin. Bir çapa kutusu # atanmamışsa, sınıfını arka plan olarak etiketliyoruz (değer sıfır # olarak kalıyor) 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:] # Ofset dönüşümü 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) .. raw:: html
.. raw:: html
Bir Örnek ~~~~~~~~~ Çapa kutusu etiketlemeyi somut bir örnekle gösterelim. İlk öğenin sınıf olduğu (köpek için 0 ve kedi için 1) ve kalan dört öğenin sol üst köşede ve sağ alt köşedeki :math:`(x, y)`-ekseni koordinatlarının (aralık 0 ile 1 arasındadır) olduğu, yüklenen imgedeki köpek ve kedi için gerçek referans değeri kuşatan kutular tanımlarız. Ayrıca sol üst köşenin ve sağ alt köşenin koordinatlarını kullanarak etiketlenecek beş çapa kutusu oluşturuyoruz: :math:`A_0, \ldots, A_4` (indeks 0'dan başlar). Sonra resimdeki bu gerçek referans değeri kuşatan kutuları ve çapa kutularını çiziyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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']); .. figure:: output_anchor_f592d1_93_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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']); .. figure:: output_anchor_f592d1_96_0.svg .. raw:: html
.. raw:: html
Yukarıda tanımlanan ``multibox_target`` işlevini kullanarak, köpek ve kedi için bu çapa kutularının sınıflarını ve ofsetlerini gerçek referans değeri kuşatan kutulara dayanarak etiketleyebiliriz. Bu örnekte, arka plan, köpek ve kedi sınıflarının indeksleri sırasıyla 0, 1 ve 2'dir. Aşağıda, çapa kutularının ve gerçek referans değeri kuşatan kutuların örnekleri için bir boyut ekliyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels = multibox_target(np.expand_dims(anchors, axis=0), np.expand_dims(ground_truth, axis=0)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels = multibox_target(anchors.unsqueeze(dim=0), ground_truth.unsqueeze(dim=0)) .. raw:: html
.. raw:: html
Döndürülen sonuçta, hepsi tensör formatında olan üç öğe vardır. Üçüncü öğe, girdi çapa kutularının etiketli sınıflarını içerir. Aşağıdaki döndürülen sınıf etiketlerini, çapa kutusu ve resimdeki gerçek referans değeri kuşatan kutu konumlarına göre analiz edelim. İlk olarak, tüm çapa kutuları ve gerçek referans değeri kuşatan kutu çiftleri arasında, :math:`A_4` bağlantı kutusunun ve kedinin gerçek referans değeri kuşatan kutusunun IoU'su en büyüğüdür. Böylece, :math:`A_4`'nın sınıfı kedi olarak etiketlenmiştir. :math:`A_4` veya kedinin gerçek referans değeri kuşatan kutusunu içeren çiftleri çıkarırken, geri kalanlar arasında :math:`A_1` çapa kutusunun ve köpeğin gerçek referans değeri kuşatan kutusunun çifti en büyük IoU'ya sahiptir. Büylece :math:`A_1` sınıfı köpek olarak etiketlenir. Ardından, kalan üç etiketlenmemiş çapa kutusundan geçmemiz gerekiyor: :math:`A_0`, :math:`A_2` ve :math:`A_3`. :math:`A_0` için, en büyük IoU'ya sahip gerçek referans değeri kuşatan kutunun sınıfı köpektir, ancak IoU önceden tanımlanmış eşiğin (0.5) altındadır, bu nedenle sınıf arka plan olarak etiketlenir; :math:`A_2` için, en büyük IoU'ya sahip gerçek referans değeri kuşatan kutunun sınıfı kedidir ve IoU eşiği aştığından sınıf kedi olarak etiketlenir; :math:`A_3` için, en büyük IoU'ya sahip gerçek referans değeri kuşatan kutunun sınıfı kedidir, ancak değer eşiğin altındadır, bu nedenle sınıf arka plan olarak etiketlenir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[2] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[0, 1, 2, 0, 2]], dtype=int32) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[2] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[0, 1, 2, 0, 2]]) .. raw:: html
.. raw:: html
Döndürülen ikinci öğe şeklin bir maske değişkenidir (toplu iş boyutu, çapa kutularının sayısının dört katı). Maske değişkenindeki her dört öğe, her bir çapa kutusunun dört ofset değerine karşılık gelir. Arka plan algılama umurumuzda olmadığından, bu negatif sınıfın ofsetleri amaç işlevini etkilememelidir. Eleman yönlü çarpımlar sayesinde, maske değişkenindeki sıfırlar, amaç işlevini hesaplamadan önce negatif sınıf ofsetlerini filtreler. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[1] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]], dtype=int32) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[1] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 1., 1., 1., 1.]]) .. raw:: html
.. raw:: html
İlk döndürülen öğe, her çapa kutusu için etiketlenmiş dört ofset değeri içerir. Negatif sınıf çapa kutularının ofsetlerinin sıfır olarak etiketlendiğini unutmayın. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[0] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 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]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python labels[0] .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output 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]]) .. raw:: html
.. raw:: html
.. _subsec_predicting-bounding-boxes-nms: Maksimum Olmayanı Bastırma ile Kuşatan Kutuları Tahmin Etme ----------------------------------------------------------- Tahmin esnasında, imge için birden çok çapa kutusu oluşturur ve her biri için sınıfları ve ofsetleri tahmin ederiz. Bir *tahmin edilen kuşatan kutu* böylece, tahmin edilen ofseti olan bir çapa kutusuna göre elde edilir. Aşağıda, girdiler olarak çapaları ve ofset tahminlerini alan ve tahmin edilen kuşatan kutu koordinatlarını döndürmek için ters ofset dönüşümleri uygulayan ``offset_inverse`` işlevini uyguluyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def offset_inverse(anchors, offset_preds): """Tahmin edilen uzaklıklara sahip çapa kutularına dayalı kuşatan kutuları tahmin edin.""" 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def offset_inverse(anchors, offset_preds): """Tahmin edilen uzaklıklara sahip çapa kutularına dayalı kuşatan kutuları tahmin edin.""" 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 .. raw:: html
.. raw:: html
Çok sayıda bağlantı kutusu olduğunda, aynı nesneyi çevreleyen birçok benzer (önemli ölçüde örtüşen) tahmin edilen kuşatan kutular potansiyel olarak çıktılanabilir. Çıktıyı basitleştirmek için, aynı nesneye ait benzer tahmin edilen kuşatan kutuları *maksimum olmayanı bastırma (non-maximum suppression)* (NMS) kullanarak birleştirebiliriz. Şimdi maksimum olmayanı bastırma nasıl çalışır anlayalım. Tahmin edilen bir kuşatan kutu :math:`B` için nesne algılama modeli her sınıf için tahmin edilen olasılığı hesaplar. :math:`p` ile tahmin edilen en büyük olasılığı ifade edersek, bu olasılığa karşılık gelen sınıf, :math:`B` için tahmin edilen sınıftır. Daha özel belirtirsek, :math:`p`'yi tahmini kuşatan kutu :math:`B`'nin *güveni* (skoru) olarak adlandırıyoruz. Aynı imgede, tahmin edilen tüm arka plan olmayan kuşatan kutular, bir :math:`L` listesi oluşturmak için azalan düzende güvene göre sıralanır. Ardından, aşağıdaki adımlarda sıralanmış listeyi :math:`L`'yi değiştiriyoruz.: 1. :math:`L`'ten en yüksek güvenle tahmin edilen kuşatan kutu :math:`B_1`'yi temel olarak seçin ve :math:`L`'den :math:`B_1` ile IoU'su önceden tanımlanmış :math:`\epsilon` eşiğini aşan temel dışı tahmin edilen tüm kuşatan kutuları kaldırın. Bu noktada, :math:`L` tahmin edilen kuşatan kutuyu en yüksek güvenle tutar, ancak buna çok benzer olan başkalarını atar. Özetle, *maksimum olmayan* güven puanı olanlar *bastırılır*. 2. Başka bir temel olarak :math:`L`'dan ikinci en yüksek güvenle tahmin edilen kuşatan kutu :math:`B_2`'yi seçin ve :math:`L`'dan :math:`B_2` ile IoU :math:`\epsilon`'u aşan temel olmayan tüm tahmini kuşatan kutuları kaldırın. 3. :math:`L`'deki tüm tahmini kuşatan kutuları temel olarak kullanılıncaya kadar yukarıdaki işlemi yineleyin. Şu anda, :math:`L`'deki tahmini kuşatan kutuların herhangi bir çiftinin IoU'su :math:`\epsilon` eşiğinin altındadır; bu nedenle, hiçbir çift birbiriyle çok benzer değildir. 4. :math:`L` listedeki tüm tahmini kuşatan kutuları çıktı olarak verin. Aşağıdaki ``nms`` işlevi, güven skorlarını azalan sırada sıralar ve indekslerini döndürür. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def nms(boxes, scores, iou_threshold): """Tahmin edilen kuşatan kutuların güven puanlarını sıralama.""" B = scores.argsort()[::-1] keep = [] # Tutulacak tahmini sınırlayıcı kutuların endeksleri 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def nms(boxes, scores, iou_threshold): """Tahmin edilen kuşatan kutuların güven puanlarını sıralama.""" B = torch.argsort(scores, dim=-1, descending=True) keep = [] # Tutulacak tahmini sınırlayıcı kutuların endeksleri 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) .. raw:: html
.. raw:: html
Aşağıdaki ``multibox_detection`` işlevi kuşatan kutuları tahmin ederken maksimum olmayanı bastırmayı uygulamak için tanımlıyoruz. Uygulamanın biraz karmaşık olduğunu görürseniz endişelenmeyin: Uygulamadan hemen sonra somut bir örnekle nasıl çalıştığını göstereceğiz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): """Maksimum olmayan bastırma kullanarak kuşatan kutuları tahmin edin.""" 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) # Tüm "tutmayan" dizinleri bulun ve sınıfı arka plana ayarlayın 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] # Burada `pos_threshold`, pozitif (arka plan dışı) tahminler için bir eşik değeridir 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python #@save def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): """Maksimum olmayan bastırma kullanarak kuşatan kutuları tahmin edin.""" 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) # Tüm "tutmayan" dizinleri bulun ve sınıfı arka plana ayarlayın 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] # Burada `pos_threshold`, pozitif (arka plan dışı) tahminler için bir eşik değeridir 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) .. raw:: html
.. raw:: html
Şimdi yukarıdaki uygulamaları dört çapa kutusuna sahip somut bir örneğe uygulayalım. Basitlik için, tahmin edilen ofsetlerin tümünün sıfır olduğunu varsayıyoruz. Bu, tahmin edilen kuşatan kutuların çapa kutuları olduğu anlamına gelir. Arka plan, köpek ve kedi arasındaki her sınıf için, tahmin edilen olasılığını da tanımlıyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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, # Tahmini arka plan olabilirliği [0.9, 0.8, 0.7, 0.1], # Tahmini köpek olabilirliği [0.1, 0.2, 0.3, 0.9]]) # Tahmini kedi olabilirliği .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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, # Tahmini arka plan olabilirliği [0.9, 0.8, 0.7, 0.1], # Tahmini köpek olabilirliği [0.1, 0.2, 0.3, 0.9]]) # Tahmini kedi olabilirliği .. raw:: html
.. raw:: html
Bu tahmini kuşatan kutuları resimdeki güvenleriyle çizebiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python fig = d2l.plt.imshow(img) show_bboxes(fig.axes, anchors * bbox_scale, ['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9']) .. figure:: output_anchor_f592d1_174_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python fig = d2l.plt.imshow(img) show_bboxes(fig.axes, anchors * bbox_scale, ['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9']) .. figure:: output_anchor_f592d1_177_0.svg .. raw:: html
.. raw:: html
Şimdi, eşiğin 0.5'e ayarlandığı maksimum olmayanı bastırmayı gerçekleştirmek için ``multibox_detection`` işlevini çağırabiliriz. Tensör girdideki örnekler için bir boyut eklediğimizi unutmayın. Döndürülen sonucun şeklinin (parti boyutu, çapa kutusu sayısı, 6) olduğunu görebiliriz. En iç boyuttaki altı eleman, aynı tahmini kuşatan kutunun çıktı bilgilerini verir. İlk eleman, 0'dan başlayan tahmini sınıf dizinidir (0 köpektir ve 1 kedidir). -1 değeri, arka planı veya maksimum olmayanı bastırmada kaldırmayı gösterir. İkinci eleman, tahmin edilen kuşatan kutunun güvenidir. Kalan dört öğe, tahmini kuşatan kutunun sırasıyla sol üst köşesinin ve sağ alt köşesinın :math:`(x, y)` eksen koordinatlarıdır (aralık 0 ile 1 arasındadır). .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: 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]]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python output = multibox_detection(cls_probs.unsqueeze(dim=0), offset_preds.unsqueeze(dim=0), anchors.unsqueeze(dim=0), nms_threshold=0.5) output .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: 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]]]) .. raw:: html
.. raw:: html
Sınıfı -1 olan bu tahmini kuşatan kutuları kaldırdıktan sonra maksimum olmayanı bastırma ile tutulan son tahmini kuşatan kutuyu çıktı yapabiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. figure:: output_anchor_f592d1_192_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. figure:: output_anchor_f592d1_195_0.svg .. raw:: html
.. raw:: html
Pratikte, maksimum olmayanı bastırmayı gerçekleştirmeden önce bile daha düşük güvenli tahmini kuşatan kutuları kaldırabilir, böylece bu algoritmadaki hesaplamayı azaltabiliriz. Aynı zamanda maksimum olmayanı bastırmanın çıktısını da sonradan işleyebiliriz, örneğin, nihai çıktıda yalnızca daha yüksek güvenli sonuçları tutarabiliriz. Özet ---- - İmgenin her pikselinde ortalanmış farklı şekillere sahip çapa kutuları oluşturuyoruz. - Jaccard indeksi olarak da bilinen birleşme (IoU) üzerindeki kesişme, iki kuşatan kutunun benzerliğini ölçer. Kesişme alanlarının birleşme alanlarına oranıdır. - Bir eğitim kümesinde, her bir çapa kutusu için iki tip etikete ihtiyacımız vardır. Biri, çapa kutusundaki ilgili nesnenin sınıfıdır ve diğeri ise çapa kutusuna göre gerçek referans değeri kuşatan kutunun ofsetidir. - Tahmin sırasında, benzer tahmini kuşatan kutuları kaldırmak için maksimum olmayanı bastırma (NMS) kullanabiliriz ve böylece çıktıyı basitleştiririz. Alıştırmalar ------------ 1. ``multibox_prior`` işlevinde ``sizes`` ve ``ratios`` değerlerini değiştirin. Oluşturulan çapa kutularındaki değişiklikler nelerdir? 2. 0.5 IoU ile iki kuşatan kutu oluşturun ve görselleştirin. Birbirleriyle nasıl örtüşürler? 3. :numref:`subsec_labeling-anchor-boxes` ve :numref:`subsec_predicting-bounding-boxes-nms` içindeki ``anchors`` değişkenini değiştirin. Sonuçlar nasıl değişir? 4. Maksimum olmayanı bastırma, tahmini kuşatan kutuları *kaldırarak* bastıran açgözlü bir algoritmadır. Bu kaldırılmış olanlardan bazılarının gerçekten yararlı olması mümkün mü? *Yumuşak* bastırma için bu algoritma nasıl değiştirilebilir? Soft-NMS :cite:`Bodla.Singh.Chellappa.ea.2017`'e başvurabilirsiniz. 5. El yapımı olmaktan ziyade, maksimum olmayanı bastırma öğrenilebilir mi? .. raw:: html
mxnetpytorch
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html