[gradCAM] CODE

공부·2022년 12월 5일
0

확인해볼 예시 코드는 ResNet imageNet 데이터를 사전학습한 신경망 모델에 gradCAM을 붙인 코드이다.

gradCAM은 CNN 모델 맨 마지막 컨볼루션 층에 연결하여 해당 데이터의 가중치를 토대로 이미지에 덧씌우는 기술이다. 그러나 LIME/SHAP은 가능한 모든 변수들을 제거한 뒤 변수의 중요도를 측정하며 진정한 의미의 설명 가능 인공지능이라고 말할 수 있다.

model=ResNet50(weights='imagenet') 
model.summary()
  • 파인 튜닝 없이 이미지넷에 있는 가중치를 그대로 쓰겠다.

input layer

layeroutput shapeparamconnect 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]'
  • 학습시킬 이미지 개수를 지정하지 않음2/224 x 224로 resize / rgb colors
  • gradiant 문제 해결하기 위해 Batch size만큼 모델을 돌릴 때 Normalization를 한다. drop out을 넣어 overfitting을 했듯 모델 중간중간 BatchNormalization layer을 쌓아준다.

지정된 영상을 불러와 크기를 조정하고 화면에 디스플레이한다.

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)

  • resNet은 모든 이미지를 255로 나누어 정규화하는 대신 데이터 별로 분산, 평균을 구하여 그 값으로 정규화하는 preprocess한다.

이미지 x를 predict함수로 예측한다.

preds=model.predict(x)
print("예측 결과:", resnet50.decode_predictions(preds,top=5)[0]) 

print("예측 결과:", resnet50.decode_predictions(preds,top=5)[0])

imageNet은 천 개의 카테고리로 분류하므로, 천 개 카데고리 별로 가능성을 보여준다. 그렇기 때문에 가장 가능성이 높은 5개의 카테고리만 표시한다.

gradiant를 구하기 위해 모델을 두 가지로 나눈다.

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")

  • 맨 마지막 컨볼루션 층을 구한다.
  • summary를 찍어봤을 때 avg_pool층 바로 위가 마지막 컨볼루션 층이라는 걸 알 수 있다.

model_1=keras.Model(model.inputs,last_conv_layer.output)

  • 특징 추출 부분으로만 이루어져있다.

input_2=keras.Input(shape=last_conv_layer.output.shape[1:])

  • last_conv 이후 layer를 input2에 담는다.

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_2 만든다.

model_1과 model_2를 이용하여 gradiant를 계산한다.

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) 적용

그래디언트를 계산한다.

(편미분 값 x 특징맵)을 계산하여 최종 heap맵을 만들고 이미지에 덧씌운다.

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)
  • 편미분 값을 구해 특징맵과 곱하여 heapmap을 만든다.
heatmap=np.maximum(heatmap,0)/np.max(heatmap) 
plt.matshow(heatmap)
  • [0,1]로 정규화한다.
  • heap맵이 여러장이 생긴다.
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]
  • [0,255] 열지도를 jet 컬러맵으로 표시한다.
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)
  • 원본 이미지를 heapmap으로 변환함.
overlay_img=color_heatmap*0.4+img 
overlay_img=keras.preprocessing.image.array_to_img(overlay_img)
plt.matshow(overlay_img)
  • heap맵을 원본 이미지에 덧씌운다.
profile
리액트

0개의 댓글