본문 바로가기

Machine Learning Tasks/Object Detection

Object Detection - YOLO v3

반응형

지난 포스트

[Machine Learning/기타] - Object Detection - YOLO v2


YOLO v2 가 나온 시점에서는 object detection 알고리즘 내에서 가장 빠르고 정확한 알고리즘 중 하나였지만 이후에 공개된 RetinaNet, SSD 등 다른 object detection 알고리즘에 비해 정확도가 낮고 작은 물체를 잘 탐지하지 못한다는 문제점이 있었습니다. YOLO v3 는 성능을 향상시키기 위한 다양한 업데이트를 YOLO v2 에 반영했는데요, 한번 살펴보도록 하겠습니다.

Updates

Darknet-53

YOLO v2 포스트에서 살펴봤듯이 YOLO v2 는 이미지 feature 를 추출하기 위해 19-layer 로 되어있는 Darknet-19 를 backbone network 로 사용하고 이후 11-layer 를 덧붙여 object detection 을 수행했는데요, 전단계 feature map 을 passthrough connection 으로 이어주었다고 해도 기본적으로 residual connection 이 없고 모델의 깊이가 상대적으로 얕습니다. YOLO v3 은 ImageNet 데이터셋으로 pretrain 시킨 Darknet-53 network 를 feature extractor 로서 사용합니다.

Darknet-19 에서 Darknet-53 모델로 넘어가면서 속도 측면에서의 (FPS) 이득은 줄이되 성능 측면에서의 이득을 취하려 설계한 것입니다. Table 2를 보면 Darknet-19 모델이 다른 모델에 비해 작으므로 FPS가 매우 크지만 Top-1/5 정확도는 낮은 것을 볼 수 있습니다. Darknet-53 모델을 보면 정확도가 ResNet-152 수준이면서 FLOPS 측면에서는 더 효율적인 것을 알 수 있습니다. (FPS 가 2배 빠릅니다.) 결국, 속도 vs 성능 tradeoff 에서 성능 측면에 더 무게를 실어준거죠.

YOLO v2 처럼 pretrain Darknet-53 모델에 416x416 크기의 이미지가 입력으로 들어갑니다. Object detection 훈련을 위해 Average Pooling layer 이후는 제거되고 3x3/1x1 conv 로 구성된 detection layer 가 뒤에 덧붙여져 훈련되며 최종 feature map 크기는 416이 stride 2의 5번 적용으로 32로 나누어져 13x13 이 됩니다. 최종 feature map 에는 각 그리드 별, 그리드에 할당된 앵커 박스 (bounding box)  별로 1) 박스의 중심점, 높이, 너비, 2) 객체존재여부, 3) 객체 종류 확률이 담기게 되고 (Figure 1) YOLO v3 에서는 COCO 데이터셋을 사용했으므로 각 박스 별로 [4+1+80]=85 의 채널이 추출됩니다. 결과적으로 최종 feature map 은 13x13x[box 개수x85] 의 3차원 텐서가 됩니다. 

Figure 1

Bounding box

Bounding box 의 중심점, 높이, 너비를 예측하는 부분은 Figure 2와 같이 YOLO v2 와 동일합니다. 중심점을 예측하는 $t_x, t_y$에 대해서는 시그모이드 함수를 거쳐 [0,1] 사이를 표현하도록 하고 $c_x, c_y$는 좌상단 그리드로부터의 오프셋을 의미합니다. 예측한 $t_h, t_w$에 대해서는 exponential 을 붙여주어 음수가 되는 것을 방지하고 훈련 데이터셋으로부터 군집화를 통해 구한 box prior $p_w, p_h$에 이를 곱해줍니다.

Loss function

Figure 1의 각 박스별 1) 박스 위치, 2) 객체 존재여부, 3) 객체 종류별 확률을 예측하기 위한 목적함수가 YOLO v3 에서 약간 수정됩니다. 박스 위치에 대해서는 기존과 같이 ground-truth 에 대해 MSE (Mean Squared Error)를 사용하나 객체 존재여부 $p_o$에 대해서는 logistic regression loss를 (Binary Cross Entorpy) 적용합니다.

80 클래스에 대한 분류로스는 multi-label classification 측면에서 접근하여 softmax 함수 대신 80개의 개별적인 logistic regression loss를 (Binary Cross Entropy) 적용합니다. 이는 80 클래스가 되면서 클래스 별로 상위호환/계층 구조를 이룰 수 있기 때문에 라벨이 one-hot 이 아닌 multi-label 이 될 수 있기 때문입니다. 예를 들어 "여자", "사람" 라벨이 있다면 해당되는 2개의 라벨이 1이 될 것이고 이로 인해 각 클래스 별로 True/False 를 구분하는 Binary Cross Entorpy 함수를 적용합니다. 기본적으로 하나의 클래스만 맞다고 전제하는 Softmax 함수는 이 상황에서 잘 들어맞지 않겠죠.

이를 간단한 psuedo-code 로 구현하면 다음과 같습니다. 먼저 [batch size, 13, 13, [box 개수]*85] 형태를 가지는 최종 detection prediction output  를 [batch size, box 개수, 13, 13, 85] 형태로 reshape 하고 $tx', ty', obj', class'$ 예측치를 만들어줍니다.

def forward(self, x, ...):
    ## x: Shape of [batch size, 13, 13, [Num box*85]]
    prediction = (
        x.view(batch size, Num box, 85, 13, 13)
        .permute(0,1,3,4,2))
        
    tx = torch.sigmoid(prediction[..., 0])
    ty = torch.sigmoid(prediction[..., 1])
    tw = prediction[...,2]
    th = prediction[...,3]
    obj = torch.sigmoid(prediction[...,4])
    cls = torch.sigmoid(prediction[...,5:])

이후에 물체가 존재하는 부분에 (obj_mask) 대해 목적함수를 다음과 같이 정의합니다.

Loss = Lambda_Coord * Sum(Mean_Square_Error((tx, ty), (tx’, ty’) * obj_mask) # x, y
 + Lambda_Coord * Sum(Mean_Square_Error((tw, th), (tw’, th’) * obj_mask) # w, h 
 + Sum(Binary_Cross_Entropy(obj, obj’) * obj_mask) # objectiveness score
 + Lambda_Noobj * Sum(Binary_Cross_Entropy(obj, obj’) * (1 -obj_mask))
 + Sum(Binary_Cross_Entropy(class, class’)) # class score
  • obj_mask 는 각 박스 별로 물체가 존재하는지 여부를 알려주는 라벨입니다. 각 물체의 ground-truth와 최대한 많이 겹치는 (IOU가 가장 큰) 박스로 할당됩니다. 따라서 객체가 존재하지 않는 그리드에 대해서는 목적함수가 적용되지 않습니다.
  • 물체가 존재하는 박스에 대해서 먼저 좌표와 높이/너비에 대한 목적함수를 MSE로 만듭니다. $t_w, t_h, t_x, t_y$ 라벨은 Figure 2의 $b_x, b_y, b_w, h_h$로부터 역변환해서 구할 수 있습니다. Lambda_Coord 는 객체의 localization 을 위한 하이퍼파라미터로서 YOLO v1 부터 사용되었고 5로 설정합니다.
  • 물체가 존재하는 박스에 대해서 라벨을 1로 설정한 BCE를 적용하고 존재하지 않는 박스에 대해서 라벨을 0으로 설정한 BCE를 적용합니다. 물체가 존재하지 않는 박스에 대해 목적함수를 추가하는 이유는 객체를 담고 있지 않은 박스가 추정 자체를 하지 않아 false positive 를 줄이기 위해서입니다. 또한, 물체가 존재하지 않는 박스는 존재하는 박스에 비해 훨씬 많을 것이므로 Lambda_Noobj=0.5 로 낮게 설정합니다.
  • Multi-label 상황이므로 각 클래스 별로 BCE를 적용합니다.

Multi-scale prediction

하지만 아직까지도 최종 feature map 크기는 13x13 으로 작은 물체를 탐지하기에는 너무 작습니다. 따라서 Figure 3과 같이 feature extractor 의 다양한 층에서 feature map 을 추출하여 detection layer 를 붙이는 multi-scaled detector 기법을 사용합니다. 최종단의 13x13 과 더불어 직전 두개의 layer의 26x26, 52x52 feature map을 포함하여 총 3가지 스케일의 feature map 을 detection에 사용합니다.

Figure 3

Detection 을 위해서 3가지 스케일의 feature map을 Figure 4와 같이 Image Pyramid 형태로 투입합니다. Figure 4를 보면 작은 해상도의 feature map 을 upsampling 하여 큰 해상도에 이어 붙이는 방식으로 detection networks 가 구성되는데요, 이 방법으로 fine-grained, semantic 정보를 추출할 수 있으며 13x13 layer는 큰 물체, 26x26 layer는 중간 물체, 52x52 layer는 작은 물체를 포착하는 역할을 맡습니다.

Figure 4

Bounding box prior 와 관련해서는 YOLO v2 와 마찬가지로 K-Means 알고리즘을 이용하여 9개의 prior 를 추출합니다. 이것을 각각의 크기에 맞게 3가지 scale 에 3개씩 각각 분배합니다. 최종적으로 416x416 이미지에 대해서 detector 출력은 [(52,52,3*85), (26,26,3*85), (13,13,3*85)]가 되며 총 bounding box 수는 10647개가 됩니다. YOLO v2 의 13x13x5=845개의 bounding box 보다 10배 이상 늘어나 성능이 더욱 향상되고 이전 layer의 feature map 을 사용하므로 작은 물체도 더 잘 포착할 수 있습니다. 

 

Results

YOLO v3 는 Figure 5에서 볼 수 있듯이 RetinaNet 의 성능보다 약간 낮지만 3배 이상 빠릅니다. 하지만 중요한 점은 맞다고 판단하기 위한 IOU 임계치를 0.5로 설정했을 때, ($AP_{50}$) RetinaNet 과 성능이 거의 비슷해진다는 것입니다. (Figure 6) 특히, Table 3에서 볼 수 있듯이 SSD 계열에 비해 성능이 더 좋으면서도 더 빠르며 multi-scale prediction 을 통해 작은 물체에 대한 $AP_{S}$가 YOLO v2 에 비해 매우 큰폭으로 상승하였습니다. 하지만 IOU 임계치가 0.75 이면 RetinaNet 에 비해 성능이 확연히 안좋아지고 ($AP_{75}$) 전반적으로도 RetinaNet 에 비해 성능적으로 개선되어야 할 부분이 많아 보입니다.

Figure 5
Figure 6

 

참조

반응형