开始讲解之前推荐一下我的专栏,本专栏的内容支持(分类、检测、分割、追踪、关键点检测),专栏目前为限时折扣,欢迎大家订阅本专栏,本专栏每周更新5-7篇最新机制,更有包含我所有改进的文件和交流群提供给大家,本人定期在群内分享发表论文方法和经验。


一、本文介绍

本文给大家带来的是YOLOv25最新改进,为大家带来最近新提出的InnerIoU的内容同时用Inner的思想结合SIoU、WIoU、GIoU、DIoU、EIOU、CIoU等损失函数,形成 InnerIoU、InnerSIoU、InnerWIoU、等新版本损失函数,同时还结合了Focus和AIpha思想,形成的新的损失函数,其中Inner的主要思想是:引入了不同尺度的辅助边界框来计算损失,(该方法在处理非常小目标的检测任务时表现出良好的性能(但是在其它的尺度检测时也要比普通的损失要好)。文章会详细探讨这些损失函数如何提高YOLOv26在各种检测任务中的性能,包括提升精度、加快收敛速度和增强模型对复杂场景的适应性。

专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制


目录

一、本文介绍

二、各种损失函数的基本原理 

2.1 交集面积和并集面积

2.2 InnerIoU的思想 

2.3 InnerSIoU

2.4 InnerWioU

2.5 InnerGIoU

2.6 InnerDIoU

2.7 InnerEIoU

2.8 InnerCIoU

2.9 FocusLoss 

三、损失函数代码块

四、添加InnerIoU等损失函数到模型中

 4.1 修改一

4.2 修改二

4.3 步骤三(未必要修改) 

4.4 步骤四

五、总结


二、各种损失函数的基本原理 

论文地址:官方Inner-IoU论文地址点击即可跳转

代码地址:官方代码地址-官方只放出了两种结合方式CIoU、SIoU

本文改进地址: 文末提供完整代码块-包括InnerEIoU、InnerCIoU、InnerDIoU等七种结合方式和其Focus变种


2.1 交集面积和并集面积

在理解各种损失函数之前我们需要先来理解一下交集面积和并集面积,在数学中我们都学习过集合的概念,这里的交集和并集的概念和数学集合中的含义是一样的。


2.2 InnerIoU的思想 

Inner-IoU(内部交并比)的主要思想是:改进目标检测中边界框回归(BBR)的准确性,特别是在处理高度重叠的目标时。传统的IoU(交并比)计算方法考虑了预测边界框和真实边界框的整体重叠区域,而Inner-IoU则专注于边界框内部的重叠部分。它通过引入辅助边界框,这些辅助框是原始边界框的缩小版本,来计算损失函数。

这种方法的优点包括:

  1. 针对性优化:Inner-IoU通过关注边界框的核心部分而非整体,提供了对重叠区域更加精确的评估。
  2. 调整尺度:通过控制辅助边界框的大小,Inner-IoU允许对不同的数据集和检测任务进行微调。
  3. 提高泛化能力:实验证明,Inner-IoU在不同的数据集上显示出比传统IoU更好的泛化性能。
  4. 处理高低IoU样本:对于高IoU样本,使用较小的辅助框可以加速模型学习;而对于低IoU样本,使用较大的辅助框可以改善回归性能。

总结:Inner-IoU是一种更细致、更专注于目标中心的性能评估指标,它通过辅助框的尺度调整提高了目标检测任务的精确度和效率。

结合InnerIoU各种损失函数的效果图 

上面的图片展示了CIoU 和 Inner-CIoU 方法。图中从左至右分别表示 CIoU 方法,以及不同比例(0.7、0.75 和 0.8)的 Inner-CIoU 方法的检测结果 

这个图片可以看出这个Innner的思想在小目标检测的时候效果能够达到极致(最适用于小范围但是其它的情况也能够有效但是小目标是效果最好的情景) 

PS:下面介绍的是融合的各种思想就是将其中的IoU替换为我们上面求出来的InnerIoU即可和其中的参数也替换为InnerIoU的思想,其中各种损失函数的本身思想并没有改变,只是改变了其中的 参数。


2.3 InnerSIoU

论文地址:SIoU: More Powerful Learning for Bounding Box Regression

适用场景:适用于需要高精度边界框对齐的场景,如精细的物体检测和小目标检测。

概念:SIoU损失通过融入角度考虑和规模敏感性,引入了一种更为复杂的边界框回归方法,解决了以往损失函数的局限性,SIoU损失函数包含四个组成部分:角度损失、距离损失、形状损失和第四个未指定的组成部分。通过整合这些方面,从而实现更好的训练速度和预测准确性。


2.4 InnerWioU

论文地址WIoU: Bounding Box Regression Loss with Dynamic Focusing Mechanism

适用场景:适用于需要动态调整损失焦点的情况,如不均匀分布的目标或不同尺度的目标检测。

概念:引入动态聚焦机制的IoU变体,旨在改善边界框回归损失。


2.5 InnerGIoU

论文地址:GIoU: A Metric and A Loss for Bounding Box Regression

适用场景:适合处理有重叠和非重叠区域的复杂场景,如拥挤场景的目标检测。

概念:在IoU的基础上考虑非重叠区域,以更全面评估边界框


2.6 InnerDIoU

论文地址:DIoU: Faster and Better Learning for Bounding Box Regression

适用场景:适用于需要快速收敛和精确定位的任务,特别是在边界框定位精度至关重要的场景。

概念:结合边界框中心点之间的距离和重叠区域。


2.7 InnerEIoU

论文地址:EIoU:Loss for Accurate Bounding Box Regression

适用场景:可用于需要进一步优化边界框对齐和形状相似性的高级场景。

概念:EIoU损失函数的核心思想在于提高边界框回归的准确性和效率。它通过以下几个方面来优化目标检测:

1. 增加中心点距离损失:通过最小化预测框和真实框中心点之间的距离,提高边界框的定位准确性。

2. 考虑尺寸差异:通过惩罚宽度和高度的差异,EIoU确保预测框在形状上更接近真实框。

3. 结合最小封闭框尺寸:将损失函数与包含预测框和真实框的最小封闭框的尺寸相结合,从而使得损失更加敏感于对象的尺寸和位置。

EIoU损失函数在传统IoU基础上增加了这些考量,以期在各种尺度上都能获得更精确的目标定位,尤其是在物体大小和形状变化较大的场景中。


2.8 InnerCIoU

论文地址:CIoU:Enhancing Geometric Factors in Model Learning

适用场景:适合需要综合考虑重叠区域、形状和中心点位置的场景,如复杂背景或多目标跟踪。

概念:综合考虑重叠区域、中心点距离和长宽比。


2.9 FocusLoss 

论文地址:Focal Loss for Dense Object Detection

适用场景:适用于需要高精度边界框对齐的场景,如精细的物体检测和小目标检测。 

Focal Loss由Kaiming He等人在论文《Focal Loss for Dense Object Detection》中提出,旨在解决在训练过程中正负样本数量极度不平衡的问题,尤其是在一些目标检测任务中,背景类别的样本可能远远多于前景类别的样本。

Focal Loss通过修改交叉熵损失,增加一个调整因子这个因子降低了那些已经被正确分类的样本的损失值,使得模型的训练焦点更多地放在难以分类的样本上。这种方式特别有利于提升小目标或者在复杂背景中容易被忽视的目标的检测性能。简而言之,Focal Loss让模型“关注”(或“专注”)于学习那些对提高整体性能更为关键的样本。


三、损失函数代码块

使用方法看章节四!

def bbox_iou(
    box1: torch.Tensor,
    box2: torch.Tensor,
    xywh: bool = True,
    GIoU: bool = False,
    DIoU: bool = False,
    CIoU: bool = False,
    eps: float = 1e-7,
    *,
    SIoU: bool = False,
    EIoU: bool = False,
    WIoU: bool = False,
    Focal: bool = False,
    Inner: bool = False,
    ratio: float = 0.7,
    alpha: float = 1.0,
    gamma: float = 0.5,
    scale: bool = False,
) -> Union[torch.Tensor, Tuple[torch.Tensor, ...]]:
    """Calculate IoU / GIoU / DIoU / CIoU / EIoU / SIoU / WIoU between bounding boxes.

    Args:
        box1 (torch.Tensor): Boxes with last dimension 4.
        box2 (torch.Tensor): Boxes with last dimension 4.
        xywh (bool): If True, boxes are in (x, y, w, h) format.
            If False, boxes are in (x1, y1, x2, y2) format.
        GIoU (bool): Enable Generalized IoU.
        DIoU (bool): Enable Distance IoU.
        CIoU (bool): Enable Complete IoU.
        eps (float): Small value to avoid division by zero.

        SIoU (bool): Enable SIoU.
        EIoU (bool): Enable EIoU.
        WIoU (bool): Enable WIoU.
        Focal (bool): Enable Focal-IoU style output.
        Inner (bool): Enable Inner-IoU. If True, IoU is calculated on scaled inner boxes.
        ratio (float): Inner box scale ratio. Commonly 0 < ratio <= 1.
        alpha (float): Power factor used by some extended losses.
        gamma (float): Focal exponent.
        scale (bool): Enable WIoU v2 / v3 dynamic scaling.

    Returns:
        torch.Tensor or Tuple[torch.Tensor, ...]
    """

    # ------------------------------------------------------------
    # 1. Get coordinates of bounding boxes
    # ------------------------------------------------------------
    if xywh:
        # Input format: x, y, w, h
        (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)

        w1 = w1.clamp(min=eps)
        h1 = h1.clamp(min=eps)
        w2 = w2.clamp(min=eps)
        h2 = h2.clamp(min=eps)

    else:
        # Input format: x1, y1, x2, y2
        b1_x1_ori, b1_y1_ori, b1_x2_ori, b1_y2_ori = box1.chunk(4, -1)
        b2_x1_ori, b2_y1_ori, b2_x2_ori, b2_y2_ori = box2.chunk(4, -1)

        w1 = (b1_x2_ori - b1_x1_ori).clamp(min=eps)
        h1 = (b1_y2_ori - b1_y1_ori).clamp(min=eps)
        w2 = (b2_x2_ori - b2_x1_ori).clamp(min=eps)
        h2 = (b2_y2_ori - b2_y1_ori).clamp(min=eps)

        x1 = (b1_x1_ori + b1_x2_ori) / 2
        y1 = (b1_y1_ori + b1_y2_ori) / 2
        x2 = (b2_x1_ori + b2_x2_ori) / 2
        y2 = (b2_y1_ori + b2_y2_ori) / 2

    # ------------------------------------------------------------
    # 2. Normal box or Inner box
    # ------------------------------------------------------------
    if Inner:
        # Inner-IoU:
        # 用 ratio 缩小 box,只在缩小后的 inner box 上计算 IoU。
        # ratio=0.7 表示使用原框中心区域的 70% 宽高。
        r = torch.as_tensor(ratio, dtype=box1.dtype, device=box1.device).clamp(min=eps)

        w1_half, h1_half = (w1 * r) / 2, (h1 * r) / 2
        w2_half, h2_half = (w2 * r) / 2, (h2 * r) / 2

        b1_x1, b1_x2 = x1 - w1_half, x1 + w1_half
        b1_y1, b1_y2 = y1 - h1_half, y1 + h1_half

        b2_x1, b2_x2 = x2 - w2_half, x2 + w2_half
        b2_y1, b2_y2 = y2 - h2_half, y2 + h2_half

        area1 = w1 * h1 * r * r
        area2 = w2 * h2 * r * r

    else:
        # Normal IoU box
        w1_half, h1_half = w1 / 2, h1 / 2
        w2_half, h2_half = w2 / 2, h2 / 2

        b1_x1, b1_x2 = x1 - w1_half, x1 + w1_half
        b1_y1, b1_y2 = y1 - h1_half, y1 + h1_half

        b2_x1, b2_x2 = x2 - w2_half, x2 + w2_half
        b2_y1, b2_y2 = y2 - h2_half, y2 + h2_half

        area1 = w1 * h1
        area2 = w2 * h2

    # ------------------------------------------------------------
    # 3. Intersection and union
    # ------------------------------------------------------------
    inter = (
        (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(min=0)
        * (b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp(min=0)
    )

    union = area1 + area2 - inter + eps

    iou = inter / union
    iou = torch.nan_to_num(iou, nan=0.0, posinf=1.0, neginf=0.0)
    iou = iou.clamp(min=0.0, max=1.0)

    # Focal weight
    focal_weight = iou.clamp(min=0, max=1).pow(gamma)

    # ------------------------------------------------------------
    # 4. Extended IoU losses
    # ------------------------------------------------------------
    if CIoU or DIoU or GIoU or EIoU or SIoU or WIoU:
        cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1)
        ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1)

        if CIoU or DIoU or EIoU or SIoU or WIoU:
            c2 = cw.pow(2) + ch.pow(2) + eps

            rho2 = (
                (b2_x1 + b2_x2 - b1_x1 - b1_x2).pow(2)
                + (b2_y1 + b2_y2 - b1_y1 - b1_y2).pow(2)
            ) / 4

            # CIoU
            if CIoU:
                v = (4 / math.pi**2) * (
                    (w2 / (h2 + eps)).atan()
                    - (w1 / (h1 + eps)).atan()
                ).pow(2)

                with torch.no_grad():
                    ciou_alpha = v / (v - iou + (1 + eps))

                out = iou - (rho2 / c2 + v * ciou_alpha)
                return (out, focal_weight) if Focal else out

            # EIoU
            if EIoU:
                rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)).pow(2)
                rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)).pow(2)

                cw2 = (cw.pow(2) + eps).pow(alpha)
                ch2 = (ch.pow(2) + eps).pow(alpha)

                out = iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
                return (out, focal_weight) if Focal else out

            # SIoU
            if SIoU:
                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 = (s_cw.pow(2) + s_ch.pow(2)).sqrt().clamp(min=eps)

                sin_alpha_1 = s_cw.abs() / sigma
                sin_alpha_2 = s_ch.abs() / sigma

                threshold = math.sqrt(2) / 2
                sin_alpha = torch.where(
                    sin_alpha_1 > threshold,
                    sin_alpha_2,
                    sin_alpha_1,
                )

                sin_alpha = sin_alpha.clamp(min=-1 + eps, max=1 - eps)
                angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)

                rho_x = (s_cw / (cw + eps)).pow(2)
                rho_y = (s_ch / (ch + eps)).pow(2)

                distance_gamma = angle_cost - 2
                distance_cost = (
                    2
                    - torch.exp(distance_gamma * rho_x)
                    - torch.exp(distance_gamma * rho_y)
                )

                omega_w = (w1 - w2).abs() / torch.max(w1, w2).clamp(min=eps)
                omega_h = (h1 - h2).abs() / torch.max(h1, h2).clamp(min=eps)

                shape_cost = (
                    (1 - torch.exp(-omega_w)).pow(4)
                    + (1 - torch.exp(-omega_h)).pow(4)
                )

                out = iou - (0.5 * (distance_cost + shape_cost) + eps).pow(alpha)
                return (out, focal_weight) if Focal else out

            # WIoU
            if WIoU:
                if Focal:
                    raise RuntimeError("WIoU does not support Focal=True.")

                distance_weight = torch.exp(rho2 / c2)

                if scale:
                    wiou_scale = WIoU_Scale(1 - iou)
                    return (
                        wiou_scale._scaled_loss(),
                        (1 - iou) * distance_weight,
                        iou,
                    )

                return iou, distance_weight

            # DIoU
            out = iou - rho2 / c2
            return (out, focal_weight) if Focal else out

        # GIoU
        c_area = cw * ch + eps
        out = iou - (((c_area - union) / c_area) + eps).pow(alpha)
        return (out, focal_weight) if Focal else out

    # Normal IoU
    return (iou, focal_weight) if Focal else iou


四、添加InnerIoU等损失函数到模型中

 4.1 修改一

第一步我们需要找到如下的文件ultralytics/utils/metrics.py,找到如下的代码,下面的图片是原先的代码部分截图的正常样子,然后我们将章节3的代码块替换下面的bbox_iou整个方法内的代码全部替换(这里这是部分截图)。


4.2 修改二

第二步我们找到另一个文件如下->"ultralytics/utils/loss.py",我们按图进行修改.

​ 

        iou = bbox_iou( pred_bboxes[fg_mask], target_bboxes[fg_mask], xywh=False, GIoU=False, DIoU=False, CIoU=True, 
                        EIoU=False, SIoU=False, WIoU=False, Focal=False, Inner=False, ratio=0.7,
                        alpha=1.0, gamma=0.5, scale=False, eps=1e-7)
        # 默认使用 CIoU;GIoU / DIoU / CIoU / EIoU / SIoU / WIoU 建议一次只开一个。
        # Inner=False 表示不启用 Inner-IoU;若使用 Inner-CIoU,只需设置 Inner=True,ratio 控制内框比例。
        # 切换时把 CIoU=False,再把目标损失设为 True,例如 SIoU=True。
        # Focal 和 WIoU 会改变返回值结构,若开启需要同步修改后续 loss 计算。

4.3 步骤三(未必要修改) 

我们找到另一个文件如下->"ultralytics/utils/loss.py"(步骤二的下一行),此处是使用Focus和WIoU时候需要修改的代码,你不使用跳过此步骤直接进行步骤三即可,

  if type(iou) is tuple:
            if len(iou) == 2:
                # increased the weight of low/high IoU
                loss_iou = ((1 - iou[1].detach().squeeze()) * (1 - iou[0].squeeze()) * weight).sum() / target_scores_sum  # Focal
                # lbox += (iou[1].detach().squeeze() * (1 - iou[0].squeeze())* weight).sum() / target_scores_sum  # Focal-inv
                # 这里有两种方法,大家可以自行尝试,这里的Focal-inv也是文章中提出的.
            else:
                loss_iou = (iou[0] * iou[1] * weight).sum() / target_scores_sum
        else:
            loss_iou = ((1.0 - iou) * weight).sum() / target_scores_sum # iou loss

4.4 步骤四

我们还需要修改一处,找到如下的文件''ultralytics/utils/tal.py''然后找到其中下面图片的代码,用我给的代码替换红框内的代码。


    return bbox_iou(gt_bboxes,  pd_bboxes, xywh=False, GIoU=False, DIoU=False, CIoU=True,
                                 EIoU=False, SIoU=False, WIoU=False, Focal=False, Inner=True,
                                 ratio=0.7, alpha=1.0, gamma=0.5, eps=1e-7, scale=False).squeeze(-1).clamp_(0)

此处和loss.py里面的最好是使用同一个参数(但非必须),但Focus和WIoU此处不能使用需要注意.


五、总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv26改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐