[모델리뷰] eXplainable Object Detection

temp·2021년 7월 24일
0

XAI / Object Detection

목록 보기
15/24

※ 본 글은 설명가능한 object detection을 다룬 블로그를 참고하여 작성하였습니다.

해당 블로그에서는 AI model의 output을 설명하는 것이 굉장히 흥미롭고, 또 어려운 AI application인 'Detecting objects in images'를 소개한다. Object detection에 대한 디테일한 설명은 하지 않고, 아래 질문에 중점을 두고 글을 전개해보자.

단순한 evaluation metrics를 넘어서 모델 자체를 이해하는 것이 왜 중요할까?

설명 가능한 인공지능(XAI)는 분명 computer vision 분야에서 새로운 기술은 아니지만, 특정 input을 기반으로 object detection model을 분석할 수 있는 explainable object detection tool은 여전히 존재하지 않는다(원문 게재 당시에). 그래서, 저자들은 디테일한 instance-based explanation을 위한 한 가지 접근법을 제안한다.

Introduction

해당 모델은 특정 모델의 결과를 해석하기 위해 SHAP library를 활용한다. SHAP는 other features(?)의 background distribution을 고려하는데, 디테일한 설명은 여기를 참고하자. 본 글에서는, 선택적으로 input의 일부분을 지울 수 있는 모델을 구축했다. 일부분을 지우는 데 사용하는 hide patches는 우리가 shapley value를 계산하고, 시각화하기 위한 surrogate(대리) feature로 기능한다.

LIME과 SHAP와 같은 XAI 라이브러리는 굉장히 다양한 기본 모델과, 다양한 input을 제공한다. 그 중에서도 대표적으로 deep neural network와 image input을 예로 들 수 있을 것 같다. 하지만, object detection model은 해당 라이브러리가 직접적으로 지원하지 않는 구조이다(가장 유사한 모델은 image classification model이다).
해당 라이브러리는 image classification model(즉, output이 '클래스 별 확률 분포'인)을 지원하지만, object detection model은 한 이미지 내에서도 여러 '객체'에 대한 요소가 포함되기 때문에 지원하지 못한다. 또한, object detection에서 state-of-the-art 성능을 보이는 모델은 소위 'non maximum-suppression step(NMS)'를 포함하기도 한다

※NMS란?
NMS(Non-Maximum-Suppression step)은 분류 score가 높은 bounding box와 특정 임계치 이상만큼 겹칠 때, 분류 score가 낮은 bounding box를 반복적으로 제거하는 과정이다. 해당 임계치 설정은 유저가 임의로 할 수 있으며 이는 object들이 주로 겹쳐 있는지, 혹은 떨어져 있는지 등의 상황에 따라 다르게 결정할 수 있다.

(COCO DATASET)

이 과정은 학습시킬 필요도 없으며, 미분가능하지도 않다. 이는 input과 output 사이에 흐르는 gradient가 없다는 뜻이며, 이전에 말했던 'multiple objects output' 문제 외에도 graident를 기반으로 하는 SHAP 라이브러리 중 DeepLift 또는 DeepExplainer를 사용할 수 없는 이유가 되기도 한다. 하지만, SHAP는 일반적인 black box model에도 사용할 수 있는 KernelExplainer 메서드를 제공하기도 한다. 설명가능한 object detection을 만들기 위한 챌린지는 두 가지 관점에 놓인다.

  1. object detection task를 KernelExplainer의 상황에 맞추어야 한다.
  2. 테크니컬한 레벨에서, XAI framework와 model을 결합해야 한다.

The Model and Dependancies

본 글에서는 object detection 알고리즘으로 YOLOv5를 사용한다. Pytorch롤 사용하며, 아주 쉬운 fine tuning만을 진행하였다.
pre-trained 모델로는 YOLOv5s를 사용한다. 이는 80 classes를 대상으로 하는 굉장히 작은 모델로, COCO dataset을 사용한다. (아래 참고에 주어진) colab에는 SHAP 라이브러리, YOLOv5, pre-trained model and weight 등만 다운받으면 된다. YOLOv5 code에서는 NMS 알고리즘과 bounding box끼리의 오버랩 등을 체크하는 함수를 참조했다.

The Target: One detection in one image

one image를 읽고, 정사각형으로 패딩한 다음 160 x 160 사이즈로 resize한다. 이 size는 pre-trained model과 맞추기 위해 32배수이기만 하면 된다. object detection과 XAI 기법은 시간을 잡아먹기 때문에 원본 이미지를 다운스케일링해 사용하긴 한다(선택).

※ 이미지의 전체 전처리 과정은 아래의 colab 파일에 있으므로 흐름 위주로 살펴보자.
(즉, cv2의 BGR format을 RGB format으로 바꾸고, 이미지를 정사각형으로 패딩하기 위한 디테일 코드들은 생략)

# 핵심 흐름
img = cv2.imread("/content/yolov5/inference/images/zidane.jpg", cv2.IMREAD_COLOR)
img = cv2.resize(img, (new_size_x, new_size_y))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)


그 후, 모델을 불러온 다음 이미지를 모델에 투입한다.

model = torch.load("../yolov5s.pt", map_location=device)['model'].float().eval()

prediction = model(torch_image)
print(prediction[0])

# 예시
[[x, y, w, h, obj. score, class1 prob., class2 prob,…, class80 prob],
 [x, y, w, h, obj. score,class1 prob., class2 prob, …, class80 prob],
    …
]

위 코드 실행의 output으로 감지된 물체의 중심 좌표 x,y와 물체의 너비, 높이인 width, ehight가 반환된다. 또한, 해당 좌표에서의 80 classes에 대한 probabilities와 해당 좌표에 물체가 있을(?) probability을 얻는다. output vector는 모든 가능한 anchor point에 대한 detection이며, 대부분은 매우 낮은 score를 보일 것이다.

여기에, NMS를 이용하여 예측들을 줄이고, 필터링 하게 되면 결과적으로 6개의 벡터(즉, 6개의 object)가 남는다.

output = non_max_suppression(prediction[0], conf_thres=0.5, iou_thres=0.5)
print(output[0])
[[ 20.64062,  15.09375,  57.75000, 118.12500,   0.86572,   0.00000],
 [117.93750,  27.06250, 149.25000, 114.31250,   0.83154,   0.00000],
 [  6.89062,  98.56250,  36.56250, 143.75000,   0.82373,  16.00000],
 [ 73.25000,  12.21875, 120.62500, 135.62500,   0.81348,   0.00000],
 [ 85.87500, 126.43750, 110.50000, 141.62500,   0.63721,  36.00000],
 [124.87500, 106.43750, 143.12500, 114.81250,   0.51025,  36.00000]]

위의 결과는 (x1,y1,x2,y2) 형태의 좌표와, object score와 highest class score를 곱한 값, 그리고 most likely class의 index를 반환한다(0=people).
해당 결과를 그림에 나타내면 아래와 같다.

즉, 최종적으로 50~87%의 score로 물체를 탐지한 것.
성능을 높히기 위해서는 더 큰 모델을 사용하거나, 더 큰 해상도의 이미지를 사용할 수 있다.

입력 이미지와 관련해 전체 model의 output을 설명하는 것은 굉장히 어려운데, 이는 모델의 결과들을 묘사할 수 있는 이미지는 당연히 없기 때문이다. 그렇기에, 한 이미지에 대한 하나의 예측만을 관심에 둔다면 XAI의 관점으로 모델을 설명하기 쉬울 것이다. 이 때, 질문은 아래와 같다.

이 특정 detection을 하기 위해 대해 이미지의 어떤 부분이, 얼마만큼 기여했을까?

Explainable Object Detection - The All-in-One Model

SHAP 라이브러리의 KernelExplainer를 사용하기 위해 아래와 같은 과정이 필요하다.

  1. Casting the image to a PyTorch tensor
  2. Applying the core model
  3. Applying NMS
  4. Calculating the score of the detection we are insterested in

1 과정은 Numpy2TorchCaster class를 이용해서, 2 과정은 위에서 사용했던 것처럼, 그리고 3과 4는 OD2Score class를 이용해 진행한다.

target detection의 score를 추출할 때, detected box가 기존의 target으로부터 벗어날 수 있다. 이 때는 correct target과 detected box 사이의 겹치는 만큼 score를 곱해준다. 즉, 최종적인 score는 모델이 person을 예측하는 데 얼마만큼의 신뢰도를 갖는지, 그리고 얼마나 box가 잘 위치하는 지에 의존한다 볼 수 있다.

※ final score를 위한 과정은 Reference 내 code에 자세히 나와있다.

scoring_model = torch.nn.Sequential(
      numpy2torch_converter,
      model,
      od2score_filter)

위와 같이 묶어서 전달하면, scroing_model은 image를 input으로 받아
'올바른 영역에서 얼마나 잘 person을 찾아내는지'와 '얼마나 box가 잘 위치해 있는지'를 output으로 반환한다. 하지만, input을 위해 하나 더 처리해주어야 하는 것이 있다.

A Grid of Superpixels

input을 약간 변형하고, output이 어떻게 변하는 지를 살펴보자.

불행하게도, 160x160 이미지만 해도 25,600개의 pixel(특히, 채널을 따로 고려한다면 76,800)을 갖기 때문에, 각 픽셀의 영향력을 계산하는 것은 비용이 너무 많이 든다. 무엇보다도 하나의 픽셀은 최종 결과에 그렇게 영향을 끼치지도 않을 것이다. 그렇기에, 픽셀들을 여러 픽셀의 연결된 집합인 superpixel로 결합할 것이다. 결합하는 방법은 많지만, 가장 간단하게 grid로 나누도록 해보자. 그 후, 특정 슈퍼 픽셀을 사용할지, 사용하지 않을 지 판단하는 layer를 추가하면 된다. 사용하지 않는 슈퍼 픽셀의 픽셀 값은 이미지의 평균 값을 사용한다.

super_pixel_model = torch.nn.Sequential(
      super_pixler
      numpy2torch_converter,
      model,
      od2score_filter)

위의 super_pixel_model은 superpixel을 사용할 지, 사용하지 않을 지 정보를 input으로 받는다. 그 후 이를 (내부적으로) target을 포함한 이미지로 변환한다. 그 후 다시 target을 얼마나 잘 예측했는 지 score를 반환한다.

160x160 이미지를 대상으로 8x8 슈퍼 픽셀을 사용했기 때문에, 최종적인 input space는 400(20x20) binary vector라 할 수 있다.

Shap Values for Superpixels

KernelExplainer는 최종적으로 super_pixel_model을 해석한다. 이는 400 binary vector를 input으로 갖기 때문에 충분히 해석할 수 있게 된다. 아래와 같은 코드를 통해, 특정 super pixel이 최종적인 output score에 얼마나 영향을 끼치는 지 Shapley value를 구해보자.

explainer = shap.KernelExplainer(super_pixel_model,background_super_pixel)
shap_values = explainer.shap_values(image_super_pixel,nsamples=3000)

그 후 간단히 값들을 픽셀에 재분배하는 과정을 거치면 된다.

Outcome


위의 결과를 보면, 머리와 어깨쪽이 큰 기여를 했으며, 내부 공간이나 하반신은 적은 기여를 한 것을 볼 수 있다. 즉, 사람을 인식하는 데는 얼굴과 어깨쪽이 Object detection score를 내는 데 핵심적이라 할 수 있다.

살짝 언급했다시피, superpixel은 더더욱 정교한 방법으로 나눠질 수 있다. image element를 클러스터링하는 방법들을 사용할 수도 있고, 물체의 특성을 기반으로 superpixel을 정하는 알고리즘들을 사용해도 된다.
또한, super pixel의 대체 값을 이미지 픽셀의 평균 값이 아닌, 여러 값으로 할당할 수 있다. 이는 pixel의 '부재'가 무엇인지에 대한 더 좋은 통찰을 모델에 전달해줄 수 있다. 이미지별로 다양한 scale과 다양한 superpixel은 중요한 역할을 할 수 있으므로, 컴퓨팅 환경에 맞게끔 모델을 확장할 수 있다.

해당 글에는 옳게 detect한 예시만을 나타냈지만, 사실 잘못 detect한 예시에 대해 실행해보는 것이 모델의 가정이나 선입견을 파악하기엔 더 좋을 수 있다
가령 아래처럼 캥거루를 기린으로 잘못 예측했다면, 어디를 보고 그렇게 예측했는 지를 살펴볼 수 있는 것이다.

또다른 한계는 false negative에 대한 설명이다. 잘 예측되지 못한 target을 보기 위해 scoring 과정을 수정할 수 있긴 하지만, 모델이 옳게 예측하기 위해 어떤 input을 봐야하는 지 결정하기 위해, image에 추가로 정보를 주어야 한다. 예를 들어, (이미 예상하고 있는 사실로) 사람의 머리가 가려진다면 사람은 인식되지 않는다. 이러한 배제(occlusion)이 문제인지 확인하기 위해, 가려진 위치에 사람의 얼굴을 넣는 등의 과정을 거쳐야 하는데, 이는 너무나도 어려울 뿐더러 missed detection이 주어졌을 때 가능한 imputation 공간이 너무나도 커지는 문제가 있다.

Let me say something

아무튼, 위의 결과들로 미루어 봤을 때, 사람의 각도가 변하거나, 부분적으로 어깨, 머리 등이 가려진다면 모델이 detecting 하기가 굉장히 어려울 것이라 예상할 수 있다. 하나의 이미지에 대한 설명은 이처럼 모델의 결정 과정에 관한 통찰을 줄 수 있긴 하지만, 예시들을 모두 inference 하기란 굉장히 힘든 점, 일반화하기 힘든 점 등이 존재하기에 약간의 한계는 있을 수 있다.

하지만 explanation for global model은 너무 어렵고 추상적이니..

해당 모델은 Object detection 과정의 기존 input인 image를 superpixel 등을 이용해 400차원의 binary vector로 변환해 새로운 input으로 사용하고, object detection 결과로 나오는 예측 위치, 신뢰도 등을 잘 결합해 도출한 score를 output으로 사용함으로써, 기존의 XAI 기법인 SHAP에 applicable한 model을 재구성 하였다.

같이 제공되는 colab code 또한 간단하게 작성돼 이해하기 어렵지 않은 torch-based code이다. 해당 코드는 torch를 이용해 new task에 기존의 task를 결합하는 실용적인 방법을 보여준 것은 물론, 복잡하지 않은 모델링으로도 object detection task에 XAI를 적용할 수 있다는 좋은 예시를 보여준 것 같다. 당연하게도 제대로 써먹으려면 코드에 살을 붙혀야 하긴 하지만 말이다.

LIME에서와 마찬가지로 이미지 관련 모델에서 input image와 output 간에 복잡한 상호작용을 단순화하기 위해 superpixel을 사용해 input을 binary vector로, output을 score로 대치시킨 (단순화)모델을 사용한다는 관점은 마음 한 켠에 잘 담아두어도 될 듯 하다.

Reference

https://www.steadforce.com/blog/explainable-object-detection
code:https://colab.research.google.com/drive/10ZgpTntUMOaru4BQ3sf6MeBxcNe75CCl?usp=sharing
https://deep-learning-study.tistory.com/403

0개의 댓글