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>
- utils/metrics.py +2 -2
- val.py +12 -11
@@ -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(
|
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(
|
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:
|
@@ -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 |
-
|
83 |
-
|
84 |
-
|
85 |
-
if x[0].shape[0]
|
86 |
-
matches =
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
|
|
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(
|
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 |
|