【YOLO改进】换遍IoU损失函数之Focal IoU Loss(基于MMYOLO)

Focal IoU损失函数

在目标检测任务中,评估预测边界框的质量是一个重要环节。传统的 IoU(Intersection over Union)损失函数虽然能够评估预测边界框与真实边界框的重叠程度,但在某些情况下存在一些问题。例如,当预测边界框与真实边界框的重叠度较低时,IoU 损失函数的梯度会非常小,导致模型难以进行优化。此外,正负样本分布不均衡也是目标检测任务中的一个常见问题,这会影响模型的训练效果。

为了解决这些问题,研究者提出了 Focal IoU Loss。该损失函数旨在平衡高质量样本和低质量样本对损失的贡献,同时强调困难样本在训练过程中的重要性。

设计原理

Focal IoU Loss 的设计原理主要基于以下两个方面:

  1. 平衡高质量样本和低质量样本对损失的贡献:通过引入一个调节因子,使得高质量样本(即 IoU 值较大的样本)在损失计算中占据更大的权重,而低质量样本(即 IoU 值较小的样本)的权重则相对较小。这样可以在一定程度上避免低质量样本对模型训练的负面影响。
  2. 强调困难样本在训练过程中的重要性:类似于 Focal Loss,Focal IoU Loss 也为具有挑战性的样本(即 IoU 值较低的样本)分配更高的权重。这样可以使模型在训练过程中更加关注这些困难样本,从而提高模型的泛化能力。

计算步骤

Focal IoU Loss 的计算步骤大致如下:

  1. 计算预测边界框与真实边界框的 IoU 值。
  2. 根据 IoU 值的大小,将样本分为高质量样本和低质量样本。具体划分标准可以根据实际情况进行调整。
  3. 引入一个调节因子 α,用于平衡高质量样本和低质量样本对损失的贡献。α 的取值可以根据实际情况进行调整,通常取值在 0 到 1 之间。
  4. 对于高质量样本,直接使用原始的 IoU 损失函数进行计算;对于低质量样本,则根据 IoU 值的大小为其分配一个较小的权重。
  5. 将所有样本的损失进行加权求和,得到最终的 Focal IoU Loss。

添加 Focal IoU损失函数(基于MMYOLO)

由于MMYOLO中没有实现Focal IoU损失函数,所以需要在mmyolo/models/iou_loss.py中添加Focal IoU的计算和对应的iou_mode,修改完以后在终端运行

python setup.py install

再在配置文件中进行修改即可。

包含前面所有IoU损失函数替换的mmyolo/models/iou_loss.py如下(Focal IoU只实现了Focal CIoU,其他同理):

# Copyright (c) OpenMMLab. All rights reserved.
import math
from typing import Optional, Tuple, Union

import torch
import torch.nn as nn
from mmdet.models.losses.utils import weight_reduce_loss
from mmdet.structures.bbox import HorizontalBoxes

from mmyolo.registry import MODELS
class WIoU_Scale:
    ''' monotonous: {
            None: origin v1
            True: monotonic FM v2
            False: non-monotonic FM v3
        }
        momentum: The momentum of running mean'''

    iou_mean = 1.
    monotonous = False
    _momentum = 1 - 0.5 ** (1 / 7000)
    _is_train = True

    def __init__(self, iou):
        self.iou = iou
        self._update(self)

    @classmethod
    def _update(cls, self):
        if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \
                                         cls._momentum * self.iou.detach().mean().item()

    @classmethod
    def _scaled_loss(cls, self, gamma=1.9, delta=3):
        if isinstance(self.monotonous, bool):
            if self.monotonous:
                return (self.iou.detach() / self.iou_mean).sqrt()
            else:
                beta = self.iou.detach() / self.iou_mean
                alpha = delta * torch.pow(gamma, beta - delta)
                return beta / alpha
        return 1

def bbox_overlaps(pred: torch.Tensor,
                  target: torch.Tensor,
                  iou_mode: str = 'ciou',
                  bbox_format: str = 'xywh',
                  siou_theta: float = 4.0,
                  gamma: float = 0.5,
                  eps: float = 1e-7,
                  focal: bool = False,) -> torch.Tensor:
    r"""Calculate overlap between two set of bboxes.
    `Implementation of paper `Enhancing Geometric Factors into
    Model Learning and Inference for Object Detection and Instance
    Segmentation <https://arxiv.org/abs/2005.03572>`_.

    In the CIoU implementation of YOLOv5 and MMDetection, there is a slight
    difference in the way the alpha parameter is computed.

    mmdet version:
        alpha = (ious > 0.5).float() * v / (1 - ious + v)
    YOLOv5 version:
        alpha = v / (v - ious + (1 + eps)

    Args:
        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2)
            or (x, y, w, h),shape (n, 4).
        target (Tensor): Corresponding gt bboxes, shape (n, 4).
        iou_mode (str): Options are ('iou', 'ciou', 'giou', 'siou').
            Defaults to "ciou".
        bbox_format (str): Options are "xywh" and "xyxy".
            Defaults to "xywh".
        siou_theta (float): siou_theta for SIoU when calculate shape cost.
            Defaults to 4.0.
        eps (float): Eps to avoid log(0).

    Returns:
        Tensor: shape (n, ).
    """
    assert iou_mode in ('iou', 'ciou', 'giou', 'siou','diou','eiou','innersiou','innerciou','shapeiou','wiou')
    assert bbox_format in ('xyxy', 'xywh')
    if bbox_format == 'xywh':
        pred = HorizontalBoxes.cxcywh_to_xyxy(pred)
        target = HorizontalBoxes.cxcywh_to_xyxy(target)

    bbox1_x1, bbox1_y1 = pred[..., 0], pred[..., 1]
    bbox1_x2, bbox1_y2 = pred[..., 2], pred[..., 3]
    bbox2_x1, bbox2_y1 = target[..., 0], target[..., 1]
    bbox2_x2, bbox2_y2 = target[..., 2], target[..., 3]

    # Overlap
    overlap = (torch.min(bbox1_x2, bbox2_x2) -
               torch.max(bbox1_x1, bbox2_x1)).clamp(0) * \
              (torch.min(bbox1_y2, bbox2_y2) -
               torch.max(bbox1_y1, bbox2_y1)).clamp(0)

    # Union
    w1, h1 = bbox1_x2 - bbox1_x1, bbox1_y2 - bbox1_y1
    w2, h2 = bbox2_x2 - bbox2_x1, bbox2_y2 - bbox2_y1
    union = (w1 * h1) + (w2 * h2) - overlap + eps

    h1 = bbox1_y2 - bbox1_y1 + eps
    h2 = bbox2_y2 - bbox2_y1 + eps

    # IoU
    ious = overlap / union

    # enclose area
    enclose_x1y1 = torch.min(pred[..., :2], target[..., :2])
    enclose_x2y2 = torch.max(pred[..., 2:], target[..., 2:])
    enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)

    enclose_w = enclose_wh[..., 0]  # cw
    enclose_h = enclose_wh[..., 1]  # ch

    if iou_mode == 'ciou':
        # CIoU = IoU - ( (ρ^2(b_pred,b_gt) / c^2) + (alpha x v) )

        # calculate enclose area (c^2)
        enclose_area = enclose_w**2 + enclose_h**2 + eps

        # calculate ρ^2(b_pred,b_gt):
        # euclidean distance between b_pred(bbox2) and b_gt(bbox1)
        # center point, because bbox format is xyxy -> left-top xy and
        # right-bottom xy, so need to / 4 to get center point.
        rho2_left_item = ((bbox2_x1 + bbox2_x2) - (bbox1_x1 + bbox1_x2))**2 / 4
        rho2_right_item = ((bbox2_y1 + bbox2_y2) -
                           (bbox1_y1 + bbox1_y2))**2 / 4
        rho2 = rho2_left_item + rho2_right_item  # rho^2 (ρ^2)

        # Width and height ratio (v)
        wh_ratio = (4
<think>好的,我现在需要帮助用户解决如何在YOLOv8中替默认损失函数Focal Loss的问题。首先,我得回想一下用户提供的引用内容,看看里面有没有相关的信息。用户引用了五篇关于YOLOv8改进损失函数的文章,特别是提到了Focal Loss结合不同的IoU变体,比如Focal-EIoUFocal-SIoU等。引用1提到YOLOv8默认使用交叉熵或二值交叉熵损失,但在复杂场景下可能不够好,所以需要替Focal Loss。引用2、3、4、5都提到了具体的实现方法和代码实践,特别是引用3和5提到了YOLOv8的代码实践和核心代码部分。 接下来,我需要确定用户在YOLOv8中替损失函数的具体步骤。通常,YOLO模型的损失函数在模型定义文件中定义,可能在ultralytics/models/yolo/v8目录下的某个文件里,比如detect.py或者loss.py。用户需要修改这部分代码,替分类损失为Focal Loss。 根据引用中的信息,Focal Loss主要用于分类损失部分,而边界框回归损失可能结合了不同的IoU变体。用户可能需要在代码中找到分类损失的定义位置,将原来的交叉熵替Focal Loss的实现。同时,可能需要调整损失函数的参数,比如alpha和gamma,这些参数在引用中提到的文章里可能有推荐值,比如alpha=0.8,gamma=2.0。 然后,我需要考虑如何编写示例代码。用户可能需要一个自定义的Focal Loss类,继承自nn.Module,并在前向传播中实现计算。此外,修改YOLOv8的模型配置文件,指定使用新的损失函数。训练时通过参数调用修改后的配置。 还要注意,YOLOv8的结构可能与其他版本如YOLOv5类似,所以引用4中提到的YOLOv5的代码实践可能有参考价值,但需要适配到YOLOv8的结构中。此外,用户可能需要调整超参数,并在训练时传递自定义的损失函数类。 现在需要检查是否有遗漏的步骤,比如是否需要在模型初始化时加载自定义的损失函数,或者是否有其他依赖需要修改。可能还需要在训练脚本中传递参数,确保模型使用新的损失函数。 最后,确保提供的代码示例正确,并提醒用户注意路径和版本兼容性问题。同时,根据用户提供的引用,正确标注引用来源,比如在提到Focal Loss应用时引用相关的文献。</think>### YOLOv8中Focal Loss实现详解 在YOLOv8中替默认的交叉熵分类损失函数Focal Loss,主要通过修改模型配置文件和损失函数实现类完成。以下是具体实现步骤: #### 一、实现原理 Focal Loss通过引入权重因子$\alpha$和调节因子$\gamma$,有效解决类别不平衡问题: $$FL(p_t) = -\alpha (1-p_t)^\gamma \log(p_t)$$ 相比交叉熵损失$CE(p_t) = -\log(p_t)$,其能降低易分类样本的损失权重[^1] #### 二、代码实现步骤 1. **自定义Focal Loss类**(新建focal_loss.py) ```python import torch import torch.nn as nn import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alpha=0.8, gamma=2.0, reduction='mean'): super(FocalLoss, self).__init__() self.alpha = alpha self.gamma = gamma self.reduction = reduction def forward(self, inputs, targets): BCE_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction='none') pt = torch.exp(-BCE_loss) FL_loss = self.alpha * (1-pt)**self.gamma * BCE_loss if self.reduction == 'mean': return torch.mean(FL_loss) elif self.reduction == 'sum': return torch.sum(FL_loss) else: return FL_loss ``` 2. **修改模型配置文件**(yolov8.yaml) ```yaml # Ultralytics YOLO config ... loss: name: CustomLoss # 自定义损失类名 cls: FocalLoss # 分类损失替FocalLoss box: CIoU # 可保持原回归损失或更Focal-CIoU ... ``` 3. **注册自定义损失函数**(在训练脚本中) ```python from ultralytics import YOLO from focal_loss import FocalLoss # 加载自定义模型配置 model = YOLO('yolov8n.yaml') # 替损失函数 model.loss = {'cls': FocalLoss(alpha=0.8, gamma=2.0), 'box': model.loss.box} # 保持原回归损失 ``` 4. **启动训练** ```bash yolo train data=coco.yaml model=yolov8n.yaml epochs=100 imgsz=640 ``` #### 三、调参建议 1. $\alpha$参数控制类别权重平衡,推荐0.25-0.85 2. $\gamma$参数调节难易样本权重,常用2.0 3. 可与Focal-CIoU等变体组合使用[^3] 4. 学习率建议降低10%-30%(初始推荐1e-4) #### 四、验证改进效果 ```python # 验证集测试 metrics = model.val() print(f"mAP@0.5: {metrics.box.map}") # 对比改进前后指标 ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值