확인해볼 예시 코드는 ResNet imageNet 데이터를 사전학습한 신경망 모델에 gradCAM을 붙인 코드이다.
gradCAM은 CNN 모델 맨 마지막 컨볼루션 층에 연결하여 해당 데이터의 가중치를 토대로 이미지에 덧씌우는 기술이다. 그러나 LIME/SHAP은 가능한 모든 변수들을 제거한 뒤 변수의 중요도를 측정하며 진정한 의미의 설명 가능 인공지능이라고 말할 수 있다.
model=ResNet50(weights='imagenet')
model.summary()
layer | output shape | param | connect to |
---|---|---|---|
input_1 (InputLayer) | (None, 224, 224, 3) | 0 | [] |
... | ... | ... | ... |
conv1_bn (BatchNormalization) | (None, 14, 14, 256) | 1024 | 'conv4_block1_1_conv[0][0]' |
... | ... | ... | ... |
avg_pool (GlobalAveragePooling2D) | (None, 2048) | 0 | 'conv5_block3_out[0][0]' |
image_path='/content/drive/MyDrive/gradcam/hummingbird.jpg'
img=image.load_img(image_path,target_size=(224,224))
plt.matshow(img)
x=image.img_to_array(img)
x=np.expand_dims(x,axis=0)
x=resnet50.preprocess_input(x)
x=resnet50.preprocess_input(x)
preds=model.predict(x)
print("예측 결과:", resnet50.decode_predictions(preds,top=5)[0])
print("예측 결과:", resnet50.decode_predictions(preds,top=5)[0])
imageNet은 천 개의 카테고리로 분류하므로, 천 개 카데고리 별로 가능성을 보여준다. 그렇기 때문에 가장 가능성이 높은 5개의 카테고리만 표시한다.
last_conv_layer=model.get_layer("conv5_block3_out")
model_1=keras.Model(model.inputs,last_conv_layer.output)
input_2=keras.Input(shape=last_conv_layer.output.shape[1:])
x_2=model.get_layer("avg_pool")(input_2)
x_2=model.get_layer("predictions")(x_2)
model_2=keras.Model(input_2,x_2)
특징 추출 부분만으로 구성된 모델 model_1을 만들고, 미분한 값들의 합을 구하기 위해 분류(전역 평균 풀링 또는 완전 연결층) 부분으로만 구성된 모델 model_2를 구한다.
last_conv_layer=model.get_layer("conv5_block3_out")
model_1=keras.Model(model.inputs,last_conv_layer.output)
input_2=keras.Input(shape=last_conv_layer.output.shape[1:])
x_2=model.get_layer("avg_pool")(input_2)
x_2=model.get_layer("predictions")(x_2)
model_2=keras.Model(input_2,x_2)
with tf.GradientTape() as tape:
output_1=model_1(x)
tape.watch(output_1)
preds=model_2(output_1)
class_id=tf.argmax(preds[0])
output_2=preds[:,class_id]
모델1을 모델2로 미분하는 함수이다.
tape.watch(output_1)
grads=tape.gradient(output_2,output_1) # 그레이디언트 계산
pooled_grads=tf.reduce_mean(grads,axis=(0,1,2)) # 식 (12.5) 적용
그래디언트를 계산한다.
output_1=output_1.numpy()[0]
pooled_grads=pooled_grads.numpy()
for i in range(pooled_grads.shape[-1]): # 식 (12.6) 적용
output_1[:,:,i]*=pooled_grads[i]
heatmap=np.mean(output_1,axis=-1)
heatmap=np.maximum(heatmap,0)/np.max(heatmap)
plt.matshow(heatmap)
img=image.load_img(image_path)
img=image.img_to_array(img)
heatmap=np.uint8(255*heatmap)
jet=cm.get_cmap("jet")
color=jet(np.arange(256))[:,:3]
color_heatmap=color[heatmap]
color_heatmap=keras.preprocessing.image.array_to_img(color_heatmap)
color_heatmap=color_heatmap.resize((img.shape[1],img.shape[0]))
color_heatmap=keras.preprocessing.image.img_to_array(color_heatmap)
overlay_img=color_heatmap*0.4+img
overlay_img=keras.preprocessing.image.array_to_img(overlay_img)
plt.matshow(overlay_img)