CIoU损失函数图解析:从原理到实战的深度学习损失函数优化指南

1次阅读
没有评论

共计 2663 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

背景痛点

目标检测任务中,IoU(Intersection over Union)是最常用的评估指标之一,但直接作为损失函数使用时存在几个明显的缺陷:

CIoU 损失函数图解析:从原理到实战的深度学习损失函数优化指南

  1. 尺度不敏感 :当两个框没有重叠时,IoU 值为 0,无法提供有效的梯度信息,导致训练难以收敛。
  2. 方向缺失 :IoU 只考虑重叠面积,忽略了两框之间的相对位置和方向信息。
  3. 收敛速度慢 :在重叠区域较小时,IoU 损失对边界框的回归效果不佳,收敛速度慢。

技术对比

损失函数 中心点距离 长宽比 收敛速度
IoU
GIoU 中等
DIoU ✔️
CIoU ✔️ ✔️ 最快

CIoU 在 DIoU 的基础上增加了长宽比惩罚项,公式如下:

$$\text{CIoU} = \text{DIoU} – \alpha v$$

其中,$v$ 是长宽比一致性度量,$\alpha$ 是权衡系数。

核心实现

  1. 公式推导
  2. 计算中心点距离:$d = \rho^2(b, b^{gt})$
  3. 计算最小包围框的对角线距离:$c$
  4. 计算长宽比一致性度量:$v = \frac{4}{\pi^2}(\arctan\frac{w^{gt}}{h^{gt}} – \arctan\frac{w}{h})^2$
  5. 计算权衡系数:$\alpha = \frac{v}{(1 – \text{IoU}) + v}$

  6. 可视化

    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    # 生成数据
    X, Y = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
    Z = 1 - X + Y  # 简化示例
    
    # 绘制曲面
    ax.plot_surface(X, Y, Z, cmap='viridis')
    ax.set_xlabel('IoU')
    ax.set_ylabel('中心点距离')
    ax.set_zlabel('CIoU 损失')
    plt.show()

代码示例

import torch
import torch.nn as nn

class CIoULoss(nn.Module):
    def __init__(self, eps=1e-7):
        super(CIoULoss, self).__init__()
        self.eps = eps

    def forward(self, pred, target):
        """
        pred: [N, 4] (x1, y1, x2, y2)
        target: [N, 4] (x1, y1, x2, y2)
        """
        # 计算交集面积
        inter_x1 = torch.max(pred[:, 0], target[:, 0])
        inter_y1 = torch.max(pred[:, 1], target[:, 1])
        inter_x2 = torch.min(pred[:, 2], target[:, 2])
        inter_y2 = torch.min(pred[:, 3], target[:, 3])

        inter_area = torch.clamp(inter_x2 - inter_x1, min=0) * torch.clamp(inter_y2 - inter_y1, min=0)

        # 计算并集面积
        pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])
        target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])
        union_area = pred_area + target_area - inter_area + self.eps

        # 计算 IoU
        iou = inter_area / union_area

        # 计算中心点距离
        pred_center = torch.stack([(pred[:, 0] + pred[:, 2]) / 2, (pred[:, 1] + pred[:, 3]) / 2], dim=1)
        target_center = torch.stack([(target[:, 0] + target[:, 2]) / 2, (target[:, 1] + target[:, 3]) / 2], dim=1)
        center_distance = torch.sum((pred_center - target_center) ** 2, dim=1)

        # 计算最小包围框的对角线距离
        enclose_x1 = torch.min(pred[:, 0], target[:, 0])
        enclose_y1 = torch.min(pred[:, 1], target[:, 1])
        enclose_x2 = torch.max(pred[:, 2], target[:, 2])
        enclose_y2 = torch.max(pred[:, 3], target[:, 3])
        enclose_distance = torch.sum((torch.stack([enclose_x2 - enclose_x1, enclose_y2 - enclose_y1], dim=1)) ** 2, dim=1)

        # 计算 DIoU
        diou = iou - (center_distance / (enclose_distance + self.eps))

        # 计算长宽比一致性度量
        pred_wh = torch.stack([pred[:, 2] - pred[:, 0], pred[:, 3] - pred[:, 1]], dim=1)
        target_wh = torch.stack([target[:, 2] - target[:, 0], target[:, 3] - target[:, 1]], dim=1)
        v = (4 / (torch.pi ** 2)) * torch.pow(torch.atan(pred_wh[:, 0] / (pred_wh[:, 1] + self.eps)) - 
                                              torch.atan(target_wh[:, 0] / (target_wh[:, 1] + self.eps)), 2)

        # 计算权衡系数
        alpha = v / ((1 - iou) + v + self.eps)

        # 计算 CIoU
        ciou = diou - alpha * v

        return 1 - ciou.mean()

避坑指南

  1. 数值不稳定 :在小目标检测时,长宽比的计算容易出现数值不稳定,可以通过添加平滑项(eps)来解决。
  2. 梯度爆炸 :在极端情况下,梯度可能会爆炸,建议使用梯度裁剪。
  3. 计算效率 :批量计算时,注意内存消耗,可以适当减小批量大小。

延伸思考

  1. CIoU 与 Focal Loss 的组合优化 :是否可以结合 Focal Loss 的思想,对难样本给予更高的权重?
  2. 旋转目标检测 :在旋转目标检测任务中,CIoU 是否可以通过引入角度信息来进一步优化?
正文完
 0
评论(没有评论)