.. _sec_ssd: Tek Atışta Çoklu Kutu Algılama ============================== :numref:`sec_bbox`—-:numref:`sec_object-detection-dataset` içinde kuşatan kutuları, çapa kutularını, çoklu ölçekli nesne algılamayı ve nesne algılama için veri kümesini kullanıma sunduk. Şimdi bir nesne algılama modeli tasarlamak için bu tür arkaplan bilgisini kullanmaya hazırız: Tek atışta çoklu kutu algılama (SSD) :cite:`Liu.Anguelov.Erhan.ea.2016`. Bu model basittir, hızlıdır ve yaygın olarak kullanılmaktadır. Bu, çok büyük miktardaki nesne algılama modellerinden sadece biri olmasına rağmen, bu bölümdeki tasarım ilkeleri ve uygulama detaylarından bazıları diğer modeller için de geçerlidir. Model ----- :numref:`fig_ssd`, tek atışta çoklu kutu algılama tasarımına genel bir bakış sağlar. Bu model esas olarak bir temel ağdan ve ardından birkaç çoklu ölçekli öznitelik haritası bloğundan oluşur. Temel ağ, girdi imgesinden öznitelikleri ayıklamak içindir, bu nedenle derin bir CNN kullanabilir. Örneğin, özgün tek atışta çoklu kutu algılama makalesi :cite:`Liu.Anguelov.Erhan.ea.2016` sınıflandırma katmanında önce budanmış bir VGG ağı benimser, her ne kadar ResNet de yaygın olarak kullanılır olsa da. Tasarımımız sayesinde, daha küçük nesneleri algılamak için daha fazla çapa kutusu üretmek için temel ağ çıktısının daha büyük öznitelik haritaları yapmasını sağlarız. Daha sonra, her çoklu ölçekli öznitelik harita bloğu önceki bloktan öznitelik haritalarının yüksekliğini ve genişliğini azaltır (örn. yarı yarıya) ve öznitelik haritalarının her biriminin girdi imgesindeki alıcı alanını artırmasını sağlar. :numref:`sec_multiscale-object-detection` içinde derin sinir ağları tarafından imgelerin katmansal gösterimleri yoluyla çoklu ölçekli nesne algılama tasarımını hatırlayın. :numref:`fig_ssd` içinde gösterilen üst kısmına daha yakın olan çoklu ölçekli öznitelik haritaları daha küçük olduğundan ancak daha büyük alıcı alanlara sahip olduklarından, daha az ama daha büyük nesneleri algılamak için uygundur. Özetle, temel ağı ve birkaç çoklu ölçekli öznitelik haritası bloğu aracılığıyla, tek atışta çoklu kutu algılama, farklı boyutlarda değişen sayıda çapa kutusu oluşturur ve bu çapa kutularının sınıflarını ve ofsetlerini (dolayısıyla kuşatan kutuları) tahmin ederek değişen boyuttaki nesneleri algılar; bu nedenle, bu bir çoklu ölçekli nesne algılama modelidir. .. _fig_ssd: .. figure:: ../img/ssd.svg Çoklu ölçekli bir nesne algılama modeli olarak, tek atışta çoklu kutu algılama temel olarak bir temel ağdan ve ardından birkaç çoklu ölçekli öznitelik haritası bloğundan oluşur. Aşağıda, :numref:`fig_ssd` içindeki farklı blokların uygulama ayrıntılarını açıklayacağız. Başlangıç olarak, sınıf ve kuşatan kutu tahmininin nasıl uygulanacağını tartışıyoruz. Sınıf Tahmin Katmanı ~~~~~~~~~~~~~~~~~~~~ Nesne sınıflarının sayısı :math:`q` olsun. O zaman çapa kutuları :math:`q+1` sınıfa sahiptir, burada sınıf 0 arkaplandır. Bazı ölçeklerde, öznitelik haritalarının yüksekliği ve genişliğinin sırasıyla :math:`h` ve :math:`w` olduğunu varsayalım. :math:`a` tane çapa kutusu, bu öznitelik haritalarının her mekansal konumu ile merkezi olarak oluşturulduğunda, toplam :math:`hwa` çapa kutusunun sınıflandırılması gerekir. Bu durum genellikle büyük olasılıkla ağır parametreleme maliyetleri nedeniyle tam bağlı katmanlarla sınıflandırmayı olanaksız kılar. :numref:`sec_nin` içinde sınıfları tahmin etmek için evrişimli katman kanallarını nasıl kullandığımızı hatırlayın. Tek atışta çoklu kutu algılama, model karmaşıklığını azaltmak için aynı tekniği kullanır. Özellikle, sınıf tahmini katmanı, öznitelik haritalarının genişliğini veya yüksekliğini değiştirmeden bir evrişimli katman kullanır. Bu şekilde, öznitelik haritalarının aynı uzamsal boyutlarında (genişlik ve yükseklik) çıktılar ve girdiler arasında birebir karşılık olabilir. Daha somut olarak, çıktı öznitelik haritalarının kanallarının herhangi bir (:math:`x`, :math:`y`) uzamsal konumu, girdi öznitelik haritalarının (:math:`x`, :math:`y`) merkezli tüm çapa kutuları için sınıf tahminlerini temsil eder. Geçerli tahminler üretmek için, :math:`a(q+1)` çıktı kanalı olmalıdır; burada aynı uzamsal konum için :math:`i(q+1) + j` dizinli çıktı kanalı, :math:`i` (:math:`0 \leq i < a`) çapa kutusu için :math:`j` (:math:`0 \leq j \leq q`) sınıfının tahminini temsil eder. Aşağıda, sırasıyla ``num_anchors`` ve ``num_classes`` argümanları vasıtasıyla :math:`a`'yı ve :math:`q`'yu belirten böyle bir sınıf tahmini katmanı tanımlıyoruz. Bu katman, 1 dolgulu bir :math:`3\times3`'lük evrişimli tabaka kullanır. Bu evrişimli katmanın girdisinin ve çıktısının genişliği ve yüksekliği değişmeden kalır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %matplotlib inline from d2l import mxnet as d2l from mxnet import autograd, gluon, image, init, np, npx from mxnet.gluon import nn npx.set_np() def cls_predictor(num_anchors, num_classes): return nn.Conv2D(num_anchors * (num_classes + 1), kernel_size=3, padding=1) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python %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) .. raw:: html
.. raw:: html
Kuşatan Kutu Tahmin Katmanı ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Kuşatan kutu tahmin katmanının tasarımı, sınıf tahmini katmanınkine benzer. Tek fark, her bir çapa kutusu için çıktı sayısında yatmaktadır: Burada :math:`q+1` sınıfından ziyade dört ofset tahmin etmeliyiz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def bbox_predictor(num_anchors): return nn.Conv2D(num_anchors * 4, kernel_size=3, padding=1) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def bbox_predictor(num_inputs, num_anchors): return nn.Conv2d(num_inputs, num_anchors * 4, kernel_size=3, padding=1) .. raw:: html
.. raw:: html
Çoklu Ölçekler için Tahminleri Bitiştirme ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Belirttiğimiz gibi, tek atışta çok kutulu algılama, çapa kutuları oluşturmak ve sınıflarını ve uzaklıklarını tahmin etmek için çoklu ölçekli öznitelik haritaları kullanır. Farklı ölçeklerde, öznitelik haritalarının şekilleri veya aynı birimde ortalanmış çapa kutularının sayısı değişebilir. Bu nedenle, farklı ölçeklerde tahmin çıktılarının şekilleri değişebilir. Aşağıdaki örnekte, aynı minigrup için ``Y1`` ve ``Y2`` olmak üzere iki farklı ölçekte öznitelik haritası oluşturuyoruz; burada ``Y2``'nin yüksekliği ve genişliği ``Y1``'inkinin yarısı kadardır. Örnek olarak sınıf tahminini ele alalım. Her birim için sırasıyla ``Y1`` ve ``Y2``'teki 5 ve 3 çapa kutusunun oluşturulduğunu varsayalım. Nesne sınıflarının sayısının 10 olduğunu varsayalım. Öznitelik haritaları ``Y1`` ve ``Y2`` için sınıf tahmini çıktılarındaki kanal sayısı sırasıyla :math:`5\times(10+1)=55` ve :math:`3\times(10+1)=33`'tür, burada (toplu iş boyutu, kanal sayısı, yükseklik, genişlik) her iki çıktının şeklidir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output ((2, 55, 20, 20), (2, 33, 10, 10)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (torch.Size([2, 55, 20, 20]), torch.Size([2, 33, 10, 10])) .. raw:: html
.. raw:: html
Gördüğümüz gibi, toplu iş boyutu hariç, diğer üç boyutun hepsinin farklı boyutları var. Daha verimli hesaplama için bu iki tahmin çıktısını bitiştirmek için bu tensörleri daha tutarlı bir formata dönüştüreceğiz. Kanal boyutunun aynı merkeze sahip çapa kutuları için tahminleri tuttuğunu unutmayın. İlk önce bu boyutu en içteki boyuta taşıyacağız. Toplu iş boyutu farklı ölçekler için aynı kaldığından, tahmin çıktısını (toplu iş boyutu, yükseklik :math:`\times` genişlik :math:`\times` kanal sayısı) şekline sahip iki boyutlu bir tensöre dönüştürebiliriz. O zaman bu tür çıktıları 1. boyut boyunca farklı ölçeklerde birleştiririz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
Bu şekilde, ``Y1`` ve ``Y2`` kanal, yükseklik ve genişliklerde farklı boyutlara sahip olmasına rağmen, bu iki tahmin çıktısını aynı minigrup için iki farklı ölçekte birleştirebiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python concat_preds([Y1, Y2]).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (2, 25300) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python concat_preds([Y1, Y2]).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output torch.Size([2, 25300]) .. raw:: html
.. raw:: html
Örnek Seyreltme Bloğu ~~~~~~~~~~~~~~~~~~~~~ Nesneleri çoklu ölçekte algılamak için girdi öznitelik haritalarının yüksekliğini ve genişliğini yarıya indiren aşağıdaki örnek seyreltme bloğu ``down_sample_blk``'i tanımlarız. Aslında, bu blok :numref:`subsec_vgg-blocks` içindeki VGG bloklarının tasarımını uygular. Daha somut olarak, her bir örnek seyreltme bloğu, 1 dolgulu iki :math:`3\times3` evrişimli katmandan ve ardından 2 uzun adımlı bir :math:`2\times2` maksimum ortaklama katmanından oluşur. Bildiğimiz gibi, 1 dolgulu :math:`3\times3` evrişimli katmanlar, öznitelik haritalarının şeklini değiştirmez. Ancak, sonraki :math:`2\times2` maksimum ortaklama, girdi öznitelik haritalarının yüksekliğini ve genişliğini yarı yarıya azaltır. Bu örnek seyreltme bloğunun hem girdi hem de çıktı öznitelik haritaları için :math:`1\times 2+(3-1)+(3-1)=6` olduğundan, çıktıdaki her birim girdi üzerinde :math:`6\times6` alıcı alanına sahiptir. Bu nedenle, aşağı örnek seyreltme bloğu, çıktı öznitelik haritalarında her birim alıcı alanını genişletir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
Aşağıdaki örnekte, inşa edilmiş örnek seyreltme bloğumuz girdi kanallarının sayısını değiştirir ve girdi öznitelik haritalarının yüksekliğini ve genişliğini yarıya indirir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python forward(np.zeros((2, 3, 20, 20)), down_sample_blk(10)).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (2, 10, 10, 10) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python forward(torch.zeros((2, 3, 20, 20)), down_sample_blk(3, 10)).shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output torch.Size([2, 10, 10, 10]) .. raw:: html
.. raw:: html
Temel Ağ Bloğu ~~~~~~~~~~~~~~ Temel ağ bloğu, girdi imgelerinden öznitelikleri ayıklamak için kullanılır. Basitlik açısından, her bloktaki kanal sayısını iki katına çıkaran üç örnek seyreltme bloğundan oluşan küçük bir temel ağ oluşturuyoruz. :math:`256\times256`'lik girdi imgesi göz önüne alındığında, bu temel ağ bloğu :math:`32 \times 32` tane (:math:`256/2^3=32`)'lik öznitelik haritası çıkarır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (2, 64, 32, 32) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output torch.Size([2, 64, 32, 32]) .. raw:: html
.. raw:: html
Tam Model ~~~~~~~~~ Tek atışta çoklu kutu algılama modeli beş bloktan oluşur. Her blok tarafından üretilen öznitelik haritaları, (i) çapa kutuları oluşturmak ve (ii) bu çapa kutularının sınıflarını ve ofsetlerini tahmin etmek için kullanılır. Bu beş blok arasında, ilki temel ağ bloğudur, ikincisinden dördüncüsüne kadarkiler örnek seyreltme bloklarıdır ve son blok hem yüksekliği hem de genişliği 1'e düşürmek için küresel maksimum ortaklama kullanır. Teknik olarak, ikincisinden beşinciye bütün bloklar, :numref:`fig_ssd` içindeki çoklu ölçekli öznitelik harita bloklarıdır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def get_blk(i): if i == 0: blk = base_net() elif i == 4: blk = nn.GlobalMaxPool2D() else: blk = down_sample_blk(128) return blk .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
Şimdi her blok için ileri yaymayı tanımlıyoruz. İmge sınıflandırma görevlerinden farklı olarak, buradaki çıktılar arasında (i) CNN öznitelik haritaları ``Y``, (ii) geçerli ölçekte ``Y`` kullanılarak oluşturulan çapa kutuları ve (iii) (``Y``'ye dayalı) bu çapa kutuları için tahmin edilen sınıfları ve uzaklıkları (ofsetleri) içerir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
:numref:`fig_ssd` içinde, tepeye daha yakın olan çoklu ölçekli bir öznitelik harita bloğunun daha büyük nesneleri algılamak için olduğunu hatırlayın; bu nedenle, daha büyük çapa kutuları oluşturması gerekir. Yukarıdaki ileri yaymada, her çoklu ölçekli öznitelik harita bloğunda, çağrılan ``multibox_prior`` işlevinin ``sizes`` argümanı (:numref:`sec_anchor` içinde açıklanmıştır) vasıtasıyla iki ölçekli değerlerden oluşan bir liste geçeririz. Aşağıda, 0.2 ile 1.05 arasındaki aralık, beş bloktaki daha küçük ölçek değerlerini belirlemek için eşit olarak beş bölüme ayrılmıştır: 0.2, 0.37, 0.54, 0.71 ve 0.88. Daha sonra daha büyük ölçek değerleri :math:`\sqrt{0.2 \times 0.37} = 0.272`, :math:`\sqrt{0.37 \times 0.54} = 0.447` vb. şeklinde verilir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
Şimdi tüm modeli, ``TinySSD``, aşağıdaki gibi tanımlayabiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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): # Eşdeğer atama `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): # Burada `getattr(self, 'blk_%d' % i)` `self.blk_i`'ye ulaşır 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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): # Eşdeğer atama `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): # Burada `getattr(self, 'blk_%d' % i)` `self.blk_i`'ye ulaşır 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 .. raw:: html
.. raw:: html
:math:`256 \times 256`'lik imgelerden oluşan ``X`` minigrubundan bir model örneği oluşturup ileri yaymayı gerçekleştirmek için kullanıyoruz. Bu bölümde daha önce gösterildiği gibi, ilk blok :math:`32 \times 32` öznitelik haritalarını çıkarır. İkinci ila dördüncü örnek seyreltme bloklarının yükseklik ve genişliği yarıya indirdiğini ve beşinci bloğun genel ortaklama kullandığını hatırlayın. Özellik haritalarının uzamsal boyutları boyunca her birim için 4 çapa kutusu oluşturulduğundan, beş ölçekli her imge için toplam :math:`(32^2 + 16^2 + 8^2 + 4^2 + 1)\times 4 = 5444` çapa kutusu oluşturulur. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output output anchors: (1, 5444, 4) output class preds: (32, 5444, 2) output bbox preds: (32, 21776) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output output anchors: torch.Size([1, 5444, 4]) output class preds: torch.Size([32, 5444, 2]) output bbox preds: torch.Size([32, 21776]) /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:: html
.. raw:: html
Eğitim ------ Şimdi tek atışta çoklu kutu algılama modelini nesne algılama için nasıl eğiteceğimizi açıklayacağız. Veri Kümesini Okuma ve Modeli İlkleme ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Başlangıç olarak, :numref:`sec_object-detection-dataset` içinde açıklanan muz algılama veri kümesini okuyalım. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python batch_size = 32 train_iter, _ = d2l.load_data_bananas(batch_size) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output read 1000 training examples read 100 validation examples .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python batch_size = 32 train_iter, _ = d2l.load_data_bananas(batch_size) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output read 1000 training examples read 100 validation examples .. raw:: html
.. raw:: html
Muz algılama veri kümesindeki tek bir sınıf var. Modeli tanımladıktan sonra parametrelerini ilklememiz ve optimizasyon algoritmasını tanımlamamız gerekir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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}) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python device, net = d2l.try_gpu(), TinySSD(num_classes=1) trainer = torch.optim.SGD(net.parameters(), lr=0.2, weight_decay=5e-4) .. raw:: html
.. raw:: html
Kayıp ve Değerlendirme Fonksiyonlarını Tanımlama ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nesne algılaması iki tür kayba sahiptir. İlk kayıp, çapa kutularının sınıflarıyla ilgilidir: Hesaplama, imge sınıflandırması için kullandığımız çapraz entropi kayıp işlevini yeniden kullanabilir. İkinci kayıp pozitif (arkaplan olmayan) çapa kutularının ofsetlerini ile ilgilenir: Bu bir bağlanım problemidir. Bununla birlikte, bu bağlanım sorunu için, burada :numref:`subsec_normal_distribution_and_squared_loss` içinde açıklanan kare kaybı kullanmıyoruz. Bunun yerine, :math:`L_1` norm kaybını, tahmin ve gerçek referans değer arasındaki farkın mutlak değerini kullanıyoruz. Maske değişkeni ``bbox_masks``, kayıp hesaplamasında negatif çapa kutularını ve geçersiz (dolgulu) çapa kutularını filtreler. Sonunda, model için yitim fonksiyonunu elde etmek için çapa kutusu sınıf kaybını ve çapa kutusu ofset kaybını toplarız. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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 .. raw:: html
.. raw:: html
Sınıflandırma sonuçlarını değerlendirmek için doğruluğu kullanabiliriz. Ofsetler için kullanılan :math:`L_1` norm kaybı nedeniyle, tahmini kuşatan kutuları değerlendirmek için *ortalama mutlak hata* kullanıyoruz. Bu tahmin sonuçları, üretilen çapa kutularından ve bunlar için tahmin edilen ofsetlerden elde edilir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def cls_eval(cls_preds, cls_labels): # Sınıf tahmini sonuçları son boyutta olduğundan, # `argmax`'ın bu boyutu belirtmesi gerekiyor 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()) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def cls_eval(cls_preds, cls_labels): # Sınıf tahmini sonuçları son boyutta olduğundan, # `argmax`'ın bu boyutu belirtmesi gerekiyor 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()) .. raw:: html
.. raw:: html
Modeli Eğitme ~~~~~~~~~~~~~ Modelin eğitimini yaparken, çoklu ölçekli çapa kutuları (``anchors``) oluşturmamız ve ileri yaymada sınıflarını (``cls_preds``) ve ofsetleri (``bbox_preds``) tahmin etmemiz gerekir. Daha sonra ``Y`` etiket bilgisine dayanarak bu tür üretilen çapa kutularının sınıflarını (``cls_labels``) ve ofsetlerini (``bbox_labels``) etiketleriz. Son olarak, sınıfların ve ofsetlerin tahmini ve etiketlenmiş değerlerini kullanarak kayıp işlevini hesaplıyoruz. Özlü uygulamalar için, test veri kümesinin değerlendirilmesini burada atlıyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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): # Eğitim doğruluğu toplamı, eğitim doğruluğu toplamında örnek sayısı, # Mutlak hata toplamı, mutlak hata toplamında örnek sayısı 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(): # Çok ölçekli bağlantı kutuları oluşturun ve sınıflarını # ve ofsetlerini tahmin edin anchors, cls_preds, bbox_preds = net(X) # Bu çapa kutularının sınıflarını ve uzaklıklarını etiketleyin bbox_labels, bbox_masks, cls_labels = d2l.multibox_target(anchors, Y) # Sınıfların ve ofsetlerin tahmin edilen ve etiketlenen # değerlerini kullanarak kayıp fonksiyonunu hesaplayın 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)}') .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output class err 3.47e-03, bbox mae 3.81e-03 2719.7 examples/sec on gpu(0) .. figure:: output_ssd_739e1b_156_1.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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): # Eğitim doğruluğu toplamı, eğitim doğruluğu toplamında örnek sayısı, # Mutlak hata toplamı, mutlak hata toplamında örnek sayısı 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) # Çok ölçekli bağlantı kutuları oluşturun ve sınıflarını # ve ofsetlerini tahmin edin anchors, cls_preds, bbox_preds = net(X) # Bu çapa kutularının sınıflarını ve uzaklıklarını etiketleyin bbox_labels, bbox_masks, cls_labels = d2l.multibox_target(anchors, Y) # Sınıfların ve ofsetlerin tahmin edilen ve etiketlenen # değerlerini kullanarak kayıp fonksiyonunu hesaplayın 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)}') .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output class err 3.37e-03, bbox mae 3.17e-03 5908.7 examples/sec on cuda:0 .. figure:: output_ssd_739e1b_159_1.svg .. raw:: html
.. raw:: html
Tahminleme ---------- Tahmin sırasında amaç, imge üzerindeki tüm nesneleri tespit etmektir. Aşağıda, bir test imgesini okuruz ve yeniden boyutlandırırız, onu evrişimli katmanların gerektirdiği dört boyutlu bir tensöre dönüştürürüz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = torchvision.io.read_image('../img/banana.jpg').unsqueeze(0).float() img = X.squeeze(0).permute(1, 2, 0).long() .. raw:: html
.. raw:: html
Aşağıdaki ``multibox_detection`` işlevini kullanarak, tahmini kuşatan kutuları çapa kutularından ve tahmin edilen ofsetlerden elde edilir. Daha sonra, benzer tahmini kuşatan kutuları kaldırmak için maksimum olmayanı bastırma kullanılır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output [22:26:30] 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) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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) .. raw:: html
.. raw:: html
Son olarak, çıktı olarak 0.9 veya üzeri güvene sahip tüm tahmini kuşatan kutuları gösteriyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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[0: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) .. figure:: output_ssd_739e1b_183_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python 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[0: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) .. figure:: output_ssd_739e1b_186_0.svg .. raw:: html
.. raw:: html
Özet ---- - Tek atışta çoklu kutu algılama, çoklu ölçekli bir nesne algılama modelidir. Temel ağı ve birkaç çoklu ölçekli öznitelik harita bloğu aracılığıyla, tek atışta çoklu kutulu algılama, farklı boyutlarda değişen sayıda çapa kutusu oluşturur ve bu çapa kutularının sınıflarını ve ofsetlerini (dolayısıyla kuşatan kutuları) tahmin ederek değişen boyuttaki nesneleri algılar. - Tek atışta çoklu kutu algılama modelini eğitirken, kayıp işlevi, çapa kutusu sınıflarının ve ofsetlerinin tahmin edilen ve etiketlenmiş değerlerine göre hesaplanır. Alıştırmalar ------------ 1. Kayıp işlevini geliştirerek tek atışta çoklu kutu algılamasını geliştirebilir misiniz? Örneğin, tahmini ofsetler için pürüzsüz :math:`L_1` norm kaybı ile :math:`L_1` norm kaybını değiştirin. Bu kayıp fonksiyonu, hiper parametre :math:`\sigma` tarafından kontrol edilen pürüzsüzlük için sıfır çevresinde bir kare işlevi kullanır: .. math:: f(x) = \begin{cases} (\sigma x)^2/2,& \text{if }|x| < 1/\sigma^2\\ |x|-0.5/\sigma^2,& \text{otherwise} \end{cases} :math:`\sigma` çok büyük olduğunda, bu kayıp :math:`L_1` norm kaybına benzer. Değeri daha küçük olduğunda, kayıp fonksiyonu daha pürüzsüzdür. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python sigmas = [10, 1, 0.5] lines = ['-', '--', '-.'] x = np.arange(-2, 2, 0.1) d2l.set_figsize() for l, s in zip(lines, sigmas): y = npx.smooth_l1(x, scalar=s) d2l.plt.plot(x.asnumpy(), y.asnumpy(), l, label='sigma=%.1f' % s) d2l.plt.legend(); .. figure:: output_ssd_739e1b_192_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def smooth_l1(data, scalar): out = [] for i in data: if abs(i) < 1 / (scalar ** 2): out.append(((scalar * i) ** 2) / 2) else: out.append(abs(i) - 0.5 / (scalar ** 2)) return torch.tensor(out) sigmas = [10, 1, 0.5] lines = ['-', '--', '-.'] x = torch.arange(-2, 2, 0.1) d2l.set_figsize() for l, s in zip(lines, sigmas): y = smooth_l1(x, scalar=s) d2l.plt.plot(x, y, l, label='sigma=%.1f' % s) d2l.plt.legend(); .. figure:: output_ssd_739e1b_195_0.svg .. raw:: html
.. raw:: html
Ayrıca, deneyde sınıf tahmini için çapraz entropi kaybı kullandık: :math:`p_j` ile :math:`p_j` ile gerçek referans değer sınıfı :math:`j` için tahmin edilen olasılığı ifade edersek, çapraz entropi kaybı :math:`-\log p_j` olur. Ayrıca odak kaybı :cite:`Lin.Goyal.Girshick.ea.2017` kullanabilirsiniz: :math:`\gamma > 0` ve :math:`\alpha > 0` hiper parametre ile, bu kayıp şu şekilde tanımlanır: .. math:: - \alpha (1-p_j)^{\gamma} \log p_j. Gördüğümüz gibi, :math:`\gamma`'i arttırmak, iyi sınıflandırılmış örnekler için göreli kaybı etkili bir şekilde azaltabilir (örneğin, :math:`p_j > 0.5`), böylece eğitim yanlış sınıflandırılmış bu zor örneklere daha fazla odaklanabilir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def focal_loss(gamma, x): return -(1 - x) ** gamma * np.log(x) x = np.arange(0.01, 1, 0.01) for l, gamma in zip(lines, [0, 1, 5]): y = d2l.plt.plot(x.asnumpy(), focal_loss(gamma, x).asnumpy(), l, label='gamma=%.1f' % gamma) d2l.plt.legend(); .. figure:: output_ssd_739e1b_201_0.svg .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def focal_loss(gamma, x): return -(1 - x) ** gamma * torch.log(x) x = torch.arange(0.01, 1, 0.01) for l, gamma in zip(lines, [0, 1, 5]): y = d2l.plt.plot(x, focal_loss(gamma, x), l, label='gamma=%.1f' % gamma) d2l.plt.legend(); .. figure:: output_ssd_739e1b_204_0.svg .. raw:: html
.. raw:: html
2. Alan kısıtları nedeniyle, bu bölümdeki tek atışta çoklu kutu algılama modelinin bazı uygulama ayrıntılarını atladık. Modeli aşağıdaki yönlerden daha da geliştirebilir misiniz? 1. Nesne imgeye kıyasla çok daha küçük olduğunda, model girdi imgesini daha büyük boyutlandırabilir. 2. Genellikle çok sayıda negatif çapa kutusu vardır. Sınıf dağılımını daha dengeli hale getirmek için negatif çapa kutularını azaltabiliriz. 3. Kayıp işlevinde, sınıf kaybına ve ofset kaybına farklı ağırlık hiper parametreleri atayın. 4. Nesne algılama modelini değerlendirmek için, tek atışta çoklu kutu algılama çalışması :cite:`Liu.Anguelov.Erhan.ea.2016` gibi diğer yöntemleri kullanın. .. raw:: html
mxnetpytorch
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html