物体検出手法 CornerNet の紹介と実験

こんにちは。先進技術部でアルバイトをしている河田です。
今回は、Law & Dangにより2018年8月に提案された物体検出手法CornerNet[1]について紹介します。

はじめに

CornerNetは
  • 主流の物体検出モデルのほとんどで用いられている Anchor Box を排除
  • Corner Poolingという新しいPooling手法を導入し、Bounding Boxの左上・右下のコーナーを検出
という点で、多くの物体検出手法の中で異彩を放っていました(発表当時)。

本記事では、まずCornerNetの仕組みについて説明したのち、弊社で再現実装を行い独自データセットで訓練した結果を紹介します。

物体検出の基礎知識

物体検出とは

CornerNetの具体的な解説にうつる前に、まず物体検出の基礎的な事柄について説明します。
物体検出とは、画像認識タスクの一つで、「画像のどの位置に」「何が写っているのか(クラス)」を検出するものです。一つの画像に複数の物体が写っていた場合、それぞれについて位置とクラスを検出しなければなりません。
物体検出タスクでは、物体を囲う矩形の位置と大きさを推定するのが一般的で、この矩形を Bounding Box (BBox)と呼びます。
Faster R-CNN以降の多くの物体検出手法では、BBoxの中心座標\left( x, \ y\right)と大きさ\left( w, \ h\right)を、ニューラルネットワークを用いて推定します。この回帰問題を直接解くのはいくつかの点で困難なため、Faster R-CNN では\left( x, \ y, \ w, \ h\right)を直接出力させるのではなく、Anchor Boxと呼ばれる矩形のテンプレートを変形させることで予測BBoxを作り出すという方法が取られました。Anchor Boxを用いたBBoxの推定はFaster R-CNN以降の物体検出手法にも受け継がれ、物体検出における事実上のスタンダードとなっています。

Anchor Box について

Anchor Boxとは、図1に示すように、異なる大きさ・縦横比を持った矩形のテンプレートです。多くの物体検出手法では、直接BBoxを推定する代わりに、これらのAnchor Boxから最も形の合うものを一つ選び、それを変形して予測BBoxとします。
物体と形状の近いAnchor Boxが存在する場合、そのAnchor Boxに小さな変形を施すだけで予測BBoxを作成できるため、問題が簡単になり、訓練の安定性や検出精度の向上が期待できます。
その一方で、適当なAnchor Boxが存在しない場合は対象の物体を検出しそこねる可能性があるため、Anchor Boxの設計は注意深く行われる必要があります。
図1 Anchor Box

One-stage detectorとTwo-stage detectorについて

ニューラルネットワークを用いた物体検出手法は、検出を1段階で行うか2段階で行うかによって、それぞれOne-stage detectorとTwo-stage detectorに分類されます。
Two-stage detectorでは、まず1段階目でBBoxの候補を抽出します。そして2段階目でクラス予測とBBoxの修正を行い、最終的な予測結果を生成します。Two-stage detectorの代表はFaster R-CNNです。
一方のOne-stage detectorはBBox生成とクラス予測を一度に行います。そのため一般にOne-stage detectorは構成がシンプルになり、学習や推論の高速化が見込めます。そのぶん検出精度はTwo-stage detectorに劣る傾向があります。YOLOやSSD、今回紹介するCornerNetはOne-stage detectorに分類されます。

CornerNet について

CornerNetでは、BBoxの中心座標と大きさではなく、左上と右下の頂点(corner)の座標\left( x, \ y\right)をそれぞれ推定します。その後、同一物体の左上と右下の頂点を対応付けることで、物体を検出します(図2)。クラス予測は、頂点の検出と同時に行います。
頂点の検出では、画像上の各ピクセルの頂点らしさを確率マップとして出力する方法をとっており、そのためAnchor Boxが不要となっています。

検出の流れをもう少し具体的に見ていきましょう。まず画像をHourglass Networkに入力して、特徴抽出を行います。得られた特徴量を、左上・右下頂点それぞれを検出するネットワークに流し込み、それらの出力をCorner Poolingレイヤーを通した後、それぞれでHeatmap、Embedding、Offsetの3つ出力させます。最後にこれらの出力を合わせて検出結果とします。
図2, 3 CornerNet概要図
以降では、それぞれのモジュールについて順に説明していきます。

Hourglass Network

先程述べたように、Hourglass NetworkはCornerNetで用いられている特徴抽出用ネットワークです。砂時計をいくつも繋げたような形になっていることからHourglass(砂時計)という名前がつけられているようです。Hourglass Networkはもともと姿勢推定[2]で提案されたアーキテクチャで、特徴マップのサイズを小さくしたり大きくしたりを繰り返すことで異なるスケールの情報を統合しようという意図で設計されています。
Hourglass NetworkはResNetと同様、Residual(残差)構造を採用しており、特徴マップの拡大縮小をまたいで空間情報が落ちてしまうのを防いでいます。
図4 Hourglass Networkの内部構造の一部。この構造を繰り返す。

Corner Pooling

Hourglass Networkで抽出された特徴量は、Corner Poolingに通されます。このCorner PoolingがCornerNetの鍵となります。というのも、物体を囲うBBoxの頂点を検出するという問題設定は、そのままでは大きな欠点を抱えているからです。
図5を御覧ください。これらの画像ではBBoxの頂点部分に物体の一部ではなく背景が写っており、そのままでは頂点の検出は困難です。なぜなら、CNNは位置関係を保存して特徴抽出を行うため、画像上で物体の存在しない領域については、特徴マップ上にも情報が存在しないと考えられるからです。
図5 頂点の画素に物体が写っていない例
そこで、物体の情報を左上と右下の頂点になんらかの方法で集める必要があります。それを担うのがCorner Poolingです。
例として、左上に情報を集めるCorner Poolingを図6に示します。
図6 左上に特徴量を集めるCorner Pooling
左上に情報を集めるためには、下から上に、また右から左にと特徴マップを走査していけばよいわけです。ReLUのような活性化関数を用いたネットワークでは、特徴の存在する位置の値は大きくなるため、走査しながら、それまでの最大値で特徴マップの値を上書きしていきます。最後に下から上、右から左に走査した結果を足し合わせて、それを出力とします。

具体的な数値で計算をしてみましょう。
図7 具体的なCorner Poolingの計算
図7の赤枠部分に注目してください。この2つの赤枠は、右から左への走査の、走査前と走査後の様子を示しています。
走査前では、一番右の数は2,その次の数は0です。これを右から順番に見ていき、それまでに見た最大値で上書きします。したがって、二番目の0は2で上書きされます。
次の数は3です。これまでの最大値は2ですから、上書きはしません。
次の数は1です。これまでの最大値は3なので、1を3で上書きします。
……と繰り返し、最後に右から左の走査結果と下から上の走査結果を足し算するわけです。

このCorner Poolingによってほんとうに頂点を検出できるかどうか、簡単な例で確かめてみたいと思います。
図8を御覧ください。一番左の図がCorner Poolingに通される特徴マップです。青い部分に物体が存在しているとします。各値はHourglass Networkで抽出された特徴量で、値が大きいほどなんらかの特徴が存在することを意味しているとしましょう。これにCorner Poolingを適用したものが、一番右の図です。最も値が大きいのが黄色で示している部分で、これが頂点として検出されます。実際、これを左上頂点とする矩形で囲ってみると、物体のある領域をうまく捉えられていることがわかります。
図8 頂点に特徴量が集まっていることが分かる

Heatmap

ではここからはCornerNetの3つの出力について順に述べていきます。
HeatmapはBBoxの頂点の存在確率を示します。Heatmapにはクラス数と同数のチャネルが存在し、各チャネルが対応するクラスの頂点に反応するよう訓練されます。HeatmapはSigmoid関数によって0~1の値をとるよう調節されており、物体頂点がありそうな位置のHeatmapの値は1に近づいていきます。
さて、Heatmapはどのように訓練されるのでしょうか。
物体検出用データセットでは、人手でアノテートされた正解BBoxとクラスラベルが与えられるのが一般的です(例えば、([10, 130, 40, 250], “person”)など)。CornerNetではこの正解データを用いて、正解Heatmapを作成し、これと推論されたHeatmapを比較してLoss(損失)を計算します。
正解Heatmapは、正解BBoxの頂点の値を1として、これを中心にガウス分布に従う広がり(式1参照)をもたせたものを利用します(図9)。
もし仮に頂点の座標のみに1を立てたようなHeatmapを正解データとして使うと、予測が1ピクセルでもずれると完全に間違いということになってしまうため、多少の誤差を許容するために正解に広がりをもたせているわけです。
その際、BBoxのサイズによってズレが予測に与える影響は変わるため、σを調節してペナルティの大きさを制御します。小さいBBoxではσを小さくして分布の広がりを狭め、逆に大きなBBoxではσを大きめにするのです。
図9 正解Heatmapの例
では今度はLossの計算式を見てみましょう。Lossは式2に示すように、「ぴったり正解BBoxの頂点に一致した点」とそれ以外の点とで計算式が異なります。
(N= 正解BBoxの数、c= チャンネル数、
H= 画像の高さ、W= 画像の横幅、
p= 推定heatmap、y= 正解heatmap、
α,β= ハイパーパラメータ(論文および再現実装ではα=2, β=4))

ぴったり正解BBoxの頂点に一致する点(y_{cij}=1)については、式2の上の式でLossを計算します。この式では、予測Heatmapの値が1に近いほどLossは小さくなります。
一方、それ以外の点では式2の下の式で計算します。こちらの式では、予測Heatmapの値が0に近いほどLossが小さくなります。正解BBoxの頂点に近くても、一致しない限りはHeatmapの値を0に近づけるということです。ここで式の前についている(1-y_{cij}) に注目してください。 y は正解Heatmapの値ですから、正解BBoxの頂点に近ければ (=y_{cij}が大きければ)Lossが小さくなる、つまりペナルティが軽減されるということになります。この動的にスケーリングされる損失関数をFocal Lossと呼びます。

Embedding

Heatmapによって各物体の左上と右下の頂点候補を得ることができますが、このままでは、どの左上頂点とどの右下頂点がペアなのかわかりません。
図10 HeatmapだけではBBoxを一意に決められない
そこで頂点ペアの決定に用いられるのがEmbedding(埋め込みベクトル)です。
Embeddingはクラス数に関係なく共通のベクトルで、各ピクセルに対して一つずつ得られます。論文ではEmbeddingは1次元になっています。
このEmbeddingが近いものがペアであるということにすれば、BBoxを一意に決定することができそうです。
図11 図中の数字がEmbedding.例えば0.34と0.36が近いのでグループ化可能
さてこのようにEmbeddingを用いて頂点のグルーピングをするためには、同一BBox上でペアになる頂点間のEmbeddingの距離が近く、逆にペアではない頂点間ではEmbeddingの距離が遠くなればよいわけです。
図12 このように学習したい
これを実現するために、式3にもとづいてLossを計算します。L_{pull} がペアとなる頂点間で定義されるLossで、L_{push}がペアではない頂点間で定義されるLossです。順に説明していきましょう。
e_{t_k}= k番目の左上の頂点のEmbedding、
e_{b_k}= k番目の右下の頂点のEmbedding、
e_k= k番目の左上と右下の頂点のEmbeddingの平均、
δ=1 (ハイパーパラメータ)

Embedding Lossの計算では、ペアとなる頂点間のEmbeddingの平均を、基準として用います。
まず、 L_{pull} では、 ペアとなる頂点のそれぞれのEmbedding ( e_{t_k}e_{b_k} )がそれらの平均( e_k )に近づくようにします。
次に L_{push} ですが、これは少々わかりにくいかもしれません。 L_{push} では、頂点ペアのEmbeddingの平均値 ( e_k )を、それ以外のすべての頂点ペアのEmbedding平均 ( e_j ) と比較します。その差が δ 以上であれば0、 δ 未満であれば δ との差分が L_{push} に加算されるのです。例えば図13では e_k=0.35 , e_j=1.10 で、2つの差は |e_k-e_j|=|0.35-1.10|=0.75 です。これは δ (今回は1)より小さいので、  1-0.75=0.25 がLossに加算されます。この L_{push} を最小化することで、異なる頂点ペアのEmbeddingを十分( δ 以上)に引き離すことができるわけです。
図13 e_ke_j

Offset

3つ目のOffsetは、BBox位置の小さなズレの修正に用いられる値ですCornerNetにおいて、HeatmapやEmbeddingなどの出力は、入力画像よりも低解像度になっています。これは計算量の都合です。
BBox検出の際は、Heatmap上で閾値以上に値の大きな点を頂点として選びますが、このときHeatmapの解像度が入力画像よりも小さいため、得られた座標を定数倍して入力画像サイズに戻す必要があります。ここでズレが生じます。例えば正解BBoxの一つの頂点のx座標が102だったとしましょう。Heatmapが入力画像の1/4に縮小されていたとすると、Heatmap上の座標値は元の画像上では4の倍数にしかなりませんから、102という正確な値は出すことができません。ここで登場するのがOffsetです。
CornerNetでは、(予測頂点のHeatmap上の座標+Offset)を定数倍して最終的な推定結果とします。OffsetはHeatmapの各値に対してx軸方向とy軸方向の2チャネルを出力します。Embedding同様こちらもクラス数には依存しません。
図14 Offsetによって座標を微修正する
正解Offsetとしては、図15に示すように、正解BBoxの各座標を出力サイズに縮小したときの小数点以下の値を使用します。図15では0.5と0.75がそれぞれx軸、y軸のOffsetとなります。OffsetのLossの計算には、Smooth L1 loss を用います。
図15 正解Offsetの計算
図16 Smooth L1 Lossのグラフ

精度

論文で報告されているCornerNetの精度です。
図17 各手法との精度比較
論文では、MSCOCOデータセットを用いて実験を行っています。筆者は、CornerNetは他のOne-stage detectorより高精度であり、Two-stage detectorに匹敵していると主張しています。
以下に論文に掲載されている物体検出結果の一部を示します。小さいボールやスプーン、また密集したキリンや人など精度よく検出できていることがわかります。
図18 CornerNetによる検出結果

CornerNet 再現実装

ここまでCornerNetの手法の解説を行ってきましたが、最後に、弊社で再現実装を行い、独自のデータセットで評価した結果を紹介します。データセットは弊社で収集した、車載カメラによる走行画像です。
データセットは1シーケンス60枚程度の連続した画像からなり、全体で598シーケンスあります。このうち512シーケンスを学習用、残りの86シーケンスを評価用に利用しました。データセットは「信号」「車」「歩行者」など全17クラスのアノテーションがされていますが、データ数の少ない(500未満)6クラスを除外し、11クラスで訓練を行いました。
画像サイズは1920×1200で、これを512×512にリサイズして使用しています。
図19 アノテーション例
次に学習条件の詳細について述べます。
訓練時にはAugmentationを行いました。利用したAugmentation手法は、色・明るさの変更、左右反転、ランダムクロップです。
OptimizerとしてはAdam(α=1e-4, beta1=0.9, beta2=0.999)を用いました。
論文では、HeatmapとOffsetのLossは1倍、Embeddingは0.4倍して足し合わせていると書かれていますが、さまざまに条件を変えて実験した結果、以下のようにLossを変更しています。
まずHeatmapについて、独自データセットは頂点の密度がMSCOCOより小さいことを考慮し、頂点のない部分のLoss(式2、下式)を1/50倍しています。
またEmbedding loss が軽視される傾向があったため、係数を0.4倍ではなく1倍に変更しました。

最後に評価時の条件についてです。まずHeatmapから値が0.4以上の点の座標をとってくることで頂点の候補を列挙します。次に、それぞれの頂点候補に対して、最もEmbedding間距離の小さいものをペアとし、BBoxを生成します。ここで重複するBBoxが多数生成されるため、NMS(Non Maximum Suppression)を施して取り除きます。これは、2つのBBoxのIoUを計算し、これが閾値以上だった場合に、予測確率(そこに物体があるという確信度。今回は2頂点のHeatmapの平均値を用いた)の大きいもののみを残すという手法です。NMSの閾値は0.4に設定しました。

評価にあたっては、予測BBoxと正解BBoxを比較して、クラスが一致していてかつIoUが0.5以上であった場合に正解とし、mAPを評価指標としました。

学習は23 epoch行い、およそ3日間かかりました。

結果

結果は、11クラスに対しmAP=0.202というものでした。
論文の値よりもかなり低くなってしまいましたが、11クラス中には訓練データが少ないものもあり、それらがmAPを大きく減少させていました。
高精度で検出できているクラスの例としては、車がAP=0.482、停止標識がAP=0.557などと健闘していました。

以下は検出結果の例です。まずうまくいったものから紹介します。
図20 検出例(うまくいったもの)
画像中の青い印は左上の頂点として検出された領域、赤いものは右下の頂点として検出された領域を示しています。
車や信号、歩行者がおおよそ検出できていることがわかります。上の画像では歩行者はかなり小さく映っており、外観も様々ですが、きちんと検出できています。

一方で検出に失敗した例を図21に示します。BBoxが大きく広がってしまっている様子がうかがえます。これは、片方の頂点がうまく検出できなかったり、誤った頂点をペアにしてしまったりした結果です。Embeddingの学習がうまくいっていないことを示しています。
図21 検出例(うまくいかなかったもの)
Embeddingについては、論文通り1次元の値を使ったのが問題ではないかと考え、128次元に拡張して実験を行ってみましたが、かえって精度が下がってしまいました。

まとめ

本記事では、CornerNetの手法の解説と、弊社で再現実験を行った結果の紹介を行いました。
再現実験では十分な結果は得られませんでしたが、これがデータセットの特性によるものなのか、実装に不備があるのかは検証できていません。
再現実装に関してはChainerを用いたのですが、cupy.ElementwiseKernelを使ってGPUでの処理を書くのが非常に難しかったです。
十分な精度は出せませんでしたが、なぜこの方法で物体検出ができるのかという論理の読み取り、読みやすいプログラムの書き方などが学べ、大変勉強になりました。

ALBERTでは、データサイエンティストを募集しています。ぜひ採用​ページをご覧ください。