当前位置:   article > 正文

【YOLOv8改进[损失函数]】使用结合InnerIoU和Focaler的各种损失函数助力YOLOv8更优秀

【YOLOv8改进[损失函数]】使用结合InnerIoU和Focaler的各种损失函数助力YOLOv8更优秀

目录

一 回归损失函数(Bounding Box Regression Loss)

1 Inner-IoU

2 Focaler-IoU:更聚焦的IoU损失

二 改进YOLOv8的损失函数

1 总体修改

① ultralytics/utils/metrics.py文件

② ultralytics/utils/loss.py文件

③ ultralytics/utils/tal.py文件

2 各种机制的使用

其他


一 回归损失函数(Bounding Box Regression Loss)

1 Inner-IoU

官方论文地址官方论文地址 点击即可跳转

官方代码地址官方代码地址 点击即可跳转

论文中分析了边界框的回归过程,指出了IoU损失的局限性,它对不同的检测任务没有很强的泛化能力。基于边界框回归问题的固有特点,提出了一种基于辅助边界框的边界框回归损失Inner-IoU。通过比例因子比率(scale factor ratio)控制辅助边界框的生成,计算损失,加速训练的收敛。它可以集成到现有的基于IoU的损失函数中。通过一系列的模拟和烧蚀消融实验验证,该方法优于现有方法。本文提出的方法不仅适用于一般的检测任务,而且对于非常小目标的检测任务也表现良好,证实了该方法的泛化性

官方的代码给出了2种结合方式,文件如下图:

Inner-IoU的描述见下图:

Inner-IoU的实验效果

CIoU 方法, Inner-CIoU (ratio=0.7), Inner-CIoU (ratio=0.75) and Inner-CIoU (ratio=0.8)的检测效果如下图所示:

SIoU 方法, Inner-SIoU (ratio=0.7), Inner-SIoU (ratio=0.75) and Inner-SIoU (ratio=0.8)的检测效果如下图所示:

2 Focaler-IoU:更聚焦的IoU损失

官方论文地址官方论文地址 点击即可跳转

官方代码地址官方代码地址 点击即可跳转

论文中分析了难易样本的分布对目标检测的影响。当困难样品占主导地位时,需要关注困难样品以提高检测性能。当简单样本的比例较大时,则相反。论文中提出了Focaler-IoU方法,通过线性区间映射重建原始IoU损失,达到聚焦难易样本的目的。最后通过对比实验证明,该方法能够有效提高检测性能

为了在不同的回归样本中关注不同的检测任务,使用线性间隔映射方法重构IoU损失,这有助于提高边缘回归。具体的公式如下所示:

将Focaler-IoU应用于现有的基于IoU的边界框回归损失函数中,如下所示: 

 实验结果如下:

 GIoU、DIoU、CIoU、EIoU和MPDIou等的概述见使用MPDIou回归损失函数帮助YOLOv9模型更优秀  点击此处即可跳转

二 改进YOLOv8的损失函数

1 总体修改

首先,我们现将后续会使用到的损失函数集成到项目中。

① ultralytics/utils/metrics.py文件

utils/metrics.py文件中,使用下述代码(替换后的部分)替换掉bbox_iou()函数,即将被替换的bbox_iou()函数如下图所示:

使用下述的替换代码替换掉下述原始代码

  • a 原始代码
  1. # before
  2. def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7):
  3. """
  4. Calculate Intersection over Union (IoU) of box1(1, 4) to box2(n, 4).
  5. Args:
  6. box1 (torch.Tensor): A tensor representing a single bounding box with shape (1, 4).
  7. box2 (torch.Tensor): A tensor representing n bounding boxes with shape (n, 4).
  8. xywh (bool, optional): If True, input boxes are in (x, y, w, h) format. If False, input boxes are in
  9. (x1, y1, x2, y2) format. Defaults to True.
  10. GIoU (bool, optional): If True, calculate Generalized IoU. Defaults to False.
  11. DIoU (bool, optional): If True, calculate Distance IoU. Defaults to False.
  12. CIoU (bool, optional): If True, calculate Complete IoU. Defaults to False.
  13. eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.
  14. Returns:
  15. (torch.Tensor): IoU, GIoU, DIoU, or CIoU values depending on the specified flags.
  16. """
  17. # Get the coordinates of bounding boxes
  18. if xywh: # transform from xywh to xyxy
  19. (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
  20. w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
  21. b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
  22. b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
  23. else: # x1, y1, x2, y2 = box1
  24. b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
  25. b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
  26. w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
  27. w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
  28. # Intersection area
  29. inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * (
  30. b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)
  31. ).clamp_(0)
  32. # Union Area
  33. union = w1 * h1 + w2 * h2 - inter + eps
  34. # IoU
  35. iou = inter / union
  36. if CIoU or DIoU or GIoU:
  37. cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) width
  38. ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height
  39. if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
  40. c2 = cw.pow(2) + ch.pow(2) + eps # convex diagonal squared
  41. rho2 = (
  42. (b2_x1 + b2_x2 - b1_x1 - b1_x2).pow(2) + (b2_y1 + b2_y2 - b1_y1 - b1_y2).pow(2)
  43. ) / 4 # center dist**2
  44. if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
  45. v = (4 / math.pi**2) * ((w2 / h2).atan() - (w1 / h1).atan()).pow(2)
  46. with torch.no_grad():
  47. alpha = v / (v - iou + (1 + eps))
  48. return iou - (rho2 / c2 + v * alpha) # CIoU
  49. return iou - rho2 / c2 # DIoU
  50. c_area = cw * ch + eps # convex area
  51. return iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdf
  52. return iou # IoU
  • b 替换代码
  1. # after
  2. class WIoU_Scale:
  3. ''' monotonous: {
  4. None: origin v1
  5. True: monotonic FM v2
  6. False: non-monotonic FM v3
  7. }
  8. '''
  9. iou_mean = 1.
  10. monotonous = False
  11. _momentum = 1 - 0.5 ** (1 / 7000)
  12. _is_train = True
  13. def __init__(self, iou):
  14. self.iou = iou
  15. self._update(self)
  16. @classmethod
  17. def _update(cls, self):
  18. if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \
  19. cls._momentum * self.iou.detach().mean().item()
  20. @classmethod
  21. def _scaled_loss(cls, self, gamma=1.9, delta=3):
  22. if isinstance(self.monotonous, bool):
  23. if self.monotonous:
  24. return (self.iou.detach() / self.iou_mean).sqrt()
  25. else:
  26. beta = self.iou.detach() / self.iou_mean
  27. alpha = delta * torch.pow(gamma, beta - delta)
  28. return beta / alpha
  29. return 1
  30. def bbox_iou(box1, box2, xywh=True, ratio=1, GIoU=False, DIoU=False, CIoU=False,
  31. SIoU=False, EIoU=False, WIoU=False, MPDIoU=False, LMPDIoU=False,
  32. Inner=False, Focal=False, alpha=1, gamma=0.5, scale=False, eps=1e-7):
  33. # 计算box1与box2之间的Intersection over Union(IoU)
  34. # 获取bounding box的坐标
  35. if Inner:
  36. (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
  37. w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
  38. b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_ * ratio, x1 + w1_ * ratio, \
  39. y1 - h1_ * ratio, y1 + h1_ * ratio
  40. b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_ * ratio, x2 + w2_ * ratio, \
  41. y2 - h2_ * ratio, y2 + h2_ * ratio
  42. # 计算交集面积
  43. inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
  44. (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
  45. # 计算并集面积
  46. union = w1 * ratio * h1 * ratio + w2 * ratio * h2 * ratio - inter + eps
  47. iou = inter / union # inner_iou
  48. else:
  49. # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4
  50. if xywh: # xywh转换为xyxy格式
  51. (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
  52. w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
  53. b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
  54. b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
  55. else: # x1, y1, x2, y2 = box1
  56. b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
  57. b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
  58. w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
  59. w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
  60. # 计算交集面积
  61. inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * \
  62. (b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp_(0)
  63. # 计算并集面积
  64. union = w1 * h1 + w2 * h2 - inter + eps
  65. # 计算IoU值
  66. iou = inter / union
  67. if CIoU or DIoU or GIoU or EIoU or SIoU or WIoU or MPDIoU or LMPDIoU:
  68. cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # 计算最小外接矩形的宽度
  69. ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # 计算最小外接矩形的高度
  70. if CIoU or DIoU or EIoU or SIoU or WIoU or MPDIoU or LMPDIoU: # Distance or Complete IoU
  71. c2 = (cw ** 2 + ch ** 2) ** alpha + eps # convex diagonal squared
  72. rho2 = (((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (
  73. b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4) ** alpha # 中心点距离的平方
  74. if CIoU:
  75. v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)
  76. with torch.no_grad():
  77. alpha_ciou = v / (v - iou + (1 + eps))
  78. if Focal:
  79. return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)), torch.pow(inter / (union + eps), gamma) # Focal_CIoU的计算
  80. else:
  81. return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)) # CIoU
  82. elif MPDIoU:
  83. d1 = (b2_x1 - b1_x1) ** 2 + (b2_y1 - b1_y1) ** 2
  84. d2 = (b2_x2 - b1_x2) ** 2 + (b2_y2 - b1_y2) ** 2
  85. w = (b2_x2 - b2_x1) # x2 - x1
  86. h = (b2_y2 - b2_y1) # y2 - y1
  87. if Focal:
  88. return iou - ((d1 + d2) / (w ** 2 + h ** 2)), torch.pow(inter / (union + eps), gamma)# Focal_MPDIoU
  89. else:
  90. return iou - (d1 + d2) / (w ** 2 + h ** 2)
  91. elif LMPDIoU:
  92. d1 = (b2_x1 - b1_x1) ** 2 + (b2_y1 - b1_y1) ** 2
  93. d2 = (b2_x2 - b1_x2) ** 2 + (b2_y2 - b1_y2) ** 2
  94. w = (b2_x2 - b2_x1) # x2 - x1
  95. h = (b2_y2 - b2_y1) # y2 - y1
  96. if Focal:
  97. return 1 - (iou - (d1 + d2) / (w ** 2 + h ** 2)), torch.pow(inter / (union + eps), gamma)# Focal_MPDIo # MPDIoU
  98. else:
  99. return 1 - iou + d1 / (w ** 2 + h ** 2) + d2 / (w ** 2 + h ** 2)
  100. elif EIoU:
  101. rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
  102. rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
  103. cw2 = torch.pow(cw ** 2 + eps, alpha)
  104. ch2 = torch.pow(ch ** 2 + eps, alpha)
  105. if Focal:
  106. return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2), torch.pow(inter / (union + eps),gamma) # Focal_EIou
  107. else:
  108. return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2) # EIou
  109. elif SIoU:
  110. # SIoU
  111. s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps
  112. s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps
  113. sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
  114. sin_alpha_1 = torch.abs(s_cw) / sigma
  115. sin_alpha_2 = torch.abs(s_ch) / sigma
  116. threshold = pow(2, 0.5) / 2
  117. sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
  118. angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)
  119. rho_x = (s_cw / cw) ** 2
  120. rho_y = (s_ch / ch) ** 2
  121. gamma = angle_cost - 2
  122. distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)
  123. omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)
  124. omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)
  125. shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
  126. if Focal:
  127. return iou - torch.pow(0.5 * (distance_cost + shape_cost) + eps, alpha), torch.pow(inter / (union + eps), gamma) # Focal_SIou的计算
  128. else:
  129. return iou - torch.pow(0.5 * (distance_cost + shape_cost) + eps, alpha) # SIou
  130. elif WIoU:
  131. self = WIoU_Scale(1 - (inter / union))
  132. dist = getattr(WIoU_Scale, '_scaled_loss')(self)
  133. return iou * dist # WIoU
  134. if Focal:
  135. return iou - rho2 / c2, torch.pow(inter / (union + eps), gamma) # Focal_DIoU
  136. else:
  137. return iou - rho2 / c2 # DIoU
  138. c_area = cw * ch + eps # convex area
  139. if Focal:
  140. return iou - torch.pow((c_area - union) / c_area + eps, alpha), torch.pow(inter / (union + eps), gamma)# Focal_GIoU
  141. else:
  142. return iou - torch.pow((c_area - union) / c_area + eps, alpha) # GIoU
  143. if Focal:
  144. return iou, torch.pow(inter / (union + eps), gamma) # Focal_IoU
  145. else:
  146. return iou # IoU的值

② ultralytics/utils/loss.py文件

接下来,需要修改loss.py文件中的内容。

before
after

③ ultralytics/utils/tal.py文件

before
after

2 各种机制的使用

与上述内容类比,如果将对应机制设置为True则开启,否则关闭。之后,可以尝试多种组合方式去训练模型。

那么。接下来开始训练模型吧!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/475307
推荐阅读
相关标签