抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

目标检测分类损失函数——Cross entropy、Focal loss

一、Cross Entropy Loss 交叉熵是用来判定实际的输出与期望的输出的接近程度,刻画的是实际输出与期望输出的距离,也就是交叉熵的值越小,两个概率分布就越接近。

  1. CE Loss的定义

假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵,则:

当 时,交叉熵取得最小值,因此可以利用交叉熵比较一个分布与另一个分布的吻合情况。交叉熵接近于熵, 便是针对 更好的逼近,事实上模型的输出与期望的输出越接近,交叉熵也会越小,这正是损失函数所需要的。

  1. CE Loss推导

在这以逻辑回归做一次交叉熵及其反向传播的推导:

逻辑回归估计的是概率,则用联合概率分布衡量损失:

似然函数如下:

取对数求极大似然估计:

将求极大值转换成求极小值乘上

反向传播采用梯度下降法求 的最小值:

其中:

所以

  1. CE Loss优缺点

均方误差也是一种比较常见的损失函数,为什么用交叉熵而不用MSE做分类损失呢?主要是因为逻辑回归配合MSE损失函数时采用梯度下降法进行学习时,其偏导值在输出概率值接近0或者接近1的时候非常小,会出现模型一开始训练时,学习速率非常慢的情况。

假设MSE Loss为: 其中:

反向传播的过程是要对损失函数进行链式法则求导:

sigmoid函数如下图所示:

值接近0或者1的时候, 的值都会接近于0。这导致模型在一开始学习的时候速率非常慢,而使用交叉熵作为损失函数则不会导致这样的情况发生。

但是在目标检测领域,一张图像上负样本数量太大,占总loss的大部分,这种样本类别不均衡的情况会导致模型的优化方向存在偏差,标准的交叉熵损失函数在这个问题上存在不足。

  1. Balanced Cross Entropy

针对上面的问题平衡交叉熵损失函数在标准交叉熵的基础上增加权重参数 解决类别不平衡导致的模型优化偏差。其定义为:

可以事先根据数据集计算出 加在正样本判别上, 的计算逻辑:假设训练集有M类,每类的样本数目为 , 从1到M。求出M个样本数目的中位数,假设是 ,所有的 除以 ,得到新的一组系数,这组系数取倒数就得到了对应类别的系数。 Balanced Cross Entropy虽然用 平衡positive/negative的重要性,但是无法区分简单easy/困难hard样本。

二、Focal Loss 针对BCE Loss中存在的问题,何凯明团队在2017年的文章《Focal Loss for Dense Object Detection》中提出了Focal Loss function。因为在目标检测中存在大量简单的背景,少量较难的前景,。所以Focal Loss通过增加类别权重 和样本难度权重调节因子 来修改CE Loss,目的是解决样本类别不平衡和样本分类难度不平衡等问题。

文章链接:https://arxiv.org/abs/1708.02002

  1. Focal Loss的定义

Focal Loss Function是在平衡交叉熵损失基础上增加一个调节因子 和可聚焦参数 ,让损失函数聚焦在困难样本的训练:

其中 >0使得减少易分类样本的损失,使得模型更专注于困难样本。直观地讲,调节因子减少了简单示例的loss贡献,并扩展了样本接收低loss的范围。

  1. 权重的理解

项用来处理类别不均衡的问题,类似机器学习中训练样本的类别权重。例如训练样本中各类别占比为20%,10%,70%。那么 的定义就是某个类别占比较高,就将该类别设置一个较小的权重,占比较低就将其设置一个较大的权重,降低占比高的loss,提高占比低的loss。 让模型专注于训练难训练的样本,对于模型所属的真实类别,模型的预测值 的值接近1,说明该样本容易训练, 值接近0,说明模型预测的很差,样本难以训练。提高难以训练样本的loss,降低好训练样本的loss。例如,在 的情况下,与CE相比,分类为 的示例的损失将降低100倍,而对于 的示例,其损失将降低1000倍。 这反过来增加了纠正错误分类示例的重要性。其中 , 一定程度上也能解决类别不均衡问题。我们经常会遇到一个问题,如果在二分类中,负样本占比0.9。此时模型倾向于将样本全部判负。在CE Loss中,由于正负样本的权重一样(90%的负样本模型判别正确,10%的正样本模型判别错误),错误样本带来的loss在CE Loss中只占10%。如果上 项后,会提高正样本判负的loss在总loss中的比重。 3.Python实现代码:

根据论文alpha=0.25 gamma=2效果最好

def focal_loss(y_true, y_pred): alpha, gamma = 0.25, 2 y_pred = K.clip(y_pred, 1e-8, 1 - 1e-8) loss = - alpha * y_true * K.log(y_pred) * (1 - y_pred)gamma - (1 - alpha) * (1 - y_true) * K.log(1 - y_pred) * y_predgamma return loss 三、CE Loss、Focal Loss对比 CE Loss能够衡量同一个随机变量中两个概率分布的差异程度,在机器学习中表示为真实概率分布与预测概率分布之间的差异,交叉熵的值越小,模型预测效果就越好。并且解决了逻辑回归中采用MSE导致梯度优化缓慢的问题。

但是CE Loss对于正样本而言,输出概率越大,损失越小;对于负样本而言,输出概率越小则损失越小。此时的损失函数在大量简单样本的迭代过程中比较缓慢且可能无法优化到最优。

Focal Loss是在标准交叉熵损失函数的基础上修改得到的,这个函数可以通过减少易分类样本的权重,使得模型在训练时更专注于难分类的样本:调节正负样本不均衡的方法:引入权重系数 ;调节难易样本训练的方法:引入权重 。其中 大于0, 常取值为2。样本简单时, 小;样本难时, 大。

一、IOU Loss 上一篇文章提到L1,L2及其变种只将Bounding box的四个角点分别求loss然后相加,没有引入box四个顶点之间的相关性并且模型在训练过程中更偏向于尺寸更大的物体。在此基础上旷视在2016文章《UnitBox: An Advanced Object Detection Network》中提出了IOU Loss将4个点构成的box看成一个整体做回归。

文章链接:https://arxiv.org/pdf/1608.01471.pdf

  1. 函数特性

IOU Loss的定义是先求出预测框和真实框之间的交集和并集之比,再求负对数,但是在实际使用中我们常常将IOU Loss写成1-IOU。如果两个框重合则交并比等于1,Loss为0说明重合度非常高。

IOU满足非负性、同一性、对称性、三角不等性,相比于L1/L2等损失函数还具有尺度不变性,不论box的尺度大小,输出的iou损失总是在0-1之间。所以能够较好的反映预测框与真实框的检测效果。

伪代码如下:

其中, 是预测Bounding box的面积, 是真实Bounding box的面积, 是两个区域的交集, 是两个区域的并集。 是对IOU的交叉熵损失函数。

box位置的修正是通过对loss的反向传播迭代计算的。关于IOU Loss的反向传播具体推到过程可以移步到论文中,这里摘出结论部分如下:

其中:

从这个公式可以看出惩罚来自两个部分,预测框四个变量和预测框和真实框相交区域:

1 .损失函数和 成正比,因此预测的面积越大,损失越多;

2 .同时损失函数和 成反比,因此我们希望交集尽可能的大;

根据求导公式为了减小IOU Loss,会尽可能增大相交面积同时预测更小的框。

Python实现如下:

def calculate_iou(box_1, box_2): """ calculate iou :param box_1: (x0, y0, x1, y1) :param box_2: (x0, y0, x1, y1) :return: value of iou """ # calculate area of each box area_1 = (box_1[2] - box_1[0]) * (box_1[3] - box_1[1]) area_2 = (box_2[2] - box_2[0]) * (box_1[3] - box_1[1])

# find the edge of intersect box
top = max(box_1[0], box_2[0])
left = max(box_1[1], box_2[1])
bottom = min(box_1[3], box_2[3])
right = min(box_1[2], box_2[2])

# if there is an intersect area
if left >= right or top >= bottom:
    return 0

# calculate the intersect area
area_intersection = (right - left) * (bottom - top)

# calculate the union area
area_union = area_1 + area_2 - area_intersection

iou = float(area_intersection) / area_union

return iou

Tensorflow实现如下:

def bbox_iou(self, boxes_1, boxes_2): """ calculate regression loss using iou :param boxes_1: boxes_1 shape is [x, y, w, h] :param boxes_2: boxes_2 shape is [x, y, w, h] :return: """ # transform [x, y, w, h] to [x_min, y_min, x_max, y_max] boxes_1 = tf.concat([boxes_1[..., :2] - boxes_1[..., 2:] * 0.5, boxes_1[..., :2] + boxes_1[..., 2:] * 0.5], axis=-1) boxes_2 = tf.concat([boxes_2[..., :2] - boxes_2[..., 2:] * 0.5, boxes_2[..., :2] + boxes_2[..., 2:] * 0.5], axis=-1) boxes_1 = tf.concat([tf.minimum(boxes_1[..., :2], boxes_1[..., 2:]), tf.maximum(boxes_1[..., :2], boxes_1[..., 2:])], axis=-1) boxes_2 = tf.concat([tf.minimum(boxes_2[..., :2], boxes_2[..., 2:]), tf.maximum(boxes_2[..., :2], boxes_2[..., 2:])], axis=-1)

# calculate area of boxes_1 boxes_2
boxes_1_area = (boxes_1[..., 2] - boxes_1[..., 0]) * (boxes_1[..., 3] - boxes_1[..., 1])
boxes_2_area = (boxes_2[..., 2] - boxes_2[..., 0]) * (boxes_2[..., 3] - boxes_2[..., 1])

# calculate the two corners of the intersection
left_up = tf.maximum(boxes_1[..., :2], boxes_2[..., :2])
right_down = tf.minimum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate area of intersection
inter_section = tf.maximum(right_down - left_up, 0.0)
inter_area = inter_section[..., 0] * inter_section[..., 1]

# calculate union area
union_area = boxes_1_area + boxes_2_area - inter_area

# calculate iou add epsilon in denominator to avoid dividing by 0
iou = inter_area / (union_area + tf.keras.backend.epsilon())

return iou
  1. 存在的问题

IOU Loss虽然解决了Smooth L1系列变量相互独立和不具有尺度不变性的两大问题,但是它也存在两个问题:

预测框和真实框不相交时,不能反映出两个框的距离的远近。根据IOU定义loss等于0,没有梯度的回传无法进一步学习训练。 预测框和真实框无法反映重合度大小。借用一张图来说,三者具有相同的IOU,但是不能反映两个框是如何相交的,从直观上感觉第三种重合方式是最差的。

二、GIOU Loss 上面指出IOU Loss的两大缺点:无法优化两个框不相交的情况;无法反映两个框如何相交的。针对此类问题斯坦福学者在2019年的文章《Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression》中提出了GIOU Loss,在IOU的基础上引入了预测框和真实框的最小外接矩形。

文章链接:https://arxiv.org/pdf/1608.01471.pdf

1.函数特性

GIOU作为IOU的升级版,保持了 IOU 的主要性质并避免了 IOU 的缺点,首先计算预测框 和真实框 的面积 和 及其最小外接矩形 的面积 ,然后计算预测框 和真实框 的IOU,再用外接框面积 减去 和 的并集U除以 得到一个数值Value,最后用IOU减去Value求出GIOU。

伪代码如下:

从GIOU的原理可以看出:

与 类似采用距离度量损失函数,并且对尺度不敏感 ,而 ,所以 不仅关注重叠区域,还关注其他的非重合区域,能更好的反映两者的重合度 当预测框和真实框完全重合时, 当预测框和真实框不重合时,不重合度越高,GIOU越趋近于-1 特别是预测框和真实框不相交时,由于引入了预测框和真实框的最小外接矩形 最大化GIOU就是促使 最小两个框 和 不断靠近。 Python实现如下:

def calculate_giou(box_1, box_2): """ calculate giou :param box_1: (x0, y0, x1, y1) :param box_2: (x0, y0, x1, y1) :return: value of giou """ # calculate area of each box area_1 = (box_1[2] - box_1[0]) * (box_1[3] - box_1[1]) area_2 = (box_2[2] - box_2[0]) * (box_1[3] - box_1[1])

# calculate minimum external frame
area_c = (max(box_1[2], box_2[2]) - min(box_1[0], box_2[0])) * (max(box_1[3], box_2[3]) - min(box_1[1], box_2[1]))

# find the edge of intersect box
top = max(box_1[0], box_2[0])
left = max(box_1[1], box_2[1])
bottom = min(box_1[3], box_2[3])
right = min(box_1[2], box_2[2])

# calculate the intersect area
area_intersection = (right - left) * (bottom - top)

# calculate the union area
area_union = area_1 + area_2 - area_intersection

# calculate iou
iou = float(area_intersection) / area_union

# calculate giou(iou - (area_c - area_union)/area_c)
giou = iou - float((area_c - area_union)) / area_c

return giou

Tensorflow实现如下:

def bbox_giou(self, boxes_1, boxes_2): """ calculate regression loss using giou :param boxes_1: boxes_1 shape is [x, y, w, h] :param boxes_2: boxes_2 shape is [x, y, w, h] :return: """ # transform [x, y, w, h] to [x_min, y_min, x_max, y_max] boxes_1 = tf.concat([boxes_1[..., :2] - boxes_1[..., 2:] * 0.5, boxes_1[..., :2] + boxes_1[..., 2:] * 0.5], axis=-1) boxes_2 = tf.concat([boxes_2[..., :2] - boxes_2[..., 2:] * 0.5, boxes_2[..., :2] + boxes_2[..., 2:] * 0.5], axis=-1) boxes_1 = tf.concat([tf.minimum(boxes_1[..., :2], boxes_1[..., 2:]), tf.maximum(boxes_1[..., :2], boxes_1[..., 2:])], axis=-1) boxes_2 = tf.concat([tf.minimum(boxes_2[..., :2], boxes_2[..., 2:]), tf.maximum(boxes_2[..., :2], boxes_2[..., 2:])], axis=-1)

# calculate area of boxes_1 boxes_2
boxes_1_area = (boxes_1[..., 2] - boxes_1[..., 0]) * (boxes_1[..., 3] - boxes_1[..., 1])
boxes_2_area = (boxes_2[..., 2] - boxes_2[..., 0]) * (boxes_2[..., 3] - boxes_2[..., 1])

# calculate the two corners of the intersection
left_up = tf.maximum(boxes_1[..., :2], boxes_2[..., :2])
right_down = tf.minimum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate area of intersection
inter_section = tf.maximum(right_down - left_up, 0.0)
inter_area = inter_section[..., 0] * inter_section[..., 1]

# calculate union area
union_area = boxes_1_area + boxes_2_area - inter_area

# calculate iou add epsilon in denominator to avoid dividing by 0
iou = inter_area / (union_area + tf.keras.backend.epsilon())

# calculate the upper left and lower right corners of the minimum closed convex surface
enclose_left_up = tf.minimum(boxes_1[..., :2], boxes_2[..., :2])
enclose_right_down = tf.maximum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate width and height of the minimun closed convex surface
enclose_wh = tf.maximum(enclose_right_down - enclose_left_up, 0.0)

# calculate area of the minimun closed convex surface
enclose_area = enclose_wh[..., 0] * enclose_wh[..., 1]

# calculate the giou add epsilon in denominator to avoid dividing by 0
giou = iou - 1.0 * (enclose_area - union_area) / (enclose_area + tf.keras.backend.epsilon())

return giou
  1. 存在的问题

在预测框和真实框没有很好地对齐时,会导致最小外接框C的面积增大,从而使GIOU的值变小,而两个矩形框不重合时,也可以计算GIOU。GIOU Loss虽然解决了IOU的上述两个问题,但是当两个框属于包含关系时,借用下图来说:GIOU会退化成IOU,无法区分其相对位置关系。

由于GIOU仍然严重依赖IOU,因此在两个垂直方向,误差很大,基本很难收敛,这就是GIoU不稳定的原因。借用下图来说:红框内部分:C为两个框的最小外接矩形,此部分表征除去两个框的其余面积,预测框和真实框在相同距离的情况下,水平垂直方向时,此部分面积最小,对loss的贡献也就越小,从而导致在垂直水平方向上回归效果较差。

三、DIOU Loss 针对上述GIOU的两个问题(预测框和真实框是包含关系的情况或者处于水平/垂直方向上,GIOU损失几乎已退化为IOU损失,即 ,导致收敛较慢)。有学者将GIOU中引入最小外接框来最大化重叠面积的惩罚项修改成最小化两个BBox中心点的标准化距离从而加速损失的收敛过程。该方法出自2020年AAAI 文章《Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression》

文章地址:https://arxiv.org/pdf/1911.08287.pdf

  1. 函数特性

DIOU损失函数公式如下:

其中, , 分别代表了预测框和真实框的中心点,且 代表的是计算两个中心点间的欧式距离, 代表的是能够同时包含预测框和真实框的最小闭包区域的对角线距离。

DIOU Loss的惩罚项能够直接最小化中心点间的距离,而GIOU Loss旨在减少外界包围框的面积,所以DIOU Loss具有以下特性:

DIOU与IOU、GIOU一样具有尺度不变性; DIOU与GIOU一样在与目标框不重叠时,仍然可以为边界框提供移动方向; DIOU可以直接最小化两个目标框的距离,因此比GIOU Loss收敛快得多; DIOU在包含两个框水平/垂直方向上的情况回归很快,而GIOU几乎退化为IOU; 当预测框和真实框完全重合时, ; 当预测框和真实框不相交时, ; Python实现如下:

def calculate_diou(box_1, box_2): """ calculate diou :param box_1: (x0, y0, x1, y1) :param box_2: (x0, y0, x1, y1) :return: value of diou """ # calculate area of each box area_1 = (box_1[2] - box_1[0]) * (box_1[3] - box_1[1]) area_2 = (box_2[2] - box_2[0]) * (box_1[3] - box_1[1])

# calculate center point of each box
center_x1 = (box_1[2] - box_1[0]) / 2
center_y1 = (box_1[3] - box_1[1]) / 2
center_x2 = (box_2[2] - box_2[0]) / 2
center_y2 = (box_2[3] - box_2[1]) / 2

# calculate square of center point distance
p2 = (center_x2 - center_x1) ** 2 + (center_y2 - center_y1) ** 2

# calculate square of the diagonal length
width_c = max(box_1[2], box_2[2]) - min(box_1[0], box_2[0])
height_c = max(box_1[3], box_2[3]) - min(box_1[1], box_2[1])
c2 = width_c ** 2 + height_c ** 2

# find the edge of intersect box
top = max(box_1[0], box_2[0])
left = max(box_1[1], box_2[1])
bottom = min(box_1[3], box_2[3])
right = min(box_1[2], box_2[2])

# calculate the intersect area
area_intersection = (right - left) * (bottom - top)

# calculate the union area
area_union = area_1 + area_2 - area_intersection

# calculate iou
iou = float(area_intersection) / area_union

# calculate diou(iou - p2/c2)
diou = iou - float(p2) / c2

return diou

Tensorflow实现如下:

def bbox_diou(self, boxes_1, boxes_2): """ calculate regression loss using diou :param boxes_1: boxes_1 shape is [x, y, w, h] :param boxes_2: boxes_2 shape is [x, y, w, h] :return: """ # calculate center distance center_distance = tf.reduce_sum(tf.square(boxes_1[..., :2] - boxes_2[..., :2]), axis=-1)

# transform [x, y, w, h] to [x_min, y_min, x_max, y_max]
boxes_1 = tf.concat([boxes_1[..., :2] - boxes_1[..., 2:] * 0.5,
                     boxes_1[..., :2] + boxes_1[..., 2:] * 0.5], axis=-1)
boxes_2 = tf.concat([boxes_2[..., :2] - boxes_2[..., 2:] * 0.5,
                     boxes_2[..., :2] + boxes_2[..., 2:] * 0.5], axis=-1)
boxes_1 = tf.concat([tf.minimum(boxes_1[..., :2], boxes_1[..., 2:]),
                     tf.maximum(boxes_1[..., :2], boxes_1[..., 2:])], axis=-1)
boxes_2 = tf.concat([tf.minimum(boxes_2[..., :2], boxes_2[..., 2:]),
                     tf.maximum(boxes_2[..., :2], boxes_2[..., 2:])], axis=-1)

# calculate area of boxes_1 boxes_2
boxes_1_area = (boxes_1[..., 2] - boxes_1[..., 0]) * (boxes_1[..., 3] - boxes_1[..., 1])
boxes_2_area = (boxes_2[..., 2] - boxes_2[..., 0]) * (boxes_2[..., 3] - boxes_2[..., 1])

# calculate the two corners of the intersection
left_up = tf.maximum(boxes_1[..., :2], boxes_2[..., :2])
right_down = tf.minimum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate area of intersection
inter_section = tf.maximum(right_down - left_up, 0.0)
inter_area = inter_section[..., 0] * inter_section[..., 1]

# calculate union area
union_area = boxes_1_area + boxes_2_area - inter_area

# calculate IoU, add epsilon in denominator to avoid dividing by 0
iou = inter_area / (union_area + tf.keras.backend.epsilon())

# calculate the upper left and lower right corners of the minimum closed convex surface
enclose_left_up = tf.minimum(boxes_1[..., :2], boxes_2[..., :2])
enclose_right_down = tf.maximum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate width and height of the minimun closed convex surface
enclose_wh = tf.maximum(enclose_right_down - enclose_left_up, 0.0)

# calculate enclosed diagonal distance
enclose_diagonal = tf.reduce_sum(tf.square(enclose_wh), axis=-1)

# calculate diou add epsilon in denominator to avoid dividing by 0
diou = iou - 1.0 * center_distance / (enclose_diagonal + tf.keras.backend.epsilon())

return diou
  1. 存在的问题

虽然DIOU能够直接最小化预测框和真实框的中心点距离加速收敛,但是Bounding box的回归还有一个重要的因素纵横比暂未考虑。

四、CIOU Loss CIOU Loss 和 DIOU Loss出自于2020年同一篇文章,CIOU在DIOU的基础上将Bounding box的纵横比考虑进损失函数中,进一步提升了回归精度。

  1. 函数特性

CIOU的惩罚项是在DIOU的惩罚项基础上加了一个影响因子 ,这个因子把预测框纵横比拟合真实框的纵横比考虑进去。惩罚项公式如下:

其中 是用于做trade-off的参数, 的定义如下:

是用来衡量长宽比一致性的参数, 定义如下:

完整的CIOU损失函数的公式如下:

CIOU Loss的梯度在长宽 的情况下, 的值通常很小,会导致梯度爆炸,因此在 实现时将替换成1。

Python实现如下:

def calculate_ciou(box_1, box_2): """ calculate ciou :param box_1: (x0, y0, x1, y1) :param box_2: (x0, y0, x1, y1) :return: value of ciou """ # calculate area of each box width_1 = box_1[2] - box_1[0] height_1 = box_1[3] - box_1[1] area_1 = width_1 * height_1

width_2 = box_2[2] - box_2[0]
height_2 = box_2[3] - box_2[1]
area_2 = width_2 * height_2

# calculate center point of each box
center_x1 = (box_1[2] - box_1[0]) / 2
center_y1 = (box_1[3] - box_1[1]) / 2
center_x2 = (box_2[2] - box_2[0]) / 2
center_y2 = (box_2[3] - box_2[1]) / 2

# calculate square of center point distance
p2 = (center_x2 - center_x1) ** 2 + (center_y2 - center_y1) ** 2

# calculate square of the diagonal length
width_c = max(box_1[2], box_2[2]) - min(box_1[0], box_2[0])
height_c = max(box_1[3], box_2[3]) - min(box_1[1], box_2[1])
c2 = width_c ** 2 + height_c ** 2

# find the edge of intersect box
left = max(box_1[0], box_2[0])
top = max(box_1[1], box_2[1])
bottom = min(box_1[3], box_2[3])
right = min(box_1[2], box_2[2])

# calculate the intersect area
area_intersection = (right - left) * (bottom - top)

# calculate the union area
area_union = area_1 + area_2 - area_intersection

# calculate iou
iou = float(area_intersection) / area_union

# calculate v
arctan = math.atan(float(width_2) / height_2) - math.atan(float(width_1) / height_1)
v = (4.0 / math.pi ** 2) * (arctan ** 2)

# calculate alpha
alpha = float(v) / (1 - iou + v)

# calculate ciou(iou - p2 / c2 - alpha * v)
ciou = iou - float(p2) / c2 - alpha * v

return ciou

Tensorflow实现如下:

def box_ciou(self, boxes_1, boxes_2): """ calculate regression loss using ciou :param boxes_1: boxes_1 shape is [x, y, w, h] :param boxes_2: boxes_2 shape is [x, y, w, h] :return: """ # calculate center distance center_distance = tf.reduce_sum(tf.square(boxes_1[..., :2] - boxes_2[..., :2]), axis=-1)

v = 4 * tf.square(tf.math.atan2(boxes_1[..., 2], boxes_1[..., 3]) - tf.math.atan2(boxes_2[..., 2], boxes_2[..., 3])) / (math.pi * math.pi)

# transform [x, y, w, h] to [x_min, y_min, x_max, y_max]
boxes_1 = tf.concat([boxes_1[..., :2] - boxes_1[..., 2:] * 0.5,
                     boxes_1[..., :2] + boxes_1[..., 2:] * 0.5], axis=-1)
boxes_2 = tf.concat([boxes_2[..., :2] - boxes_2[..., 2:] * 0.5,
                     boxes_2[..., :2] + boxes_2[..., 2:] * 0.5], axis=-1)
boxes_1 = tf.concat([tf.minimum(boxes_1[..., :2], boxes_1[..., 2:]),
                     tf.maximum(boxes_1[..., :2], boxes_1[..., 2:])], axis=-1)
boxes_2 = tf.concat([tf.minimum(boxes_2[..., :2], boxes_2[..., 2:]),
                     tf.maximum(boxes_2[..., :2], boxes_2[..., 2:])], axis=-1)

# calculate area of boxes_1 boxes_2
boxes_1_area = (boxes_1[..., 2] - boxes_1[..., 0]) * (boxes_1[..., 3] - boxes_1[..., 1])
boxes_2_area = (boxes_2[..., 2] - boxes_2[..., 0]) * (boxes_2[..., 3] - boxes_2[..., 1])

# calculate the two corners of the intersection
left_up = tf.maximum(boxes_1[..., :2], boxes_2[..., :2])
right_down = tf.minimum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate area of intersection
inter_section = tf.maximum(right_down - left_up, 0.0)
inter_area = inter_section[..., 0] * inter_section[..., 1]

# calculate union area
union_area = boxes_1_area + boxes_2_area - inter_area

# calculate IoU, add epsilon in denominator to avoid dividing by 0
iou = inter_area / (union_area + tf.keras.backend.epsilon())

# calculate the upper left and lower right corners of the minimum closed convex surface
enclose_left_up = tf.minimum(boxes_1[..., :2], boxes_2[..., :2])
enclose_right_down = tf.maximum(boxes_1[..., 2:], boxes_2[..., 2:])

# calculate width and height of the minimun closed convex surface
enclose_wh = tf.maximum(enclose_right_down - enclose_left_up, 0.0)

# calculate enclosed diagonal distance
enclose_diagonal = tf.reduce_sum(tf.square(enclose_wh), axis=-1)

# calculate diou
diou = iou - 1.0 * center_distance / (enclose_diagonal + tf.keras.backend.epsilon())

# calculate param v and alpha to CIoU
alpha = v / (1.0 - iou + v)

# calculate ciou
ciou = diou - alpha * v

return ciou
  1. 存在的问题

纵横比权重的设计还不太明白,是否有更好的设计方式有待更新。

五、EIOU Loss CIOU Loss虽然考虑了边界框回归的重叠面积、中心点距离、纵横比。但是通过其公式中的v反映的纵横比的差异,而不是宽高分别与其置信度的真实差异,所以有时会阻碍模型有效的优化相似性。针对这一问题,有学者在CIOU的基础上将纵横比拆开,提出了EIOU Loss,并且加入Focal聚焦优质的锚框,该方法出自于2021年的一篇文章《Focal and Efficient IOU Loss for Accurate Bounding Box Regression》

文章链接:https://arxiv.org/pdf/2101.08158.pdf

  1. 函数特性

EIOU的惩罚项是在CIOU的惩罚项基础上将纵横比的影响因子拆开分别计算目标框和锚框的长和宽,该损失函数包含三个部分:重叠损失,中心距离损失,宽高损失,前两部分延续CIOU中的方法,但是宽高损失直接使目标盒与锚盒的宽度和高度之差最小,使得收敛速度更快。惩罚项公式如下:

其中 Cw 和 Ch 是覆盖两个Box的最小外接框的宽度和高度。

考虑到BBox的回归中也存在训练样本不平衡的问题,即在一张图像中回归误差小的高质量锚框的数量远少于误差大的低质量样本,质量较差的样本会产生过大的梯度影响训练过程。作者在EIOU的基础上结合Focal Loss提出一种Focal EIOU Loss,梯度的角度出发,把高质量的锚框和低质量的锚框分开,惩罚项公式如下:

其中IOU = |A∩B|/|A∪B|, γ为控制异常值抑制程度的参数。该损失中的Focal与传统的Focal Loss有一定的区别,传统的Focal Loss针对越困难的样本损失越大,起到的是困难样本挖掘的作用;而根据上述公式:IOU越高的损失越大,相当于加权作用,给越好的回归目标一个越大的损失,有助于提高回归精度。

  1. 存在的问题

本文针对边界框回归任务,在之前基于CIOU损失的基础上提出了两个优化方法:

将纵横比的损失项拆分成预测的宽高分别与最小外接框宽高的差值,加速了收敛提高了回归精度; 引入了Focal Loss优化了边界框回归任务中的样本不平衡问题,即减少与目标框重叠较少的大量锚框对BBox 回归的优化贡献,使回归过程专注于高质量锚框。 不足之处或许在于Focal的表达形式是否有待改进。

六、IOU、GIOU、DIOU、CIOU、EIOU对比 边界框回归的三大几何因素:重叠面积、中心点距离、纵横比

IOU Loss:考虑了重叠面积,归一化坐标尺度; GIOU Loss:考虑了重叠面积,基于IOU解决边界框不相交时loss等于0的问题; DIOU Loss:考虑了重叠面积和中心点距离,基于IOU解决GIOU收敛慢的问题; CIOU Loss:考虑了重叠面积、中心点距离、纵横比,基于DIOU提升回归精确度; EIOU Loss:考虑了重叠面积,中心点距离、长宽边长真实差,基于CIOU解决了纵横比的模糊定义,并添加Focal Loss解决BBox回归中的样本不平衡问题。 IOU Loss GIOU Loss DIOU Loss CIOU Loss EIOU Loss 优点 IOU算法是目标检测中最常用的指标,具有尺度不变性,满足非负性;同一性;对称性;三角不等性等特点。 GIOU在基于IOU特性的基础上引入最小外接框解决检测框和真实框没有重叠时loss等于0问题。 DIOU在基于IOU特性的基础上考虑到GIOU的缺点,直接回归两个框中心点的欧式距离,加速收敛。 CIOU就是在DIOU的基础上增加了检测框尺度的loss,增加了长和宽的loss,这样预测框就会更加的符合真实框。 EIOU在CIOU的基础上分别计算宽高的差异值取代了纵横比,同时引入Focal Loss解决难易样本不平衡的问题。 缺点 1.如果两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小 1.当检测框和真实框出现包含现象的时候GIOU退化成IOU 2.两个框相交时,在水平和垂直方向上收敛慢 回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间 1. 纵横比描述的是相对值,存在一定的模糊 2. 未考虑难易样本的平衡问题 待定

评论