Anton Lebedev glenn-jocher pre-commit-ci[bot] commited on
Commit
43569d5
β€’
1 Parent(s): f43cd53

Bug fix mAP0.5-0.95 (#6787)

Browse files

* Improve mAP0.5-0.95

Two changes provided
1. Added limit on the maximum number of detections for each image likewise pycocotools
2. Rework process_batch function

Changes #2 solved issue #4251
I also independently encountered the problem described in issue #4251 that the values for the same thresholds do not match when changing the limits in the torch.linspace function.
These changes solve this problem.

Currently during validation yolov5x.pt model the following results were obtained:
from yolov5 validation
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 157/157 [01:07<00:00, 2.33it/s]
all 5000 36335 0.743 0.626 0.682 0.506
from pycocotools
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.505
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.685

These results are very close, although not completely pass the competition issue #2258.
I think it's problem with false positive bboxes matched ignored criteria, but this is not actual for custom datasets and does not require an additional solution.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove line to retain pycocotools results

* Update val.py

* Update val.py

* Remove to device op

* Higher precision int conversion

* Update val.py

Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

Files changed (2) hide show
  1. utils/metrics.py +2 -2
  2. val.py +12 -11
utils/metrics.py CHANGED
@@ -90,7 +90,7 @@ def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names
90
  p, r, f1 = p[:, i], r[:, i], f1[:, i]
91
  tp = (r * nt).round() # true positives
92
  fp = (tp / (p + eps) - tp).round() # false positives
93
- return tp, fp, p, r, f1, ap, unique_classes.astype('int32')
94
 
95
 
96
  def compute_ap(recall, precision):
@@ -156,7 +156,7 @@ class ConfusionMatrix:
156
  matches = np.zeros((0, 3))
157
 
158
  n = matches.shape[0] > 0
159
- m0, m1, _ = matches.transpose().astype(np.int16)
160
  for i, gc in enumerate(gt_classes):
161
  j = m0 == i
162
  if n and sum(j) == 1:
 
90
  p, r, f1 = p[:, i], r[:, i], f1[:, i]
91
  tp = (r * nt).round() # true positives
92
  fp = (tp / (p + eps) - tp).round() # false positives
93
+ return tp, fp, p, r, f1, ap, unique_classes.astype(int)
94
 
95
 
96
  def compute_ap(recall, precision):
 
156
  matches = np.zeros((0, 3))
157
 
158
  n = matches.shape[0] > 0
159
+ m0, m1, _ = matches.transpose().astype(int)
160
  for i, gc in enumerate(gt_classes):
161
  j = m0 == i
162
  if n and sum(j) == 1:
val.py CHANGED
@@ -79,16 +79,17 @@ def process_batch(detections, labels, iouv):
79
  """
80
  correct = torch.zeros(detections.shape[0], iouv.shape[0], dtype=torch.bool, device=iouv.device)
81
  iou = box_iou(labels[:, 1:], detections[:, :4])
82
- x = torch.where((iou >= iouv[0]) & (labels[:, 0:1] == detections[:, 5])) # IoU above threshold and classes match
83
- if x[0].shape[0]:
84
- matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detection, iou]
85
- if x[0].shape[0] > 1:
86
- matches = matches[matches[:, 2].argsort()[::-1]]
87
- matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
88
- # matches = matches[matches[:, 2].argsort()[::-1]]
89
- matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
90
- matches = torch.from_numpy(matches).to(iouv.device)
91
- correct[matches[:, 1].long()] = matches[:, 2:3] >= iouv
 
92
  return correct
93
 
94
 
@@ -265,7 +266,7 @@ def run(
265
  tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)
266
  ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
267
  mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
268
- nt = np.bincount(stats[3].astype(np.int64), minlength=nc) # number of targets per class
269
  else:
270
  nt = torch.zeros(1)
271
 
 
79
  """
80
  correct = torch.zeros(detections.shape[0], iouv.shape[0], dtype=torch.bool, device=iouv.device)
81
  iou = box_iou(labels[:, 1:], detections[:, :4])
82
+ correct_class = labels[:, 0:1] == detections[:, 5]
83
+ for i in range(len(iouv)):
84
+ x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match
85
+ if x[0].shape[0]:
86
+ matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou]
87
+ if x[0].shape[0] > 1:
88
+ matches = matches[matches[:, 2].argsort()[::-1]]
89
+ matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
90
+ # matches = matches[matches[:, 2].argsort()[::-1]]
91
+ matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
92
+ correct[matches[:, 1].astype(int), i] = True
93
  return correct
94
 
95
 
 
266
  tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)
267
  ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
268
  mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
269
+ nt = np.bincount(stats[3].astype(int), minlength=nc) # number of targets per class
270
  else:
271
  nt = torch.zeros(1)
272