[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개의 댓글