YOLOv5改进 | 损失函数 | EIoU、SIoU、DIoU、FocusIoU等多种损失函数

💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡

在目标检测领域内,尽管YOLO系列的算法傲视群雄,但在某些方面仍然存在改进的空间。在YOLOv5的损失函数中,默认是使用的CIoU,但是CIoU仍然存在一定的问题。例如CIOU的计算方式相对复杂,需要对边界框的坐标进行更多的处理和计算。本文给大家带来的教程是将原来的CIoU替换为EIoU、SIoU、WIoU、DIoU、FocusIoU。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。

 专栏地址 YOLOv5改进+入门——持续更新各种有效涨点方法 点击即可跳转

更多损失函数讲解百面算法工程师 | 损失函数篇 

目录

1. ✒️CIoU

1.1 CIoU原理

1.2 CIoU计算

1.3 📌CIoU代码实现

2. ✒️EIOU(Efficient-IoU)

2.1 EIoU原理

2.2 代码实现

3. 将EIoU、SIoU、WIoU、DIoU、FocusIoU添加到YOLOv5中

3.1 添加代码

3.2 回调函数

4.完整代码分享

5. 进阶

6. 总结


1. ✒️CIoU

1.1 CIoU原理

img

论文地址Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression——点击即可跳转

论⽂考虑到bbox回归三要素中的⻓宽⽐还没被考虑到计算中,为此,进⼀步在DIoU的基础上提出了CIoU,同时考虑两个矩形的长宽比,也就是形状的相似性。所以CIOU在DIOU的基础上添加了长宽比的惩罚项。

img

其中, 是权重函数, 而用来度量长宽比的相似性。计算公式为:

在这里插入图片描述

☀️优点

更准确的相似性度量:CIOU考虑了边界框的中心点距离和对角线距离,因此可以更准确地衡量两个边界框之间的相似性,尤其是在目标形状和大小不规则的情况下。 鲁棒性更强:相比传统的IoU,CIOU对于目标形状和大小的变化更具有鲁棒性,能够更好地适应各种尺寸和形状的目标检测任务。

⚡️缺点

计算复杂度增加:CIOU引入了额外的中心点距离和对角线距离的计算,因此相比传统的IoU,计算复杂度有所增加,可能会增加一定的计算成本。 实现难度较高:CIOU的计算方式相对复杂,需要对边界框的坐标进行更多的处理和计算,因此在实现上可能会相对困难一些,需要更多的技术和经验支持。

1.2 CIoU计算

中心点 b、中心点 bgt的坐标分别为:(3,4)、(6,6),由此CIoU计算公式如下:

img

img

1.3 📌CIoU代码实现

import numpy as np
import IoU
import DIoU
# box : [左上角x坐标,左上角y坐标,右下角x坐标,右下角y坐标]
box1 = [0, 0, 6, 8]
box2 = [3, 2, 9, 10]
# CIoU
def CIoU(box1, box2):
    x1, y1, x2, y2 = box1
    x3, y3, x4, y4 = box2
    # box1的宽:box1_w,box1的高:box1_h,
    box1_w = x2 - x1
    box1_h = y2 - y1
    # box2的宽:box2_w,box2的高:box2_h,
    box2_w = x4 - x3
    box2_h = y4 - y3
    iou = IoU(box1, box2)
    diou = DIoU(box1, box2)
 
    # v用来度量长宽比的相似性
    v = (4 / (np.pi) ** 2) * (np.arctan(int(box2_w / box2_h)) - np.arctan(int(box1_w / box1_h)))
    # α是权重函数
    a = v / ((1 + iou) + v)
    ciou = diou - a * v
    return ciou
 
print(CIoU(box1, box2))
输出结果:0.1589460263493413

2. ✒️EIOU(Efficient-IoU)

2.1 EIoU原理

img

论文地址:Focal and Efficient IOU Loss for Accurate Bounding Box Regression

EIOU是在 CIOU 的惩罚项基础上将预测框和真实框的纵横比的影响因子拆开,分别计算预测框和真实框的长和宽,并且加入Focal聚焦优质的锚框,来解决 CIOU 存在的问题。先前基于iou的损失,例如CIOU和GIOU,不能有效地测量目标盒和锚点之间的差异,这导致BBR(边界框回归)模型优化的收敛速度慢,定位不准确。

img

针对上述问题,对CIOU损失进行了修正,提出了一种更有效的IOU损失,即EIOU损失,定义如下:

img

其中和是覆盖两个盒子的最小围框的宽度和高度。也就是说,我们将损失函数分为三个部分:IOU损失LIOU,距离损失和方向损失。这样,我们可以保留CIOU损失的有效特点。同时,EIOU损失直接使目标盒与锚盒宽度和高度的差值最小化,从而使收敛速度更快,定位效果更好。

优点:

更稳定的训练过程:通过引入中心点距离和宽高比的惩罚项,即使在IoU为0的情况下,EIou也能提供有效的梯度信息,确保模型能够继续学习。

提高定位精度:通过综合考虑位置和尺寸的匹配,EIou可以显著提升目标检测模型的定位精度,尤其在目标物体大小和形状变化较大时表现更优。

2.2 代码实现

import numpy as np
 
def calculate_eiou(box1, box2):
    # 计算嵌入向量(这里简化为使用中心点坐标作为嵌入向量)
    center1 = np.array([(box1[0] + box1[2]) / 2, (box1[1] + box1[3]) / 2])
    center2 = np.array([(box2[0] + box2[2]) / 2, (box2[1] + box2[3]) / 2])
 
    # 计算嵌入向量之间的欧式距离
    euclidean_distance = np.linalg.norm(center1 - center2)
 
    # 计算目标框的面积
    area_box1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area_box2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
 
    # 计算交集和并集的面积
    intersection = max(0, min(box1[2], box2[2]) - max(box1[0], box2[0])) * \
                   max(0, min(box1[3], box2[3]) - max(box1[1], box2[1]))
    union = area_box1 + area_box2 - intersection
 
    # 计算EIOU
    eiou = 1 - intersection / union + euclidean_distance
    return eiou
 
box1 = [0, 0, 6, 8]
box2 = [3, 2, 9, 10]
print(calculate_eiou(box1, box2))
输出结果:4.374782044694758

3. 将EIoU、SIoU、WIoU、DIoU、FocusIoU添加到YOLOv5中

3.1 添加代码

关键步骤一:utils/metrics.py中,找到bbox_iou函数,可以把原有的注释掉,换成下面的代码:

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))
                if Focal:
                    return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)), torch.pow(inter/(union + eps), gamma)  # Focal_CIoU
                else:
                    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)
                if Focal:
                    return iou - torch.pow(0.5 * (distance_cost + shape_cost) + eps, alpha), torch.pow(inter/(union + eps), gamma) # Focal_SIou
                else:
                    return iou - torch.pow(0.5 * (distance_cost + shape_cost) + eps, alpha) # SIou
            if Focal:
                return iou - rho2 / c2, torch.pow(inter/(union + eps), gamma)  # Focal_DIoU
            else:
                return iou - rho2 / c2  # DIoU
        c_area = cw * ch + eps  # convex area
        if Focal:
            return iou - torch.pow((c_area - union) / c_area + eps, alpha), torch.pow(inter/(union + eps), gamma)  # Focal_GIoU https://arxiv.org/pdf/1902.09630.pdf
        else:
            return iou - torch.pow((c_area - union) / c_area + eps, alpha)  # GIoU https://arxiv.org/pdf/1902.09630.pdf
    if Focal:
        return iou, torch.pow(inter/(union + eps), gamma)  # Focal_IoU
    else:
        return iou  # IoU

3.2 回调函数

关键步骤二:utils/loss.py中,找到ComputeLoss类中的__call__()函数,把Regression loss中计算iou的代码,换成下面这句:

主要对红框部分替换为以下代码

iou = bbox_iou(pbox, tbox[i], CIoU=True)  # iou(prediction, target)
if type(iou) is tuple:
    lbox += (iou[1].detach().squeeze() * (1 - iou[0].squeeze())).mean()
    iou = iou[0].squeeze()
else:
    lbox += (1.0 - iou.squeeze()).mean()  # iou loss
    iou = iou.squeeze()

最后修改参数就在调用bbox_iou中进行修改即可,比如上面的代码就是使用了CIoU,如果你想使用Focal_EIoU那么你可以修改为下:

iou = bbox_iou(pbox, tbox[i], EIoU=True, Focal=True)

改进后: 

4.完整代码分享

https://pan.baidu.com/s/1NIlQkf1lUVl5hKRkT1VVIA?pwd=yp1s

提取码: yp1s

5. 进阶

可以融合其他的注意力机制,修改backbone以及neck,多个模块进行改进。

6. 总结

EIou(Enhanced Intersection over Union)损失函数是对传统IoU损失函数的改进,旨在提高目标检测模型的精度和鲁棒性。与仅考虑预测框与真实框重叠程度的IoU不同,EIou在损失计算中引入了几何中心点的距离和宽高比的差异。通过这种方式,即使在预测框和真实框完全不重叠的情况下,EIou也能提供有效的梯度信息,从而确保模型训练的连续性和稳定性。此外,EIou综合考虑位置和尺寸的匹配,有助于减少预测框的位置偏移和形状不匹配,显著提升目标检测的定位精度。总体而言,EIou损失函数适用于各种需要高精度定位的目标检测任务,如自动驾驶和无人机视觉,能够显著提升检测模型的整体性能。

  • 53
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kay_545

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值