一、本文介绍
这篇文章介绍了YOLOv26的重大改进,特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体,如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU,还融合了“Focus”思想,创造了一系列新的损失函数。这些组合形式的损失函数超过了二十余种,每种都针对特定的目标检测挑战进行优化。文章会详细探讨这些损失函数如何提高YOLOv26在各种检测任务中的性能,包括提升精度、加快收敛速度和增强模型对复杂场景的适应性。

专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制
目录
一、本文介绍
二、各种损失函数的基本原理
2.1 交集面积和并集面积
2.2 IoU
2.3 SIoU
2.4 WioU
2.5 GIoU
2.6 DIoU
2.7 EIoU
2.8 CIoU
2.9 FocusLoss
三、核心代码
四、添加EIoU、SIoU、EIoU、FocusIoU等损失函数到模型中
4.1 修改一
4.2 修改二
4.3 步骤三(未必要修改)
4.4 步骤四
五、总结
二、各种损失函数的基本原理
2.1 交集面积和并集面积
在理解各种损失函数之前我们需要先来理解一下交集面积和并集面积,在数学中我们都学习过集合的概念,这里的交集和并集的概念和数学集合中的含义是一样的。

2.2 IoU
论文地址:IoU Loss for 2D/3D Object Detectio
适用场景:普通的IoU并没有特定的适用场景
概念: 测量预测边界框和真实边界框之间的重叠度(最基本的边界框损失函数,后面的都是居于其进行计算)。
2.3 SIoU
论文地址:SIoU: More Powerful Learning for Bounding Box Regression
适用场景:适用于需要高精度边界框对齐的场景,如精细的物体检测和小目标检测。
概念: SIoU损失通过融入角度考虑和规模敏感性,引入了一种更为复杂的边界框回归方法,解决了以往损失函数的局限性,SIoU损失函数包含四个组成部分:角度损失、距离损失、形状损失和第四个未指定的组成部分。通过整合这些方面,从而实现更好的训练速度和预测准确性。
2.4 WioU
论文地址:WIoU: Bounding Box Regression Loss with Dynamic Focusing Mechanism
适用场景:适用于需要动态调整损失焦点的情况,如不均匀分布的目标或不同尺度的目标检测。
概念:引入动态聚焦机制的IoU变体,旨在改善边界框回归损失。
2.5 GIoU
论文地址:GIoU: A Metric and A Loss for Bounding Box Regression
适用场景:适合处理有重叠和非重叠区域的复杂场景,如拥挤场景的目标检测。
概念: 在IoU的基础上考虑非重叠区域,以更全面评估边界框
2.6 DIoU
论文地址:DIoU: Faster and Better Learning for Bounding Box Regression
适用场景:适用于需要快速收敛和精确定位的任务,特别是在边界框定位精度至关重要的场景。
概念:结合边界框中心点之间的距离和重叠区域。
2.7 EIoU
论文地址:EIoU:Loss for Accurate Bounding Box Regression
适用场景:可用于需要进一步优化边界框对齐和形状相似性的高级场景。
概念:EIoU损失函数的核心思想在于提高边界框回归的准确性和效率。它通过以下几个方面来优化目标检测:
1. 增加中心点距离损失:通过最小化预测框和真实框中心点之间的距离,提高边界框的定位准确性。
2. 考虑尺寸差异:通过惩罚宽度和高度的差异,EIoU确保预测框在形状上更接近真实框。
3. 结合最小封闭框尺寸:将损失函数与包含预测框和真实框的最小封闭框的尺寸相结合,从而使得损失更加敏感于对象的尺寸和位置。
EIoU损失函数在传统IoU基础上增加了这些考量,以期在各种尺度上都能获得更精确的目标定位,尤其是在物体大小和形状变化较大的场景中。
2.8 CIoU
论文地址: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让模型“关注”(或“专注”)于学习那些对提高整体性能更为关键的样本。
三、核心代码
代码使用方式看章节四!
import math
from typing import Tuple, Union
import torch
class WIoU_Scale:
"""Wise-IoU dynamic focusing mechanism.
monotonous:
None -> origin v1
True -> monotonic FM v2
False -> non-monotonic FM v3
momentum:
running mean momentum
"""
iou_mean = 1.0
monotonous = False
_momentum = 1 – 0.5 ** (1 / 7000)
_is_train = True
def __init__(self, iou: torch.Tensor):
self.iou = iou
self._update()
def _update(self) -> None:
cls = self.__class__
if cls._is_train:
cls.iou_mean = (
(1 – cls._momentum) * cls.iou_mean
+ cls._momentum * self.iou.detach().mean().item()
)
def _scaled_loss(
self,
gamma: float = 1.9,
delta: float = 3.0,
) -> Union[torch.Tensor, float]:
cls = self.__class__
if isinstance(cls.monotonous, bool):
beta = self.iou.detach() / cls.iou_mean
if cls.monotonous:
return beta.sqrt()
alpha = delta * beta.new_tensor(gamma).pow(beta – delta)
return beta / alpha
return self.iou.new_tensor(1.0)
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,
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.
alpha (float): Power factor used by some extended losses.
gamma (float): Focal exponent.
scale (bool): Enable WIoU v2 / v3 dynamic scaling.
Returns:
torch.Tensor:
Normal IoU variant result.
Tuple[torch.Tensor, torch.Tensor]:
Returned when Focal=True:
(iou_variant, focal_weight)
Tuple[torch.Tensor, torch.Tensor]:
Returned when WIoU=True and scale=False:
(iou, distance_weight)
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
Returned when WIoU=True and scale=True:
(scaled_loss, wiou_loss, iou)
"""
# Get coordinates of bounding boxes
if xywh:
# xywh -> xyxy
(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
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
else:
# xyxy
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
w2, h2 = b2_x2 – b2_x1, b2_y2 – b2_y1
# Intersection area
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 area
union = w1 * h1 + w2 * h2 – inter + eps
# IoU
iou = inter / union
# Focal weight
focal_weight = iou.clamp(min=0, max=1).pow(gamma)
# Extended IoU losses
if CIoU or DIoU or GIoU or EIoU or SIoU or WIoU:
# Convex box width and height
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:
# Convex diagonal squared
c2 = cw.pow(2) + ch.pow(2) + eps
# Center distance squared
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()
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,
)
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
四、添加EIoU、SIoU、EIoU、FocusIoU等损失函数到模型中
4.1 修改一
第一步我们需要找到如下的文件ultralytics/utils/metrics.py,找到如下的代码,下面的图片是原先的代码部分截图的正常样子,然后我们将整个代码块一将下面的整个方法(这里这是部分截图)内容全部替换
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, alpha=1.0, gamma=0.5, scale=False, eps=1e-7)
# 默认使用 CIoU;GIoU / DIoU / CIoU / EIoU / SIoU / WIoU 建议一次只开一个。
# 切换时把 CIoU=False,再把目标损失设为 True,例如 SIoU=True。
# Focal 和 WIoU 会改变返回值结构,若开启需要同步修改后续 修改步骤3。
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,
alpha=1.0, gamma=0.5, eps=1e-7, scale=False).squeeze(-1).clamp_(0)
此处和loss.py里面的最好是使用同一个参数(但非必须),但Focus和WIoU此处不能使用需要注意.

五、总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv26改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~
专栏链接:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制




