[XAI] grad_cam 라이브러리를 활용한 CAM (Class Activation Mapping) 사용법

es.Seong·2024년 7월 13일
0

CAM (Class Activation Mapping)

개발 환경
os : linux
python == 3.10.12
torch == 2.1.0
torchvision == 0.16.0
grad-cam ==1.5.2
opencv-python == 4.8.0.74

Classification 프로젝트를 진행하면서 각 Class가 이미지의 어떤 부분의 보고 분류되었는지 시각적으로 나타낼 수 있다.

출처 : Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization (2019)

위 이미지와 같이 히트맵을 통해 모델이 이미지의 어느 부분을 보고 특정 클래스로 분류했는지를 시각적으로 알 수 있게 하는 XAI(eXplainable Artificial Intelligence) 기법이다.

현재 CAM 기법은 Grad-CAM, Ablation-CAM, Score-CAM,Grad-CAM++, Eigen-CAM 등 다양한 변형 기법이 존재한다.

이 글에서는 이론적인 부분보다는 파이토치 환경에서 사용하는 방법에 대해 알아본다.

grad_cam GitHub
https://github.com/jacobgil/pytorch-grad-cam

현재 CAM 시리즈는 파이썬 라이브러리로 제공되고 있다.

pip install grad-cam ==1.5.2
pip install opencv-python == 4.8.0.74

grad-cam 라이브러리 설치 후 라이브러리 import에서 cv2 충돌 오류가 발생한다.
grad-cam에 호환되는 opencv 버전을 찾아 해결해주었다.

주로 모델의 Evaluate 과정에서 사용하기 때문에 학습 중 Validation Dataset 평가 코드에 사용하거나 Test Data의 평가 코드에 부가적으로 사용하면된다.

예시 코드

import torch
import matplotlib.pyplot as plt
import seaborn as sns
from pytorch_grad_cam import GradCAM, AblationCAM, ScoreCAM, GradCAMPlusPlus, XGradCAM, EigenCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
import os
import numpy as np
from PIL import Image

def evaluate(parameters...):
    model.eval()
    correct = 0
    total = 0

    # 마지막 컨볼루션 레이어 이름
    target_layers = [model.LastLayerName]

    cam = GradCAM(model=model, target_layers=target_layers)
	
    with torch.no_grad():
	...모델 평가 코드 ...
    
    # Grad-CAM을 위해 requires_grad를 True로 설정
    for idx, (inputs, labels) in enumerate(test_loader):
        inputs, labels = inputs.to(device), labels.to(device)
        inputs.requires_grad = True
        outputs = model(inputs)
        
        # Grad-CAM 시각화 1채널 데이터 사용
        grayscale_cam = cam(input_tensor=inputs, targets=None)

        for i in range(inputs.size(0)):
            input_image = inputs[i].detach().cpu().numpy().transpose(1, 2, 0)
            input_image = (input_image - input_image.min()) / (input_image.max() - input_image.min())
            visualization = show_cam_on_image(input_image, grayscale_cam[i, :], use_rgb=True)
      
            # PIL을 사용하여 이미지 저장
            visualization = Image.fromarray(visualization)
            visualization.save(f'./grad_cam_{idx}_{i}.png')

    test_acc = correct / total

CAM은 컨볼루션의 제일 마지막 레이어의 그래디언트를 활용하여 나타낸다.
그렇기 때문에 target_layers라는 값이 반드시 필요하다.
이는 print(model)을 출력해보면 마지막 레이어의 이름을 알 수 있다.

예시로 사용한 컨볼루션 기반의 분류 모델이다. 드래그 된 conv4가 [model.LastLayerName]의 LastLayerName 대신 사용해주면된다.
즉, target_layers = [model.conv4] 이렇게 입력하면 된다.

cam = GradCAM(model=model, target_layers=target_layers)을 통해 사용할 CAM을 정의한다.

그리고 inputs.requires_grad = True을 사용하용하였다. CAM은 입력 이미지에 대한 그래디언트를 계산하여 활성화 맵을 생성하기 때문에, 입력 텐서가 그래디언트를 계산할 수 있도록 requires_grad 속성을 True로 설정해야 한다. 코드를 추가하지 않을 경우 에러가 발생하였다.

visualization = show_cam_on_image(input_image, grayscale_cam[i, :], use_rgb=True)
show_cam_on_image 함수를 통해 CAM의 히트맵을 얻고 이를 저장해주면 코드는 종료된다.

위 코드는 1차원 Gray Scale 이미지를 기준으로 진행된 코드이며, 비공개 코드로 인하여 수정, 생략된 부분이 있습니다.

감사합니다.

profile
Graduate student at Pusan National University, majoring in Artificial Intelligence

0개의 댓글

관련 채용 정보