目标检测——yolov4损失函数

损失函数

yolo损失分为3个部分类别损失、置信度损失、位置损失

1. 类别损失

只有有目标的地方才会有类别判断,从而才会有类别损失,所以需要解决两个问题:1.有目标的地方;2.类别损失

1.1有目标的地方:object_mask

object_mask根据 y_true(真实值)确定,如何通过前处理编码y_true,通过计算实际框(ground_truth)与anchor框的iou来确定

achor的9个框分为3组,分别负责下小目标(8倍下采样)、中目标(16倍下采样)、大目标(32倍下采样) ,计算每个框与anchor框的iou,iou最大的anchor负责回归这个真实框,

以13*13 (32倍下采样)为例,整幅图被分为13*13个gird,真实框的信息应该放在哪个grid里面呢?答案是真实框的中心落在哪个grid里面,这个grid就负责存储这个框的所有信息,

包括(x, y, h, w),(confidence=1),(class)。object_mask,顾名思义,目标掩码,即有目标的地方为true,所以 object_mask = y_true[l][..., 4:5]

2.1类别损失

类型损失,一般采用交叉熵损失,在目标检测中,采用二元交叉熵损失,对每一个类别计算交叉熵损失,进行求和;

class_loss = object_mask * K.binary_crossentropy(true_class_prob, raw_pred[..., 5:], from_logits=True)
class_loss = K.sum(class_loss) / mf

此处为什么不采用softmax交叉熵损失呢?此处笔者也不清楚,欢迎有答案的同学留言交流。我猜测是因为各个类别之间相互独立,不是非A即B的关系

所以没有采用softmax,将所有类别的概率之和调整为1。

2.位置损失

目标检测的一项重要任务就是确定目标的位置,即(x, y, h, w),所以损失值的计算中包含位置损失

通常计算位置损失有

1. L1 Loss 平均绝对误差(Mean Absolute Error, MAE

梯度值为1或-1,在接近准确值时,会以learning_rate在准确值附近波动,比较难获得准确效果

2. L2 Loss  均方误差损失(Mean Square Error, MSE

在距离准确值较远的地方梯度值较大,训练初期收敛难度大,容易受到噪声的干扰

3. Smooth L1 Loss

克服了以上两种损失函数的缺点,但将x,y,h,w作为独立的变量的看待,割裂了他们之间的相对关系

4. IoU Loss

考虑重合面积,可以较好的反应预测框和真实框的接近程度,但是但二者不相交时,损失值恒为1

5. GIoU Loss

考虑了不重合部分的面积对于损失的影响

6. DIoU Loss

考虑了中心点的偏移,即两个框中心的距离与两个框最远距离的比值

7. CIoU Loss

考虑了长和宽的比值

在yoloV4中,采用了CIou Loss

def box_ciou(b1, b2):

    b1_xy = b1[..., :2]
    b1_wh = b1[..., 2:4]
    b1_wh_half = b1_wh / 2.

    b1_mins = b1_xy - b1_wh_half
    b1_maxes = b1_xy + b1_wh_half

    b2_xy = b2[..., :2]
    b2_wh = b2[..., 2:4]
    b2_wh_half = b2_wh / 2.

    b2_mins = b2_xy - b2_wh_half
    b2_maxes = b2_xy + b2_wh_half

    intersect_mins = K.maximum(b1_mins, b2_mins)
    intersect_maxes = K.minimum(b1_maxes, b2_maxes)

    intersect_wh = K.maximum(intersect_maxes-intersect_mins, 0.)
    # 两个框相交部分面积
    intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]

    b1_area = b1_wh[..., 0] * b1_wh[..., 1]

    b2_area = b2_wh[..., 0] * b2_wh[..., 1]

    union_area = b1_area + b2_area - intersect_area
    # 两个框的iou
    iou = intersect_area / K.maximum(union_area, K.epsilon())

    # 两个框中心点的距离
    center_distance = K.sum(K.square((b1_xy - b2_xy)), axis=-1)

    enclose_mins = K.minimum(b1_mins, b2_mins)
    enclose_maxes = K.maximum(b1_maxes, b2_maxes)
    enclose_wh = K.maximum(enclose_maxes - enclose_mins, 0.0)
     
    # 两个框的最远距离
    enclose_diagonal = K.sum(K.square(enclose_wh), axis=-1)
   
    # 计算DIOU(CIOU的前半部分)
    ciou = iou - 1.0 * (center_distance) / K.maximum(enclose_diagonal, K.epsilon())
   
   # 计算两个框的长宽比的差值的平方
    v = 4*K.square(tf.math.atan2(b1_wh[..., 0], K.maximum(b1_wh[..., 1],K.epsilon())) - tf.math.atan2(b2_wh[..., 0], K.maximum(b2_wh[..., 1],K.epsilon()))) / (math.pi * math.pi)

    alpha = v / K.maximum((1.0 - iou + v), K.epsilon())
    ciou = ciou - alpha * v

    ciou = K.expand_dims(ciou, -1)
    return ciou                                
ciou_loss = object_mask * box_loss_scale * (1 - ciou)
ciou_loss = K.sum(ciou_loss) / mf

其中 box_loss_scale 为 (2 - 对应真实框的面积),范围为(1-2),当真实框的面积越大,box_loss_scale越小,意味着对大框的偏差容忍度越大。

解决了以上问题以后,我们还有一个重要问题没有解决,预测框的(x, y, h, w)如何得到?

在yolo中,我们的(x,y)是通过对应grid左上角偏移得到;(h,w)是通过对对应anchor的长宽缩放得到,

box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[..., ::-1], K.dtype(feats))
box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[..., ::-1], K.dtype(feats))

前半部分(斜体加粗),公式转化如图所示

 

后半部分,box_xy 除以grid_shape 将中心点坐标转化为(0-1),与y_true中中心点的坐标的编码对应,同样转化为(0-1),即中心点相对于输入图像的位置;同样也是作归一化处理;box_hw 除以input_shape 将长宽转化为(0-1),与y_true中长宽的编码对应。

3.置信度损失

置信度损失分为两个部分,有目标的置信度损失,无目标的置信度损失

3.1 有目标的置信度损失

有目标的地方,即object_mask,

置信度损失:采用二元交叉熵损失

K.binary_crossentropy(object_mask, raw_pred[..., 4:5]

总的损失为

object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True)

3.2 无目标的置信度损失

无目标的地方,即(1-object_mask)

(1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True)

存在一个问题,因为一张图中大部分是背景,即大部分是负样本,如果所有的负样本都参与计算,会极大的放大负样本的损失,导致训练结果偏向于负样本,

所以在计算无目标的置信度损失时只会选择部分负样本,如何选择负样本?原则是计算预测值框和真实值框的iou,每个预测值的每个grid中都有三个框(以13*13)为例,每个真实值(一幅图)中有那个框

依次计算iou,会得到(13*13*3*n)个iou,选取最大的iou作为预测值和真实值的iou,得到的维度为(13,13,3,1),设置一个iou阈值,小于此阈值的视为负样本,其实质笔者认为是一种随机取样的方法。

def loop_body(b, ignore_mask):
    true_box = tf.boolean_mask(y_true[l][b, ..., 0:4], object_mask_bool[b, ..., 0])

    iou = box_iou(pred_box[b], true_box)

    best_iou = K.max(iou, axis=-1)

    # 13, 13, 3
    ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))

    return b+1, ignore_mask

得到ignore_mask ,所以无目标的置信度损失为:

confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) + \
(1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) * ignore_mask
confidence_loss = K.sum(confidence_loss) / mf

所以最终的目标检测损失为三者的损失之和:

loss += location_loss + confidence_loss + class_loss

 



 

 

 

 

 




posted @ 2020-12-06 19:48  learningcaiji  阅读(7240)  评论(0编辑  收藏  举报