tf-keras-visでの特徴部位特定(Saliency・GradCAM)


 

関連記事: OpenCVで顕著性検出(Saliency Detection)  TensorFlow 2.0 主な変更点

前回の記事はOpenCVに入っている顕著性(Saliency)について解説しました。今回の記事はkeras-visでの特徴部位特定(Saliency・GradCAM)について解説したいと思います。

目次

1. tf-keras-visの特徴部位特定の概要
__1.1 Saliencyとは
__1.2 tf-keras-visのSaliencyのライブラリ

2. 実験・コード
__2.1 環境の準備
__2.2 モデルの読み込み
__2.3. データロード
__2.4. Vanilla Saliency
__2.5. SmoothGrad
__2.6. GradCAM

1.keras-visの特徴部位特定の概要

1.1 Saliencyとは

Saliencyの背景にあるアイデアは今となっては かなりシンプルです。 入力画像に対する出力カテゴリの勾配(変化率)を計算します。入力の小さな変化に関して出力値がどのように変化するかを可視化します。 出力の最も大きな変化の要因となる入力領域を強調するために、勾配(変化率)を使うことができます。

1.2  tf-keras-visのライブラリ

tf-keras-visは、Tensorflow2でtf.kerasモデルを可視化するための視覚化ツールキットです。ただしtf-keras-visの機能はkeras-visに基づいていますが、tf-keras-visのAPIは通常のTensorflowのラッパーの独立していた時のKerasとのAPIとの互換性を備えていません。

tf-keras-visの可視化は3つの大きな特徴があります。
1. 高密度レイヤーを視覚化(Visualize Dense Layer)
2. 畳み込みレイヤーの可視化(Visualize Convolutional Filer)
3. 顕著性マップGradCAM(Saliency Map and GradCAM)

今回は3番目の変化率の計算をしている顕著性マップ(Sileancy Map) と GradCAMを紹介します。

2. 実験・コード

概要:
入力データ: ウィキペディアからの画像
環境:Google Colab GPU
ライブラリ: tf-keras-vis
実験:Vanilla Saliency  SmoothGrad  GradCAM

2.1 環境の準備

tf-keras-visのライブラリをインストールします。

!pip install tf-keras-vis tensorflow

ライブラリのインポート

%reload_ext autoreload
%autoreload 2

import tensorflow as tf
from tf_keras_vis.utils import print_gpus

print_gpus()

1 Physical GPUs, 1 Logical GPU

2.2 モデルの読み込み

VGG16モデルを読み込みます。

from tensorflow.keras.applications.vgg16 import VGG16 as Model
from tensorflow.keras.applications.vgg16 import preprocess_input

# Load model
model = Model(weights='imagenet', include_top=True)
model.summary()

2.3. データロード

ウィキメディアから2枚の画像をテスト用にダウンロードします。

# 画像をロード
import urllib
img_src1 = "https://upload.wikimedia.org/wikipedia/commons/f/ff/Cristiano_Ronaldo_Euro2012_training_01.jpg"
img_src2 = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Eurasian_tree_sparrows_feeding_from_the_hand_in_Ueno_Park%2C_Tokyo%2C_Japan.jpg/480px-Eurasian_tree_sparrows_feeding_from_the_hand_in_Ueno_Park%2C_Tokyo%2C_Japan.jpg"

img_path1 = 'input_image1.jpg'
img_path2 = 'input_image2.jpg'
urllib.request.urlretrieve(img_src1, img_path1)
urllib.request.urlretrieve(img_src2, img_path2)

画像を加工して、画像を確認します。

import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
from tensorflow.keras.preprocessing.image import load_img

# 画像をロード
img1 = load_img('input_image1.jpg', target_size=(224, 224))
img2 = load_img('input_image2.jpg', target_size=(224, 224))
images = np.asarray([np.array(img1), np.array(img2)])

# データセットの加工
X = preprocess_input(images)

# 画像を表示
subprot_args = {
   'nrows': 1,
   'ncols': 2,
   'figsize': (6, 3),
   'subplot_kw': {'xticks': [], 'yticks': []}
}
f, ax = plt.subplots(**subprot_args)
for i in range(len(images)):
   ax[i].imshow(images[i])
plt.tight_layout()
plt.show()

損失関数を設定します。

# loss functionの設定
def loss(output):
   return (output[0][1], output[1][294])

# Define modifier to replace a softmax function of the last layer to a linear function.
def model_modifier(m):
   m.layers[-1].activation = tf.keras.activations.linear
   return m

2.4. Vanilla Saliency

顕著性(Saliency)は、入力値の変化が出力値に寄与する入力領域に表示される顕著性マップ(もっとも簡単な変化率)を生成します。
結果: 両方の画像は特徴部位特定がはっきりしません。このままでは使えないためデータ加工は必要です。

from tensorflow.keras import backend as K
from tf_keras_vis.saliency import Saliency
from tf_keras_vis.utils import normalize

# Create Saliency object
saliency = Saliency(model, model_modifier, clone=False)

# Generate saliency map
saliency_map = saliency(loss, X)
saliency_map = normalize(saliency_map)

f, ax = plt.subplots(**subprot_args)
for i in range(len(saliency_map)):
   ax[i].imshow(saliency_map[i], cmap='jet')
plt.tight_layout()
plt.show()

2.5. SmoothGrad

ノイズを追加することによりノイズを低減するスムージングで顕著性マップを生成します。
結果: 左の画像は顔の特徴部位特定ができました。右の画像は鳥の特徴部位特定ができました。

# SmoothGrad saliency作成
saliency_map = saliency(loss, X, smooth_samples=20)
saliency_map = normalize(saliency_map)

f, ax = plt.subplots(**subprot_args)
for i in range(len(saliency_map)):
   ax[i].imshow(saliency_map[i], cmap='jet')
plt.tight_layout()
plt.show()

2.6. GradCAM

GradCAMは、入力に対する注意を視覚化するもう1つの方法であり、モデル出力に関して勾配を使用する代わりに、最後から2番目(高密度層)のConv層出力を使用します。
結果:画像の特徴部位特定が出来ているように見えますが、左の画像では人の顔の部分の特徴がやや薄れていることがわかります。

from matplotlib import cm
from tf_keras_vis.gradcam import Gradcam
from tf_keras_vis.utils import normalize

# Create Gradcam object
gradcam = Gradcam(model, model_modifier, clone=False)

# Generate heatmap with GradCAM
cam = gradcam(loss, X)
cam = normalize(cam)

f, ax = plt.subplots(**subprot_args)
for i in range(len(cam)):
   heatmap = np.uint8(cm.jet(cam[i])[..., :3] * 255)
   ax[i].imshow(images[i])
   ax[i].imshow(heatmap, cmap='jet', alpha=0.5)
   plt.tight_layout()
plt.show()