ディープラーニングの畳み込みニューラルネットワーク(CNN)のヒートマップ(CAM)を可視化してみました。可視化に使用したのは、5種類の花の分類に使用したVGG16を転移学習したモデルです。VGG16は、ディープラーニングによる画像応用の代表的なモデルの一つです。ヒートマップにより、テスト画像のどの部分が分類の決め手になったのかが分かります。
VGG16(転移学習)モデルのヒートマップを表示してみる。
In [1]:
import os
import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Activation, Flatten
from keras import optimizers
from keras.applications.vgg16 import VGG16
In [2]:
keras.__version__
Out[2]:
訓練画像、検証画像、テスト画像のディレクトリ
In [3]:
# 分類クラス
classes = ['daisy', 'dandelion','rose','sunflower','tulip']
nb_classes = len(classes)
batch_size_for_data_generator = 20
base_dir = "."
test_dir = os.path.join(base_dir, 'test_images')
test_daisy_dir = os.path.join(test_dir, 'daisy')
test_dandelion_dir = os.path.join(test_dir, 'dandelion')
test_rose_dir = os.path.join(test_dir, 'rose')
test_sunflower_dir = os.path.join(test_dir, 'sunflower')
test_tulip_dir = os.path.join(test_dir, 'tulip')
# 画像サイズ
img_rows, img_cols = 200, 200
VGG16モデル
In [4]:
input_tensor = Input(shape=(img_rows, img_cols, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
#vgg16.summary()
VGG16モデルに全結合分類器を追加する
In [5]:
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(nb_classes, activation='softmax'))
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))
model.summary()
In [6]:
model.compile(loss='categorical_crossentropy',optimizer=optimizers.RMSprop(lr=1e-5), metrics=['acc'])
学習結果を読み出す
In [7]:
hdf5_file = os.path.join(base_dir, 'flower-model.hdf5')
model.load_weights(hdf5_file)
In [8]:
import matplotlib.pyplot as plt
In [9]:
%matplotlib inline
実際にテスト画像を分離してみる
In [10]:
import numpy as np
from keras.preprocessing.image import load_img, img_to_array
from keras.applications.vgg16 import preprocess_input
In [11]:
filename = os.path.join(test_dir, 'sunflower')
filename = os.path.join(filename, '3681233294_4f06cd8903.jpg')
filename
Out[11]:
In [12]:
from PIL import Image
In [13]:
img = np.array( Image.open(filename))
plt.imshow( img )
Out[13]:
In [14]:
img = load_img(filename, target_size=(img_rows, img_cols))
x = img_to_array(img)
x = np.expand_dims(x, axis=0)
predict = model.predict(preprocess_input(x))
for pre in predict:
y = pre.argmax()
print("test result=",classes[y], pre)
ヒートマップを出力する予測を選択する
In [15]:
from keras import backend as K
In [16]:
img = load_img(filename, target_size=(img_rows, img_cols))
x = img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
In [17]:
np.argmax(predict[0])
Out[17]:
In [18]:
model_output = model.output[:,3]
In [19]:
last_conv_layer = model.get_layer('block5_conv3')
In [20]:
grads = K.gradients(model_output, last_conv_layer.output)[0]
In [21]:
pooled_grads = K.mean(grads, axis =(0,1,2))
In [22]:
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])
In [23]:
pooled_grads_value, conv_layer_output_value = iterate([x])
In [24]:
for i in range(512):
conv_layer_output_value[:,:,i] *= pooled_grads_value[i]
In [25]:
heatmap = np.mean(conv_layer_output_value, axis = -1)
In [26]:
heatmap = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
In [27]:
plt.matshow(heatmap)
Out[27]:
ヒートマップを入力画像にスーパーインポーズする
In [28]:
import cv2
In [29]:
img = cv2.imread(filename)
In [30]:
heatmap = cv2.resize(heatmap,(img.shape[1],img.shape[0]))
heatmap = np.uint8(255 * heatmap)
In [31]:
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
superimposed_img = heatmap * 0.4 + img
In [32]:
cv2.imwrite('flower_cam.jpg', superimposed_img)
Out[32]:
In [33]:
img = np.array( Image.open('flower_cam.jpg'))
plt.imshow( img )
Out[33]:
<参考>
Francois Chollet 著、株式会社クイープ 翻訳、巣籠悠輔 監訳、「PythonとKerasによるディープラーニング」、マイナビ出版