논문 읽기/Object Detection

[논문 읽기] DIoU Loss(2020), Distance-IoU Loss, Faster and Better Learning for Bounding Box Regression

AI 꿈나무 2021. 5. 15. 13:25
반응형

 안녕하세요, 오늘 읽은 논문은 Distance-IoU Loss Fater and Better Learning for Bounding Box Regression 입니다.

 

 이 논문에서는 bounding box regression loss인 CIoU Loss와 DIoU Loss를 제안합니다.

 

 DIoU Loss는 target box와 predicted box 사이의 IoU와 중심점을 요소로 Loss를 계산합니다. DIoU Loss는 NMS의 threshold로 사용합니다. IoU를 threshold로 사용하는 것보다 중심점과 IoU를 고려하는 DIoU를 threshold로 사용한다면 class가 동일한 ground-truth box가 겹쳐있는 경우에 supression 하는 것을 방지할 수 있습니다.

 

 CIoU Loss는 IoU, 중심점, aspect ration 세 가지 요소로 Loss를 계산합니다.

 

IoU Loss와 GIoU의 문제점

 DIoU Loss와 CIoU Loss는 GIoU Loss와 IoU Loss의 문제점을 개선하기 위해 제안되었습니다.

 

IoU Loss

 

 IoU Loss는 target box와 predicted box가 겹쳐진 경우만 고려합니다. 따라서 non overlapping 된 박스에 대해서는 gradient를 제공하지 않습니다.

 

GIoU Loss

 

 GIoU Loss는 IoU Loss가 non overlapping 된 경우를 고려하지 않는 문제점을 개선하기 위해 panalty term을 추가한 것입니다. C는 target과 predicted를 다 덮는 박스의 넓이를 의미합니다. panalty term을 추가함으로써 non-overlapping 된 box도 gradient 정보를 제공합니다.

 

 하지만 GIoU Loss는 수렴 속도가 느리고 부정확하게 바운딩 박스를 예측한다는 단점이 있습니다.

 

 

 위 그림은 iteration에 따른 GIoU Loss의 바운딩 박스 예측 과정입니다. 검은 박스는 anchor, 초록 박스는 target, 파랑색 박스는 predicted를 의미합니다. 매 반복이 진행하면서 target과의 overlap을 위해 predicted box의 영역이 넓어 집니다. 그리고, target과 overlap이 된 이후로부터 IoU를 높이기 위해 predicted box의 크기를 줄여 나갑니다.

 

DIoU Loss

 

 반면에 DIoU Loss는 IoU와 중심점 좌표를 함께 고려하므로 predicted box가 서서히 target으로 이동하는 것을 확인할 수 있습니다. GIoU Loss보다 훨씬 더 적은 iteration으로 수렴하는 모습을 보여줍니다.

 

 

 위 그림을 살펴보면, IoU Loss와 GIoU Loss는 target과 predicted가 overlap 된 경우에 IoU만 고려하고 위치를 전혀 고려하지 않는 모습을 보여줍니다. IoU만 일치하다면 두 박스가 어느 위치에 존재하던지 동일한 Loss를 계산합니다. 하지만 DIoU Loss는 중심점 좌표를 고려하므로 overlap 된 경우에 두 바운딩 박스의 위치까지 고려를 합니다.

 

 즉, GIoU Loss의 문제점은 수렴 속도가 너무 늦고 수평 또는 수직 방향에 대해서 Loss가 너무 높다는 것입니다. 이 문제를 개선하기 위해 CIoU Loss는 IoU, 중심점, aspect ration 세 가지 요소를 고려합니다.

 

DIoU Loss(Distance-IoU Loss)

 위 식은 IoU Loss에 중심점을 고려하는 panalty term을 추가한 식입니다. DIoU Loss의 panalty term은 다음과 같습니다.

 

 

 로우는 Euclidean distance를 의미하고, b와 b^gt는 predicted box, target box의 중심점 좌표를 의미합니다. c는 predicted box와 target box를 덮는 박스의 대각 길이를 의미합니다. 중심점을 고려하는 panalty term을 추가한 것입니다.

 

 

 DIoU Loss는 IoU Loss와 GIoU Loss의 장점을 모두 갖고 있습니다. non overlapping 된 경우에 두 박스의 거리를 최소화 하는 방향을 제공하므로 GIoU Loss보다 빠르게 수렴하는 장점이 있습니다. GIoU Loss는 non overlabbping 된 경우에 단순히 target 과 predicted의 overlap을 위해 predicted box의 크기만을 증가시킵니다.

 

 DIoU Loss는 nms의 threshold로 사용합니다. 

 

 IoU를 threshold로 사용하는 nms는 동일한 class를 지닌 ground truth box가 겹쳐져 있다면 해당 박스를 supression 하는 문제점이 존재합니다. 예를 들어, 얼룩말들이 뭉쳐져 있다면, 하나의 얼룩말만 남겨두고 나머지 얼룩말은 supression 됩니다. DIoU를 threshold로 사용한다면 바운딩 박스의 중심점이 다른 경우도 고려하므로 ground-truth가 겹쳐져 있는 경우에 대해서 robust 하다는 장점이 있습니다.

 

 아래 그림은 기존 NMS와 DIoU-NMS의 비교입니다.

 

CIoU Loss(Complete IoU Loss)

 CIoU Loss는 IoU, 중심점, aspect ration 세 가지 요소를 함께 고려합니다. target과 predicted의 aspect ration가 일치하도록 하는 가중치를 추가합니다.

 

CIoU Loss의 panelty term

 

CIoU Loss

 

 v는 두 박스의 aspect ration의 일치성을 측정합니다. a는 non-overlapping case와 overlapping case의 군형을 조절합니다. 특히 non-overlapping 경우에 우선순위를 부여하는 역할을 합니다.

 

 CIoU Loss의 gradient는 다음과 같이 계산합니다.

 

 

 h와 w가 [0,1] 범위에 있는 경우에 w^2 + h^2의 값이 매우 작아져 gradient explosion 문제가 발생됩니다. 따라서 저자는 w^2 + h^2를 1로 대체합니다.

 

Performance

 YOLOv3을 VOC dataset에 IoU, GIoU, DIoU, CIoU loss로 학습한 것을 비교한 결과합니다. (D)는 DIoU NMS를 의미합니다.

 

 SSD

 

Faster R-CNN

 

Code

 Code 출처: https://github.com/miaoshuyu/object-detection-usages/blob/master/src/ciou_loss.py

"""
IOU explanation: https: // zhuanlan.zhihu.com/p/47189358
"""
import torch
import math


def diou_loss(preds, bbox, eps=1e-7, reduction='mean'):
    '''
    https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/loss/multibox_loss.py
    :param preds:[[x1,y1,x2,y2], [x1,y1,x2,y2],,,]
    :param bbox:[[x1,y1,x2,y2], [x1,y1,x2,y2],,,]
    :param eps: eps to avoid divide 0
    :param reduction: mean or sum
    :return: diou-loss
    '''
    ix1 = torch.max(preds[:, 0], bbox[:, 0])
    iy1 = torch.max(preds[:, 1], bbox[:, 1])
    ix2 = torch.min(preds[:, 2], bbox[:, 2])
    iy2 = torch.min(preds[:, 3], bbox[:, 3])

    iw = (ix2 - ix1 + 1.0).clamp(min=0.)
    ih = (iy2 - iy1 + 1.0).clamp(min=0.)

    # overlaps
    inters = iw * ih

    # union
    uni = (preds[:, 2] - preds[:, 0] + 1.0) * (preds[:, 3] - preds[:, 1] + 1.0) + (bbox[:, 2] - bbox[:, 0] + 1.0) * (
            bbox[:, 3] - bbox[:, 1] + 1.0) - inters

    # iou
    iou = inters / (uni + eps)

    # inter_diag
    cxpreds = (preds[:, 2] + preds[:, 0]) / 2
    cypreds = (preds[:, 3] + preds[:, 1]) / 2

    cxbbox = (bbox[:, 2] + bbox[:, 0]) / 2
    cybbox = (bbox[:, 3] + bbox[:, 1]) / 2

    inter_diag = (cxbbox - cxpreds) ** 2 + (cybbox - cypreds) ** 2

    # outer_diag
    ox1 = torch.min(preds[:, 0], bbox[:, 0])
    oy1 = torch.min(preds[:, 1], bbox[:, 1])
    ox2 = torch.max(preds[:, 2], bbox[:, 2])
    oy2 = torch.max(preds[:, 3], bbox[:, 3])

    outer_diag = (ox1 - ox2) ** 2 + (oy1 - oy2) ** 2

    diou = iou - inter_diag / outer_diag

    # calculate v,alpha
    wbbox = bbox[:, 2] - bbox[:, 0] + 1.0
    hbbox = bbox[:, 3] - bbox[:, 1] + 1.0
    wpreds = preds[:, 2] - preds[:, 0] + 1.0
    hpreds = preds[:, 3] - preds[:, 1] + 1.0
    v = torch.pow((torch.atan(wbbox / hbbox) - torch.atan(wpreds / hpreds)), 2) * (4 / (math.pi ** 2))
    alpha = v / (1 - iou + v)
    ciou = diou - alpha * v
    ciou = torch.clamp(ciou, min=-1.0, max=1.0)

    ciou_loss = 1 - ciou
    if reduction == 'mean':
        loss = torch.mean(ciou_loss)
    elif reduction == 'sum':
        loss = torch.sum(ciou_loss)
    else:
        raise NotImplementedError
    return loss


if __name__ == '__main__':
    pred_bboxes = torch.tensor([[15, 18, 47, 60],
                                [50, 50, 90, 100],
                                [70, 80, 120, 145],
                                [130, 160, 250, 280],
                                [25.6, 66.1, 113.3, 147.8]], dtype=torch.float)
    gt_bbox = torch.tensor([[70, 80, 120, 150]], dtype=torch.float)
    print(diou_loss(pred_bboxes, gt_bbox))

참고자료

[1] https://arxiv.org/abs/1911.08287

반응형