"SOLO" 簡単すぎるInstance Segmentation

# はじめに
本稿では昨年度末に提案された**Instance Segmentationに関する新しいアプローチ**
**SOLO: Segmenting Objects by Locations** (https://arxiv.org/abs/1912.04488) を紹介します。

物体検出でお馴染みのYOLOをもじったようなタイトルですが、こちらはInstance Segmentationです。しかし、YOLOに負けず劣らず**シンプルな面白いアプローチで、精度等のパフォーマンスも高い**(らしい)ので、是非トライください。

なお、先日(2020年3月)、SOLOv2も発表されました。
SOLOv2: Dynamic, Faster and Stronger (https://arxiv.org/abs/2003.10152)
こちらも完全にYOLOv2を彷彿とさせるタイトルですね。


### Instance Segmentationとは？
物体の領域をオブジェクトごとにピクセルレベルで検出および分類する手法です。
**矩形で囲うObject Detectionのように物体の数量を判断でき、尚且つSemantic Segmentationのようにピクセルレベルの厳密な領域分割ができる、**というものです。
<img width="700" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/42c0402f-919c-347f-a7d3-924e6eadcd33.png">

### Instance Segmentationの従来手法
Instance Segmentation分野では、**Mask R-CNN**が最も有名でデファクトスタンダードにあたると思います。下図のように、**矩形を見つけてから色塗り**するような手法です。
<img width="600" alt="2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/dfc3f54e-e9a4-dc0d-de2d-b1ce5ca64fc1.jpeg">

Mask R-CNNについては以下の良記事をご参考ください。
https://qiita.com/shtamura/items/4283c851bc3d9721ed96

# SOLO [Segmenting Objects by Locations]
### SOLOってどんな手法？
下図がわかりやすいです。
<img width="700" alt="3.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/5a6533ab-2f07-7e14-30c8-506ad5005f24.jpeg">
**CNNのEncoder & Decoderを経て、Category BranchとMask Branchに分岐します。Category Branchではクラス分類をおこない、Mask Branchではセグメンテーションをおこないます。**

**(1) Category Branch**
入力画像をグリッド状に分けて、グリッドの個数分だけクラス分類をおこないます。各グリッドセルに物体の中心が存在する場合はそのクラスを、存在しない場合は「背景」を返す形です。

**(2) Mask Branch**
各グリッドセルに物体が存在するとしてセグメンテーションしてしまいます。したがって、**グリッドの個数分だけ画像全体をセグメンテーションする**ことになります。例えば12x12でグリッド分割した場合、マスクの最終出力は144枚(144ch)のセグメンテーション画像ということになります。ここがユニークなポイントですね。

全体のネットワークをイメージで書くと下図のような形になります。
<img width="700" alt="4.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/9be667d6-bc59-314c-cd96-0d221d2568a9.jpeg">

Mask Branchの出力は、下図のようなイメージになります。**オブジェクトの中心が存在するセルには、マスクが出力されている**ことが見てわかります。近傍のセルにも似たようなマスクがはられることになりますが、最終出力はNMS(Non Maximum Suppression)をおこなうことで、重複したマスクを除外することができます。
<img width="600" alt="5.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/7fb77950-5c61-cd5f-fde4-69ff44aedde3.jpeg">


### 従来手法とココが違う！
**「矩形検出⇒セグメンテーション色塗り」のような、二段階のアプローチではなく、シングルステージでダイレクト一発検出**なのがアツいところです。
コーディングも容易で、**損失関数もマスクのDice Lossと分類のFocal Lossだけ**なので、お気持ちはほぼU-Netです。Bounding Boxの回帰予測の類がないのがらくちんです。

### SOLOのここが面白い①： Coord Convの活用
SOLOを特徴づけるのはグリッド状のマスク出力ですが、**グリッドにマスクを割り振るところが難しく**、工夫無しではあまりうまくいきません。通常、CNNは座標情報を得ることができないので、畳み込んで得られたマスクを各グリッドセルに分類しにくいためです。

それに対して、SOLOでは**Coord Convを用いることでこれを解決しています**。Coord Convは、height方向とwidth方向の座標値を-1~+1で割り振った値をチャンネル方向に追加(concatenate)するだけです。
<img width="400" alt="6.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/e3d23ab9-19c5-a069-8bd4-292b34590e81.jpeg">
(https://arxiv.org/abs/1807.03247)

論文によると、Coord Convだけで大幅に精度が改善するとのことです。

### SOLOのここが面白い②： Mask Kernel Branch
さらに、SOLO(v2)では**マスクの割り振りをより明示的におこなう手法**が提案されています。
上述したMask Branchをさらに分岐させて、最終的なマスクの割り振りを位置情報ベースの重みで与えるような方法です。**グリッドの場所毎にchannel-wiseなattentionを与える**といっていいと思います。

下図の一番左が通常のSOLO、右側がMask Kernel Branch付きのSOLOです。
<img width="600" alt="7.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/ec6cd138-d23d-0501-e8ad-e88e346509c9.jpeg">

### SOLOのここが面白い③： Matrix NMS
Non Maximum Suppressionは、重複するマスクを除去するための必須の後処理ですが、繰り返し演算で処理時間を無駄に要するのがネックです。

NMSの詳細は別の記事をご参考いただきたいと思います。ざっくり説明すると、NMSではスコアの高いマスクから順番に選びとっていき、下位スコアのマスクは、上位スコアのマスクと重複(IoU)が大きい場合は除去します。思わずfor文を書きたくなるような処理です。

一方、**Matrix NMSでは上述の重複(IoU)が大きい下位マスクを除去する代わりに、相対的にスコアを落とすようなペナルティを与えます。**

以下のコードが論文中に記述されており、decayの処理でスコアの低減がおこなわれています。IoUは0~1の間の値を撮りますが、これが大きくなるほどdecayが小さくなり、スコアが低減されます。しかし、上位のマスクとのIoUが大きい場合(ious.maxが大きい場合)には、そのマスクの影響は除外されます(decayは小さくなりません)。
<img width="500" alt="8.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/d820ee00-703e-4a12-ac44-2683aba2bce6.jpeg">

これを用いると、for文要らずで**処理が通常のNMS(Hard NMS)よりも9倍速く、精度も少し向上**したとのことです(下表)。少し雑な説明をしておりますので、厳密な説明はSOLOv2論文をご参考ください。
<img width="500" alt="9.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/96a001ed-85b6-3bb3-0ebf-2cd83d151d36.jpeg">


### パフォーマンス
そんなシンプルなSOLOですが、速度・精度とも結構いいパフォーマンスが出るそうです。COCOのAverage Precisionが論文中にありますが、SOLO v2は多くの従来手法を凌駕しています。
<img width="500" alt="10.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/6c769635-5e36-e6a7-ea93-c15b2399c0a2.jpeg">

# 所感
論文中で書かれているものもありますが、**個人的に感じる欠点**としては以下です。

**1. 物品が多いとグリッドを細かくする必要があり、出力サイズが大きくなる (メモリ圧迫)**
**2. 検出物品のサイズによらず、画像全域のマスク画像を出力するので、各出力に無駄(背景)が多い。**
**3. 物品が検出されない場合であってもグリッドセルの数だけ出力がある。背景が多いと損した気分になる。**
**4. グリッドの割り当てが中心点でおこなわれるので、中心が被ると検出漏れしやすい。**

モデルを大幅に変更しない限り、物品のサイズやバラつきっぷりを元にグリッドサイズ等を決めて、無駄なく構成するしかありません。

上述4については、面積比などでグリッドに割り振るのも手だと思います。下図の犬と猫は同一中心ですが、各グリッド毎の面積比をとれば、犬のセルも猫のセルもあります。少し試しましたが、こちらの方が有効なケースもありそうです。
<img width="200" alt="11.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/497e14e9-cef7-00e6-e505-9eca657b6818.jpeg">



# さいごに
SOLOを用いるとシングルステージで簡単にInstance Segmentationができます。
Mask R-CNNに飽きてきた皆さんも、Instance Segmentationを敬遠していた皆さんも、気が向いたら是非トライください。

まだまだ勉強不足のため、不備や誤りがあるかと存じます。何かございましたらご指摘・コメントください。