—— 主要解决常规损失函数无法很精确的反映目标检测中预测框和真实框的重合度问题。
—— 让损失函数更精确的反应预测框和真实框的重合程度
YOLO v4中提及的Bounding Box regression loss有:MSE, IoU, GIoU, DIoU, CIoU
YOLO v4最终用的是CIoU Loss
背景
对于分类问题而言,我们常常会使用一些L1,L2损失函数,但是对于预测框回归问题而言,L2损失函数真的是合适的吗?答案是否定的,如下图:
图中左中右三种情况的L2损失都是相等的,但是他们的回归效果却千差万别。所以L2损失并不能很好的反应我们预测框和真实框的重合程度,于是就有了我们后来的IoU以及IoU Loss。
一、IoU Loss
IoU Loss考虑到了预测框与真实框重叠区域(IoU)
1.1、定义
IoU(Intersection over Union),也就是我们常说的交并比。简单来说IOU就是用来度量目标检测中预测框与真实框的重叠程度。
IoU公式:
显而易见,IOU的值越高也说明A框与B框重合程度越高,代表模型预测越准确。反之,IOU越低模型性能越差。
IoU Loss公式:
也有另外一种更常见的形式:
1.2、优缺点
优点:
- 相比与L2损失而言,可以更好的反映检测框与真实检测框的检测效果。
- 具有尺度不变性,也就是对尺度不敏感 (无论两个框是大是小,它们的从重合程度是和尺度大小无关的)。
缺点:
- 如果两个框没有相交,根据定义,IoU=0,不能反映两者的距离大小(重合度)。同时因为loss=0,没有梯度回传,无法进行学习训练。
- 虽然IoU相较于L2损失而言有很大的改进,但是IoU只关注重叠区域,不关注其他的非重合区域。还是无法很精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。
1.3、应用场景
除了作为目标检测的评价指标,IOU还有其他应用场景:
- 在anchor-based方法的目标检测中,根据IOU的值来区分正样本和负样本。
- 可以直接作为边界框回归的loss函数进行优化。
- 在NMS(非极大值抑制)对预测框筛选。
二、GIoU Loss
GIoU Loss考虑到了预测框与真实框的重叠区域与非重叠区域。
来源:
https://arxiv.org/pdf/1902.09630.pdf
2.1、定义
GIoU(Generalized Intersection over Union),GIoU Loss的目的就是解决1、IoU Loss中当B与G不相交时,Loss为0的问题;2、解决IoU Loss只关注重叠区域,不关注其他的非重合区域,不能更精确的反应两者重合程度的问题。
GIoU 公式:
先计算两个框的最小闭包区域面积 AcA_cAc? (通俗理解:同时包含了预测框和真实框的最小矩形框的面积),再计算出IoU,再计算闭包区域中不属于两个框的区域占闭包区域的比重。最后用IoU减去这个比重得到GIoU。
举例:AcA_cAc? 等于下图中蓝色的面积(两个框的最小闭包区域); uuu等于绿色和红色两个矩形框的并集。
GIoU Loss 公式:
很明显,当两个框不相交时,LGIoU=?1L_{GIoU}=-1LGIoU?=?1, LGIoUL_{GIoU}LGIoU?=2,此时我们也能正常进行训练。
2.2、优缺点
优点:
- 与IoU相似,GIoU也是一种距离度量,作为损失函数的话,LGIoU=1?GIoUL_{GIoU} = 1 - GIoULGIoU?=1?GIoU ,满足损失函数的基本要求。
- GIoU对scale不敏感。
- GIoU是IoU的下界,在两个框无限重合的情况下,IoU=GIoU=1
- IoU取值[0,1],但GIoU有对称区间,取值范围[-1,1]。在两者重合的时候取最大值1,在两者无交集且无限远的时候取最小值-1,因此GIoU是一个非常好的距离度量指标。由此可见,只有当B与G重合时,GIoU Loss才会为0,相比IoU Loss,GIoU Loss在任意情况下都可以进行训练。
- 与IoU只关注重叠区域不同,GIoU不仅关注重叠区域IoUIoUIoU,还关注其他的非重合区域∣Ac?u∣∣Ac∣\frac{|A_c-u|}{|A_c|}∣Ac?∣∣Ac??u∣?,能更好的反映两者的重合度。
缺点:
- 在两个框水平对齐的时候(如下图),Ac=uA_c = uAc?=u , GIoU就退化到了IoU。
- 收敛速度过慢
下面是论文原作者在voc2007上的实验,GIoUGIoUGIoU大概能提高一个点。
其实,GIoU也并不是最好的,它仍然有它的局限性(1、收敛速度太慢;2、定位的精度还是不够),这就需要我们另外两个升级版IoULossIoU LossIoULoss来解决。
三、DIoU Loss
DIoU Loss考虑到了预测框与真实框的重叠率(主要是关注重叠区域)和中心点距离。
来源:
https://arxiv.org/pdf/1911.08287.pdf
3.1、背景
上面说到GIoU有两个很严重的缺点:
-
收敛速度慢
-
在两个框水平对齐的时候(如下图),Ac=uA_c = uAc?=u , GIoU就退化到了IoU。
从上面可以看出,GIoU在速度和精度都存在问题,那么如何来改进它呢?这就要引入DIoU。
3.2、定义
DIoU Loss(Distance-IoU Loss)。简单地在IoU loss基础上添加一个惩罚项,该惩罚项用于最小化两个bbox的中心点距离。
DIoU公式:
如下图,绿色框代表真实框,黑色框代表预测框, bbb为预测框的中心, bgtb^{gt}bgt为真实框的中心, ρ2(b,bgt)\rho^2(b,b^{gt})ρ2(b,bgt)代表真实框与预测框中心距离的平方 d2d^2d2, ccc表示两个框的最小闭包区域(同时包含了预测框和真实框的最小矩形框)的对角线长度。
DIoU Loss公式:
3.3、优缺点
优点:
- 与GIoU loss类似,DIoU loss在与目标框不重叠时,仍然可以正常训练。
- DIoU损失能够直接最小化预测框与真实框的距离,而GIOU loss优化的是预测框与真实框的面积,因此收敛速度快很多。
- 对于包含两个框在水平方向和垂直方向上这种情况,DIoU损失可以使回归非常快,而GIoU损失几乎退化为IoU损失。
- DIoU要比GIou更加符合目标框回归的机制,将目标与anchor之间的距离,重叠率(关注重叠区域)都考虑进去,使得目标框回归变得更加稳定,收敛效果更好,不会像IoU和GIoU一样出现训练过程中发散问题。
缺点:
- 还要有个重要指标预测框与真实框的长宽比没考虑到。
四、CIoU Loss
CIoU Loss考虑到了预测框与真实框的重叠面积、中心点距离、长宽比。
来源(和DIoU同一篇):
https://arxiv.org/pdf/1911.08287.pdf
4.1、背景
GIoU为了归一化坐标尺度,利用IoU,并初步解决IoU为零的情况。DIoU Loss 只是考虑了边界框的重叠面积和真实框与预测框中心点距离,然而真实框和预测框之间的w,h比的一致性也是极其重要的。基于此作者提出了Complete-IoU Loss(CIoU)。
4.2、定义
CIoU公式:
其中 α\alphaα是权重函数, ν\nuν用来衡量anchor框和目标框之间的长宽比的相似性。由 α\alphaα定义可以看出来,损失函数会更加倾向于往重叠区域增多方向优化。
CIoU Loss公式:
4.3、优点
- 同时考虑到了预测框与真实框的重叠面积、中心点距离、长宽比。收敛更快,效果更好。
三、PyTorch实现
下面函数实现了LIoUL_{IoU}LIoU?, LGIoUL_{GIoU}LGIoU?,LDIoUL_{DIoU}LDIoU?,LCoUL_{CoU}LCoU?:
def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False):"""Args:box1: 预测框box2: 真实框x1y1x2y2: FalseReturns:box1和box2的IoU/GIoU/DIoU/CIoU"""# Returns the IoU of box1 to box2. box1 is 4, box2 is nx4box2 = box2.t() # 转置 ???# Get the coordinates of bounding boxesif x1y1x2y2: # x1, y1, x2, y2 = box1b1_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 xyxyb1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 # b1左上角和右下角的x坐标b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 # b1左下角和右下角的y坐标b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 # b2左上角和右下角的x坐标b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2 # b2左下角和右下角的y坐标# Intersection area tensor.clamp(0): 将矩阵中小于0的元数变成0inter = (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 Areaw1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1union = (w1 * h1 + 1e-16) + w2 * h2 - inter # 1e-16: 防止分母为0iou = inter / union # iouif GIoU or DIoU or CIoU:cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # 两个框的最小闭包区域的widthch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # 两个框的最小闭包区域的heightif GIoU: # Generalized IoU https://arxiv.org/pdf/1902.09630.pdfc_area = cw * ch + 1e-16 # convex areareturn iou - (c_area - union) / c_area # return GIoUif DIoU or CIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1# convex diagonal squaredc2 = cw ** 2 + ch ** 2 + 1e-16# centerpoint distance squaredrho2 = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2)) ** 2 / 4 + ((b2_y1 + b2_y2) - (b1_y1 + b1_y2)) ** 2 / 4 # 中心点距离 d^2if DIoU:return iou - rho2 / c2 # DIoUelif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)with torch.no_grad():alpha = v / (1 - iou + v)return iou - (rho2 / c2 + v * alpha) # CIoUreturn iou
四、实验结果
理论上: CIoU Loss > DIoU Loss > GIoU Loss > IoU Loss > BCE Loss > L2 Loss
但是具体的做项目的时候,还是需要自己一个个的试试,可能不同的数据集得到的结果并不一样。
Reference
- https://zhuanlan.zhihu.com/p/94799295
- https://blog.csdn.net/weixin_42384432/article/details/107736707
- https://blog.csdn.net/weixin_42392454/article/details/111152035
- https://arxiv.org/pdf/1902.09630.pdf
- https://zhuanlan.zhihu.com/p/105470021
- https://blog.csdn.net/leonardohaig/article/details/103394369