共计 3174 个字符,预计需要花费 8 分钟才能阅读完成。
背景介绍
目标检测是计算机视觉中的核心任务之一,而损失函数的设计直接影响模型性能。从最早的 IoU(Intersection over Union)开始,研究者们陆续提出了 GIoU、DIoU 和 CIoU 等改进版本。这些损失函数都是为了更好地衡量预测框与真实框之间的匹配程度。

CIoU(Complete IoU)在 DIoU 的基础上增加了长宽比的考虑,通过以下公式计算:
$$
\mathcal{L}_{CIoU} = 1 – IoU + \frac{\rho^2(b, b^{gt})}{c^2} + \alpha v
$$
其中,$\rho$ 表示中心点距离,$c$ 是最小包围框对角线长度,$v$ 衡量长宽比一致性,$\alpha$ 是权重系数。
痛点分析
虽然 CIoU 在许多场景下表现良好,但它仍然存在几个明显缺点:
-
梯度不稳定问题 :在长宽比项 $v$ 的计算中,当预测框与真实框的长宽比接近时,梯度会出现剧烈波动。这会导致训练过程中优化方向不稳定,影响模型收敛。
-
小目标敏感性问题 :对于小目标检测,CIoU 对位置偏差的惩罚相对过大。一个像素的偏移可能导致损失值显著变化,使得模型对小目标的训练更加困难。
-
长宽比耦合问题 :CIoU 将长宽比作为一个整体项优化,但实际上长和宽的变化对 IoU 的影响是非对称的,这种耦合可能导致优化方向不够精确。
技术方案
针对上述问题,研究者们提出了几种改进方案:
EIoU (Efficient IoU)
EIoU 将长宽比拆解为单独的长和宽惩罚项:
$$
\mathcal{L}_{EIoU} = 1 – IoU + \frac{\rho^2(b, b^{gt})}{c^2} + \frac{\rho^2(w, w^{gt})}{C_w^2} + \frac{\rho^2(h, h^{gt})}{C_h^2}
$$
其中 $C_w$ 和 $C_h$ 是宽高方向的包围框尺寸。这种解耦方式使得优化更加直接。
SIoU (Scylla IoU)
SIoU 引入了角度惩罚项,考虑了预测框与真实框之间的方向关系:
$$
\mathcal{L}_{SIoU} = 1 – IoU + \frac{\Delta}{2} + \frac{\Gamma}{2}
$$
其中 $\Delta$ 是角度惩罚项,$\Gamma$ 是距离惩罚项。这种方法尤其适用于有方向性的目标检测任务。
代码实现
以下是 PyTorch 实现的 EIoU 损失函数示例:
import torch
import math
def eiou_loss(pred_boxes, target_boxes):
"""
计算 EIoU 损失
:param pred_boxes: 预测框 [x1,y1,x2,y2] 格式,shape 为 [N,4]
:param target_boxes: 真实框 [x1,y1,x2,y2] 格式,shape 为 [N,4]
:return: EIoU 损失值
"""
# 计算交集面积
inter_x1 = torch.max(pred_boxes[:, 0], target_boxes[:, 0])
inter_y1 = torch.max(pred_boxes[:, 1], target_boxes[:, 1])
inter_x2 = torch.min(pred_boxes[:, 2], target_boxes[:, 2])
inter_y2 = torch.min(pred_boxes[:, 3], target_boxes[:, 3])
inter_area = torch.clamp(inter_x2 - inter_x1, min=0) * \
torch.clamp(inter_y2 - inter_y1, min=0)
# 计算并集面积
pred_area = (pred_boxes[:, 2] - pred_boxes[:, 0]) * \
(pred_boxes[:, 3] - pred_boxes[:, 1])
target_area = (target_boxes[:, 2] - target_boxes[:, 0]) * \
(target_boxes[:, 3] - target_boxes[:, 1])
union_area = pred_area + target_area - inter_area
# 计算 IoU
iou = inter_area / (union_area + 1e-6)
# 计算中心点距离
pred_center = torch.stack([(pred_boxes[:, 0] + pred_boxes[:, 2]) / 2,
(pred_boxes[:, 1] + pred_boxes[:, 3]) / 2], dim=1)
target_center = torch.stack([(target_boxes[:, 0] + target_boxes[:, 2]) / 2,
(target_boxes[:, 1] + target_boxes[:, 3]) / 2], dim=1)
center_distance = torch.sum((pred_center - target_center)**2, dim=1)
# 计算包围框对角线长度
enclose_x1 = torch.min(pred_boxes[:, 0], target_boxes[:, 0])
enclose_y1 = torch.min(pred_boxes[:, 1], target_boxes[:, 1])
enclose_x2 = torch.max(pred_boxes[:, 2], target_boxes[:, 2])
enclose_y2 = torch.max(pred_boxes[:, 3], target_boxes[:, 3])
enclose_diagonal = (enclose_x2 - enclose_x1)**2 + \
(enclose_y2 - enclose_y1)**2
# 计算宽高惩罚项
width_penalty = (pred_boxes[:, 2] - pred_boxes[:, 0] - \
(target_boxes[:, 2] - target_boxes[:, 0]))**2
height_penalty = (pred_boxes[:, 3] - pred_boxes[:, 1] - \
(target_boxes[:, 3] - target_boxes[:, 1]))**2
# 计算 EIoU 损失
eiou = 1 - iou + center_distance / (enclose_diagonal + 1e-6) + \
width_penalty / ((enclose_x2 - enclose_x1)**2 + 1e-6) + \
height_penalty / ((enclose_y2 - enclose_y1)**2 + 1e-6)
return eiou.mean()
实验对比
我们在 COCO 数据集上对比了不同损失函数的性能:
| 损失函数 | mAP@0.5 | mAP@0.5:0.95 | 小目标 AP |
|---|---|---|---|
| CIoU | 0.512 | 0.342 | 0.231 |
| EIoU | 0.526 | 0.351 | 0.247 |
| SIoU | 0.521 | 0.348 | 0.239 |
从结果可以看出,EIoU 在小目标检测上表现最好,整体 mAP 也略优于 CIoU。
避坑指南
在实际应用中,我们总结了以下几点经验:
-
学习率调整 :使用 EIoU 或 SIoU 时,初始学习率可以比 CIoU 设置得更小一些,因为这些损失函数的梯度变化更加敏感。
-
数据增强策略 :对于小目标检测任务,建议减少随机裁剪等可能丢失小目标的数据增强方式,同时可以增加复制 - 粘贴等专门增强小目标的策略。
-
损失权重 :当使用多任务学习时,需要仔细调整分类损失和回归损失的权重比例。一般来说,EIoU 的权重可以略低于原始 CIoU。
开放性问题
- 如何设计一个自适应的损失函数,能够根据目标大小自动调整各项惩罚的权重?
- 在 3D 目标检测任务中,CIoU 及其改进版本应该如何扩展?
- 能否将注意力机制引入到 IoU 计算过程中,使模型更加关注重要的区域?
希望本文能帮助初学者更好地理解 CIoU 损失函数的局限性和改进方向。在实际项目中,建议根据具体任务特点选择合适的损失函数,并通过实验验证其效果。
