🚀該系列將會持續整理和更新BBR相關的問題,如有錯誤和不足懇請大家指正,歡迎討論!!!
📦目標檢測的損失函數一般包含三個部分,分別是邊界框損失也可稱為定位損失、置信度損失和分類損失,這一小節主要講解一下邊界框損失!
首先,邊界框損失也就是目標檢測中的邊界框回歸任務(bounding box regression, BBR),之前用的最廣泛的就是n范數損失,目前基本就是關于IoU相關的損失了。n范數損失可以參考一些文獻,比如發表在2015年CVPR會議上的《Fast R-CNN》。
接下來,先回顧一下n范數損失,一般有L1loss、L2loss和Lloss。
L1loss(絕對值損失)?
,其中
是真值,
是預測值;對于異常較為魯棒,但不處處可導;
L2loss(平方損失)?
,其中
是真值,
是預測值;對于異常比較敏感;
L
loss(最大模損失)?考慮的是預測值與真實值之間差的最大絕對值,適用于最小化最大誤差。
BBR相關的任務可追溯到YOLOv1中,使用平方誤差來計算邊界框的位置損失,即預測框與真實框之間的中心坐標(x, y)和寬高(w, h)的誤差,彌補了尺度敏感性。這種計算兩個bbox相似度有一定的局限性,導致定位不準,所以在2016年,由UIUC和曠視發表的論文引入了IoU損失,就是用1減去IoU取個均值就ok了。《UnitBox: An Advanced Object Detection Network》
?
但是,將IoU作為loss的話,會有一個問題就是預測框和真實框之間不重疊,IoU值為0,兩個框之間的距離無法確定,梯度將為0,無法優化。
在2019年,斯坦福在CVPR上發表了一篇論文,提出了GIoU,取預測框和真實框的最小外接邊界框加入運算。《Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression》
? ? 具體計算如下:?
GIoU loss通常不能很好地收斂,導致檢測不準確。 2019年,天津大學發表在AAAI上的一篇論文《Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression》提出了DIoU和CIoU。DIoU考慮了兩個邊界框(預測和真實的)中心點坐標之間的距離,CIoU包含了三個幾何測度,分別是預測和真實邊界框之間的重疊面積、中心點坐標之間的距離和縱橫比。
具體計算如下:?
然而CIoU不能有效地測量目標盒和錨點之間的差異,導致BBR模型優化收斂緩慢,定位不準確。2022年,CASIA在《?Neurocomputing》上發表了一篇論文《Focal and Efficient IOU Loss for Accurate Bounding Box Regression》提出了一種有效的聯合交集(EIOU)損失,它明確地衡量了BBR中三個幾何因素的差異,即重疊區域、中心點和邊長。之后,針對有效的示例挖掘 (EEM) 問題,并提出了一個焦點損失的回歸版本解決樣本不平衡問題,以使回歸過程專注于高質量的錨框。最后,將上述兩個部分組合起來得到一個新的損失函數,即Focal-EIOU損失。 EIoU其實就是將CIoU中的縱橫比修改為了優化框的寬度和高度的差異。
?在ultralytics-main/ultralytics/utils/metrics.py中的實現:
def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, EIoU=False, Focal=False, eps=1e-7):# Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4)# Get the coordinates of bounding boxesif xywh: # transform from xywh to xyxy(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_else: # x1, y1, x2, y2 = box1b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps)w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps)# Intersection areainter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * \(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp(0)# Union Areaunion = w1 * h1 + w2 * h2 - inter + eps# IoUiou = inter / unionif CIoU or DIoU or GIoU or EIoU:cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) widthch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex heightif CIoU or DIoU or EIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squaredrho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center dist ** 2if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)with torch.no_grad():alpha = v / (v - iou + (1 + eps))if EIoU:rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2cw2 = cw**2 + epsch2 = ch**2 + epsif Focal:return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2), torch.pow(inter / (union + eps), 0.5) # Focal_EIoUelse:return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2) # EIoUreturn iou - (rho2 / c2 + v * alpha) # CIoUreturn iou - rho2 / c2 # DIoUc_area = cw * ch + eps # convex areareturn iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdfreturn iou # IoU
在ultralytics-main/ultralytics/utils/loss.py中的調用:?
# iou = bbox_iou(pbox, tbox[i], CIoU=True).squeeze() # iou(prediction, target)
# lbox += (1.0 - iou).mean() # iou lossiou = bbox_iou(pbox, tbox[i], EIoU=True, Focal=True).squeeze() # iou(prediction, target)
if type(iou) is tuple:lbox += (iou[1].detach() * (1.0 - iou[0])).mean()
else:lbox += (1.0 - iou).mean()