共计 2663 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
目标检测任务中,IoU(Intersection over Union)是最常用的评估指标之一,但直接作为损失函数使用时存在几个明显的缺陷:

- 尺度不敏感 :当两个框没有重叠时,IoU 值为 0,无法提供有效的梯度信息,导致训练难以收敛。
- 方向缺失 :IoU 只考虑重叠面积,忽略了两框之间的相对位置和方向信息。
- 收敛速度慢 :在重叠区域较小时,IoU 损失对边界框的回归效果不佳,收敛速度慢。
技术对比
| 损失函数 | 中心点距离 | 长宽比 | 收敛速度 |
|---|---|---|---|
| IoU | ❌ | ❌ | 慢 |
| GIoU | ❌ | ❌ | 中等 |
| DIoU | ✔️ | ❌ | 快 |
| CIoU | ✔️ | ✔️ | 最快 |
CIoU 在 DIoU 的基础上增加了长宽比惩罚项,公式如下:
$$\text{CIoU} = \text{DIoU} – \alpha v$$
其中,$v$ 是长宽比一致性度量,$\alpha$ 是权衡系数。
核心实现
- 公式推导 :
- 计算中心点距离:$d = \rho^2(b, b^{gt})$
- 计算最小包围框的对角线距离:$c$
- 计算长宽比一致性度量:$v = \frac{4}{\pi^2}(\arctan\frac{w^{gt}}{h^{gt}} – \arctan\frac{w}{h})^2$
-
计算权衡系数:$\alpha = \frac{v}{(1 – \text{IoU}) + v}$
-
可视化 :
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()
避坑指南
- 数值不稳定 :在小目标检测时,长宽比的计算容易出现数值不稳定,可以通过添加平滑项(eps)来解决。
- 梯度爆炸 :在极端情况下,梯度可能会爆炸,建议使用梯度裁剪。
- 计算效率 :批量计算时,注意内存消耗,可以适当减小批量大小。
延伸思考
- CIoU 与 Focal Loss 的组合优化 :是否可以结合 Focal Loss 的思想,对难样本给予更高的权重?
- 旋转目标检测 :在旋转目标检测任务中,CIoU 是否可以通过引入角度信息来进一步优化?
正文完
