yolov3从头实现(六)损失计算

损失计算

一、损失计算的输入

损失计算的预测值输入是yolov3从头实现(五)中的yolov3网络块的输出
损失计算的真实值输入是yolov3从头实现(三)中我们制作出的标签值

二、损失的结构

yolov3的损失分为三个部分:
1、位置损失
2、置信度损失
3、分类损失
在这里插入图片描述

在计算损失时我们是一个尺度一个尺度的计算损失的,然后将最终的损失组合到一起。

三、计算某一个尺度的损失

以13*13尺度损失计算为例
yolov3网络输出结果为:
输出结果的意义:
坐标:中心宽高坐标的中心点坐标偏离因子,该因子需要经过sigmoid函数在加上相应像素点坐标才能变成中心宽高坐标的中心坐标值,且该坐标值是相对于对应特征图的坐标,其宽高是预测anchor的宽高因子,该因子需要经过exp()函数处理乘以相应的人为给定的相对于统一图anchor才会变成相对于统一图的anchor的宽高
置信度:置信度因子,需要经过sigmoid函数才会变成预测的置信度
分类:分类概率因子,需要经过sigmoid函数才会变成预测的分类概率。真实的预测概率是让其与置信度相乘的到的,这表明该anchor有物体并且是某一类的概率为此概率的概率。例如一个anchor的置信度为p1分类值为p2则分类概率为sigmoid(p2)*p1
在这里插入图片描述我们对yolov3网络输出结果做以下操作
1、我们对yolov3网络输出结果中的置信度设置阈值,标签anchor与预测anchor的iou值小于阈值的预测值的置信度置0。

2、由于预测的位置坐标是相对于特征图像素位置的偏移和宽w高h因子(因为宽高因子需要转换才能得到相对于特征图的宽高,转换公式anchor_wexp(w)和anchor_hexp(h)),所以我们将预测的位置偏移做sigmoid再加上特征图像素位置来得到相对于特征图的中心宽高坐标的中心点坐标

3、将结果分离并放进一个列表中,
[相对于特征图大小的中心宽高坐标的中心点坐标,相对于特征图对应的anchor的宽高因子,置信度,分类值]
我们定义函数conf_delta_tensor(y_true, y_pred, anchors, ignore_thresh):实现以上1个步骤
我们定义函数adjust_pred_tensor(y_pred):实现以上2、3个步骤

# 处理某一个尺度特征图的所有anchor的预测值的置信度,将不合格的置信度预测值置0  不合格的标准 预测值与真实值的相对于统一图的iou值小于某个阈值
def conf_delta_tensor(y_true, y_pred, anchors, ignore_thresh):
    '''
    :param y_true: 某一尺度标签
    :param y_pred: 某一尺度预测值
    :param anchors: 对应于某一特征图的相对于统一图的自定义anchors
    :param ignore_thresh: 标签anchor与预测anchor的iou阈值
    :return:处理后的预测值的置信度  # 标签anchor与预测anchor的iou值小于os小于阈值的预测值的置信度置0
    '''
    # 获取预测值的中心宽高坐标的中心点坐标、宽高、置信度
    pred_box_xy, pred_box_wh, pred_box_conf = y_pred[..., :2], y_pred[..., 2:4], y_pred[..., 4]
    # 生成候选框高宽网格矩阵 [batch_size,grid_h,grid_w,n_box,2] = [2,13,13,3,2]
    anchor_grid = _create_mesh_anchor(anchors, *y_pred.shape[:4])   # y_pred.shape为(2,13,13,3,15) 分10类
    true_wh = y_true[:,:,:,:,2:4]                                   # 获取某一尺度特征图所有像素点的所有anchor的相对于特征图的宽高的标签 t_w,t_h
    true_wh = anchor_grid * tf.exp(true_wh)                         # 将标签还原为相对于统一图的anchor的宽高 t_w,t_h -> bw,bh 形状[2,13,13,3,2]
    true_wh = true_wh * tf.expand_dims(y_true[:,:,:,:,4], 4)        # 增加一个维度形状[2,13,13,3,2,1]
    anchors_ = tf.constant(anchors, dtype='float', shape=[1,1,1,y_pred.shape[3],2])     # y_pred.shape[3]为候选框个数
    # anchors_ =[[[[[116.  90.]
    #               [156. 198.]
    #               [373. 326.]]]]]
    # 标签的中心宽高坐标转换为左上右下坐标
    true_xy = y_true[..., 0:2]                                      # 获取中心点
    true_wh_half = true_wh / 2.
    true_mins    = true_xy - true_wh_half                           # 计算起始坐标--左上角坐标 标签
    true_maxes   = true_xy + true_wh_half                           # 计算尾部坐标--右下角坐标 标签

    pred_xy = pred_box_xy                                           # 网络预测值的中心点坐标
    pred_wh = tf.exp(pred_box_wh) * anchors_                        # 将网络预测值的anchor宽高还原为相对于统一图的anchor的宽高
    # 网络预测的中心宽高坐标转换为左上右下坐标
    pred_wh_half = pred_wh / 2.
    pred_mins    = pred_xy - pred_wh_half                           # 计算起始坐标--左上角坐标 网络预测
    pred_maxes   = pred_xy + pred_wh_half                           # 计算尾部坐标--右下角坐标 网络预测
    # 计算预测anchor和标签anchor的重叠部分的左上右下坐标
    intersect_mins  = tf.maximum(pred_mins,  true_mins)             # 返回两个左上角坐标横坐标较大的值和纵坐标较大的值
    intersect_maxes = tf.minimum(pred_maxes, true_maxes)            # 返回两个右下角坐标横坐标较大的值和纵坐标较大的值

    # 计算计算预测anchor和标签anchor的重叠面积
    intersect_wh    = tf.maximum(intersect_maxes - intersect_mins, 0.) # 右下角坐标减去左上角坐标= w h
    intersect_areas = intersect_wh[..., 0] * intersect_wh[..., 1]      # 计算重叠面积

    true_areas = true_wh[..., 0] * true_wh[..., 1]                     # 标签anchor面积
    pred_areas = pred_wh[..., 0] * pred_wh[..., 1]                     # 预测anchor面积
    #   计算不重叠面积
    union_areas = pred_areas + true_areas - intersect_areas            # 标签anchor与预测anchor的并集面积
    best_ious  = tf.truediv(intersect_areas, union_areas)              # 计算标签anchor与预测anchor的iou值,相对于统一图的iou值
    #   ios小于阈值将作为负向的loss
    conf_delta = pred_box_conf * tf.cast(best_ious < ignore_thresh,tf.float32) # iou小于os小于阈值的预测值的置信度置0
    return conf_delta
def adjust_pred_tensor(y_pred): # 将网格信息融入坐标,置信度做sigmoid。并重新组合
    '''
    :param y_pred: yolov3的某一个特征的预测值
    :return:[相对于特征图大小的中心宽高坐标的中心点坐标,相对于特征图对应的anchor的宽高因子,置信度,分类值]
    (宽高因子需要转换才能得到相对于特征图的宽高,转换公式anchor_w*exp(w)和anchor_h*exp(h))
    '''
    grid_offset = _create_mesh_xy(*y_pred.shape[:4])  # 传递多参数,创建一个和y_pred.shape[:4]一样的维度的网格坐标y_pred.shape = [2,13,13,3,15]
    pred_xy    = grid_offset + tf.sigmoid(y_pred[..., :2])  # 计算该尺度矩阵上的坐标sigma(t_xy) + c_xy 即将预测的中心点坐标转换为相对于尺度特征图的中心点坐标
                                                            # 形状[2,13,13,3,2]
    pred_wh    = y_pred[..., 2:4]                           # 取出预测物体的尺寸t_wh     exp(t_wh)*anchors 才会的到相对于统一图的anchors 形状[2,13,13,3,2]
    pred_conf  = tf.sigmoid(y_pred[..., 4])                 # 对分类概率(置信度)做sigmoid转化 形状[2,13,13,3,1]
    pred_classes = y_pred[..., 5:]                          # 取出分类结果 形状[2,13,13,3,num_classes]
    # 按照最后一个维度 重新组合
    preds = tf.concat([pred_xy, pred_wh, tf.expand_dims(pred_conf, axis=-1), pred_classes], axis=-1)
    return preds
##########################辅助函数1#########################
# 辅助函数1
# 生成一个矩阵。每个格子里放有3个候选框(相对于统一图的自定义候选框)----生成候选框高宽网格矩阵
def _create_mesh_anchor(anchors, batch_size, grid_h, grid_w, n_box):
    '''

    :param anchors:某一特征图对应的相对于统一图的自定义的n_box候选框 此处是3
    :param batch_size:批次数
    :param grid_h:某一特征图的高
    :param grid_w:某一特征图的高
    :param n_box:特征图的像素点对应的anchor数量
    :return:候选框高宽网格矩阵 [batch_size,grid_h,grid_w,n_box,2]
    '''
    mesh_anchor = tf.tile(anchors, [batch_size*grid_h*grid_w]) # 将anchors复制 像素点个数*批次数倍
    mesh_anchor = tf.reshape(mesh_anchor, [batch_size, grid_h, grid_w, n_box, 2])  # 每个候选框有2个值 一个值是相对于统一图的自定义anchor的高,一个值是相对于统一图的自定义宽
    mesh_anchor = tf.cast(mesh_anchor, tf.float32)  # 修改数据类型为float32  用于计算
    return mesh_anchor

标签值为:
坐标:坐标为中心宽高坐标其中心点坐标是相对于特征图的坐标,宽高是anchor的宽高因子
置信度:0、1标志着此anchor是否有物体,在制作时依据阈值大小判定某一个像素点对应的anchor的置信度位置给0还是1
分类:onehot编码值
在这里插入图片描述

1、某一尺度对应的预测值的位置损失计算

位置损失 = [某一尺度特征图的标签值的置信度
(某一尺度特征图的预测值的中心宽高坐标
某一尺度特征图的标签的中心宽高坐标)
预测的anchor面积占统一图的面积比
位置损失权重]的平方

# 位置损失计算 为标签位置和预测值位置之差乘缩放比,所得的结果,再进行平方求和
def loss_coord_tensor(object_mask, pred_box, true_box, wh_scale, xywh_scale):
    '''
    :param object_mask:获取到某一尺度特征图的标签值的置信度  形状(2, 13, 13, 3, 1) 其值为  有物体为1 没物体为0
    :param pred_box:某一尺度特征图的预测值的中心宽高坐标
    :param true_box:某一尺度特征图的标签的中心宽高坐标
    :param wh_scale:预测的anchor面积占统一图的面积比
    :param xywh_scale:xywh_scale=1 这个是 位置损失权重
    :return: 位置损失
    '''
    xy_delta    = object_mask   * (pred_box-true_box) * wh_scale * xywh_scale
    loss_xy    = tf.reduce_sum(tf.square(xy_delta),       list(range(1,5)))   # 某一特征尺度下的所有anchor的位置损失的平方和
    return loss_xy
2、某一尺度对应的预测值的置信度损失计算
# 置信度损失
def loss_conf_tensor(object_mask, pred_box_conf, true_box_conf, obj_scale, noobj_scale, conf_delta):
    '''

    :param object_mask:获取到某一尺度特征图的标签值的置信度  例如形状(2, 13, 13, 3, 1) 其值为  有物体为1 没物体为0
    :param pred_box_conf:某一尺度特征图的预测值的置信度
    :param true_box_conf:某一尺度特征图的标签的置信度
    :param obj_scale:有物体的置信度损失权重
    :param noobj_scale:没有物体的置信度损失权重
    :param conf_delta:处理后的预测置信度,不合格的置信度预测值置0 (预测值)
    :return: 置信度损失
    '''
    object_mask_ = tf.squeeze(object_mask, axis=-1)
    # 该函数返回一个张量,这个张量是将原始input中所有维度为1的那些维都删掉的结果
    # axis可以用来指定要删掉的为1的维度,此处要注意指定的维度必须确保其是1,否则会报错
    conf_delta  = object_mask_ * (pred_box_conf-true_box_conf) * obj_scale + (1-object_mask_) * conf_delta * noobj_scale    # 置信度损失计算  有物体的置信度损失+没物体的置信度损失
    # 对于没有物体的anchors如果预测其有物体,conf_delta值就不为0,则损失就会变大
    loss_conf  = tf.reduce_sum(tf.square(conf_delta),     list(range(1,4)))     # 某一特征尺度下的所有anchor的置信度损失的平方和
    return loss_conf
3、某一尺度对应的预测值的分类损失计算
# 分类损失
def loss_class_tensor(object_mask, pred_box_class, true_box_class, class_scale):
    '''

    :param object_mask:获取到某一尺度特征图的标签值的置信度  形状(2, 13, 13, 3, 1) 其值为  有物体为1 没物体为0
    :param pred_box_class:某一尺度特征图的预测值的分类概率
    :param true_box_class:某一尺度特征图的标签值的分类概率
    :param class_scale:分类损失的权重
    :return:分类损失
    '''
    true_box_class_ = tf.cast(true_box_class, tf.int64) # 将分类标签值的数据类型修改为tf.int64
    class_delta = object_mask * \
                  tf.expand_dims(tf.nn.softmax_cross_entropy_with_logits_v2(labels=true_box_class_, logits=pred_box_class), 4) * \
                  class_scale

    loss_class = tf.reduce_sum(class_delta, list(range(1,5))) # 某一特征尺度下的所有anchor的分类损失的平方和
    return loss_class
4、某一尺度对应的预测值的总损失
# 某一个尺度特征的损失函数
def lossCalculator(y_true, y_pred, anchors,image_size):  # image_size【h,w】
    '''

    :param y_true: 某一尺度特征图对应的标签值
    :param y_pred:某一尺度特征图对应的预测值
    :param anchors:某一尺度特征图对应的anchors
    :param image_size:统一图的大小
    :return:某一个尺度特征的损失函数
    '''
    y_pred = tf.reshape(y_pred, y_true.shape)       # (2, 13, 13, 3, 45) # 将预测值形状调整到跟真实标签的形状一致 (本来就一致)

    object_mask = tf.expand_dims(y_true[..., 4], 4) # (2, 13, 13, 3, 1) # 获取到的是真实值(标签中的置信度)的置信度,并增加维度,便于计算
    preds = adjust_pred_tensor(y_pred)              # 返回[相对于特征图大小的中心宽高坐标的中心点坐标,anchor的宽高因子,置信度,分类值]

    conf_delta = conf_delta_tensor(y_true, preds, anchors, ignore_thresh)   # 将不合格的置信度预测值置0,返回处理后的置信度
    wh_scale =  wh_scale_tensor(y_true[..., 2:4], anchors, image_size)      # 返回预测的anchor面积占统一图的面积比

    loss_box = loss_coord_tensor(object_mask, preds[..., :4], y_true[..., :4], wh_scale, xywh_scale)    # 位置损失
    loss_conf = loss_conf_tensor(object_mask, preds[..., 4], y_true[..., 4], obj_scale, noobj_scale, conf_delta) # 置信度损失
    loss_class = loss_class_tensor(object_mask, preds[..., 5:], y_true[..., 5:], class_scale) # 分类损失
    loss = loss_box + loss_conf + loss_class  # 总损失
    return loss*grid_scale
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值