14.5. マルチスケール物体検出

14.4 章 では、入力画像の各画素を中心とする複数のアンカーボックスを生成した。
本質的には、これらのアンカーボックスは画像のさまざまな領域のサンプルを表している。
しかし、すべての画素に対して生成すると、計算すべきアンカーボックスが多すぎる場合がある。
\(561 \times 728\) の入力画像を考えてみよう。
各画素を中心として、形状の異なる5個のアンカーボックスを生成すると、200万個を超えるアンカーボックス(\(561 \times 728 \times 5\))に対して、画像上でラベル付けと予測を行う必要がある。

14.5.1. マルチスケールのアンカーボックス

画像上のアンカーボックスを減らすのは、それほど難しくないと気づくかもしれない。
たとえば、入力画像から画素を少数だけ一様にサンプリングし、その画素を中心とするアンカーボックスを生成すればよいのである。
さらに、異なるスケールで、異なるサイズのアンカーボックスを異なる個数だけ生成できる。
直感的には、小さな物体のほうが大きな物体よりも画像中に現れやすいである。
例として、\(2 \times 2\) の画像には、\(1 \times 1\)\(1 \times 2\)\(2 \times 2\) の物体がそれぞれ 4 通り、2 通り、1 通りの現れ方で存在しえる。
したがって、小さな物体を検出するために小さなアンカーボックスを使う場合は、より多くの領域をサンプリングでき、
一方で大きな物体に対しては、より少ない領域をサンプリングすればよいのである。
複数のスケールでアンカーボックスを生成する方法を示すために、画像を読み込みよう。
その高さと幅はそれぞれ 561 ピクセルと 728 ピクセルである。
%matplotlib inline
from d2l import torch as d2l
import torch

img = d2l.plt.imread('../img/catdog.jpg')
h, w = img.shape[:2]
h, w
(561, 728)
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import image, np, npx

npx.set_np()

img = image.imread('../img/catdog.jpg')
h, w = img.shape[:2]
h, w
[06:59:11] ../src/storage/storage.cc:196: Using Pooled (Naive) StorageManager for CPU
(561, 728)
display_anchors(fmap_w=4, fmap_h=4, s=[0.15])
display_anchors(fmap_w=4, fmap_h=4, s=[0.15])
7.2 章 で、畳み込み層の2次元配列出力を特徴マップと呼ぶことを思い出してほしい。
特徴マップの形状を定義することで、任意の画像上で一様にサンプリングされたアンカーボックスの中心を決定できる。
以下に display_anchors 関数を定義する。
特徴マップ (fmap) 上の各単位(画素)をアンカーボックスの中心としてアンカーボックス (anchors) を生成する。
アンカーボックス (anchors) の \((x, y)\) 軸の座標値は、特徴マップ (fmap) の幅と高さで割られているため、
これらの値は 0 から 1 の間にあり、特徴マップ上でのアンカーボックスの相対位置を表す。
アンカーボックス (anchors) の中心は特徴マップ (fmap) 上のすべての単位に広がっているので、
これらの中心は、相対的な空間位置の観点から、任意の入力画像上で一様に分布していなければならない。
より具体的には、特徴マップ fmap_wfmap_h の幅と高さがそれぞれ与えられたとき、
以下の関数は任意の入力画像上で fmap_hfmap_w 列の画素を一様にサンプリングする。
この一様にサンプリングされた画素を中心として、スケール s のアンカーボックス(s のリスト長が 1 であると仮定)と、異なるアスペクト比(ratios)を持つアンカーボックスが生成される。
def display_anchors(fmap_w, fmap_h, s):
    d2l.set_figsize()
    # Values on the first two dimensions do not affect the output
    fmap = d2l.zeros((1, 10, fmap_h, fmap_w))
    anchors = d2l.multibox_prior(fmap, sizes=s, ratios=[1, 2, 0.5])
    bbox_scale = d2l.tensor((w, h, w, h))
    d2l.show_bboxes(d2l.plt.imshow(img).axes,
                    anchors[0] * bbox_scale)
def display_anchors(fmap_w, fmap_h, s):
    d2l.set_figsize()
    # Values on the first two dimensions do not affect the output
    fmap = np.zeros((1, 10, fmap_h, fmap_w))
    anchors = npx.multibox_prior(fmap, sizes=s, ratios=[1, 2, 0.5])
    bbox_scale = np.array((w, h, w, h))
    d2l.show_bboxes(d2l.plt.imshow(img.asnumpy()).axes,
                    anchors[0] * bbox_scale)
display_anchors(fmap_w=2, fmap_h=2, s=[0.4])
display_anchors(fmap_w=2, fmap_h=2, s=[0.4])
まず、小さな物体の検出 を考えよう。
表示時に見分けやすくするため、ここでは中心の異なるアンカーボックス同士が重ならないようにしている。
アンカーボックスのスケールは 0.15 に設定し、特徴マップの高さと幅は 4 に設定する。
画像上の 4 行 4 列にあるアンカーボックスの中心が一様に分布していることがわかる。
display_anchors(fmap_w=4, fmap_h=4, s=[0.15])
../_images/output_multiscale-object-detection_f4262f_33_0.svg
display_anchors(fmap_w=4, fmap_h=4, s=[0.15])
../_images/output_multiscale-object-detection_f4262f_36_0.svg
display_anchors(fmap_w=1, fmap_h=1, s=[0.8])
display_anchors(fmap_w=1, fmap_h=1, s=[0.8])
次に、特徴マップの高さと幅を半分に減らし、より大きなアンカーボックスを使って大きな物体を検出する ことを考える。
スケールを 0.4 に設定すると、いくつかのアンカーボックスは互いに重なる。
display_anchors(fmap_w=2, fmap_h=2, s=[0.4])
../_images/output_multiscale-object-detection_f4262f_46_0.svg
最後に、特徴マップの高さと幅をさらに半分に減らし、アンカーボックスのスケールを 0.8 に増やす と、
アンカーボックスの中心は画像の中心になる。
display_anchors(fmap_w=1, fmap_h=1, s=[0.8])
../_images/output_multiscale-object-detection_f4262f_48_0.svg

14.5.2. マルチスケール検出

マルチスケールのアンカーボックスを生成したので、
それらを用いてさまざまなサイズの物体を異なるスケールで検出す。
以下では、CNN ベースのマルチスケール物体検出手法を紹介する。これは 14.7 章 で実装する。
あるスケールで、たとえば形状が \(h \times w\)\(c\) 個の特徴マップがあるとする。
14.5.1 章 の方法を用いると、\(hw\) 組のアンカーボックスを生成できる。
各組は同じ中心を持つ \(a\) 個のアンカーボックスからなる。
たとえば、 14.5.1 章 の実験の最初のスケールでは、
10 個(チャネル数)の \(4 \times 4\) 特徴マップが与えられたとき、
16 組のアンカーボックスを生成した。各組には同じ中心を持つ 3 個のアンカーボックスが含まれる。
次に、各アンカーボックスに対して、正解のバウンディングボックスに基づいてクラスとオフセットをラベル付けする。
現在のスケールでは、物体検出モデルは入力画像上の \(hw\) 組のアンカーボックスのクラスとオフセットを予測する必要があり、
異なる組は異なる中心を持つ。
ここでの \(c\) 個の特徴マップは、入力画像に基づく CNN の順伝播によって得られた中間出力だと仮定する。
各特徴マップには \(hw\) 個の異なる空間位置があるので、同じ空間位置は \(c\) 個の単位を持つと考えられる。
7.2 章 における受容野の定義によれば、
特徴マップの同じ空間位置にあるこれら \(c\) 個の単位は、入力画像上で同じ受容野を持つ。
つまり、それらは同じ受容野における入力画像情報を表している。
したがって、特徴マップの同じ空間位置にある \(c\) 個の単位を、
この空間位置を用いて生成された \(a\) 個のアンカーボックスのクラスとオフセットへ変換できる。
本質的には、ある受容野における入力画像の情報を用いて、
その受容野に近い入力画像上のアンカーボックスのクラスとオフセットを予測しているのである。
異なる層の特徴マップが、入力画像上で異なる大きさの受容野を持つ場合、それらは異なるサイズの物体の検出に利用できる。
たとえば、出力層に近い特徴マップの単位ほど広い受容野を持つようなニューラルネットワークを設計すれば、
入力画像からより大きな物体を検出できる。
要するに、深層ニューラルネットワークによる画像の層ごとの表現を複数のレベルで活用することで、
マルチスケール物体検出を実現できる。
これがどのように機能するかは、 14.7 章 の具体例で示す。

14.5.3. 要約

  • 複数のスケールで、異なるサイズの物体を検出するために、異なるサイズのアンカーボックスを生成できる。

  • 特徴マップの形状を定義することで、任意の画像上で一様にサンプリングされたアンカーボックスの中心を決定できる。

  • ある受容野における入力画像の情報を用いて、その受容野に近い入力画像上のアンカーボックスのクラスとオフセットを予測する。

  • 深層学習により、画像の層ごとの表現を複数のレベルで活用して、マルチスケール物体検出を行える。

14.5.4. 演習

  1. 8.1 章 での議論によれば、深層ニューラルネットワークは、画像に対して抽象度が高まる階層的な特徴を学習する。マルチスケール物体検出では、異なるスケールの特徴マップは異なる抽象度のレベルに対応するのだろうか。なぜそう言えるのか、あるいは言えないのか。

  2. 14.5.1 章 の実験の最初のスケール(fmap_w=4, fmap_h=4)で、重なりうる一様分布のアンカーボックスを生成しなさい。

  3. 形状が \(1 \times c \times h \times w\) の特徴マップ変数が与えられたとする。ここで、\(c\), \(h\), \(w\) はそれぞれ特徴マップのチャネル数、高さ、幅である。この変数をアンカーボックスのクラスとオフセットにどのように変換できるか。出力の形状はどうなるか。