【高速化】Ano-Unet V2で異常部分の可視化

異常個所を「教師無し学習」で可視化するAno-Unet V2を開発しました。
[Ano-Unet](https://qiita.com/shinmura0/items/a0f8cf8847bda24cc2b5)に比べ**速度と安定性**を向上させました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/b9ad1945-fc99-aa68-d99b-e479fed9359a.png)

コード全体は[こちら](https://github.com/shinmura0/PConv-Keras/blob/master/Ano-Unet_V2.ipynb)に置きました。

※本稿は[【Zoom】Pythonデータ分析勉強会#13　その1](https://pythondata.connpass.com/event/146737/)の発表資料です。

#Ano-Unetの問題点
以前に開発した[Ano-Unet](https://qiita.com/shinmura0/items/a0f8cf8847bda24cc2b5)は以下の問題点を抱えていました。

+ 画像一枚に対し、学習を行っているので**非常に遅い**。（たぶん画像1枚に20分くらい）
+ ハイパーパラメータの調整が、画像毎に必要なため大変。

#Ano-Unet V2
そこで、問題点を解消すべくAno-Unet V2を開発しました。
##特長
+ 高速化
+ ハイパーパラメータの調整はほとんど不要
+ metric learningに限らず、オートエンコーダでも**何でも**後付け可能なネットワークになった

通常の機械学習と同じく、学習フェーズと推論フェーズを切り離したため
推論は非常に速くなりました（処理時間は画像一枚に**1分**くらい）。
ただし、学習フェーズは**5,6時間かかります。**

##構造

全体の構造は以下のとおりです。
![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/c37d41f5-ad92-b01f-3546-bb52d82f0081.jpeg)

+ あらかじめ、正常画像で異常検知を学習させておきます。
+ さらに、正常画像でPartial Convolutionsを学習させておきます。
+ 元画像に対しマスクをかけます（Masked Image）。
+ それをPartial Convolutionsに入力し、マスクの部分を補完した画像を取得します（Predicted Image）。
+ 取得した画像に対し、異常検知を行い異常スコアを取得します(Anomaly Score)。
+ 最後に、取得した異常スコアをヒートマップのマスクした部分に代入します(Heat map)。
+ マスクを移動させ、同じ作業を繰り返します。

今回使っている技術は以下のとおりです。

+ [Partial Convolutions](https://qiita.com/knyrc/items/743cc56a19456f176d2b)
+ [ディープラーニングによる異常検知](https://qiita.com/shinmura0/items/06d81c72601c7578c6d3)

※　Ano-Unet V2とは名ばかりで、メインはPartial Convolutionsの表現力を
　　頼りにしています。ただ、U-netは一応、Partial Convolutionsの中で
　　使われているので、Ano-Unet V2という名前でも許されるかなぁと思います。

以下、各技術を説明します。

##Patial Convolutions

Patial Convolutions（以下PCs）はマスクした部分をリアルな画像で補完
するネットワークです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/25bf3410-d667-8285-9c6b-70cdc63e1423.png)

上の図は、PCsの[論文](https://arxiv.org/abs/1804.07723)より引用したものです。
かなりリアルな画像が再現されています。

Ano-Unet V2では、まず、PCsを**正常画像で**学習させます。そして、異常部分をマスクして
PCsで補完すれば異常部分が正常に変わるのでは？という発想で成り立っています。

以下の図が分かりやすいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/1f1bb2c7-a3ac-3d17-3c34-c3c7d51ead3b.png)

これは、異常部分をマスクした画像を学習させたPCsに入れ、補完させた画像
を取得したものです。期待通り異常部分が消え、**正常っぽい画像**に変化しました。

PCsは正常画像で学習させているため、マスクした部分は必ず正常っぽい絵柄で
補完してくれます。

そして、マスクを移動させながら、補完した画像を異常検知させ、異常スコアで
評価すれば、異常部分のときだけ異常スコアが下がるため、どこが異常なのかを
特定することができます。

マスクは画像の端から端まで満遍なく移動させます。そして、マスクのサイズは
**4種類を用意し**、微小な異常個所から大きい異常個所までをカバーしています。

##ディープラーニングによる異常検知
あらかじめディープラーニングによる異常検知を学習させておく必要がありますが、
これはディープラーニングじゃなくても何でも良いです。画像の異常度を算出できる
手法であれば良いです。

極端なことをいえば、画像を二値化して明るいところの面積で合否判定する手法でも
適用できます。（精度は保証できませんが。。。）

###適した手法
ただし、スコアを出す異常検知手法はオートエンコーダのような面積ベースの
評価方法の方が向いている気がします。metric learningのような特徴ベースの
ものだと**最も異常な部分**を消さないと、スコアが全然下がってくれないため、
少しの異常は見過ごされる傾向にあると思われます。

ただ、オートエンコーダによる異常検知は元々可視化できる構造なので、そもそも
Ano-Unet V2は使う必要ないよ！というジレンマもあるわけですが。。。

#実験
今回は、cifar-10は使わずに、製造業で出てきそうな画像を使います。
使ったデータセットは以下の二つ。

+ [MVTec-AD](https://www.mvtec.com/company/research/datasets/mvtec-ad/)
+ [DAGM](https://qiita.com/leetmikeal/items/7c0d23e39bf38ab8be23#dagm-2007)

そして、異常検知手法として[黒魔術](https://qiita.com/shinmura0/items/6c805498c390d5b35714)を使いました。

##MVTec-ADを使った結果
コード全体は[こちら](https://github.com/shinmura0/PConv-Keras/blob/master/Ano-Unet_V2.ipynb)に置きました。
Colabで動くはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/b74b7db8-a1b1-f0f5-94d4-cf8dde61e0d1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/a37f78a7-f184-ed40-94cd-07a55491f63b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/b9ad1945-fc99-aa68-d99b-e479fed9359a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/8ac5e169-2d1b-0ec9-6a94-518582cfc907.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/6d52c8f9-771a-9833-a480-021bf43563d2.png)

うまくいく確率は約70％という印象。

@daisukelab さんの[自己教師あり学習](https://qiita.com/daisukelab/items/e0ff429bd58b2befbb1b#%E5%AE%9F%E9%A8%933%E9%80%9A%E5%B8%B8%E3%81%AE%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%81%8B%E3%82%89%E7%95%B0%E5%B8%B8%E3%81%AA%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E8%87%AA%E5%B7%B1%E6%95%99%E5%B8%AB%E3%81%82%E3%82%8A%E5%AD%A6%E7%BF%92)と比べると見劣りしますが、
教師無し学習なので良しとします(^^；

##DAGMを使った結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/14aebbd4-1420-e060-c9f9-0b4403e74f72.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/d9297fd2-6451-fab5-33d7-0abe143c0766.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/b0c0fb0b-9cc5-b406-1224-3c438f5d3cf0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/77e281da-658b-f687-73fc-3a39aad35715.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/e7334f3b-6a74-5d21-676d-8aaaa644c812.png)


うまくいく確率は約50％という印象。
結構難しいデータセットでした。

#まとめ
+ Ano-Unet V2の推論は比較的高速、パラメータ調整もほとんど不要
+ 学習時間が長いのがネック（5,6時間）
+ metric learningに適した可視化手法かといわれると、ベストではない（改善の余地あり）