目标检测任务的损失函数一般由目标分类损失函数(Object Classificition Loss)和边界框回归损失函数(Bounding Box Regeression Loss)两部分构成,当然在YOLO系列中,还引入了置信度损失(Object Confidence Loss)。本文主要对边界框回归损失函数(Bounding Box Regeression Loss)进行讨论。
若想尝试改进YOLOv5-7.0中的损失函数:
ACM 2016 论文链接:UnitBox: An Advanced Object Detection Network
IOU(Intersection over Union)是一种用于衡量目标检测性能的评估指标。它主要用于计算两个边界框(Bounding Box)之间的重叠度,也就是所说的交并比。通常用于测量模型预测的边界框与真实边界框之间的匹配程度。
def IOU(box1, box2, eps=1e-7):
b1_x1, b1_y1, b1_x2, b1_y2 = box1
b2_x1, b2_y1, b2_x2, b2_y2 = box2
# 获取矩形框交集对应的左上角和右下角的坐标(intersection)
xx1 = np.max([b1_x1, b2_x1])
yy1 = np.max([b1_y1, b2_y1])
xx2 = np.min([b1_x2, b2_x2])
yy2 = np.min([b1_y2, b2_y2])
# 计算两个矩形框面积
area1 = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
area2 = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
inter = (np.max([0, xx2 - xx1])) * (np.max([0, yy2 - yy1])) # 计算交集面积
uniou = area1 + area2 - inter + eps
iou = inter / uniou # 计算交并比
return iou
if __name__ == "__main__":
box1 = np.array([0, 0, 100, 100])
box2 = np.array([50, 0, 150, 100])
print(IOU(box1, box2))
CVPR 2019 论文链接:Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression
GIOU通过引入预测框和真实框的最小外接矩形来获取预测框、真实框在闭包区域中的比重,从而解决了两个目标没有交集时梯度为零的问题。
G I o U = I o U − ∣ C − ( A ∪ B ) ∣ ∣ C ∣ GIoU=IoU-\frac{\left | C-\left ( A\cup B \right ) \right | }{\left | C \right | } GIoU=IoU−∣C∣∣C−(A∪B)∣
其中 C C C是两个框的最小外接矩形的面积。
AAAI 2020 论文链接:Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression
考虑到GIOU的缺点,DIOU在IoU的基础上直接回归两个框中心点的欧式距离,加速了收敛速度。DIOU的惩罚项是基于中心点的距离和对角线距离的比值。这样就避免了GIOU在两框距离较远时产生较大闭包时所造成的Loss值较大而难以优化的情况。
D I o U = I o U − ρ 2 ( b , b g t ) c 2 DIoU=IoU-\frac{\rho^{2} \left ( b,b^{gt} \right ) }{c^{2}} DIoU=IoU−c2ρ2(b,bgt)
其中 b b b, b g t b^{gt} bgt 分别表示预测框和真实框的中心点,且 ρ \rho ρ 表示两个中心点间的欧式距离。 c c c 表示能够同时包含预测框和真实框的最小闭包区域的对角线距离。
AAAI 2020(与DIOU同一篇文章) 论文链接:Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression
论文考虑到bbox回归三要素中的长宽比还没被考虑到计算中,因此CIOU在DIOU的基础上添加了长宽比的惩罚项,定义如下:
C I o U = I o U − ρ 2 ( b , b g t ) c 2 − α υ CIoU=IoU-\frac{\rho^{2} \left ( b,b^{gt} \right ) }{c^{2}}-\alpha \upsilon CIoU=IoU−c2ρ2(b,bgt)−αυ
υ \upsilon υ 是用来衡量长宽比一致性的参数, υ \upsilon υ 定义如下:
υ = 4 π 2 ( arctan ω g t h g t − arctan ω h ) 2 \upsilon =\frac{4}{\pi ^2} \left ( \arctan \frac{\omega ^{gt}}{h^{gt}} - \arctan \frac{\omega}{h} \right ) ^2 υ=π24(arctanhgtωgt−arctanhω)2
α = υ ( 1 − I o U ) + υ \alpha = \frac{\upsilon }{\left ( 1- IoU \right ) + \upsilon } α=(1−IoU)+υυ
arXiv 2021 论文链接:Focal and Efficient IOU Loss for Accurate Bounding Box Regression
EIOU是在 CIOU 的惩罚项基础上将预测框和真实框的纵横比的影响因子拆开,分别计算预测框和真实框的长和宽,并且加入Focal聚焦优质的锚框,来解决 CIOU 存在的问题。
EIoU损失函数公式如下:
L E I O U = L I O U + L d i s + L a s p = 1 − I O U + ρ 2 ( b , b g t ) c 2 + ρ 2 ( ω , ω g t ) C ω 2 + ρ 2 ( h , h g t ) c h 2 \begin{array}{l} L_{EIOU} = L_{IOU}+L_{dis}+L_{asp} \\ = 1-IOU+\frac{\rho^{2} \left ( b,b^{gt} \right ) }{c^{2}}+\frac{\rho^{2} \left ( \omega ,\omega^{gt} \right ) }{C^{2}_{\omega}}+\frac{\rho^{2} \left ( h,h^{gt} \right ) }{c^{2}_{h}} \end{array} LEIOU=LIOU+Ldis+Lasp=1−IOU+c2ρ2(b,bgt)+Cω2ρ2(ω,ωgt)+ch2ρ2(h,hgt)
其中 C ω 2 C_\omega^2 Cω2 和 C h 2 C_h^2 Ch2分别是预测框和GT框最小外接矩形的宽和高。
利用Focal Loss对EIOU进行加权处理:
L F o c a l − E I O U = I O U γ ∗ L E I O U L_{Focal-EIOU}=IOU^\gamma *L_{EIOU} LFocal−EIOU=IOUγ∗LEIOU
其中 γ \gamma γ为控制异常值抑制程度的参数。该损失中的Focal与传统的Focal Loss有一定的区别,传统的Focal Loss针对越困难的样本损失越大,起到的是困难样本挖掘的作用;而根据上述公式:IOU越高的损失越大,相当于加权作用,给越好的回归目标一个越大的损失,有助于提高回归精度。
NeurIPS 2021 论文链接:Alpha-IoU: A Family of Power Intersection over Union Losses for Bounding Box Regression
基于IoU Loss推广到一个新的Power IoU系列 Loss,该系列具有一个Power IoU项和一个附加的Power正则项,具有单个Power参数 α \alpha α。称这种新的损失系列为 α \alpha α-IoU Loss。
⭐当 α = 1 \alpha=1 α=1时,则回归到原始各个Loss。
论文链接:SIoU Loss: More Powerful Learning for Bounding Box Regression
SIOU损失函数是引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,从而加速网络的收敛,进一步提升回归的准确性。
SIOU损失函数由4个Cost函数组成:
Λ = 1 − 2 ∗ sin 2 ( arcsin ( C h σ ) − π 4 ) = 1 − 2 ∗ sin 2 ( α − π 4 ) = cos 2 ( α − π 4 ) − sin 2 ( α − π 4 ) = cos ( 2 α − π 2 ) = sin ( 2 α ) \begin{array}{l} \Lambda =1-2*\sin^2\left ( \arcsin \left ( \frac{C_h}{\sigma} \right ) - \frac{\pi}{4} \right ) \\ = 1-2*\sin^2\left ( \alpha - \frac{\pi}{4} \right ) \\ = \cos ^2\left ( \alpha - \frac{\pi}{4} \right ) - \sin ^2 \left ( \alpha - \frac{\pi}{4} \right ) \\ = \cos \left ( 2\alpha - \frac{\pi}{2} \right ) \\ = \sin \left ( 2\alpha \right ) \end{array} Λ=1−2∗sin2(arcsin(σCh)−4π)=1−2∗sin2(α−4π)=cos2(α−4π)−sin2(α−4π)=cos(2α−2π)=sin(2α)
其中 C h C_h Ch 为真实框和预测框中心点的高度差, σ \sigma σ为真实框和预测框中心点的距离, arcsin ( C h σ ) \arcsin (\frac{C_h}{\sigma}) arcsin(σCh) 等于角度 α \alpha α。
C h σ = sin ( α ) \frac{C_h}{\sigma} = \sin \left ( \alpha \right ) σCh=sin(α)
2023 论文链接:Wise-IoU: Bounding Box Regression Loss with Dynamic Focusing Mechanism
⭐这是作者的论文解读:Wise-IoU 作者导读:基于动态非单调聚焦机制的边界框损失,非常详细,本文则不再进行叙述。
WIoU v1 构造了基于注意力的边界框损失,WIoU v2 和 v3 则是在此基础上通过构造梯度增益 (聚焦系数) 的计算方法来附加聚焦机制。
所涉及的聚焦机制有以下几种:
IOU | GIOU | DIOU | CIOU | EIOU | α \alpha α-IOU | SIOU | WIOU | |
---|---|---|---|---|---|---|---|---|
优点 | 目标检测中最常用的指标,具有尺度不变性,满足非负性;同一性;对称性;三角不等性等特点 | GIOU在基于IOU特性的基础上引入最小外接框,能更好的反映两者的重合度 | DIOU在IoU的基础上直接回归两个框中心点的欧式距离,加速收敛 | CIOU在DIOU的基础上增加了检测框尺度的Loss,增加了长宽比的Loss,使得目标框回归更加稳定 | EIOU在CIOU的基础上分别计算宽高的差异值取代了纵横比,同时引入Focal Loss解决难易样本不平衡的问题 | 基于IoU Loss推广到一个新的Power IoU系列 Loss,通过调节 α \alpha α,使检测器在实现不同水平的bbox回归精度方面具有更大的灵活性 | SIOU是引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,加速网络的收敛,进一步提升回归的准确性 | WIoU v1 构造了基于注意力的边界框损失,WIoU v2 和 v3 则是在此基础上通过构造梯度增益 (聚焦系数) 的计算方法来附加聚焦机制。 |
缺点 | 1.若两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小 |
1.当检测框和真实框出现重叠退化成IOU 2.两个框相交时,在水平和垂直方向上收敛慢 |
回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间 | 若预测框和gt框的长宽比是相同的,那么长宽比的惩罚项恒为0,会阻碍模型有效的优化 | / | / | / | / |
Code
本代码摘抄于YOLOv5官方7.0版本,并在其基础上添加了Alpha-IOU、EIOU、SIOU和WIOU(后续添加)损失函数。
def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, SIoU=False, EIoU=False, Focal=False, alpha=1, gamma=0.5, eps=1e-7):
# Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4)
# Get the coordinates of bounding boxes
if 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 / 2
b1_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 = box1
b1_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 area
inter = (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 Area
union = w1 * h1 + w2 * h2 - inter + eps
# IoU
# iou = inter / union # ori iou
iou = torch.pow(inter/(union + eps), alpha) # alpha iou
if CIoU or DIoU or GIoU or EIoU or SIoU:
cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) width
ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height
if CIoU or DIoU or EIoU or SIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
c2 = (cw ** 2 + ch ** 2) ** alpha + eps # convex diagonal squared
rho2 = (((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4) ** alpha # center dist ** 2
if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)
with torch.no_grad():
alpha_ciou = v / (v - iou + (1 + eps))
return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)) # CIoU
elif EIoU:
rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
cw2 = torch.pow(cw ** 2 + eps, alpha)
ch2 = torch.pow(ch ** 2 + eps, alpha)
if Focal:
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2), torch.pow(inter/(union + eps), gamma) # Focal_EIou
else:
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2) # EIou
elif SIoU:
# SIoU Loss https://arxiv.org/pdf/2205.12740.pdf
s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps
s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps
sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
sin_alpha_1 = torch.abs(s_cw) / sigma
sin_alpha_2 = torch.abs(s_ch) / sigma
threshold = pow(2, 0.5) / 2
sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)
rho_x = (s_cw / cw) ** 2
rho_y = (s_ch / ch) ** 2
gamma = angle_cost - 2
distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)
omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)
omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)
shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
return iou - torch.pow(0.5 * (distance_cost + shape_cost) + eps, alpha) # SIou
return iou - rho2 / c2 # DIoU
c_area = cw * ch + eps # convex area
return iou - torch.pow((c_area - union) / c_area + eps, alpha) # GIoU https://arxiv.org/pdf/1902.09630.pdf
return iou # IoU