Grad CAM 에 대한 이해
Topic Proposal : Option II
멤버
노창호 전자공학부 sshckdgh0889@naver.com
류다윤 경영학과 ryudy4977@naver.com
신동현 전자공학부 donghyun3966@naver.com
김창성 전자공학부 kcs933442@naver.com
목차
1.글소개
AI+X:딥러닝 수업에서 Image Classification하는 CNN(Convolutional Neural Network) 모델들을 직접 실행시키며 실습을 했습니다. 저희는 이러한 CNN 모델들이 어떻게 사진들을 보고 고양이나 강아지처럼 카테고리를 나누는지에 대한 궁금증이 생겼습니다. 이러한 궁금증을 해결하기 위해서 여러 솔루션들이 제안되고 있습니다.
최근 발표된 Selvaraju RR 등의 Grad CAM (Gradient-weighted Class Activation Mapping)은 CNN 모형에 적용할 수 있는 모형 해석 방법론입니다. 쉽게 설명하면 CNN 층(마지막)에 들어가는 Gradient를 가지고 자료의 어느 부분에 가중치를 주는지 계산하는 방법이 될 것 같습니다. 별다른 구조 변경이나 재훈련 없이 바로 적용해볼 수 있다는 점, CNN이 사용되는 영역이 무척 넓다는 점이 매력적이어서 여러 솔루션 중 Grad CAM을 선정하였습니다.
이 글에서는 CNN모델이 Image Classification을 어떻게 하는지를 알기 위해 CAM이란 무엇이며 Grad CAM과 CAM의 알고리즘 차이를 통해서 소개를 하고, 이미 학습된 CNN모델에 Grad CAM적용시켜 어떤 결과가 나오는지를 확인하고자 합니다.
2.배경지식
CAM 과 Grad-CAM에대한 본격적인 설명에 앞서 뒤의 내용들을 이해하기 전에 언급될 기초적인 지식에 대해 미리 알고간다면 이해가 쉬울것입니다. 이 파트에서는 배경지식(Convolution, Pooling, Zero-Padding, Backpropagation) 에대해 말하고자 합니다.
1) Convolution (합성곱)
Convolution(합성곱)은 CNN(Convolutional Neural Network)알고리즘에 사용되기 이전부터 영상처리(image processing)분야에서 사용되던 연산 기법입니다. 우선 이미지의 픽셀마다 존재하는 벡터값과 같은 위치에 존재하는 Mask(Filter, Kernel)값을 곱하고 이 값을을 모두 합한뒤 다시 Mask가 위치했던 부분의 중앙에 값을 대입한다. 그리고 이과정을 반복하여 feature map을 만들어내는 기법입니다.
그림의 과정은 input에 image를 입력하여 픽셀단위로 값을 부여한뒤 Mask(Filter, Kernel)를 합성곱하여 feature map을 만들어내는 과정입니다. 이 과정에서 Mask(Filter, Kernel)의 역할을 이해하기 쉽게 비유하자면 neural network에서 가중치와 같은 역할을 수행한다고 보아도 무방합니다.
2) Zero-Padding(패딩)
위와 같은 과정을 거쳐서 feature map을 구성하게 되면 feature map의 크기가 input의 image보다 작아지게 되는데 이때 Convolution 레이어의 출력데이터가 줄어드는 것을 방지하기 위해서 사용되는 방법이 패딩(Padding)입니다.
패딩은 그림과 같이 입력데이터의 외각에 지정된 픽셀만큼 특정데이터 값을 채워 넣는 것으로 이과정을 통해 feature map의 데이터값이 input값과 동일해집니다.
3) Pooling(풀링)
풀링은 Convolution레이어의 출력데이터를 입력으로 받아서 출력 데이터 (Activation map)의 크기를 줄이거나 특정 데이터를 강조하는 용도로 사용되는 과정입니다.
풀링의 종류에는 Max Pooling, Average Pooling, Min Pooling이 있으며 지정된 픽셀의 영역의 값을 어떤식으로 처리하여 해상도를 낮추는지에 따라 종류가 구별됩니다. Max Pooling은 영역의 값중 최댓값을 최종값으로 처리하고 Average Pooling, Min Pooling은 각각 평균값, 최소값을 최종값으로 처리합니다.
4) Backpropagation(역전파)
역전파란 ANN(Artificial Neural Network)을 학습시키기 위한 기본적인 알고리즘으로 내가 뽑고자하는 target값과 실제모델이 계산한 output값이 얼마만큼 차이가 나는지 오차값을 구하고 그 오차값을 다시 뒤로 전파해 나아가면서 각 노드들의 변수값을 갱신해 나아가는 알고리즘입니다.
역전파 알고리즘의 순서는 다음과 같습니다. input값을 입력하여 가중치와 활성화 함수들을 거쳐서 정상적으로 output값을 출력하는 단계를 Feedfoward라 합니다. Feedfoward단계에서 출력된 output값과 목표로 하는 taget값 사이의 오차를 구한뒤 출력층에서 입력층 방향으로 경사하강법(Gradient descent method)을 사용하여 오차를 최소화하는 가중치w값을 찾아내 업데이트시키는 단계를 Backpropagation라 합니다.
이러한 과정을 통해 효율적으로 가중치를 업데이트 해나갈수 있기 때문에 널리 사용되는 역전파 이지만 한계가 존재합니다. 바로 경사하강법(Gradient descent method)에서 오는 한계인데, 경사하강법 알고리즘은 항상 전역 최소값(global minimum)을 찾는다고 보장할수 없습니다. 극소값이 2개 이상 존재하는 함수에 대해 가장 작은 최소값을 찾는다고 할수 없는 것입니다.
3-1 CAM의 정의 및 개요
CAM(Class Activation Map)은 CNN을 하는데 있어 이미지의 어떤 부분이 결정에 가장 큰 영향을 주는지에 대해 분석하는 목적으로 시작되었습니다. 대부분의 Image Classification 모델들은 여러 층의 Convolutional layer을 걸친 뒤 Fully-Connected Layer를 통해서 Classification을 진행하게 됩니다. 하지만 Convolutional layer는 layer를 거친 뒤에서 spatial information을 보존하지만 Fully-Connected Layer는 flatten 과정을 거치게 되면 spatial information의 손실이 발생하게 됩니다. 그래서 CAM은 Convolutional layer를 거친 후 Fully-Connected Layer를 바로 사용하고 GAP을 사용한 후 마지막 하나의 Fully-Connected Layer를 사용하였습니다. 여기서 CAM은 기존과 다르게 GAP(Gloval Average Pooling)을 적용시켜 마지막 판별 전까지 데이터 위치 정보가 훼손되지 않게 하였습니다.
예를 들자면, 우리는 CNN이 '개'의 이미지를 보고 '개'라고 예측을 할 때, 어느 부분을 보고 그 이미지를 '개'라고 판정을 했는지에 대해 궁금한 것 입니다. CAM이란 특정 분류의 이미지를 어떠한 분류라고 예측하게 한 그 이미지의 부분의 정보를 의미합니다.
위 사진을 보면 기본적인 구조는 Network in Networkdhk Goolenet과 비슷하지만 차이점을 보자면 CAM은 Conv Layer을 Fc-Layer로 납작하게 하지 않고, GAP을 통해 새로운 Weight값을 만들어 내고 있는 것입니다. 마지막 Conv Layer가 총 n개의 channel로 이루어져 있다면 각각의 채널들은 GAP에 의해 하나의 Weight 값으로 나타나게 되며, 총 n개의 Weight들이 생기게 됩니다. 마지막 Softmax 함수로 인해 연결되어 Weight들도 백프롭을 통해 학습시키는 것입니다. N 개의 Weight가 생겼다면 CAM은 이 Weight들과 마지막 n개의 Conv Layer들과 Weighted Sum을 하여 하나의 특정 분류의 이미지의 히트맵이 나오게 합니다. 위 사진의 아래쪽 사진을 보면 히트맵을 확인할 수 있는데 강아지의 얼굴부분의 중요도가 가장 큰 사실을 확인 할 수 있습니다.
3-2 CAM 계산
GAP(Global Average Pooling)방식을 적용하면 위의 그림에서와 같이 각 채널에서 색 별로 average 하나의 값만을 가져오게 됩니다. 그 이후에 FC를 적용하는데 이때의 weight값이 CAM을 만드는 w1, w2, w3, ... 값이 됩니다. FC의 weight는 [n_Ch, n_classes]의 크기를 갖는 matrix인데, FC matrix의 클래스index 열 벡터가 각각 w1, w2, wn,...가 됩니다.(*n_classes는 분류하려는 객체의 수) 이 weight들을 마지막 feature map에 각각 곱한 후에 더해주면 CAM이 됩니다.
라고 합니다. 이것은 본질적으로 의 중요성을 나타내며 그 크기가 클수록 c에서 가 미치는 영향이 커지게 됩니다. class c에 대해서 softmax에 입력으로 주어지는 값, 는
가 됩니다. 식을 조금 더 변형하면 3-3 CAM의 문제점
하지만 CAM 방법론은 GAP가 반드시 필요하다는 것이 가장 큰 단점입니다. GAP가 이미 포함되어 있는 경우는 괜찮지만 그렇지 않은 경우에는 마지막 convolutional layer 뒤에 GAP를 붙여서 다시 fine-tuning 해야 하며 그 결과 약간의 성능 감소가 동반될 수 있습니다. 따라서 GAP을 사용하지 않고 CNN모델 마지막 feature값과 연결 될 수 있는 weight를 활용할 수 있는 방법이 무엇일지에 대한 고민 끝에 나온 방법론이 Grad_CAM입니다.
4-1 GRAD-CAM의 정의 및 개요
4-2 Grad- CAM알고리즘
위 그림은 논문(Grad-CAM: Visual Explanations from Deep Networks via Gradient- based Localization)에 나온 대략적인 Grad-CAM의 구조입니다. 구조를 살펴보면 Classification 문제에서 Backprop을 통해 gradient 값들을 얻어 Grad-CAM을 얻게 됩니다. 이때 얻는 gradient 값은 softmax 전 단계의 클래스에 대한 특징 맵에서 얻게 되는데 이를 식으로 쓰면 다음과 같습니다.
4-3 CAM과 Grad-CAM의 차이
Grad-CAM 수식
이번에는 Image Classification에 널리 사용되고 있는 ResNet50에 Grad-CAM을 적용시킨 실제 코드를 가지고 CAM을 구하는 과정과 결과를 확인해 보겠습니다. ResNet50 Model은 Pytorch에서 기본적으로 제공되는 Model을 사용했고, ResNet50의 구조는 다음과 같이 생겼습니다.
if __name__ == '__main__':args = get_args()model = models.resnet50(pretrained=True)grad_cam = GradCam(model=model, feature_module=model.layer4, \target_layer_names=["2"], use_cuda=args.use_cuda)img = cv2.imread(args.image_path, 1)img = np.float32(cv2.resize(img, (224, 224))) / 255input = preprocess_image(img)target_index = Nonemask = grad_cam(input, target_index)cam = show_cam_on_image(img, mask)cv2.imwrite("cam.jpg", np.uint8(255 * cam))
class GradCam:
def __init__(self, model, feature_module, target_layer_names, use_cuda):
self.model = model
self.feature_module = feature_module
self.model.eval()
self.cuda = use_cuda
if self.cuda:
self.model = model.cuda()
self.extractor = ModelOutputs(self.model, self.feature_module, target_layer_names)
def forward(self, input):
return self.model(input)
def __call__(self, input, index=None):
if self.cuda:
features, output = self.extractor(input.cuda())
else:
features, output = self.extractor(input)
if index == None:
index = np.argmax(output.cpu().data.numpy())
one_hot = np.zeros((1, output.size()[-1]), dtype=np.float32)
one_hot[0][index] = 1
one_hot = torch.from_numpy(one_hot).requires_grad_(True)
if self.cuda:
one_hot = torch.sum(one_hot.cuda() * output)
else:
one_hot = torch.sum(one_hot * output)
self.feature_module.zero_grad()
self.model.zero_grad()
one_hot.backward(retain_graph=True)
grads_val = self.extractor.get_gradients()[-1].cpu().data.numpy()
target = features[-1]
target = target.cpu().data.numpy()[0, :]
weights = np.mean(grads_val, axis=(2, 3))[0, :]
cam = np.zeros(target.shape[1:], dtype=np.float32)
for i, w in enumerate(weights):
cam += w * target[i, :, :]
cam = np.maximum(cam, 0)
cam = cv2.resize(cam, input.shape[2:])
cam = cam - np.min(cam)
cam = cam / np.max(cam)
return cam
-결과
6 참고문헌 7맡은 파트
2.배경지식
1. Convolution, Zero-Padding, Pooling
블로그 자료
- http://taewan.kim/post/cnn/
- https://gruuuuu.github.io/machine-learning/cnn-doc/#
- https://untitledtblog.tistory.com/150
youtube자료
DeepLearning.ai 채널 강의 (Pooling)
- https://www.youtube.com/watch?v=8oOgPUO-TBY
2. Backpropagation
블로그 자료
- https://blog.naver.com/samsjang/221033626685
- https://goofcode.github.io/back-propagation
3-1 CAM의 배경지식 및 개요
블로그 자료
https://poddeeplearning.readthedocs.io/ko/latest/CNN/CAM%20-%20Class%20Activation%20Map/
https://jays0606.tistory.com/4
https://www.secmem.org/blog/2020/01/17/gradcam/
https://dryjelly.tistory.com/147
3-2 CAM의 계산, 3-3 CAM의 문제점
4-1 Grad-CAM의 배경지식 및 개요
블로그 자료
https://butter-shower.tistory.com/181
https://www.secmem.org/blog/2020/01/17/gradcam/
https://bskyvision.com/413
https://bskyvision.com/639

























댓글
댓글 쓰기