0


yolov7损失函数源码解析(一句一句解析,)

  1. 下面是函数 find_3_positive的详细解析。
  1. 本博客bs2,即batch_size=2,采用的数据集不同于coco85类,我们采用10类。
  2. 函数find_3_positive的参数解析:
  3. 参数p为模型输出的预测值,有3层,size=[(bs,3,20,20,15),(bs,3,40,40,15),(bs,3,80,80,85)], targetssize=(nt, 6)为(标注的数量,6),如右边所示6的含义(nt, image_index + cls_id + x,y,w,h)
  4. def find_3_positive(self, p, targets)
  5. #######na为anchors的数量,nt为标注的数量,例 na=3,nt=156
  6. na, nt = self.na, targets.shape[0]
  7. indices, anch = [], []
  8. #######归一化网格空间(normalized to gridspace gain)
  9. gain = torch.ones(7, device=targets.device).long()
  10. #######得到维度(3,156),即(na,nt),ai具体样子[[0,0...,0],[1,1..,1],[2,2...,2]]。
  11. ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt)
  12. ########按照第二个维度进行堆叠,这里向最后一个维度添加了anchor索引,得到的targets维度(3,156,7),(nt, image_index + cls_id + bbox + anchor_index)。这里使用repeat函数使输入进来的targets(156,6)重复了na=3次。
  13. targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2)
  14. g = 0.5
  15. ########这里使用的是五个点,yolov7采用的是预测框的中心点落入那个grid里,使用该grid和周围的上下左右总共五个中的三个grid的anchor框来进行回归。主要看中心点的x落在左上角还是右下角。需要注意的是在这个函数里面是针对周围五个grid,而后面的函数可知,每一个中心点只能利用相邻的两个grid。
  16. off = torch.tensor([[0, 0],
  17. [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m
  18. # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm
  19. ], device=targets.device).float() * g #0ff维度(5,2)
  20. for i in range(self.nl):
  21. anchors = self.anchors[i] # anchors的维度(3,2)
  22. #######这里当i=0的时候将gain的[2:6]对应的位置换位80(第一层特征层大小)。p[0].shape = 2,3,80,80,15,根据下标索引得到[80,80,80,80]
  23. gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]
  24. #######由于标签是0-1之间,所以对应位置乘以特征图尺寸。形状还是(na,nt, image_index + cls_id + bbox + anchor_index),例如(3,156,7)
  25. t = targets * gain
  26. if nt:
  27. #######用gt框的wh除anchor的wh,结果的维度r=(na,nt,wh),例如(3,156,2)。
  28. r = t[:, :, 4:6] / anchors[:, None] # wh的比率。
  29. #######这里hyp['anchor_t']=4,j是为了筛选比值大于4的,即正样本为:预测框wh/anchor wh<4.0的,同时比较r和1/r选择最大。j的维度为(na,nt),例如(3,156)。
  30. j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t']
  31. # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']
  32. # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))
  33. t = t[j] # 从3*156个gt框过滤出220个gt框(filter_nt,image_index + cls_id + bbox + anchor_index)。例如过滤出220个gt框(220,7)。
  34. #######筛选得到gt框的xy,维度为(filter_nt,xy),例如(220,2)
  35. gxy = t[:, 2:4] # grid xy
  36. #######用对应特征层尺度减去经过筛选得到gt框的xy,比如用80去减。这里得到维度为:(220,2)。
  37. gxi = gain[[2, 3]] - gxy # inverse
  38. #######把相对于各个网格左上角x<0.5,y<0.5和相对于右下角的x<0.5,y<0.5的框提取出来,也就是j,k,l,m。这里.T就是转置。这里
  39. j, k = ((gxy % 1. < g) & (gxy > 1.)).T
  40. l, m = ((gxi % 1. < g) & (gxi > 1.)).T
  41. #######这里j经过堆叠之后维度(5,filter_nt)。这里的例子(5,220)
  42. j = torch.stack((torch.ones_like(j), j, k, l, m))
  43. t = t.repeat((5, 1, 1))[j] #和上面的过滤220框一样。从(5,220,7)过滤出(657,7)个gt框(相比于上面多了457个gt框,这得益与扩充),而位置索引(5,220)。这里要注意的是首先使用repeat函数使目标扩充五倍,先使用ones_like(j)将五个维度中的第一个维度全部索引出来,那就是220个框,加上j,k,l,m四个维度索引,理应2*220=440个框,总共能筛选660个框,但由于计算问题,掉了13个gt框,这里有待改进。这里将再次筛选的gt框数量计为final_filter_nt,于是得到t的维度(final_filter_nt,image_index + cls_id + bbox + anchor_index)。
  44. #######得到中心点掉入某gird,该网格相对于周围五个框的偏移量。
  45. offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]
  46. else:
  47. t = targets[0]
  48. offsets = 0
  49. #######得到image_index(batch_size为2的话就是0或者1) 和 cls_id。两个维度都是(final_filter_nt)。例如维度(648,)。这里.long()可以取整。
  50. b, c = t[:, :2].long().T # image, class
  51. #######得到gt框中心点xy坐标。两个维度都是(final_filter_nt,xy)。例如我的例子中维度为(645,2)。
  52. gxy = t[:, 2:4] # grid xy (648,2)
  53. #######同理
  54. gwh = t[:, 4:6] # grid wh (648,2)
  55. #######用xy坐标减去offset,这样做的目的是将本grid的坐标点,分别映射到周围五个grid的对应位置。这里.long是取整,得到各个网格坐标。
  56. gij = (gxy - offsets).long()
  57. #######将最后一个维度的xy拆散,并且利用转置,将x和y进行分开。
  58. gi, gj = gij.T # grid xy indices
  59. # Append index(4,645)image class ,y , x
  60. #######得到anchor_index(这里是0,1,2),维度(final_filter_nt),例如(648,)。
  61. a = t[:, 6].long() # anchor indices
  62. #######将b(image_index)和a(anchor_index),还有gj(表示y)和gi(表示x)进行堆叠。这里clamp_(0, gain[3] - 1)函数表示将输入input张量每个元素的值压缩到区间 [min,max](这里就是(0,79)),并返回结果到一个新张量,由于函数加了_,所以在旧张量上面进行修改,不用返回一个新张量。返回后的维度为:(image_index+anchor_index+yx(grid),final_filter_nt)。本例为(4,648,)。
  63. indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))
  64. #######对anchors(形状为(na,2)),以a(表现为anchor_index,他表示每个gt框的anchor索引)为索引,找到每个gt框所对应的anchor。最后输出维度为:(final_filter_nt,anchors),例如(645,2)。
  65. anch.append(anchors[a]) # anchors
  66. ########对上述最后两步的 indices(image_index+anchor_index+yx(grid),final_filter_nt)和anch(final_filter_nt,anchors)进行返回。
  67. return indices, anch
  1. def build_targets(self, p, targets, imgs):
  2. #indices, anch = self.find_positive(p, targets)
  3. ###上述函数的返回值。indices为(image_index+anchor_index+yx,final_filter_nt)和anch(final_filter_nt,anchors),这里yx是grid网格的坐标。
  4. indices, anch = self.find_3_positive(p, targets)
  5. #indices, anch = self.find_4_positive(p, targets)
  6. #indices, anch = self.find_5_positive(p, targets)
  7. #indices, anch = self.find_9_positive(p, targets)
  8. matching_bs = [[] for pp in p]
  9. matching_as = [[] for pp in p]
  10. matching_gjs = [[] for pp in p]
  11. matching_gis = [[] for pp in p]
  12. matching_targets = [[] for pp in p]
  13. matching_anchs = [[] for pp in p]
  14. nl = len(p) # nl(number layer)= 3,预测特征层的层数。
  15. ###这里batch_size=2,所以batch_idx的范围为0,1。
  16. for batch_idx in range(p[0].shape[0]):
  17. ###判断是否image_index是否为0,返回维度(gt,),例如 (156,)。
  18. b_idx = targets[:, 0]==batch_idx
  19. ###根据b_idx进行筛选,得到图片0(这里batch_size=2,就图片0和1)的目标框数量,图片0的维度是(也就是this_target),(gt,image_index+class+bbox),这里是(93,6),那么后面图片1就是(63,6)。
  20. this_target = targets[b_idx] #(93,6)
  21. ###判断该图片是不是没有目标框
  22. if this_target.shape[0] == 0:
  23. continue
  24. ###这里对gt框的xywh都乘以80来得到,gt框的xywh。
  25. txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1]
  26. ###通过函数xywh2xyxy,得到gt框左上角和右下角的xy。
  27. txyxy = xywh2xyxy(txywh)
  28. pxyxys = []
  29. p_cls = []
  30. p_obj = []
  31. from_which_layer = []
  32. all_b = []
  33. all_a = []
  34. all_gj = []
  35. all_gi = []
  36. all_anch = []
  37. ###预测层p的维度为:(batch_size,3,80,80,15),(batch_size,3,40,40,15),(batch_size,3,20,20,15)。
  38. for i, pi in enumerate(p):
  39. ###将函数的返回值进行赋值b,a,gj,gi的维度(645,)
  40. b, a, gj, gi = indices[i]
  41. ###这里的b也就是image_index,如果是图片0就True,否则False。得到维度为(648,)。
  42. idx = (b == batch_idx)
  43. ###筛选得到图片0里面的b,a,gj,gi值,也就是image_index,anchor_index,和x,y他们四个维度,他们都是(273,)。
  44. b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx]
  45. all_b.append(b)
  46. all_a.append(a)
  47. all_gj.append(gj)
  48. all_gi.append(gi)
  49. ###对anchor框进行筛选,得到(273,2)个ancho框。这也是图片0所对应的anchor数量。
  50. all_anch.append(anch[i][idx])
  51. from_which_layer.append(torch.ones(size=(len(b),)) * i)
  52. ###将find_3_positive函数粗筛选后的[b,a,gj,gi]与预测层结果对应的点进行匹配。原本有640个预测点,有273个点被索引出,需要注意的是这里是图片0的。
  53. fg_pred = pi[b, a, gj, gi]
  54. p_obj.append(fg_pred[:, 4:5]) #得到obj的预测值
  55. p_cls.append(fg_pred[:, 5:]) #得到cls的预测值
  56. ###将gi和gj在第一维度进行堆叠,这里gi和gj分别是x和y,得到有gt框中心点分别落在那个位置,最后得到的维度(273,2),这里也是图片0里面gt框的中心点。
  57. grid = torch.stack([gi, gj], dim=1)
  58. ###通过下面的公式得到预测层里的预测框的xy。
  59. pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i]
  60. #pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i]
  61. ###通过下面的公式得到预测层里的预测框的xy。这里维度为(273,2)
  62. pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #/ 8. 预测的wh值
  63. ####将xywh值进行堆叠,他的维度变化为(273,4)。
  64. pxywh = torch.cat([pxy, pwh], dim=-1)
  65. ####将wywh转换成,预测框左上角和右下角坐标。
  66. pxyxy = xywh2xyxy(pxywh)
  67. pxyxys.append(pxyxy)
  1. ###将本张图片的所有正样本合并。shape=(P,4)
  2. pxyxys = torch.cat(pxyxys, dim=0)
  3. if pxyxys.shape[0] == 0:
  4. continue
  5. p_obj = torch.cat(p_obj, dim=0)
  6. p_cls = torch.cat(p_cls, dim=0)
  7. from_which_layer = torch.cat(from_which_layer, dim=0)
  8. all_b = torch.cat(all_b, dim=0)
  9. all_a = torch.cat(all_a, dim=0)
  10. all_gj = torch.cat(all_gj, dim=0)
  11. all_gi = torch.cat(all_gi, dim=0)
  12. all_anch = torch.cat(all_anch, dim=0

本文转载自: https://blog.csdn.net/clhmliu/article/details/128025315
版权归原作者 clhmliu 所有, 如有侵权,请联系我们删除。

“yolov7损失函数源码解析(一句一句解析,)”的评论:

还没有评论