마지막으로는 원본 이미지에 hitmap을 그리는 방법입니다.

- 예제에 쓰일 모델 로드

from tensorflow.python.keras.applications.vgg16 import VGG16

model = VGG16(weights = 'imagenet')

 

- VGG16을 위한 이미지 전처리

from tensorflow.python.keras.preprocessing import image
from tensorflow.python.keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

img_path = './datasets/creative_commons_elephant.jpg'
img = image.load_img(img_path, target_size = (224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis= 0) # (1, 224, 224, 3)
x = preprocess_input(x) # 데이터 전처리  - Imagenet의 전처리는 조금 다르다.

 

이를 통해, 

decode_predictions 함수는 ImageNet 데이터셋에 대한 top 매개변수 만큼의 최상위 항목을 반환해 줍니다.

다음은 이미지에서 가장 아프리카 코끼리 같은 부위를 시각화하기 위해 Grad_CAM 처리 과정을 구현한 것입니다.

import tensorflow as tf

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())


    # 예측 벡터의 '아프리카 코끼리' 항목
    african_elephant_output = model.output[:, 386]
    
    # VGG16의 마지막 합성곱 층인 block5_conv3 층의 특성 맵
    last_conv_layer = model.get_layer('block5_conv3')

    # block5_conv3의 특성 맵 출력에 대한 '아프리카 코끼리' 클래스의 그래디언트
    grads = K.gradients(african_elephant_output, last_conv_layer.output)[0]

    # 특성 맵 채널별 그래디언트 평균 값이 담긴 (512,) 크기의 벡터
    pooled_grads = K.mean(grads, axis=(0, 1, 2))

    # 샘플 이미지가 주어졌을 때 방금 전 정의한 pooled_grads와 block5_conv3의 특성 맵 출력을 구합니다
    iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])

    # 두 마리 코끼리가 있는 샘플 이미지를 주입하고 두 개의 넘파이 배열을 얻습니다
    pooled_grads_value, conv_layer_output_value = iterate([x])

    # "아프리카 코끼리" 클래스에 대한 "채널의 중요도"를 특성 맵 배열의 채널에 곱합니다
    for i in range(512):
        conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

    # 만들어진 특성 맵에서 채널 축을 따라 평균한 값이 클래스 활성화의 히트맵입니다
    heatmap = np.mean(conv_layer_output_value, axis=-1)

 

heatmap을 얻은 뒤, 정규화를 진행하고 

다음과 같은 사진을 얻었습니다.

OpenCv를 활용하여 이미지에 덧붙이면

import cv2

img = cv2.imread(img_path)
# heatmap을 원본 이미지 크기에 맞게 변경
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))
# heatmap을 RGB 포맷으로 변환
heatmap = np.uint8(255 * heatmap)
# 히트맵으로 변환
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
# 0.4는 히트맵의 강도
superimposed_img = heatmap * 0.4 + img

cv2.imwrite('./datasets/elephant_cam.jpg', superimposed_img)

 

다음과 같은 히트맵을 얻을 수 있습니다.

( 결과가 다를 수 있습니다 )

 

Reference

본 코드의 내용은 '케라스 창시자에게 배우는 딥러닝' 교재의 github를 참조 하였습니다. -- 

https://github.com/fchollet/deep-learning-with-python-notebooks