1. 数据集预处理

将标签文件转为YOLO格式,即[x中心点坐标,y中心点坐标,w,h](都进行了归一化)
图片通过模型输出后,得到归一化并数据增强的图片信息
box预测为[x相对于网格左上角的偏移量,y相对于网格左上角的偏移量,w开根号,h开根号]

2. 获取正样本

  1. 将真实框标签信息乘上最后特征图的尺寸,将归一化的标签信息转为特征图上的信息
  2. 将真实框信息转换为[0,0,w,h],维度为[n,4],n代表图片中有几个目标(即有几个真实框)
  3. 将先验框信息转换为[0,0,w,h],维度为[9,4],9代表有9个先验框(三个特征层,每层三个先验框)
  4. 计算所有先验框和真实框的IOU,并找出IOU最大的先验框(返回先验框的索引),并找出该先验框属于哪个特征层;目标(真实框)所在的网格,最后正样本所在的位置即[[b, k, j, i],其中b代表该批次图片中的哪张图片,k代表第几个先验框,j代表目标所在网格的y坐标,i代表目标所在网格的x坐标。
  5. 将正样本的五个维度信息赋给代表正样本的变量(置信度设为1),负样本变量的正样本所在位置置为0,同时记录下当前目标相比当前层的比例系数当成loss的权重(即当前目标大小/特征层大小)

    3. 获取负样本

    image.png

  6. 将预测框信息转为x中心点坐标,y中心点坐标,YOLOV4预处理 - 图2YOLOV4预处理 - 图3

  7. 根据yolo格式进行解码,调整将维度转为(n,4),n代表预测框数量,即b313*13(13为当前特征层的宽高)
  8. 计算所有调整后先验框框和真实框的IOU,将IOU大于阈值的先验框所在位置设为正样本,其余全为负样本

    4. 计算loss

    obj_mask代表正样本掩膜,有目标的位置为True,其他为false,

  9. 计算预测框和正样本的IOU,并计算IOU损失

    1. def box_iou(self, b1, b2):
    2. """
    3. 输入格式:
    4. ----------
    5. b1: tensor, shape=(batch, anchor_num, feat_w, feat_h, 4), xywh
    6. b2: tensor, shape=(batch, anchor_num, feat_w, feat_h, 4), xywh
    7. 返回格式
    8. -------
    9. out: tensor, shape=(batch, anchor_num, feat_w, feat_h)
    10. """
    11. #计算预测框左上角和右下角坐标
    12. b1_xy = b1[..., :2]
    13. b1_wh = b1[..., 2:4]
    14. b1_wh_half = b1_wh / 2.
    15. b1_mins = b1_xy - b1_wh_half
    16. b1_maxes = b1_xy + b1_wh_half
    17. #计算真实框左上角和右下角坐标
    18. b2_xy = b2[..., :2]
    19. b2_wh = b2[..., 2:4]
    20. b2_wh_half = b2_wh / 2.
    21. b2_mins = b2_xy - b2_wh_half
    22. b2_maxes = b2_xy + b2_wh_half
    23. #家孙真实框和预测框的IOU
    24. intersect_mins = torch.max(b1_mins, b2_mins)
    25. intersect_maxes = torch.min(b1_maxes, b2_maxes)
    26. intersect_wh = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes))
    27. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
    28. b1_area = b1_wh[..., 0] * b1_wh[..., 1]
    29. b2_area = b2_wh[..., 0] * b2_wh[..., 1]
    30. union_area = b1_area + b2_area - intersect_area
    31. iou = intersect_area / torch.clamp(union_area,min = 1e-6)
    32. #计算两个框中心点距离
    33. center_wh = b1_xy - b2_xy
    34. #找到包裹两个框的最小框的左上角和右下角
    35. enclose_mins = torch.min(b1_mins, b2_mins)
    36. enclose_maxes = torch.max(b1_maxes, b2_maxes)
    37. enclose_wh = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes))
    38. if self.iou_type == 'ciou':
    39. #计算两个框框中心点欧氏距离
    40. center_distance = torch.sum(torch.pow(center_wh, 2), axis=-1)
    41. #计算包裹框对角线距离
    42. enclose_diagonal = torch.sum(torch.pow(enclose_wh, 2), axis=-1)
    43. ciou = iou - 1.0 * (center_distance) / torch.clamp(enclose_diagonal, min = 1e-6)
    44. #v 为长宽比惩罚项
    45. v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(b1_wh[..., 0] / torch.clamp(b1_wh[..., 1],min = 1e-6)) - torch.atan(b2_wh[..., 0] / torch.clamp(b2_wh[..., 1], min = 1e-6))), 2)
    46. alpha = v / torch.clamp((1.0 - iou + v), min = 1e-6)
    47. out = ciou - alpha * v
    48. elif self.iou_type == 'siou':
    49. #计算中心点距离
    50. sigma = torch.pow(torch.sum(torch.pow(center_wh, 2), axis=-1), 0.5)
    51. #求h和w方向上的sin比值
    52. sin_alpha_1 = torch.clamp(torch.abs(center_wh[..., 0]) / torch.clamp(sigma, min = 1e-6), min = 0, max = 1)
    53. sin_alpha_2 = torch.clamp(torch.abs(center_wh[..., 1]) / torch.clamp(sigma, min = 1e-6), min = 0, max = 1)
    54. #求门限,二分之根号二,0.707
    55. #如果门限大于0.707,代表某个方向的角度大于45度
    56. #此时求取另一个方向的角度
    57. threshold = pow(2, 0.5) / 2
    58. sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
    59. #alpha越接近45度,angel_cost越接近1,gamma越接近1
    60. #alpha越接近0度,angel_cost越接近0,gamma越接近2
    61. angle_cost = torch.cos(torch.asin(sin_alpha) * 2 - math.pi / 2)
    62. gamma = 2 - angle_cost
    63. #----------------------------------------------------#
    64. # Distance cost
    65. # 求中心与包裹框宽高的比值
    66. rho_x = (center_wh[..., 0] / torch.clamp(enclose_wh[..., 0], min = 1e-6)) ** 2
    67. rho_y = (center_wh[..., 1] / torch.clamp(enclose_wh[..., 1], min = 1e-6)) ** 2
    68. distance_cost = 2 - torch.exp(-gamma * rho_x) - torch.exp(-gamma * rho_y)
    69. #----------------------------------------------------#
    70. # Shape cost
    71. # 真实框和预测框的宽高差与最大值的比值
    72. # 差异越小,costshape_cost越小
    73. omiga_w = torch.abs(b1_wh[..., 0] - b2_wh[..., 0]) / torch.clamp(torch.max(b1_wh[..., 0], b2_wh[..., 0]), min = 1e-6)
    74. omiga_h = torch.abs(b1_wh[..., 1] - b2_wh[..., 1]) / torch.clamp(torch.max(b1_wh[..., 1], b2_wh[..., 1]), min = 1e-6)
    75. shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
    76. out = iou - 0.5 * (distance_cost + shape_cost)
    77. return out
  10. 类别损失采用二值交叉熵损失函数(类别分类相对于二分类,分类当前目标是否为当前类别)

2fd7e1221064c1e2ff414f607a06de4e_v2-3c3b487d11e931f6db5db863dba23e38_720w.png

  1. torch.mean(self.BCELoss(pred_cls[obj_mask], y_true[..., 5:][obj_mask]))
  1. 置信度损失采用focal_loss

YOLOV4预处理 - 图5

  1. #正样本是
  2. pos_neg_ratio = torch.where(obj_mask, torch.ones_like(conf) * self.alpha, torch.ones_like(conf) * (1 - self.alpha))
  3. hard_easy_ratio = torch.where(obj_mask, torch.ones_like(conf) - conf, conf) ** self.gamma
  4. loss_conf = torch.mean((self.BCELoss(conf, obj_mask.type_as(conf)) * pos_neg_ratio * hard_easy_ratio)[noobj_mask.bool() | obj_mask]) * self.focal_loss_ratio