why do Recall / F1Score use a different matching algorithm than ConfusionMatrix
#2376
Replies: 1 comment
|
This doesn’t look intentional. For this repro, the expected matching at IoU That gives The mismatch comes from the older “sort by IoU, then dedupe predictions, then dedupe targets” style of matching. In your matrix it sees The This would be a good regression test case. Something like your repro reduced to just the two-box IoU setup should assert: assert len(tp) == 2
assert len(fp) == 0
assert len(fn) == 0
assert float(Recall().update(preds, gts).compute().recall_at_50) == 1.0
assert float(F1Score().update(preds, gts).compute().f1_50) == 1.0If current If my answer solved your problem, you can click answered the question. I'm really here to help, and along the way I'm also collecting Galaxy Brain badges haha 😆 |
Uh oh!
There was an error while loading. Please reload this page.
Hi,
I've been reading through (and trying to learn) the metrics module to learn how it works, and I noticed something I can't fully explain. On the same input, the matcher used inside
ConfusionMatrix.evaluate_detection_batchand_split_detections_by_outcomegives me a different TP count than the matcher used insideRecall._match_detection_batch,F1Score._match_detection_batchandMeanAverageRecall._match_detection_batch.Wanted to ask the maintainers if this is intentional before I assume anything.
Minimal repro (uses the COCO val image of two cats just so the example sits on a real image, no model needed):
Output I'm seeing:
The IoU matrix for these boxes is
[[1.000, 0.667], [0.333, 0.538]]. At threshold 0.5 there's exactly one valid pairing (T0 with P0, T1 with P1) which I think should give 2 TPs, but the metric classes seem to be returning 1.Reading the code on
develop, here's what I'm finding:One style of matcher, which uses
np.lexsortplus a greedy loop that tracks which targets/predictions are already taken:_split_detections_by_outcome(metrics/detection.py:196-340,np.lexsortat line 310)ConfusionMatrix.evaluate_detection_batch(metrics/detection.py:832-994,np.lexsortat line 955)Another style, which sorts the candidate matches by IoU and then deduplicates with two
np.uniquecalls:Recall._match_detection_batch(lines 311 and 312)F1Score._match_detection_batch(lines 357 and 358)MeanAverageRecall._match_detection_batch(lines 540 and 541)MeanAveragePrecision._match_detection_batch(lines 1524 and 1525)ConfusionMatrix._drop_extra_matches(looks like it isn't called anymore but I wasn't sure)I get that the
np.uniquestyle is what YOLOv5'sprocess_batchand thekaanakanupstream do (with a documented caveat in kaanakan#21 and yolov5#12673 about it dropping valid matches in some overlap patterns), so I assume that's where it came from originally. What I'm trying to figure out is why the two styles now sit side by side in the same module.A few things I wanted to ask:
np.uniquestyle being kept on purpose inRecall/F1Score/MeanAverageRecallfor parity with YOLOv5, whileConfusionMatrix.evaluate_detection_batchand_split_detections_by_outcomewere intentionally moved to the other style?np.lexsortnot meant to stop at those two functions, and the sibling classes just haven't been updated yet?np.lexsortstyle out into a single helper and points the other matchers at it?Happy to send the PR if (2) is what you'd want. Thanks for reading and apologies if I'm missing context here.
All reactions