※△1 2019.02.01 動かない環境があった為修正
※△2 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を使って作成したモデルの評価を行っていました
いつものデータセットを使いVGG16のファインチューニングでval_acc「0.967」くらいの結果なので
まぁまぁ識別できているモデルかと思われます
キャラ識別は顔(髪、目)をきちんとみているんだなぁ・・・
Grad-CAMすご・・・
???
????
どこを注視しているんですかね?(笑
識別の結果はラベル通り(正解)ですし
若干の変態感が気になりますが、とりあえず識別結果は出ているのと
Grad-CAMは動いたということで放置していました。
ただ、他のデータセットでも同じような現象が発生したことと
画像にコンターっぽい色がつくと業務で~~だまし~~
説得力が増すので違う方法を考えておりました
#暫定の対応策
とりあえず下記のように、全結合層前の**「block5_conv3」**の
特長マップを見てなんとかならないかと作業しておりました
※△12019.02.01修正
新しくインストールした環境で動かなかったので
特長マップを抜きだすところを修正
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もあるし
どうやって表示するか・・・
縦横に並べてもすごい数だし、めんどくさいなぁ・・・と・・・
修正前
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]
#バカの考え休むになんとか
反応していないチャンネルもあるし
大体似たような位置に反応があるので・・・
「全部足して割ればいいんじゃない?」
とりあえずやってみます
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()
#それっぽく見せる
詳細は別途解説しますが
1.特長量マップを平坦化
2.モノクロにコンターっぽい色を付ける
3.14x14を入力画像サイズに
4.合成
を追加しまして
わりと見れる感じに塗り絵できました
素人にならGrad-CAM(のようなもの)で押し切れる!?!?
#スクリプト全体
ファイル名とかフォルダ名は適時適当に変換してください
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))