bbox loss
目标检测任务中 bbox 的预测 采用回归损失实现,可分为 L1 L2 SmoothL1 等
三种损失求导,分析对 loss 的影响
L1:导数恒等于 -1 1,在训练后期,来回震荡,无法收敛到足够精细的值
L2:在前期,x(loss) 较大,收敛过快,容易过拟合
SmoothL1:对L1进行平滑,其实就是 融合了 L1 L2 ,在 x 较大时,导数 恒等于 -1 1,即 L1,在 x 较小时,导数变成 x,即 L2
在 bbox 评价时采用的是 IOU,由于 之前的 loss 和 评价方式不同,故后来把 loss 变成了 IOU loss
Liou = 1 - IOU
IOU
def iou(rec1, rec2): """ rec1: [left1,top1,right1,bottom1] rec2: [left2,top2,right2,bottom2] """ # 重合部分 left_max = max(rec1[0], rec2[0]) top_max = max(rec1[1], rec2[1]) right_min = min(rec1[2], rec2[2]) bottom_min = min(rec1[3], rec2[3]) # 两矩形相交时计算IoU if (left_max > right_min or bottom_min < top_max): # 没有交集 return 0 else: rect1_area = (rec1[2] - rec1[0]) * (rec1[3] - rec1[1]) rect2_area = (rec2[2] - rec2[0]) * (rec2[3] - rec2[1]) area_cross = (bottom_min - top_max) * (right_min - left_max) return area_cross / (rect1_area + rect2_area - area_cross)
存在问题
1. 无法解决两个目标无重叠的问题,如果两个目标没有重叠,iou 为 0,loss 恒为 1,微调位置 无法反应 两目标之间的位置变化,也就是说 IOU loss 无法反应 两个框 之间的距离;
2.iou 无法区分 两个目标 的对齐方式,不同的对齐方式下,iou 可能相等,如下图
GIOU
当 AB 完全重合时,IOU = 1,后半部分为0,GIOU = 1,loss = 0;
当 AB 无重叠时,假设离得很远,AC 无穷大,u 在 AC 面前可忽略,IOU = 0,后半部分近似为1,GIOU = -1,loss = 2;
GIOU 取值 [-1, 1],Loss 取值 [0,2]
存在问题
1. GIOU 训练较慢
2. GIOU 倾向得到一个较大的 bbox
3. GIOU 区分两个对象之间的对齐方式比较间接,仅通过引入C的方式来反应重叠的方式,不够直接。如下图所示。第二幅图展示来当GIoU一样的情况下,DIoU是不一致的
DIOU 【Distance-IoU】
b 代表预测 bbox 的中心,bgt 代表 ground truth 的中心,分子ρ是 距离,c 是对角线长度
CIOU
在DIoU的基础上增加了对长宽比的惩罚项
EIOU
w=(w1-w2)*(w1-w2) h=(h1-h2)*(h1-h2) eiou = iou-(rho2/c2+w/(cw**2)+h/(ch**2)) Leiou = 1 - eiou
这些 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
参考资料:
https://www.bilibili.com/video/BV1Ra411i7cw?spm_id_from=333.337.search-card.all.click&vd_source=f0fc90583fffcc40abb645ed9d20da32 目标检测iou loss 一网打尽 【讲得挺好】
https://blog.csdn.net/weixin_40922744/article/details/102988751 超简单的IoU python3实现
https://www.cnblogs.com/wojianxin/p/12581056.html python实现IoU
https://blog.csdn.net/weixin_41735859/article/details/89288493 GIoU详解
https://zhuanlan.zhihu.com/p/57992040 CVPR2019: 使用GIoU作为检测任务的Loss
https://blog.csdn.net/liangdong2014/article/details/114380202 详解GIoU、DIoU、CIoU Loss
https://mp.weixin.qq.com/s/0U4Y_ZEI2YvW1sMHxRfwMQ YOLOv5改进之七:损失函数改进