前回は、深層距離学習にGrad-CAMを適用し、可視化する手法を紹介しました。
今回は、その論文を応用し、リアルタイムでGrad-CAMを処理できる
「Faster-Grad-CAM」を開発したので紹介します。
※コードはこちら
※こちらはPythonデータ分析勉強会#18の発表資料です。
#結論から
- Faster-Grad-CAMを使えば、通常のGrad-CAMより10倍以上高速化することが可能
- しかも、前回ご紹介した技術を使っているため、高精度化にも成功
#デモ
まずは試してみたいという方は、こちらにパーとグーを見分けるデモ機を作りました。
パーのときは反応せず、グーにするとFaster-Grad-CAMが発動します。
##環境
- Keras 2.2.4
- TensorFlow 1.9.0
- sklearn 0.19.0
- Opencv 3.4.3.18
- RaspberryPi3 modelB(もしくはPC)
##使い方
カメラを準備し、以下のコマンドで実行できます。
python3 janken_demo.py
「Faster-Grad-CAM」で、CNNの注視部分の可視化。ラズパイ3で実行。 pic.twitter.com/omQbTw8svx
— shinmura0 @ 3/14参加者募集中 (@shinmura0) March 4, 2020
RaspberryPi3で5~6FPSの速度です。
さらに、得られたヒートマップを二値化すれば、枠で囲うこともできます。
「s」キーを押せば、物体検出(ObjectDetection)風にすることも可能です。 pic.twitter.com/ePdN9itSy8
— shinmura0 @ 3/14参加者募集中 (@shinmura0) March 4, 2020
#手法の紹介
※前回の記事をご覧になっていない方は、こちらも併せてご覧ください。
Faster-Grad-CAMの適用条件はただ一つ、深層距離学習を使うことです。
TripletでもArcFaceでも適用できます。
前回紹介した論文では、深層距離学習にTripletを使っていました。
「Faster」ではTripletに代わりにArcFaceを用いています。
※データセットは@karaage0703 さんのjanken_datasetを使っております。
##学習方法
まずは、MobileNet V2をArcFaceを使って普通に分類問題で学習させます(step1)。
次に、データベースを作ります(step2)。
データベースは、embeded vector
とweight vector
の二つがあります。
embeded vector
は通常のArcFaceで出力されるものです。
このベクトルを使うことで、分類や異常検知ができます。
weight vector
は、block_16_expan_relu
から出力されるベクトルを使って出力yを
偏微分したものです。といっても、通常のGrad-CAMと同じ操作をしているだけです。
weight vector
は前回の論文でいうと$\alpha_k$に相当します。
次に、Faster-Grad-CAMを学習させます(step3)。
まずは、学習データのembeded vector
を求め、k-meansを適用してクラスタリングを行います。
論文ではクラスタ数は50でしたが、Fasterでは10に変更しています。
そして、各クラスタ内でクラスタに属するweight vector
の平均値を計算します。
最後に平均化されたベクトルのうち、Top50を取得し、その番地channel No
と
平均値の大きさweight
を保存します。
※論文では、Tripletを使っているため、似たもの同士はユークリッド距離が近い傾向にありました。
従って、k-meansを使うと、似たもの同士が同じクラスタに存在する可能性が高いです。一方、
「Faster」はコサイン類似度が大きいもの同士が似た傾向を持っているため、k-meansで
似たもの同士でどこまで固まるのか議論の余地があります。
##推論方法
最後に、Faster-Grad-CAMで推論します(step4)。
まずは、テストデータを学習したMobileNet V2に入れ、テストデータのembeded vector
と
weight vector
を得ます。そして、得たembeded vector
にk-meansを適用し、属する
クラスタを求めます。上の図では、cluster_Noが1のテストデータが得られたとしています。
次に、属するクラスタのchannel No
とweight
をロードして、得られたweight vector
に
適用します。channel No
は、可視化に使うNoだけを指定し、weight
はそれらNoに掛ける重みが
保存されています。
最後に、得られたヒートマップをリサイズして、画像と合成しています。
##速さの秘密
上の図から分かるとおり、「Faster」の推論で行う処理は以下のとおりです。
- k-meansの推論
- 保存データのロードやリサイズ
一方、普通のGrad-CAMは以下の処理を行っています。
- ArcFaceの層とソフトマックス層の計算
- 偏微分の計算
- リサイズ
step2では、前述したとおり、普通のGrad-CAMとほぼ同じ操作をしています。
「Faster」では、推論時、GAP以下の処理は不要なためネットワークから削除しています。
しかし、普通のGrad-CAMではそれが必要で、余分に計算時間がかかっています。
ただ、その計算時間は微々たるもので、一番重たいのはやはり偏微分です(上の図でいうと
"Normal" Grad-CAM
)。どうしてここまで時間がかかるのか真剣に考えたことはありませんが、
とりあえず重いです。画像のサイズによりますが、1秒以下で終わったことはありません。
一方、「Faster」の方は一番重くてもk-meansくらいなので、可視化の計算はCPUでも楽々できます。
下手したら数msecです。
##学習方法
提供しているデモ機は、自分の画像で学習させることも可能です。
学習方法は、こちらのTrain_Faster-Grad-CAM.ipynb
を参考にしてください。
###ハイパーパラメータの調整
-
thresh_hand
_janken_demo.py
内でこの値をいじると、グーとパーの判定基準を変えることができます。
この値の範囲は0~1で、ArcFaceのコサイン類似度に相当します。この値を大きくすると、グーは
認識しやすくなりますが、パーの認識は厳しめになります。 -
thresh_OD
_janken_demo.py
内でこの値をいじると、物体検出風の枠の閾値を変えることができます。
この値を大きくすると、枠は小さめになり、小さくすると、枠は大きめになります。 -
ArcFaceのパラメータ
ArcFaceのパラメータは主に、sとmの二つ存在し、分類精度に影響を与えます。2分類や3分類程度
であれば、初期値から変える必要はないかと思います。10分類以上になると、いじる必要があるかも
しれませんが、面倒な方はAdaCosを使う手もあります。AdaCosはパラメータを自動的に決めて
くれる手法です。
#応用例
##異常部分の可視化
自己教師あり学習を使った異常検知では、Grad-CAMを適用することで「異常部分を
可視化すること」ができました。
本当にやりたかったのはコレ!Faster-Grad-CAMで異常部分の可視化。
— shinmura0 @ 4/11参加者募集中 (@shinmura0) March 9, 2020
〇を正常として学習させました。(余分な線、欠けた線、異物は「異常」です。)リアルタイムで「可視化」と「異常検知」が同時にできるなんて夢のよう...(^^)。 pic.twitter.com/66BFAc8Efz
余分な線や欠けた線があると「異常」となり、異常部分を枠で囲ってくれます。
##オートアノテーションツール
以前にアノテーションを自動化するツールを作りました。
これは、Grad-CAMで二値化したマップにベイズ最適化を適用し、過不足なく
アノテーションするものです。
この事例にFasterを適用すると、Grad-CAMで5秒かかっていたものが、ほぼ一瞬で
処理することができます。全体として、動作時間を25%削減することができます。
(20秒→15秒/画像1枚)
#まとめ
- Faster-Grad-CAMを使えば、通常のGrad-CAMより10倍以上高速化することが可能
- しかも、高精度化にも成功
- 速さの秘密は、推論時に偏微分を使っていないこと