[CNN] HeatMap

2021. 6. 12. 10:32Artificial_Intelligence/Computer Vision

저장된 CNN 모델을 가져와서 이를 사용해 이미지의 필터를 먼저 시각화 해보았다.

그리고 고양이사진을 하나 가져와서 이를 전처리하여 출력하였다.

img_path = './dogs-vs-cats/small/test_dir/test_cats_dir/cat.1700.jpg'

from keras.preprocessing import image
import numpy as np

img = image.load_img(img_path, target_size=(150, 150))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
# 모델이 훈련될 때 입력에 적용한 전처리 방식을 동일하게 사용합니다
img_tensor /= 255.
print(img_tensor.shape)

import matplotlib.pyplot as plt

plt.imshow(img_tensor[0])
plt.show()

그리고 이 사진에 대한 특성맵을 출력해보았다.

layer_names = []
for layer in model.layers[:8]:
    layer_names.append(layer.name)

images_per_row = 16

for layer_name, layer_activation in zip(layer_names, activations):
    # 특성 맵에 있는 특성의 수
    n_features = layer_activation.shape[-1]

    # 특성 맵 크기 (1, size, size, n_features)
    size = layer_activation.shape[1]

    # 활성화 채널을 위한 그리드 크기
    n_cols = n_features // images_per_row
    display_grid = np.zeros((size * n_cols, images_per_row * size))

    # 각 활성화를 그리드에 채우기
    for col in range(n_cols):
        for row in range(images_per_row):
            channel_image = layer_activation[0,
                                             :, :,
                                             col * images_per_row + row]
           
            channel_image -= channel_image.mean()
            channel_image /= channel_image.std()
            channel_image *= 64
            channel_image += 128
            channel_image = np.clip(channel_image, 0, 255).astype('uint8')
            display_grid[col * size : (col + 1) * size,
                         row * size : (row + 1) * size] = channel_image

    scale = 1. / size
    plt.figure(figsize=(scale * display_grid.shape[1],
                        scale * display_grid.shape[0]))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')

plt.show()

 

초기 레이어는 모든 정보가 유지되지만, 상위층으로 갈수록 활성화는 더 추상적이고 고수준의 개념을 인코딩함.

필터에 인코딩된 패턴이 입력 이미지에 없어서 비어있는 활성화가 깊어질수록 많아진다.

 

이제 VGG16과 케라스 백엔드 라이브러리를 가져와 사용하는데, 케라스가 업데이트되면서 backend 지원이 통합되서 따로 못쓴다. 이전 버전의 코드를 사용하기위해 아래의 코드를 추가하여 이전 버전 코드 사용을 가능하게 만든다.

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
from keras.applications import VGG16
from keras import backend as K

model = VGG16(weights='imagenet')
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

img_path = './animal/a1.jpg'

# 224 × 224 크기의 파이썬 이미징 라이브러리(PIL) 객체로 반환
img = image.load_img(img_path, target_size=(224, 224))

# (224, 224, 3) 크기의 넘파이 float32 배열
x = image.img_to_array(img)

# 차원을 추가하여 (1, 224, 224, 3) 크기의 배치로 배열을 변환
x = np.expand_dims(x, axis=0)

# 데이터 전처리(채널별 컬러 정규화)
x = preprocess_input(x)

a1 사진은 코뿔소 사진으로 하였다.

a1.jpg

preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])

np.argmax(preds[0])

여기서 Predicted 값이 출력되면서 무엇으로 예측했는지, 몇프로의 확률로 확신하는지 출력이 되고, 이에 대한 index값이 출력된다. 이를 통해 사진과 히트맵을 합쳐서 확인을 한다.

# 이미지 경로
img_path = './animal/a10.jpg'

# 224 × 224  파이썬 이미징 라이브러리(PIL) 객체로 반환
img = image.load_img(img_path, target_size=(224, 224))

# (224, 224, 3) 넘파이 float32 배열
x = image.img_to_array(img)

# 차원 추가, (1, 224, 224, 3)
x = np.expand_dims(x, axis=0)

# 데이터 전처리(채널별 컬러 정규화)
x = preprocess_input(x)

#이미지 출력 위해 다시
img1 = image.load_img(img_path, target_size=(150, 150))
img_tensor = image.img_to_array(img1)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor /= 255.

#이미지출력
plt.imshow(img_tensor[0])
plt.show()


preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])

np.argmax(preds[0])
import cv2

a10_output = model.output[:, 975]

last_conv_layer = model.get_layer('block5_conv3')

grads = K.gradients(a10_output, last_conv_layer.output)[0]

pooled_grads = K.mean(grads, axis=(0, 1, 2))

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 = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
plt.matshow(heatmap)
plt.show()

img = cv2.imread(img_path)

heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))

heatmap = np.uint8(255 * heatmap)

heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)

superimposed_img = heatmap * 0.4 + img

cv2.imwrite('./animal/b10.jpg', superimposed_img)

이렇게 시각화를 하여 어떤 동물로 예측하고, 이유는 무엇인지 히트맵을 통해 알 수 있게 된다.

728x90