CNNの学習結果を可視化する

**※△１　2019.02.01　動かない環境があった為修正**
**※△２　2019.02.03　文章を各所修正**

CNNの任意の特長マップを使用して可視化をするスクリプトです

下記にある通りGrad-CAMで表示がおかしくなる学習結果に対しても
そこそこ良好な可視化結果を得られています。

ただし問題がありまして、識別率が低い画像を可視化すると
判別に使用していない特長フィルターも可視化されてしまうので
関係ないところまで反応してしまうという問題があります

この辺はGrad-CAMの手法で解決できるので
両方を使い分けるのがいいと思います

#確認環境
　python3.6.6 , 3.6.7 , 3.6.8
　Tensorflow:1.10.0
　Keras：2.2.2 , 2.2.4

#やり始めたきっかけ
Grad-CAMを使って作成したモデルの評価を行っていました
[いつものデータセット](https://qiita.com/God_KonaBanana/items/2cf829172087d2423f58)を使いVGG16のファインチューニングでval_acc「0.967」くらいの結果なので
まぁまぁ識別できているモデルかと思われます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/b996b57f-c839-0351-e2dd-19fec4f0ab1c.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/92b6b474-d7c1-cce9-48c5-387688958ba0.png)

キャラ識別は顔（髪、目）をきちんとみているんだなぁ・・・
Grad-CAMすご・・・
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/9640733e-ccc2-f4b1-3d03-888646cc8769.png)
？？？
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/9a41d382-a2f4-97ee-a0ab-19d7d1d6669e.png)

？？？？
**どこを注視しているんですかね？（笑**

識別の結果はラベル通り（正解）ですし
**若干の変態感**が気になりますが、とりあえず識別結果は出ているのと
Grad-CAMは動いたということで放置していました。

ただ、他のデータセットでも同じような現象が発生したことと
画像にコンターっぽい色がつくと業務で~~**だまし**~~
**説得力**が増すので違う方法を考えておりました


#暫定の対応策

とりあえず下記のように、全結合層前の**「block5_conv3」**の
特長マップを見てなんとかならないかと作業しておりました

**※△１2019.02.01修正**
**新しくインストールした環境で動かなかったので
特長マップを抜きだすところを修正**

```python
get_layer_output = K.function([model.layers[0].input],[model.layers[17].output])
print(model.layers[17])
layer_output = get_layer_output([img_nad])[0]
```
ただVGG16ですと、この位置の特長マップ512もあるし
どうやって表示するか・・・
縦横に並べてもすごい数だし、めんどくさいなぁ・・・と・・・

**<font color="Red">修正前</font>**

```python
get_layer_output = K.function([model.layers[0].input,
                               K.learning_phase()],
                               [model.layers[17].output])
print(model.layers[17])
layer_output = get_layer_output([img_nad, 0])[0]
```

#バカの考え休むになんとか
反応していないチャンネルもあるし
大体似たような位置に反応があるので・・・

**「全部足して割ればいいんじゃない？」**

とりあえずやってみます

```python
G, R ch = layer_output.shape[1:]
res = np.zeros((G,R))
    
for i in range(ch):
    img_res = layer_output[0,:,:,i]
    res = res + img_res 

res = res/ch
plt.imshow(res)
plt.show() 
```

それっぽいのが出力されました！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/46603b52-f2d6-4d5b-a6b1-40f818c15890.png)

#それっぽく見せる
詳細は別途解説しますが
　１．特長量マップを平坦化
　２．モノクロにコンターっぽい色を付ける
　３．14x14を入力画像サイズに
　４．合成
を追加しまして
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/5dc6ece9-974b-e3b5-4068-22c1b29513ae.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/286585/1ed164fd-b21e-d35a-a037-b821b379d357.png)

わりと見れる感じに塗り絵できました
~~素人にならGrad-CAM（のようなもの）で押し切れる！？！？~~



#スクリプト全体
ファイル名とかフォルダ名は適時適当に変換してください

```python
from keras.models import load_model
import numpy as np
from keras import backend as K
from keras.preprocessing.image import array_to_img, img_to_array, load_img, save_img
import cv2
from IPython.display import display_png

#モデルの読込み
file_name='vgg16_madomagi_SGD0.001_G_D4096_D512'
model=load_model('./result/' + file_name+'.h5')
model.summary()

#対象イメージの読込み
jpg_name = 'madoka0137'
img_path = ('./madoka_magica_images/display/' + jpg_name + '.jpg')
img = img_to_array(load_img(img_path, target_size=(224,224)))
H,W =img.shape[:2]
img_nad = img_to_array(img)/255
img_nad = img_nad[None, ...]

#特長マップを抜き出すレイヤー指定
get_layer_output = K.function([model.layers[0].input],[model.layers[17].output])
print(model.layers[17])
layer_output = get_layer_output([img_nad])[0]

#特長マップ合成
G, R, ch = layer_output.shape[1:]
res = np.zeros((G,R))
    
for i in range(ch):
    img_res = layer_output[0,:,:,i]
    res = res + img_res       

res = res/ch

#特長マップ平均の平坦化
res_flatte = np.ma.masked_equal(res,0)
res_flatte = (res_flatte - res_flatte.min())*255/(res_flatte.max()-res_flatte.min())
res_flatte = np.ma.filled(res_flatte,0)

#色付け
acm_img = cv2.applyColorMap(np.uint8(res_flatte), cv2.COLORMAP_JET)
acm_img = cv2.cvtColor(acm_img, cv2.COLOR_BGR2RGB)
acm_img = cv2.resize(acm_img,(H,W))

#元絵と合成
mixed_img = (np.float32(acm_img)*0.6 + img *0.4)

#表示
out_img = np.concatenate((img, acm_img, mixed_img), axis=1)
display_png(array_to_img(out_img))
```