공부/AIFFEL

Going Deeper(CV)_DJ 10 : 나를 찾아줘 - Class Activation Map 만들기

dong_dong_2 2021. 4. 20. 17:21

1. 들어가며
    - 이번 시간에는 CAM(Class Activation Map)을 얻기 위한 방법 중 CAM와 Grad-CAM을 구현해보고 시각화해 보겠다.
    - 그리고 CAM을 활용해서 물체의 위치를 찾는 object detection을 수행하고 이를 정답 데이터와 비교해 보겠다.
2. 데이터셋 준비하기
    - CAM(Class Activation Map)은 특성을 추출하는 CNN 네트워크 뒤에 GAP(Global Average Pooling)와 소프트맥스 레이어(Softmax Layer)가 붙는 형태로 구성되어야 한다는 제약이 있다. 반면에 Grad-CAM은 이런 제약이 없다.
    - 이번 시간에 이 두 가지를 모두 구현해 보겠다.
    - 우선 CAM을 위한 모델을 먼저 구성해 기본적인 CAM을 추출해보고, 이 모델에서 Grad-CAM을 활용해 다시 시각화 결과물을 추출하겠다.
    - Grad-CAM은 모델의 구조에 제약이 없기 때문에 CAM에만 모델을 맞춰도 충분하기 때문이다.
    - CAM은 클래스에 대한 활성화 정도를 나타낸 지도이다.
    - 따라서 기본적으로 우리의 모델은 분류(Classification)을 수행하는 모델이여야 한다.
    - 하지만 최종 목표는 이미지 내에서 클래스가 활성화 된 위치를 확인하고 이를 정답과 비교하는 것이므로 위치 정보가 기록된 데이터가 함께 있어야 한다.
    - Tensorflow Datasets의 카탈로그에서 이런 데이터를 확인할 수 있다.
    - 여기서는 Cars196 데이터셋을 사용했다.
    - 이는 196종의 자동차 종류를 사진으로 판별하는 분류 문제 데이터셋으로, 라벨이 위치 정보인 바운딩 박스(bounding box) 정보를 포함하고 있다.
    - 코드를 통해 실습했다.
3. 물체의 위치정보
    - 원본 이미지 위에 물체의 위치정보는 어떻게 시각화 하여 표시할 수 있을까?
    - 일단 데이터셋의 메타 정보인 df_info를 조회해 features가 어떻게 구성되었는지 확인했다.
    - image, label, bbox가 있다. 여기서 image와 label은 익숙하다.
    - 그럼 bbox는 무엇일까? bbox는 바운딩 박스로 물체의 위치를 사각형 영역으로 표기하는 방법이다. 이 데이터셋에서는 BBoxFeature이라는 타입으로 정의되어 있다.
    - 바운딩 박스를 표시하는 방법들
       1) 바운딩 박스를 라벨링 하는 방법은 아주 다양하다. 그 중 주로 사용되는 방법은 "xywh" 또는 "minmax"로 표기하는 방법이다.
       2) "xywh"
          (1) 바운딩박스 중심점을 x, y로 표기하고, 사각형의 너비 w와 높이 h를 표기하는 방법이다.
          (2) x, y 가 중심정이 아니라 좌측 상단의 점을 가리킬 수도 있다.
          (3) 예시) (x_center, y_center, width, height)
       3) "minmax"
          (1) 바운딩 박스를 이루는 좌표의 최소값과 최대값을 통해 표기하는 방법이다.
          (2) 좌표의 절대값이 아니라, 전체 이미지의 너비와 높이를 기준으로 normalize한 상대적인 값을 표기하는 것이 일반적이다.
          (3) 예시) (x_min, x_max, y_min, y_max)
       4) 위 두가지 뿐만 아니라 아미지의 상하좌우 끝단으로부터 거리로 표현하는 방법, 좌우측의 x값과 상하측의 y값 네 개로 표시하는 방법(LRTB), 네 점의 x, y 좌표 값을 모두 표시하는 방법(QUAD) 등 여러 가지 방법이 있다.
       5) 따라서 새로운 데이터셋을 접하거나 라이브러리를 활용할 때는 간단한 바운딩 박스 정보라도 한 번 더 표기법을 확인하고 넘어가는 것이 좋다.
       6) 그럼 주어진 데이터셋에서 BBoxFeature 타입으로 표기된 bbox 필드는 어떤 방식으로 바운딩박스를 라벨링하고 있나?
          (1) 해당 bounding box는 minmax 방식이다. tensorflow datasets의 경우 height를 첫번째 axis로 삼고있어서 [minY, minX, maxY, maxX]를 의미하게 된다.
4. CAM을 위한 모델 만들기
    - 이제 본격적으로 CAM을 얻기 위한 모델을 만든다.
    - 이번에는 이미지넷(imagenet) 데이터에 훈련된 resnet50을 기반으로 활용하고, 이후 pooling layer 뒤에 소프트맥스 레이어를 붙여 Grad-CAM 모델을 만들겠다.
    - 참고로, 소프트맥스 레이어는 소프트맥스 함수를 활성화 함수로 사용하는 fully connected 레이어이다.
    - 코드를 통해 구현했다.
    - CAM은 특성 맵(feature map)을 사용해 만든다. 모델의 레이어들 중 우리가 사용하는 특성 맵은 어떤 레이어의 출력값일까?
       1) 코드로 만든 모델에서 summary()를 통해 확인해보면 마지막에 avg를 하기 전 레이어인 conv5_block3_out의 output이 feature map이 된다.
       2) 따라서 이 feature map에 Global Average Pooling을 하여 feature의 크기를 줄이고, 줄어든 feature 전체에 Dense layer를 붙여 classification을 수행하게 된다.
5. CAM 모델 학습하기
    - 학습을 하기 위한 과정은 이전 프로젝트와 비슷하다.
    - 학습 데이터와 검증 데이터에 normalizing과 resizing을 포함한 간단한 전처리를 normalize_and_resizze_img()에서 수행하며, 이를 포함하여 apply_mormalize_on_dataset()에서 배치를 구성한다.
    - input에 이전과 다르게 bbox 정보가 포함되어있지만, 지금 수행해야 할 CAM 모델의 학습에는 필요가 없으므로 normalize_and_resize_img과정에서 제외해 줬다.
    - CAM모델은 object detection이나 segmentation에도 활용될 수 있지만, bounding box같은 직접적인 라벨을 사용하지 않고 weaky supervised learning을 통해 물체 영역을 간접적으로 학습시키는 방식이기 때문이다.
    - 코드를 통해 모델을 컴파일하고 학습시키고 가중치를 저장했다.
6. CAM
    - 이 노드 처음에서는 이전에 학습한 코드로 인해 메모리가 부족할 수 있으니 커널을 재시작하고 노드를 시작하는 것을 권장한다.
    - 이제 학습한 모델을 활용하여 CAM을 생성해 볼 차례이다.
    - CAM을 생성하기 위해서는 아래의 3개가 필요하다
       1) 특성 맵
       2) 클래스 별 확률을 얻기 위한 소프트맥스 레이어의 가중치
       3) 원하는 클래스의 출력값
    - 또 이미지에서 모델이 어떤 부분을 보는지 직관적으로 확인하려면 네트워크에서 나온 CAM을 입력 이미지 사이즈와 같게 만들어 함께 시각화 해야 한다.
    - 이를 고려해서 model과 item을 받았을 때 입력 이미지와 동일한 크기의 CAM을 반환하는 함수를 만들어야 한다.
    - 이 함수를 구현하기 위해 여기서는 conv_outputs와 같이 특정 레이어의 결과값을 output으로 받기 위해 새로운 모델을 정의하고, feedforward를 거친 후 CAM을 계산하도록 구현했다.
    - 마지막에는 입력 이미지의 크기에 맞춰 CAM을 resize했다.
    - 코드를 통해 실습했다.
    - 생성된 CAM이 차종을 식별하는데 중요한 이미지 부분을 잘 포착한다.
    - 주로 차량의 전면 엠블렘이 있는 부분이 강조되는 경향이 있는데, 이것은 사람이 차종을 식별할 때 유의해서 보는 부분과 비슷한 것을 확인할 수 있다.
7. Grad-CAM
    - 이번에는 Grad-CAM을 이용해서 CAM을 얻어보도록 하겠다.
    - 적용 모델을 유연하게 선택할 수 있는 Grad-CAM의 특성 상 아까 만들었던 모델을 다시 활용한다.
    - Grad_CAM은 관찰을 원하는 레이어와 정답 클래스에 대한 예측값 사이의 그래디언트를 구하고, 여기에 GAP 연산을 적용함으로써 관찰 대상이 되는 레이어의 채널별 가중치를 구한다.
    - 최종 CAM 이미지를 구하기 위해서는 레이어의 채널별 가중치(weight)와 레이어에서 나온 채널별 특성 맵을 가중합 해서 이미지를 얻게 된다.
    - CAM와 달리 Grad-CAM은 어떤 레이어든 CAM 이미지를 뽑아낼 수 있어서 코드에서 _out으로 끝나는 레이어를 모두 출력해봤다.
    - 코드를 통해 실습했다.
8. Detection with CAM
    - 마지막으로 CAM에서 물체의 위치를 찾는 detection을 해봤다.
    - 코드를 통해 바운딩박스를 그려봤다.
    - 그럼 정답 데이터인 바운딩 박스와 모델이 만든 바운딩 박스를 비교 평가 하기 위해 어떻게 해야할까?
    - 여기서는 IoU를 이용하여 수치로 계산했다.
    - 이 또한 코드로 실습했다.
9. 프로젝트 : CAM을 만들고 평가해 보자.
    - 이번 프로젝트는 앞에서 실습해본 CAM 구현하기, Grad-CAM 구현하기, 바운딩박스 구하기, IoU 구하기를 최대한 혼자 구현해보는 것이다.