Object Detection


  • 작성자: 10기 강병규
  • 원문: https://kangbk0120.github.io/articles/2018-01/dlcv-week3

오늘은 Coursera - Convolution Neural Networks의 3주차 강의 내용에 대해 정리해보고자 합니다. 3주차의 주된 내용은 Object Detection입니다.

Object localization

Object Detection이라는 것은 어떤 물체가 어디에 있는지를 파악하는 것입니다. 자율주행자동차 등에서 이용되는 알고리즘이죠. 이 강의에서도 계속 자동차와 관련된 예제를 사용합니다. 본격적으로 시작하기 전에 여기에서 사용할 용어들을 먼저 정의하고 들어갑시다.

pic-1

가장 먼저 image classification 은 어떤 사진이 주어졌을 때, 이를 보고 무슨 물체의 사진인지 분류하는 것입니다. 제일 간단한 문제죠. 그 다음인 Classification with localization (이하 localization)은 어떤 사진이 주어졌을 때, 이 사진이 어떤 물체의 사진인가뿐만 아니라, 그 위치 또한 Bounding Box(이하 바운딩 박스)로 표현해줍니다. 마지막으로 Object detection 은 사진에 여러 물체가 담겨있는 경우입니다. 결국 localization을 여러 물체에 대해 수행하는 것이죠. 앞의 두 개에서는 사진에 물체가 하나밖에 없다는 전제가 깔려있습니다.

pic-2

그렇다면 localization은 어떻게 할 수 있을까요? 기본적인 틀은 classification과 크게 다르지 않습니다. 일단 물체가 있는지를 알아야 바운딩 박스를 그릴 수 있을테니까요. 보행자/차/오토바이/배경(이 셋 중 아무것도 없는 경우)를 예측해야한다고 해봅시다. classification에서는 단순히 어떤 물체에 속하는지만을 결과로 내보내면 됐지만 여기서는 바운딩 박스의 좌표 또한 함께 출력해주어야 합니다. label의 첫번째 값은 $P_c$로, 사진에 물체가 있다/없다를 의미합니다. 만약 위에서 배경에 해당한다면 $P_c$는 0이 되겠죠.

그리고나서 만약 $P_c$가 1이라면, 즉 물체가 사진에 존재한다면 바운딩 박스를 그려야합니다. 따라서 $b_x, b_y, b_h, b_w$도 함께 출력합니다. $b_x$와 $b_y$는 바운딩 박스의 중점의 좌표입니다. 이때 우리는 이미지의 좌상단이 (0,0), 우하단이 (1,1)이라고 가정하므로 $b$의 값들은 아마 0~1사이가 될 것입니다.

마지막으로 이 물체가 어떤 class에 속하는지를 출력합니다. 우리는 총 4개의 class를 예측하려 했지만 배경은 $P_c$가 0인 경우에 포함되므로 나머지 3개를 예측해주면 됩니다. 만약 $P_c$가 0이라면 나머지 값들은 무엇이 되든 상관이 없습니다.

pic-3

다음으로 넘어가기전에 잠깐, 우리가 얼굴 인식을 한다고 해봅시다. 단순히 얼굴에 바운딩 박스를 치는 걸로는 부족할 수도 있습니다. 스노우 필터같은 걸 생각해보면 중요한 점들, 이를테면 눈의 위치나 입의 위치도 알아야할 수 있겠죠. 이런 경우에 사용하는 것이 Landmark Detection 입니다. 큰 틀은 위의 localization과 다르지 않습니다. 단지 더 많은 좌표들을 출력하는 것이죠. 이때 주의해야하는 것은 각 값의 의미가 동일해야합니다. 어떤 사진에서 첫번째 값이 눈의 위치지만 다른 사진에서는 입의 위치라던가 하면 안되겠죠. 물론 학습을 위해서는 이런 값들이 labeling된 데이터가 필요하다는 건 안비밀.


Object Detection

이제 본격적인 Object Detection으로 넘어가봅시다. 가장 먼저 설명하는 방법은 Sliding Window Detection입니다.

Sliding Window Detection

pic-4

자동차를 detection하고 싶다고 해봅시다. 제일 먼저 해야할 일은 당연히 자동차 데이터를 모으는 것이죠. 자동차만을 cropping한 사진과 아무것도 없는 사진을 모아 이들을 labeling해야합니다. 단 여기서는 바운딩 박스의 좌표없이 그저 0/1, 곧 있다/없다만을 표시합니다. 그 다음에는 이 데이터들로 네트워크를 학습시킵니다.

pic-5

실제 테스트 과정에서 Sliding Window가 사용됩니다. 어떤 큰 사진에서 윈도우 크기를 정합니다. 그 다음에 사진의 좌상단부터 이 윈도우만큼의 영역을 네트워크에 넣어 결과를 예측합니다. CNN에서 필터가 움직이는 과정과 유사하다고 생각하면 됩니다. 이후에는 더 큰 윈도우를 사용해 이 과정을 반복합니다. 만약 윈도우 안에 우리가 찾고 싶어하는 물체, 여기서의 경우 자동차가 있다면 1을 출력하겠죠.

이러한 방법의 단점은 많은 연산량에 있습니다. 이미지를 윈도우만큼 잘라서 넣는 과정과 네트워크를 거치는 과정까지하면 꽤나 많은 연산이 필요합니다. 또 윈도우의 stride가 크다면 결과가 불안정하겠죠. 많은 영역을 건너뛰게 되니까요. 사실 이러한 방법은 Neural Network 이전의 linear function 등을 사용해 구현했었습니다. 그러나 이후 이 방법을 Convolution 연산을 사용해 구현할 수 있게 되었습니다.

With Convolution + YOLO

pic-6

그전에 잠깐, Fully Connected를 Convolution 연산으로 바꿀 수 있다는 것 을 아시나요? 일반적인 Classification을 한다고 생각해보면 끝부분에는 FC를 얹어 class를 예측하도록 하죠. 이때 flatten하는 과정없이 기존의 volume에서 Convolution연산을 바로 적용할 수도 있습니다. 위 그림에서 5x5x16을 flatten하는 대신 5x5의 필터를 사용하면 1x1x400의 값을 얻을 수 있습니다. 담겨있는 수 자체는 똑같지만 그냥 Volume 벡터가 되는 것이죠. FC를 Convolution으로 대체하는 이 아이디어를 끌고 가봅시다.

pic-7

학습을 14x14x3의 이미지에 대해서 수행했다고 해봅시다(위 그림에서 FC는 Convolution으로 구현한 FC를 의미합니다). 그런데 만약 테스트 이미지가 16x16x3이라면? 원래는 이미지를 떼서 네트워크에 넣어야하죠. 그런데 여기서 FC를 Convolution으로 구현한게 빛을 발합니다. 저번 시간에 잠깐 논의했던 내용인 것 같은데요, Convolution 연산 자체는 입력의 크기와는 상관이 없습니다. 결국에는 테스트 이미지를 통째로 넣어도 된단 얘기입니다! 이렇게 되면 연산을 공유하게 되므로 시간을 절약할 수 있게 됩니다. 파란색으로 칠해진 부분이 보이시나요? 결국 테스트의 좌상단은 결과에서도 좌상단이 됩니다. 여기서 Maxpool 2x2를 사용하는데 이것이 원래 이미지에서 stride 2를 적용한 것과 같으므로, 결과인 2x2x4는 16x16x3에서 14x14x3를 stride 2만큼 떼어낸 것과 동일합니다.

결국에는 Crop할 필요 없이 원래의 이미지를 통째로 넣으면 되는 것입니다. 즉 연산량을 줄일 수 있는 것이죠. 그러나 여전히 결과가 불안정할 수 있다는 문제가 존재합니다. 정해진 윈도우의 크기만큼만을 감지하며, 거기다가 stride까지 적용하기 때문이죠.

Sliding window 방식의 문제는 윈도우가 물체를 정확하게 잡지 못할 수도 있다는 것입니다. 또한 정확한 바운딩 박스가 정사각형이 아닐 수도 있겠죠. 이를 해결하고자 한 것이 YOLO(You Only Look Once) 입니다.

pic-8

입력이 100x100의 이미지라고 해봅시다. 이들을 작은 grid로 나눕니다 - 여기서는 설명을 위해 3x3을 사용했지만 실제에서는 19x19 등 더 작은 단위를 사용합니다. 또 예측에 사용하는 label은 제일 먼저 설명했던 $P_c$ 등의 8가지 값입니다.

이 3x3의 각 그리드에서 localization을 수행합니다. 그렇게 되면 결과적으로 9개의 label 벡터를 구할 수 있겠죠. 만약 물체가 여러 그리드에 걸쳐있는 경우에는 중점을 기준으로 한 그리드에만 할당해줍니다.

결국 100x100x3인 입력을 ConvNet에 넣어 3x3x8의 값을 얻어내는 것입니다. 이렇게 하면 정확한 바운딩 박스를 찾아낼 수가 있죠. 결국 이전의 localization 문제와 유사한 방법을 사용하면서 sliding window 방식처럼 이미지를 통째로 넣어주는 것이라고 요약할 수 있습니다. 이때 좌표는 각 그리드를 기준으로 합니다. 즉 각 그리드의 좌 상단이 (0,0)이 됩니다. 따라서 $b_x, b_y$는 따라서 0~1사이의 값이 되겠죠. $b_h, b_w$의 경우에는 물체가 여러 그리드에 걸쳐있을 수 있으므로 1보다 클 수도 있습니다.


IoU

좋아요, 이런 방식을 쓰면 정확한 바운딩 박스를 찾아낼 수 있을 것 같습니다. 그런데 바운딩 박스가 얼마나 정확한지는 어떻게 평가할까요? 이전의 Sliding Window 방식에서는 이런 문제가 필요없었습니다. 윈도우만큼 바운딩 박스를 그리면 됐으니까요.

이를 위해 등장한 것이 IoU 입니다. 풀어쓰면 Intersection over Union인데요. 간단히 얘기하면 예측한 박스와 Ground truth, 정답이 있다고 할 때, 둘의 교집합/둘의 합집합 입니다. 일반적으로 0.5 이상이면 정답이라고 보지만, 여기서 0.5는 사람이 임의로 정한 값이므로 그때 그때 달라질 수도 있습니다. 엄격한 기준을 원한다면 0.6으로 설정한다거나 할 수 있지만 일반적으로 0.5밑으로 잡지는 않습니다.

Non-max suppression

또 다른 문제가 하나 있습니다. 한 물체가 여러 그리드에 걸쳐져 있다면 중점을 기준으로 판단한다고 했는데요, 중점을 어떻게 정확하게 알 수 있을까요? 한 물체를 여러 번 감지할 수도 있겠죠. 즉 한 물체에 대한 여러 개의 바운딩 박스가 존재할 수도 있습니다.

이를 해결하기 위한 방법이 Non-max suppression 입니다. $P_c$를 비교하고, IoU를 통해 많이 겹치는 박스는 제거하는 과정을 반복하는거죠. 최대확률이 아닌 박스들을 제거하기 때문에 non-max입니다.

pic-9

구체적인 예를 들어봅시다. 19x19 grid에서 값들을 얻어냅니다. 가장 먼저 $P_c$가 특정 값 밑인 박스들은 날립니다. 0.5같은 값을 사용하면 되겠죠. 이후 남아있는 박스들 중에서 가장 높은 값을 고릅니다. 그 다음에는 얘랑 높은 IoU를 가지는 박스들을 제거합니다. 위 과정을 반복해서 수행하면 되는데요, 이는 class가 하나일 때의 얘기고, multiple object라면 각 class 별로 반복 수행해주면 됩니다.

Anchor Boxes

pic-10

이전 방식의 경우 각 그리드가 하나의 물체만을 인식할 수 있습니다. 만약 여러 물체가 있는 경우에는 어떻게 해야할까요? 이때 사용하는 것이 Anchor Boxes입니다. 얘는 사람이 미리 여러 형태를 정의해주는 것인데요. 여기서는 두 가지의 형태를 사용했습니다. 즉 두 개의 물체를 인식할 수 있는 것이죠. 보행자의 경우에는 1번에 가까우므로 1번 박스를 적용하면 될 것이고, 자동차의 경우에는 2번을 사용하면 되겠죠. 결국 label이 8개 값이 아닌, 2배가 되어 16개가 됩니다.

기본적으로는 중점을 기준으로 물체를 할당하지만, 앵커 박스와 물체의 형태 간의 IoU도 반영합니다.즉 물체를 감지했다고 했을 때, 어떤 앵커 박스와 가장 높은 IoU를 가지나 찾고 이를 반영하는 것이죠.

만약 앵커 박스는 2개인데 한 그리드안에 물체가 3개라면? 이 경우는 처리할 수 없습니다. 또 두 물체 모두 같은 앵커 박스의 형태라면? 이 또한 처리할 수 없습니다. 그러나 실제 상황에서 위 같은 경우는 잘 일어나지 않습니다. 그래서 어떻게 앵커박스를 정하냐구요? 그냥 다양한 형태를 가진 것을 임의로 설정합니다.

YOLO

pic-11

이제 YOLO에 대해서 제대로 알아봅시다. 가장 먼저 우리가 해야할 일은 training data를 만드는 것이죠. 보행자/자동차/오토바이를 감지해야한다고 해봅시다. 3x3 그리드에 앵커 박스를 2개 사용한다면 label은 3x3x2x8이 될 것입니다. 사진에 차가 있다면 아마 가로가 긴 앵커 박스와 IoU가 높겠죠. 따라서 이 박스에 해당하는 부분에 값들을 채워주면 됩니다.

pic-12

예측은 어떻게 할까요? 각 그리드에서 앵커 박스가 2개이므로, 최대 2개의 바운딩 박스가 나올 수 있겠죠. 3x3x2x8(3x3x16)을 얻고, $P_c$를 보고 판단한다음, Non-max Suppression을 수행합니다. 즉 특정 값 미만인 애들을 제거하고 높은 값을 가지는 박스는 살리는 과정을 반복하는데요, 중요한 것은 각 클래스 별로 Non-max Suppression을 해주어야한다는 것입니다. 이 사진의 경우에는 3개의 class를 예측하므로 세 번 반복해주어야 하는 것이죠.

+ Region Proposals

pic-13

Sliding Window방식이나 YOLO의 경우 전체 이미지에서 Convolution 연산을 적용합니다. 그러나 R-CNN(Region CNN)은 특정 region에서만 window 연산을 수행합니다. 이를 위해서 이미지를 segmentation한 다음 사용하죠. 즉 이미지를 segmentation해 배경이 아닌 물체들을 파악하고, 이 물체를 윈도우에 넣고 분류하는 것입니다. 이렇게 하면 ConvNet을 모든 이미지에 적용하는 경우보다 연산을 절약할 수 있습니다.

그러나 어떤 알고리즘이 제안한 영역, 즉 segmentation에 대해 분류를 수행하고, 각 영역에 대한 레이블과 바운딩 박스를 출력해야므로 더 많은 시간이 필요하게 됩니다. 대신 segmentation한 다음 박스를 그리므로 더 정확한 바운딩 박스를 얻을 수 있겠죠. 느린 연산속도를 개선한 Fast R-CNN이나 Faster R-CNN도 있지만 YOLO보다 느릴 것이라고 같다고 얘기하면서 끝이납니다.