[번역] YOLO(v3) 밑바닥부터 구현해보기: 1탄

Coffee Time☕·2021년 9월 10일
0

YOLO Object Detector

목록 보기
1/2

YOLO (v3)를 이용하는 프로젝트를 시작하게 되어 yolo에 대한 공부를 하던 중
아래와 같은 YOLO 처음부터 구현하는 좋은 글을 발견하여 번역하며 공부해보기로 하였다.

본문 내용 바로 가기 ‖ Series: YOLO object detector in PyTorch


Object detectio은 딥러닝의 발전으로 크게 혜택을 본 분야이다. 최근 몇 년간은 YOLO,SSD, Mask RCNN, RetinaNet을 포함한 많은 object detection 알고리즘을 개발하는 사람들을 볼 수 있다.

요 몇달 간, 필자는 object detection 성능을 향상 시키기 위해 연구실에서 일을 하고 있었다. 이 경험으로부터 얻은 중요한 점은 object detection에 대해 알기 위해서는 알고리즘을 직접 밑바닥부터 구현 해보는 것이 가장 좋은 방법이라는 것이다. 이것이 정확히 이번 튜토리얼에서 우리가 하게 될 것이다.

우리는 가장 빠른 object detection 알고리즘 중 하나인, YOLO v3 기반의 object detector을 이용하기 위해서 PyTorch를 이용할 것이다.

이 튜토리얼이 필요한 코드는 python 3.5 , PyTorch 0.4 를 이용한다. 이 Github repo에서 확인할 수 있다.

이 튜토리얼은 5부분으로 구성 되어 있다(각 시리즈 번역 완료 후 링크 업데이트 예정) :
1. 1탄(이번것):Yolo가 어떻게 작동하는지 이해하기
2. 2탄: 네트워크 구조의 layer 생성하기
3. 3탄: 네트워크의 forward pass 구현하기
4. 4탄: Objectness score과 Non-maximun suppression
5. 5탄: 파이프라인의 입력값과 출력값 설계하기

전제 조건

  • CNN이 어떻게 작동하는지 이해하고 있어야 한다. 이 튜토리얼은 Residual Blocks, skip connections, Upsampling에 대한 내용을 포함하고 있다.
  • Object detection, bounding box regression, IoU, non-maximum suppression이 각각 무엇인지 알아야 한다.
  • 기본적인 PyTorch 사용법을 알아야한다. 간단한 neural network를 만들 수 있어야 한다.

위의 조건을 만족하지 못하는 사람들을 위하여 마지막 부분에 링크를 걸어 두었다. (이것도 번역할 예정)

YOLO 란?

Yolo는 You Only Look Once라는 약자를 가진다. DNN을 통해 학습한 feature을 이용하여 object를 검출해내는 object detector이다. 직접 구현을 해보기 전에 YOLO가 어떻게 동작하는지 알아보자.

A Fully Convolutional Neural Network

YOLO는 오로지 convolutional layer만을 이용하여, 완벽한 fully convolutional network라고 할 수 있다(FCN). 75개의 convolutional layer을 가지고, skip connection과 upsampling layer들을 가진다. 어떠한 종류의 pooling도 사용되지 않고, strid 2의 값을 가지는 convolutional layer가 feature map을 downsample한다.

YOLO는 FCN 이기에 입력된 이미지의 사이즈에 영향을 받지 않는다. 하지만 실제로 우리는 알고리즘을 구현할 때 head에서 보이는 다양한 이유로 일정한 input size로 고정하고 사용하는 편이 좋다.

가장 큰 문제는 이미지를 batch로 실행하고싶은 경우 (batch로 된 이미지는 GPU로 parallel하게 실행할 수 있어서 실행 속도가 향상됨), 우리는 모든 이미지의 가로와 세로를 고정해야한다는 것이다. 그렇게 되면 여러 이미지를 연결해서 큰 batch로 이용해야하는 것이다.(많은 PyTorch tensor을 하나로 연결하여)

네트워크는 stride라고 하는 요소를 이용하여 이미지를 downscale한다. 예를 들어, stride가 32고 input 이미지의 크기가 416x416라면 출력값은 크기는 13x13이 된다. 일반적으로, layer의 어떠한 네트워크에 있는 stride는 layer의 출력값이 입력 이미지보다 작게 해주는 요소와 같다.

출력값 해석하기

보통은 ,(다른 object detector도 마찬가지로) convolution layer로 학습한 feature은 detection을 예측하는 classifier/regressor한테 전달된다(bounding box나 class label을 생성하는 등).

YOLO에서는 1x1 convolution을 쓰는 convolutional layer을 이용해서 예측(prediction)이 이루어진다.

가장 먼저 관심을 가져야 할 점이 출력값이 feature map이라는 점이다. 1x1 convloution을 이용했기 때문에, prediction map의 크기는 정확히 이전의 feature map과 크기가 같다.YOLO v3(그리고 이 하위 버전들)에서 , 각 cell이 고정된 수의 bounding box를 예측할 수 있다는 점을 이용하여 prediction map을 해석하는 한다.

비록 feature map에서 unit은 기술적으로 neuron 이라고 하는 것이 적합하나, 여기에서는 cell이라는 표현이 문맥적으로 직관적이다.

깊이로 보았을 때, Feature map은 (B x (5 + C))개의 entry를 가진다. B는 우리가 예측할 수 있는 bounding box를 의미한다. 논문에 따르면, 이 bounding box B는 특정 object를 detect하는 것을 담당 할 수 있다. 각각의 bounding box는 5+C개의 속성(attribute)를 가지는데, 이는 각 bounding box별로 center coordinates, 차원(dimensions), objectiveness score, C class confidence를 가진다. YOLO V3는 각 cell이 3개의 bounding box를 예측한다.

중앙에 있는 object가 cell의 receptive field에 빠지게 되면, feature map의 각cell이 bounding box 중 하나로 prediction을 진행하게 된다. (receptive field란 cell에 보이는 입력 이미지의 부분이다. 이에 관해서는 cnn 관련 링크를 참조하라.)

이것은 하나의 bounding box가 주어진 object에 대한 detection 책임을 가지는 YOLO의 training 방식과 관련이 있다. 우리는 먼저 어떤 cell에 이 bounding box가 속해 있는지를 알아내야한다.

그러기 위해서는 input 이미지를 최종 feature map과 같도록 여러 차원의 grid로 나누어야한다.

아래의 예시를 보자. input 이미지가 416x416이고, stride는 32이다. 앞서 말한 것과 같이, feature map은 13x13일 것이다.
그러면 이미지를 13x13의 cell로 나눠보자.

그러면,ground truth의 가장 중앙에 있는 cell( input 이미지의)이 물체를 predict할 책임을 가지게 된다. 이미지에서는 빨간색으로 표시된 cell이고, 노란색으로 표시된 ground truth box의 중앙이다.

이제, 빨간 cell이 grid의 7번째 줄에 있다. 우리는 이제 7번째 줄에 있는 7번째 cell을 강아지를 detect하도록 책임을 지울 것이다.

지정한 cell은 3개의 bounding box로 예측을 할 수 있다. 어떠한 것이 강아지의 ground truth 라벨로 선택 받을까? 이를 이해하기 위해서는 우리는 anchor라는 개념을 알아야 한다.

Anchor Boxes

bounding box의 가로 세로 값을 예측하는 것이 쉬울 것 같지만, 현실에서는 예측하게 되면 안정적이지 못한 unstable gradient를 가지게 된다. 대신, 대부분의 현대 object detector는 log space transform을 이용해서 예측하거나, 미리 정의 된 bounding box anchor을 사용한다.

그렇다면, 이러한 변환은 예측값을 얻기 위해 anchor box에 적용된다. YOLO v3는 3개의 anchor이 있고, 그 결과 cell당 3개의 bounding box 예측값을 내어 놓는다.

강아지 문제로 돌아가면, 강아지를 detect 해내는데 책임이 있는 bounding box는 anchor 중에 ground truth 와의 IoU가 가장 높은 것이 된다.

Making Predictions 예측하기

아래의 공식은 bounding box 예측값을 얻기 위해 네트워크 출력값을 어떻게 변환했는지를 나타낸다.

bx, by, bw, bh 가 x,y 중앙 좌표 값이고 가로w 세로h가 우리의 예측 값이다. tx, ty, th 가 네트워크 출력값이다. grid의 cx,cy는 죄측 상단 좌표이다. pw, ph가 anchor 차원 값이다.

중앙 좌표값

중앙 좌표값 예측을 sigmoid 함수를 이용해서 한다는 것을 알아야 한다.
이는 결과 값이 0과 1사이에 오도록 해준다. 왜 이러한 방식으로 할까?

일반적으로, YOLO는 절대 좌표로 bounding box의 좌표를 예측하지 않는다. 대신 다음 offset 들을 예측한다:

  • object를 예측하는 grid cell의 좌측 상단의 상대 값
  • feature map으로 정규화된 cell의 차원: 1

강아지 사진 예시를 들어보자. 만약에 중앙의 값을 (0,4, 0,7)로 예측 했다면, 13x13 featur map에서는 (6.4,6.7)에 중앙값이 있다는 의미이다. (좌측 상단의 좌표가 (6,6))

하지만 만약 예측된 x,y 좌표가 1보다 크다면 예를 들어 (1.2,0.7). 이것은 중앙 값이 (7.2,6.7)에 있다는 뜻이다. 중앙값이 이제 우리의 빨간색으로 표시된 cell, 혹은 그 바로 옆의 cell인 7번째 줄에 8번째 cell 을 가르키는 것을 알 수 있다. 이것은 YOLO의 이론에 어긋난다. 왜냐하면 우리가 빨간색 box가 책임지고 강아지를 detect 하도록 한다면 강아지의 정중앙이 그 옆의 box가 아닌 빨간 box가 되어야하기 때문이다.

따라서 이러한 문제를 해결하기 위해서,출력값을 sigmoid 함수에 넣어 0부터 1사이로 값을 조정한 뒤, 예측 시 grid의 가장 중앙에 오도록 하였다.

Bounding box의 차원

bounding box의 차원은 출력값에 log-space transformation을 적용하고 anchor과 곱하여 구할 수 있다.

detector 결과값이 최종 prediction으로 변환되는 것을 보여주는 그림이다.

결과적인 예측값 bw, bh는 이미지의 h와 w로 결정된다.(Training label도 이렇게 결정). 그래서 만약 강아지를 포함한는 예측값 bx,by가 (0.3,0.8)이라면, 실제 13x13 feature map에서는 (13x0.3,13x0.8)이 된다.

Objectness Score

Object score은 bounding box안에 object가 들어있을 확률을 나타낸다. 빨간 box와 그 주변은 1과 거의 가까운 값이여야하고, 모서리는 0에 가깝다.

objectness score 역시 sigmoid를 통과하고 확률적으로 변환한다.

Class Confidences

Class confidencs란 일정한 class에 detect한 object가 속할 확률이다.(강아지, 고양이, 바나나 등). v3 이전에는 YOLO는 이를 계산하기 위해 softmax를 이용했다.

그러나, v3에서는 그러한 방식을 버리고, sigmoid를 이용한다. 그 이유는 class score을 softmax는 각 class가 상호 배제라고 가정하기 때문이다. 간단히 말하면, 만약 한 object가 한 class에 속하면, 다른 class에는 속하지 않는 다는 것을 의미한다.
이는 우리가 연습할 COCO 데이터에도 마찬가지이다.

다른 scale에 대한 예측

Yolo v3는 3가지의 다른 scale에 대해 예측한다. detection layer은 서로 다른 3가지 크기의 feature map을 생성할 때 쓰이고 각각의 stride는 32, 16 ,8이다. 이는 입력이 416x416이라면 우리는 13x13, 26x26,52x52의 크기로 detection을 하게 되는 것이다.

네트워크는 이미지를 첫번째 detection layer까지 downsample하고, 이 layer는 32의 stride로 feature map을 detect한다. 이 후, layer은 2배로 upsample 되고, 같은 feature map size를 가지는 이전 layer의 feature map이 있다면 서로 합쳐지게 된다. 그 다음 16의 stride로 detection이 진행되고 이 전과 같은 upsampling이 진행되며, stride 8로 된 layer도 진행한다.

각 scale에서 각각의 cell은 3 anchor을 이용하여 3개의 bounding box를 예측하고, 따라서 총 9개의 anchor 사용 횟수가 된다. (각 scale 별로 각각 다른 anchor을 가지기 때문에)

YOLO v3 개발자는 이러한 방식이 이전 버전에서는 잘 하지 못했던 작은 object를 detect하는데 도움이 되었다고 한다. Upsampling이 신경망이 더 세세한 요소를 학습할 수 있도록 한 것이다.

Output processing

416x416 크기의 이미지에 대해서 YOLO는 ((52 x 52) + (26 x 26) + 13 x 13)) x 3 = 10647 bounding boxes를 예측한다. 하지만, 우리 이미지의 경우 강아지 하나의 object뿐이다. 그렇다면 어떻게 detection을 10647에서 1로 바꿀 수 있을까?

Thresholding by Object Confidence

먼저, 우리는 objectness score을 이용해서 box를 필터링한다. 일반적으로, 특정 임계값 이하의 score값을 가지는 box는 무시된다.

Non-maximum Suppression

같은 이미지에 대한 여러 detection 문제를 해결하는 것이 NMS이다. 예를 들어, 3개의 bounding box 모두가 물체를 detect할 수도 있고, 그 옆의 box가 물체를 detect할 수도 있다.

우리가 하게 될 구현

YOLO는 신경망을 train할 때 사용한 데이터에 있었던 class 만을 detect 할 수 있다. 우리는 공식적으로 제공하는 weight 파일을 detector에 사용할 것이다. weight는 COCO 데이터셋을 training하면서 생긴 것이고 따라서 80개의 object category를 가진다.

이제 드디어 1탄이 끝났다. 이번 포스팅은 detector을 구현할 수 잇을 정도로 충분히 YOLO 알고리즘에 대해서 설명했다. 하지만 그래도 더 깊이 있게 알고 싶다면, 정식 논문을 읽는 것을 추천한다.

0개의 댓글