#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)

【参考文档】江大白的yolo解析
后面会给出我的完整代码,先来分段看看!
转化格式

if x1y1x2y2:# x1, y1, x2, y2 = box1 b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] else:# transform from xywh to xyxy b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2

转换成这种格式:
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

IOU 这个应该都很熟了
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) #.clamp:将小于0的元素修改为0,截断元素的取值空 # Union Area w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps union = w1 * h1 + w2 * h2 - inter + ep iou = inter / union

clamp
def clamp(self, min: _float=-inf, max: _float=inf, *, out: Optional[Tensor]=None) -> Tensor: ...

inf:无穷大
-inf:负无穷
out:输出,默认即可,不用设定
在 yolov5的使用中,应该是截断掉小于0的部分
(torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0)

torch.clamp
DIOU 在正式进入各种iou之前
cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)# 最小包裹矩形宽度 ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)# 最小包裹矩形高度

cw :最小外包矩形宽度
ch :最小外包矩形高度
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

分子部分的一次项代表:GT框和bbox框中心点的距离
c:两个框对角线的距离
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

考虑了重叠面积和中心点距离
c2 = cw ** 2 + ch ** 2 + eps# 勾股定理,使用两边的平方和来代替斜边的平方 rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 if DIoU: return iou - rho2 / c2# DIoU

c2: 勾股定理,使用两边的平方和来代替斜边的平方
GIOU #|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

c_area = cw * ch + eps# convex area return iou - (c_area - union) / c_area# GIoU

CIoU CIOU_Loss和DIOU_Loss前面的公式都是一样的,不过在此基础上还增加了一个影响因子,将预测框和目标框的长宽比都考虑了进去。
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

其中v是衡量长宽比一致性的参数,我们也可以定义为:
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

这样CIOU_Loss就将目标框回归函数应该考虑三个重要几何因素:重叠面积、中心点距离,长宽比全都考虑进去了。
elif 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

EIOU 【参考博文】IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU损失函数
前两部分延续CIOU中的方法,但是宽高损失直接使目标盒与锚盒的宽度和高度之差最小,使得收敛速度更快。
#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

该损失函数包含三个部分:重叠损失,中心距离损失,宽高损失
rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4# center distance squared elif EIoU: w=(w1-w2)*(w1-w2) h=(h1-h2)*(h1-h2) return iou-(rho2/c2+w/(cw**2)+h/(ch**2))

w:宽度差的平方
h:高度差的平方
SIOU 直接看这篇的解析吧。就不重复写了
然后上一下完整代码
def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, SIoU=False, EIoU = False, eps=1e-7): # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4 box2 = box2.T# Get the coordinates of bounding boxes if x1y1x2y2:# x1, y1, x2, y2 = box1 b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] else:# transform from xywh to xyxy b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2# 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 w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps union = w1 * h1 + w2 * h2 - inter + epsiou = inter / union if GIoU or DIoU or CIoU or SIoU or EIoU: 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 SIoU:# SIoU Loss 2022.08.01 sigma = torch.pow(cw ** 2 + ch ** 2, 0.5) sin_alpha_1 = ch / sigma sin_alpha_2 = cw / sigma threshold = pow(2, 0.5) / 2 sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1) # angle_cost = 1 - 2 * torch.pow( torch.sin(torch.arcsin(sin_alpha) - np.pi/4), 2) angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - np.pi / 2) rho_x = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) / cw) ** 2 rho_y = ((b2_y1 + b2_y2 - b1_y1 - b1_y2) / ch) ** 2 gamma = 2 - angle_cost distance_cost = 2 - torch.exp(-1 * gamma * rho_x) - torch.exp(-1 * gamma * rho_y) omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2) omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2) shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4) return iou - 0.5 * (distance_cost + shape_cost)elif CIoU or DIoU or EIoU:# 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 distance squared if DIoU: return iou - rho2 / c2# DIoU elif 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 elif EIoU: w=(w1-w2)*(w1-w2) h=(h1-h2)*(h1-h2) return iou-(rho2/c2+w/(cw**2)+h/(ch**2))#EIOU else:# GIoU https://arxiv.org/pdf/1902.09630.pdf c_area = cw * ch + eps# convex area return iou - (c_area - union) / c_area# GIoU else: return iou# IoU

在yolov5使用方法
1.metrics.py修改一下注释掉原来的bbox_iou,复制上面的完整代码
2.loss.py中把想要使用的iou设置为True(下图以EIOU为例)
【#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)】#|yolov5 优化方法(四)修改bbox损失函数(补充EIOU,SIOU)
文章图片

    推荐阅读