728x90
반응형
Class Activation Mapping (CAM)과 Gradient-weighted Class Activation Mapping (Grad-CAM)은 딥러닝 모델,
특히 CNN(Convolutional Neural Network)의 예측 과정을 시각화하는 강력한 도구들입니다.
이 방법들은 모델이 이미지의 어느 부분을 보고 특정 클래스를 예측했는지 시각적으로 이해할 수 있게 도와줍니다.
1. Class Activation Mapping (CAM)
CAM은 특정 클래스에 대한 예측에 기여하는 이미지의 영역을 시각화하는 방법입니다.
이 방법은 일반적으로 모델의 마지막 컨볼루션 레이어와 완전 연결(FC) 레이어 사이에서 이루어집니다.
CAM의 기본 아이디어는 각 클래스에 대해, 모델이 어느 영역에 주목했는지를 확인하는 것입니다.
CAM의 작동 방식
- 모델 구조 변경
CAM을 사용하려면 모델 구조를 약간 변경해야 합니다. 특히, 전통적인 완전 연결 레이어를
글로벌 평균 풀링(Global Average Pooling, GAP) 레이어로 대체합니다. - Class Activation Map 생성:
GAP 레이어와 그 뒤에 이어진 FC 레이어의 가중치로부터 각 클래스에 대한 활성화 맵을 생성합니다.
이 맵은 이미지의 특정 부분이 모델의 예측에 얼마나 기여했는지를 나타냅니다.
CAM의 한계
- CAM은 모델 구조를 수정해야 하며, 기존의 잘 학습된 모델에는 적용하기 어렵습니다.
- 또한, 마지막 컨볼루션 레이어와 FC 레이어 사이의 정보만을 시각화할 수 있기 때문에,
보다 세밀한 시각화에는 적합하지 않을 수 있습니다.
2. Gradient-weighted Class Activation Mapping (Grad-CAM)
Grad-CAM은 CAM의 확장된 버전으로, 모델 구조를 변경하지 않고도 활성화 맵을 생성할 수 있는 방법입니다.
Grad-CAM은 이미지에서 특정 클래스에 대한 활성화 맵을 계산할 때, 해당 클래스의 그라디언트(gradient)를 이용합니다. 이 방법은 기존에 학습된 모델에도 적용할 수 있으며, 더 많은 유연성을 제공합니다.
Grad-CAM의 작동 방식:
- 모델의 마지막 컨볼루션 레이어 선택
Grad-CAM은 모델의 마지막 컨볼루션 레이어를 사용하여, 그 레이어의 출력을 기반으로 활성화 맵을 생성합니다. - Gradient 계산
특정 클래스에 대한 출력의 그라디언트를 계산하여, 각 특징 맵의 중요도를 측정합니다. - 가중치 합산
각 특징 맵을 그 중요도에 따라 가중치합을 수행하여 최종 Class Activation Map을 생성합니다. - ReLU 적용
음수의 기여도를 제거하기 위해 ReLU 함수를 적용하여 최종적으로 양수의 영역만 강조합니다.
3. Grad-CAM 적용 코드
import torch
import torch.nn.functional as F
from torchvision import models, transforms
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from torchvision.models import VGG16_Weights
import os
# 사전 학습된 모델(VGG16)을 로드하고 평가 모드로 설정
model = models.vgg16(weights=VGG16_Weights.IMAGENET1K_V1)
model.eval()
# 이미지를 전처리하는 함수
preprocess = transforms.Compose([
transforms.Resize((224, 224)),
transforms.Lambda(lambda img: img.convert("RGB")),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
# 이미지를 로드하고 전처리
img = Image.open('./tiger.jpg')
input_tensor = preprocess(img)
input_batch = input_tensor.unsqueeze(0) # 배치 차원을 추가
# Grad-CAM을 계산하기 위해 마지막 컨볼루션 레이어의 출력을 추출
def get_activation(model, input_batch):
activation = None
def hook(model, input, output):
nonlocal activation
activation = output
activation.retain_grad() # 이 위치에서 retain_grad() 호출
layer = model.features[-1] # VGG16의 마지막 컨볼루션 레이어
hook_handle = layer.register_forward_hook(hook)
output = model(input_batch)
hook_handle.remove()
return activation, output
# 그라디언트를 추출하고 Grad-CAM 맵을 생성
def compute_gradcam(activation, model_output, target_class):
model_output[:, target_class].backward(retain_graph=True) # 타겟 클래스에 대한 그라디언트 계산
gradients = activation.grad # 그라디언트 추출
pooled_gradients = torch.mean(gradients, dim=[0, 2, 3]) # 채널별로 평균
for i in range(activation.shape[1]):
activation[:, i, :, :] *= pooled_gradients[i] # 가중 합산
gradcam = torch.mean(activation, dim=1).squeeze() # 채널 평균
gradcam = F.relu(gradcam) # ReLU 적용
return gradcam
# 모델의 출력을 계산하고 타겟 클래스를 설정
activation, output = get_activation(model, input_batch)
target_class = output.argmax(dim=1).item()
# Grad-CAM 맵 계산
gradcam = compute_gradcam(activation, output, target_class)
# Grad-CAM 맵을 시각화
gradcam = gradcam.detach().numpy()
gradcam = np.maximum(gradcam, 0) # ReLU를 다시 적용 (혹시 모를 음수 제거)
gradcam = gradcam / gradcam.max() # 정규화
# 원본 이미지와 Grad-CAM을 오버레이하여 시각화
gradcam = Image.fromarray(np.uint8(gradcam * 255), 'L')
gradcam = gradcam.resize((img.size), Image.Resampling.LANCZOS)
gradcam = np.array(gradcam)
plt.imshow(img)
plt.imshow(gradcam, alpha=0.5, cmap='jet')
plt.axis('off')
plt.show()
결과
빨간색 영역
모델이 가장 주목한 부분입니다. 이 영역이 예측에 가장 큰 영향을 미쳤으며, 모델이 이 부분을 통해 클래스를 결정했다고 볼 수 있습니다.
노란색과 녹색 영역
이 부분도 예측에 기여했지만, 빨간색 영역보다는 덜 중요하게 작용했습니다.
파란색 영역
모델이 상대적으로 덜 주목하거나 거의 주목하지 않은 부분입니다.
728x90
반응형
'AI Naver boost camp > [Week 04] CV 1' 카테고리의 다른 글
딥러닝 Motion Magnification (6) | 2024.08.29 |
---|---|
ZFNet 딥러닝 모델 시각화와 데이터 증강(Augumentation) 이해 (2) | 2024.08.29 |
댓글