YOLOv5/YOLOv8/YOLOv9改进——将损失函数改进为shape_iou

💡 🎈🎄🎋🎍🎉🎑🎫🎞🎪🎁🎎🧵🛒🧶🥽🏅🥉🥇🥇🏅🏆🏆🥉

损失函数shape_iou

本文使用的YOLOv5版本为v7.0,YOLOv8、YOLOv9均为最新版本。默认的损失函数为CIOU。shape_iou论文链接:https://arxiv.org/pdf/2312.17663.pdf

🎃代码修改

1.在mertric.py中将def bbox_iou

def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, 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 + eps
        w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps

    # Intersection area
    inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
            (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)

    # Union Area
    union = w1 * h1 + w2 * h2 - inter + eps

    # IoU
    iou = inter / union
    if CIoU or DIoU or GIoU:
        cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)  # convex (smallest enclosing box) width
        ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)  # convex height
        if CIoU or DIoU:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
            c2 = cw ** 2 + ch ** 2 + eps  # convex diagonal squared
            rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4  # 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.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
                with torch.no_grad():
                    alpha = v / (v - iou + (1 + eps))
                return iou - (rho2 / c2 + v * alpha)  # CIoU
            return iou - rho2 / c2  # DIoU
        c_area = cw * ch + eps  # convex area
        return iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdf
    return iou  # IoU

修改为:

def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, feat_h=640, feat_w=640, shape_iou=False, scale=0, hw=None, 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
        w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1

    # Intersection area
    inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
            (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)

    # Union Area
    union = w1 * h1 + w2 * h2 - inter + eps

    # IoU
    iou = inter / union
    if CIoU or DIoU or GIoU:
        cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)  # convex (smallest enclosing box) width
        ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)  # convex height
        if CIoU or DIoU:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
            c2 = cw ** 2 + ch ** 2 + eps  # convex diagonal squared
            rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4  # 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.pow(torch.atan(w2 / (h2 + eps)) - torch.atan(w1 / (h1 + eps)), 2)
                with torch.no_grad():
                    alpha = v / (v - iou + (1 + eps))
                return iou - (rho2 / c2 + v * alpha)  # CIoU
            return iou - rho2 / c2  # DIoU
        c_area = cw * ch + eps  # convex area
        return iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdf
    elif shape_iou:
        ww = 2 * torch.pow(w2, scale) / (torch.pow(w2, scale) + torch.pow(h2, scale))
        hh = 2 * torch.pow(h2, scale) / (torch.pow(w2, scale) + torch.pow(h2, scale))
        cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)  # convex width
        ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)  # convex height
        c2 = cw ** 2 + ch ** 2 + eps  # convex diagonal squared
        center_distance_x = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2) / 4
        center_distance_y = ((b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4
        center_distance = hh * center_distance_x + ww * center_distance_y
        distance = center_distance / c2

        omiga_w = hh * torch.abs(w1 - w2) / torch.max(w1, w2)
        omiga_h = ww * 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)
        iou = iou - distance - 0.5 * (shape_cost)
    return iou  # IoU

2.使用shape_iou

💯在loss.py中找到class ComputeLoss:

def __call__(self, p, targets):

将:

iou = bbox_iou(pbox, tbox[i], CIoU=True).squeeze()  # iou(prediction, target)

替换为:

iou = bbox_iou(pbox, tbox[i], shape_iou=True).squeeze()  # iou(prediction, target)

### 改进 YOLOv8 损失函数以提升性能 #### 1. 使用 Varifocal Loss (VFLoss) Varifocal Loss 是一种专门为密集目标检测设计的损失函数,能够有效处理类别不平衡和正负样本分配问题。相比传统的 Focal Loss 和 Cross Entropy Loss,它通过引入质量感知权重来动态调整分类损失[^1]。 具体实现如下: ```python import torch.nn.functional as F def varifocal_loss(pred_score, gt_score, label, alpha=0.75, gamma=2.0): """ 计算 Varifocal Loss 的核心部分。 参数: pred_score: 预测的概率分数 [N, C] gt_score: 地面真值的质量分数 [N, ] label: 类别标签 [N, ] alpha: 权重因子,默认为 0.75 gamma: 衰减指数,默认为 2.0 返回: loss_varifocal: Varifocal Loss 值 """ # 获取预测概率 prob_pred = pred_score.gather(1, label.unsqueeze(-1)).squeeze() focal_weight = (prob_pred - gt_score).abs().pow(gamma) # 计算交叉熵损失 ce_loss = -(gt_score * torch.log(prob_pred.clamp(min=1e-7))) loss_varifocal = alpha * focal_weight * ce_loss return loss_varifocal.mean() ``` 此方法可以显著改善模型在复杂场景中的表现,尤其是在存在大量背景噪声的情况下。 --- #### 2. 替换 IoU 损失函数 除了改进分类损失之外,还可以尝试替换边界框回归的 IoU 损失函数YOLOv8 默认使用的可能是 CIoU 或 DIoU,但更先进的 SIoU 和 Wise-IoU 可能会带来额外增益[^4]。 以下是基于 SIoU 的实现代码片段: ```python from utils.metrics import bbox_iou def siou_loss(pred_bboxes, target_bboxes, fg_mask, weight=None): """ 实现 SIoU 损失计算。 参数: pred_bboxes: 预测边框坐标 [B, N, 4] target_bboxes: 真实边框坐标 [B, N, 4] fg_mask: 正样本掩码 [B, N] weight: 边框加权系数 [B, N] 返回: loss_siou: SIoU 损失值 """ iou = bbox_iou( pred_bboxes[fg_mask], target_bboxes[fg_mask], xywh=False, SIoU=True ) if isinstance(iou, tuple): # 如果返回的是元组形式 loss_siou = ((1.0 - iou[0]) * iou[1].detach() * weight).sum() / weight.sum() else: loss_siou = ((1.0 - iou) * weight).sum() / weight.sum() return loss_siou ``` 这种替代方案有助于更好地捕捉物体形状的变化并减少定位误差。 --- #### 3. 结合多任务学习策略 为了平衡分类、定位以及置信度估计之间的关系,可采用联合优化的方式定义总损失函数。例如,在原版基础上加入更多约束条件或将不同子任务赋予独立权重[^2]: \[ L_{total} = \lambda_1 L_{cls} + \lambda_2 L_{box} + \lambda_3 L_{obj} \] 其中 \(L_{cls}\), \(L_{box}\),\(L_{obj}\) 分别表示分类损失、边界框回归损失和对象性损失;\(\lambda\) 则用于调节各分量的重要性程度。 实际应用时可根据数据集特性微调这些超参数设置。 --- #### 4. 解决输出尺度上的锚点分布不均衡问题 如果发现某些特定尺寸的目标难以被准确定位,则可能源于预设先验框(anchors)配置不合理所致。针对此类情况可通过聚类分析重新生成一组更适合当前训练集合特征的新 anchor box 形状[^3]。 执行 K-means++ 聚类操作获取最佳候选解的过程大致如下所示: ```python from sklearn.cluster import KMeans def generate_anchors(bounding_boxes, num_clusters=9): """ 根据标注好的 bounding boxes 自动生成 anchors. 参数: bounding_boxes: 所有图像中标记的真实框宽高比例列表 [[w,h], ...] num_clusters: 聚类簇数 返回: clusters: 得到的最佳 anchors 尺寸数组 shape=(num_clusters,) """ kmeans = KMeans(n_clusters=num_clusters, random_state=0).fit(bounding_boxes) clusters = sorted(kmeans.cluster_centers_, key=lambda x: x[0]*x[1]) return np.array(clusters) ``` 上述逻辑可以帮助缓解因初始设定不当引发的小型或大型目标漏检现象。 --- ### 总结 通过对 YOLOv8损失函数模块进行定制化改造——无论是选用更加鲁棒性的 VFLoss 还是升级至新一代 IoU 度量标准,均能在不同程度上促进整体精度指标攀升。与此同时合理规划各项子任务间协作机制同样不可或缺。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长沙有肥鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值