一、本文介紹
本文給大家帶來的是YOLOv10最新改進,為大家帶來最近新提出的InnerIoU的內容同時用Inner的思想結合SIoU、WIoU、GIoU、DIoU、EIOU、CIoU等損失函數,形成?InnerIoU、InnerSIoU、InnerWIoU、等新版本損失函數,同時還結合了Focus和AIpha思想,形成的新的損失函數,其中Inner的主要思想是:引入了不同尺度的輔助邊界框來計算損失,(該方法在處理非常小目標的檢測任務時表現出良好的性能(但是在其它的尺度檢測時也要比普通的損失要好)。文章會詳細探討這些損失函數如何提高YOLOv10在各種檢測任務中的性能,包括提升精度、加快收斂速度和增強模型對復雜場景的適應性。
?專欄回顧:YOLOv10改進系列專欄——本專欄持續復習各種頂會內容——科研必備?
目錄
一、本文介紹
二、各種損失函數的基本原理?
2.1 交集面積和并集面積
2.2 InnerIoU的思想?
2.2.1結合InnerIoU各種損失函數的效果圖?
2.3 InnerSIoU
2.4 InnerWioU
2.5 InnerGIoU
2.6 InnerDIoU
2.7 InnerEIoU
2.8 InnerCIoU
2.9 FocusLoss?
三、InnerIoU等損失函數代碼塊
3.1 代碼一
四、添加InnerIoU等損失函數到模型中
4.1 步驟一?
4.2 步驟二
4.3 步驟三?
4.4 什么時候使用損失函數改進
五、本文總結
二、各種損失函數的基本原理?
論文地址:官方Inner-IoU論文地址點擊即可跳轉
官方代碼地址:官方代碼地址-官方只放出了兩種結合方式CIoU、SIoU
本文改進地址: 文末提供完整代碼塊-包括InnerEIoU、InnerCIoU、InnerDIoU等七種結合方式和其Focus變種
2.1 交集面積和并集面積
在理解各種損失函數之前我們需要先來理解一下交集面積和并集面積,在數學中我們都學習過集合的概念,這里的交集和并集的概念和數學集合中的含義是一樣的。
2.2 InnerIoU的思想?
Inner-IoU(內部交并比)的主要思想是:改進目標檢測中邊界框回歸(BBR)的準確性,特別是在處理高度重疊的目標時。傳統的IoU(交并比)計算方法考慮了預測邊界框和真實邊界框的整體重疊區域,而Inner-IoU則專注于邊界框內部的重疊部分。它通過引入輔助邊界框,這些輔助框是原始邊界框的縮小版本,來計算損失函數。
這種方法的優點包括:
- 針對性優化:Inner-IoU通過關注邊界框的核心部分而非整體,提供了對重疊區域更加精確的評估。
- 調整尺度:通過控制輔助邊界框的大小,Inner-IoU允許對不同的數據集和檢測任務進行微調。
- 提高泛化能力:實驗證明,Inner-IoU在不同的數據集上顯示出比傳統IoU更好的泛化性能。
- 處理高低IoU樣本:對于高IoU樣本,使用較小的輔助框可以加速模型學習;而對于低IoU樣本,使用較大的輔助框可以改善回歸性能。
總結:Inner-IoU是一種更細致、更專注于目標中心的性能評估指標,它通過輔助框的尺度調整提高了目標檢測任務的精確度和效率。
2.2.1結合InnerIoU各種損失函數的效果圖?
上面的圖片展示了CIoU 和 Inner-CIoU 方法。圖中從左至右分別表示 CIoU 方法,以及不同比例(0.7、0.75 和 0.8)的 Inner-CIoU 方法的檢測結果?
這個圖片可以看出這個Innner的思想在小目標檢測的時候效果能夠達到極致(最適用于小范圍但是其它的情況也能夠有效但是小目標是效果最好的情景)?
PS:下面介紹的是融合的各種思想就是將其中的IoU替換為我們上面求出來的InnerIoU即可和其中的參數也替換為InnerIoU的思想,其中各種損失函數的本身思想并沒有改變,只是改變了其中的 參數。
2.3 InnerSIoU
論文地址:SIoU: More Powerful Learning for Bounding Box Regression
適用場景:適用于需要高精度邊界框對齊的場景,如精細的物體檢測和小目標檢測。
概念:SIoU損失通過融入角度考慮和規模敏感性,引入了一種更為復雜的邊界框回歸方法,解決了以往損失函數的局限性,SIoU損失函數包含四個組成部分:角度損失、距離損失、形狀損失和第四個未指定的組成部分。通過整合這些方面,從而實現更好的訓練速度和預測準確性。
2.4 InnerWioU
論文地址:WIoU: Bounding Box Regression Loss with Dynamic Focusing Mechanism
適用場景:適用于需要動態調整損失焦點的情況,如不均勻分布的目標或不同尺度的目標檢測。
概念:引入動態聚焦機制的IoU變體,旨在改善邊界框回歸損失。
2.5 InnerGIoU
論文地址:GIoU: A Metric and A Loss for Bounding Box Regression
適用場景:適合處理有重疊和非重疊區域的復雜場景,如擁擠場景的目標檢測。
概念:在IoU的基礎上考慮非重疊區域,以更全面評估邊界框
2.6 InnerDIoU
論文地址:DIoU: Faster and Better Learning for Bounding Box Regression
適用場景:適用于需要快速收斂和精確定位的任務,特別是在邊界框定位精度至關重要的場景。
概念:結合邊界框中心點之間的距離和重疊區域。
2.7 InnerEIoU
論文地址:EIoU:Loss for Accurate Bounding Box Regression
適用場景:可用于需要進一步優化邊界框對齊和形狀相似性的高級場景。
概念:EIoU損失函數的核心思想在于提高邊界框回歸的準確性和效率。它通過以下幾個方面來優化目標檢測:
1. 增加中心點距離損失:通過最小化預測框和真實框中心點之間的距離,提高邊界框的定位準確性。
2. 考慮尺寸差異:通過懲罰寬度和高度的差異,EIoU確保預測框在形狀上更接近真實框。
3. 結合最小封閉框尺寸:將損失函數與包含預測框和真實框的最小封閉框的尺寸相結合,從而使得損失更加敏感于對象的尺寸和位置。
EIoU損失函數在傳統IoU基礎上增加了這些考量,以期在各種尺度上都能獲得更精確的目標定位,尤其是在物體大小和形狀變化較大的場景中。
2.8 InnerCIoU
論文地址:CIoU:Enhancing Geometric Factors in Model Learning
適用場景:適合需要綜合考慮重疊區域、形狀和中心點位置的場景,如復雜背景或多目標跟蹤。
概念:綜合考慮重疊區域、中心點距離和長寬比。
2.9 FocusLoss?
論文地址:Focal Loss for Dense Object Detection
適用場景:適用于需要高精度邊界框對齊的場景,如精細的物體檢測和小目標檢測。?
Focal Loss由Kaiming He等人在論文《Focal Loss for Dense Object Detection》中提出,旨在解決在訓練過程中正負樣本數量極度不平衡的問題,尤其是在一些目標檢測任務中,背景類別的樣本可能遠遠多于前景類別的樣本。
Focal Loss通過修改交叉熵損失,增加一個調整因子,這個因子降低了那些已經被正確分類的樣本的損失值,使得模型的訓練焦點更多地放在難以分類的樣本上。這種方式特別有利于提升小目標或者在復雜背景中容易被忽視的目標的檢測性能。簡而言之,Focal Loss讓模型“關注”(或“專注”)于學習那些對提高整體性能更為關鍵的樣本。
三、InnerIoU等損失函數代碼塊
3.1 代碼一
此代碼塊塊的基礎版本來源于Github的開源版本,我在其基礎上將Inner的思想加入其中形成了各種Inner的思想同時融合各種改良版本的損失函數形成對應版本的InnerIoU、InnerCIoU等損失函數。
import numpy as np
import torch
import math
from ultralytics.utils import opsclass WIoU_Scale:''' monotonous: {None: origin v1True: monotonic FM v2False: non-monotonic FM v3}momentum: The momentum of running mean'''iou_mean = 1.monotonous = False_momentum = 1 - 0.5 ** (1 / 7000)_is_train = Truedef __init__(self, iou):self.iou = iouself._update(self)@classmethoddef _update(cls, self):if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \cls._momentum * self.iou.detach().mean().item()@classmethoddef _scaled_loss(cls, self, gamma=1.9, delta=3):if isinstance(self.monotonous, bool):if self.monotonous:return (self.iou.detach() / self.iou_mean).sqrt()else:beta = self.iou.detach() / self.iou_meanalpha = delta * torch.pow(gamma, beta - delta)return beta / alphareturn 1def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, EIoU=False, SIoU=False, WIoU=False, ShapeIoU=False,hw=1, mpdiou=False, Inner=False, alpha=1, ratio=0.7, eps=1e-7, scale=0.0):"""Calculate Intersection over Union (IoU) of box1(1, 4) to box2(n, 4).Args:box1 (torch.Tensor): A tensor representing a single bounding box with shape (1, 4).box2 (torch.Tensor): A tensor representing n bounding boxes with shape (n, 4).xywh (bool, optional): If True, input boxes are in (x, y, w, h) format. If False, input boxes are in(x1, y1, x2, y2) format. Defaults to True.GIoU (bool, optional): If True, calculate Generalized IoU. Defaults to False.DIoU (bool, optional): If True, calculate Distance IoU. Defaults to False.CIoU (bool, optional): If True, calculate Complete IoU. Defaults to False.EIoU (bool, optional): If True, calculate Efficient IoU. Defaults to False.SIoU (bool, optional): If True, calculate Scylla IoU. Defaults to False.eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.Returns:(torch.Tensor): IoU, GIoU, DIoU, or CIoU values depending on the specified flags."""if Inner:if not xywh:box1, box2 = ops.xyxy2xywh(box1), ops.xyxy2xywh(box2)(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)b1_x1, b1_x2, b1_y1, b1_y2 = x1 - (w1 * ratio) / 2, x1 + (w1 * ratio) / 2, y1 - (h1 * ratio) / 2, y1 + (h1 * ratio) / 2b2_x1, b2_x2, b2_y1, b2_y2 = x2 - (w2 * ratio) / 2, x2 + (w2 * ratio) / 2, y2 - (h2 * ratio) / 2, y2 + (h2 * ratio) / 2# Intersection areainter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * \(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp_(0)# Union Areaunion = w1 * h1 * ratio * ratio + w2 * h2 * ratio * ratio - inter + epsiou = inter / union# Get the coordinates of bounding boxeselse: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 / 2b1_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 = box1b1_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 + epsw2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps# Intersection areainter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * \(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp_(0)# Union Areaunion = w1 * h1 + w2 * h2 - inter + eps# IoUiou = inter / unionif CIoU or DIoU or GIoU or EIoU or SIoU or ShapeIoU or mpdiou or WIoU:cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) widthch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex heightif CIoU or DIoU or EIoU or SIoU or mpdiou or WIoU or ShapeIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squaredrho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center dist ** 2if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)with torch.no_grad():alpha = v / (v - iou + (1 + eps))return iou - (rho2 / c2 + v * alpha) # CIoUelif EIoU:rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2cw2 = cw ** 2 + epsch2 = ch ** 2 + epsreturn iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2) # EIoUelif SIoU:# SIoU Loss https://arxiv.org/pdf/2205.12740.pdfs_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + epss_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + epssigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)sin_alpha_1 = torch.abs(s_cw) / sigmasin_alpha_2 = torch.abs(s_ch) / sigmathreshold = pow(2, 0.5) / 2sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)rho_x = (s_cw / cw) ** 2rho_y = (s_ch / ch) ** 2gamma = angle_cost - 2distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(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) + eps # SIoUelif ShapeIoU:#Shape-Distance #Shape-Distance #Shape-Distance #Shape-Distance #Shape-Distance #Shape-Distance #Shape-Distanceww = 2 * torch.pow(w2, scale) / (torch.pow(w2, scale) + torch.pow(h2, scale))hh = 2 * torch.pow(h2, scale) / (torch.pow(w2, scale) + torch.pow(h2, scale))cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex widthch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex heightc2 = cw ** 2 + ch ** 2 + eps # convex diagonal squaredcenter_distance_x = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2) / 4center_distance_y = ((b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4center_distance = hh * center_distance_x + ww * center_distance_ydistance = center_distance / c2#Shape-Shape #Shape-Shape #Shape-Shape #Shape-Shape #Shape-Shape #Shape-Shape #Shape-Shape #Shape-Shapeomiga_w = hh * torch.abs(w1 - w2) / torch.max(w1, w2)omiga_h = ww * 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 - distance - 0.5 * shape_costelif mpdiou:d1 = (b2_x1 - b1_x1) ** 2 + (b2_y1 - b1_y1) ** 2d2 = (b2_x2 - b1_x2) ** 2 + (b2_y2 - b1_y2) ** 2return iou - d1 / hw.unsqueeze(1) - d2 / hw.unsqueeze(1) # MPDIoUelif WIoU:self = WIoU_Scale(1 - iou)dist = getattr(WIoU_Scale, '_scaled_loss')(self)return iou * dist # WIoU https://arxiv.org/abs/2301.10051return iou - rho2 / c2 # DIoUc_area = cw * ch + eps # convex areareturn iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdfreturn iou # IoU
四、添加InnerIoU等損失函數到模型中
4.1 步驟一?
上面的代碼我們首先找到'ultralytics/utils/metrics.py'文件,然后其中有一個完全同名字的方法,原始樣子如下,我們將我們的代碼完整替換掉這個代碼,記得是全部替換這個方法內的代碼。
4.2 步驟二
替換成功后,我們找到另一個文件'ultralytics/utils/loss.py'然后找到如下一行代碼原始樣子下面的圖片然后用我給的代碼替換掉其中的紅框內的一行即可。
iou = bbox_iou(pred_bboxes[fg_mask], target_bboxes[fg_mask],xywh=False, GIoU=False, DIoU=False, CIoU=False, EIoU=False, SIoU=False,WIoU=False, ShapeIoU=False Inner=False,ratio=0.75, eps=1e-7, scale=0.0)
上面的代碼我來解釋一下,我把所有的能選用的參數都寫了出來,其中IoU很好理解了,對應的參數設置為True就是使用的對應的IoU包括本文的IoU,需要注意的是Inner這個參數,比如我Inner設置為True然后Shape_IoU也設置為True那么此時使用的就是Inner_Shape_IoU,其它的都是,其中ratio和eps是inner的參數大家可以自己嘗試我這里定義了兩個基本值。?
替換完后的樣子如下->
4.3 步驟三?
我們還需要修改一處,找到如下的文件''ultralytics/utils/tal.py''然后找到其中下面圖片的代碼,用我給的代碼替換紅框內的代碼。
overlaps[mask_gt] = bbox_iou(gt_boxes, pd_boxes, xywh=False, GIoU=False, DIoU=False, CIoU=False,EIoU=False, SIoU=False, WIoU=True, ShapeIoU=False, Inner=False,ratio=0.7, eps=1e-7, scale=0.0).squeeze(-1).clamp_(0)
此處和loss.py里面的最好是使用同一個參數。
4.4 什么時候使用損失函數改進
在這里多說一下,就是損失函數的使用時間,當我們修改模型的時候,損失函數是作為一種保底的存在,就是說當其它模型結構都修改完成了,已經無法在提升精度了,此時就可以修改損失函數了,不要上來先修改損失函數,當然這是我個人的建議,具體還是由大家自己來選擇。
YOLOv10調用YOLOv8損失函數計算修改了YOLOv8的損失函數即代表修改了YOLOv10!?
五、本文總結
到此本文的正式分享內容就結束了,在這里給大家推薦我的YOLOv10改進有效漲點專欄,本專欄目前為新開的平均質量分98分,后期我會根據各種最新的前沿頂會進行論文復現,也會對一些老的改進機制進行補充,如果大家覺得本文幫助到你了,訂閱本專欄,關注后續更多的更新~
?專欄回顧: