Skip to content

Instantly share code, notes, and snippets.

@you359
Last active August 22, 2018 04:35
Show Gist options
  • Save you359/3874531b11f337ceeecda3a5174501b8 to your computer and use it in GitHub Desktop.
Save you359/3874531b11f337ceeecda3a5174501b8 to your computer and use it in GitHub Desktop.
Class Activation Map
from keras.applications.resnet50 import ResNet50
from keras.applications.resnet50 import preprocess_input, decode_predictions
from keras.preprocessing import image
import keras.backend as K
import numpy as np
import cv2
def load_image(path, target_size=(224, 224)):
x = image.load_img(path, target_size=target_size)
x = image.img_to_array(x)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
return x
def generate_cam(img_tensor, model, class_index, last_conv):
"""
generate cam(class activation map)
:param img_tensor: CNN model의 입력으로 사용될 image tensor
:param model: 사용할 CNN model
- softmax(+ dense) 이전의 layer가 GlobalAveragePooling2D이어야 함
- dense(fc) layer로 구성되면 안됨 -> VGGNet과 같은 모델은 Dense layer를 GAP로 변경 후 재학습(re-train)시킨 후 적용
:param class_index: cam을 얻고자 하는 class index
:param last_conv: GAP 이전의 마지막 Conv layer 이름
:return:
cam: class activation map
- shape는 last_conv layer의 feature map과 동일
"""
model_input = model.input
model_output = model.layers[-1].output
# f_k(x, y) : 마지막 conv layer의 출력 feature map
f_k = model.get_layer(last_conv).output
# model의 입력에 대해서,
# 마지막 conv layer의 출력(f_k) 계산
get_output = K.function([model_input], [f_k])
[last_conv_output] = get_output([img_tensor])
# batch size가 포함되어 shape가 (1, width, height, k)이므로
# (width, height, k)로 shape 변경
# 여기서 width, height는 마지막 conv layer인 f_k feature map의 width와 height를 의미함
last_conv_output = last_conv_output[0]
# softmax(+ dense) layer와 GAP layer 사이의 weight matrix에서
# class_index에 해당하는 class_weight_k(w^c_k) 계산
# ex) w^2_1, w^2_2, w^2_3, ..., w^2_k
class_weight_k = model.layers[-1].get_weights()[0][:, class_index]
# 마지막 conv layer의 출력 feature map(last_conv_output)과
# class_index에 해당하는 class_weight_k(w^c_k)를 k에 대응해서 weighted sum을 구함
# feature map(last_conv_output)의 (width, height)로 초기화
cam = np.zeros(dtype=np.float32, shape=last_conv_output.shape[0:2])
for k, w in enumerate(class_weight_k):
cam += w * last_conv_output[:, :, k]
return cam
if __name__ == "__main__":
img_width = 224
img_height = 224
model = ResNet50(weights='imagenet')
print(model.summary())
img_path = '../image/elephant.jpg'
img = load_image(path=img_path, target_size=(img_width, img_height))
preds = model.predict(img)
predicted_class = preds.argmax(axis=1)[0]
# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)
print("predicted top1 class:", predicted_class)
print('Predicted:', decode_predictions(preds, top=1)[0])
# Predicted: [(u'n02504013', u'Indian_elephant', 0.82658225), (u'n01871265', u'tusker', 0.1122357), (u'n02504458', u'African_elephant', 0.061040461)]
cam = generate_cam(img, model, predicted_class, 'res5c_branch2c')
## 최대값이 1이 되도록 normalize
cam = cam / cam.max()
cam = cam * 255
print(cam)
cam = cv2.resize(cam, (img_width, img_height))
cam = np.uint8(cam)
img = cv2.imread(img_path)
img = cv2.resize(img, (img_width, img_height))
cv_cam = cv2.applyColorMap(cam, cv2.COLORMAP_JET)
fin = cv2.addWeighted(cv_cam, 0.7, img, 0.3, 0)
cv2.imshow('cam', cv_cam)
cv2.imshow('image', img)
cv2.imshow('cam on image', fin)
cv2.waitKey()
cv2.destroyAllWindows()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment