{"record":{"id":"a4d43905a431df09","repo":"roboflow/supervision","slug":"f1score-metric-requires-confidence-on-prediction","errorCode":null,"errorMessage":"F1Score metric requires `confidence` on predictions.","messagePattern":"F1Score metric requires `confidence` on predictions\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/metrics/f1_score.py","lineNumber":237,"sourceCode":"                if len(predictions) == 0:\n                    target_class_ids = np.asarray(targets.class_id, dtype=np.int32)[\n                        target_size_mask\n                    ]\n                    if len(target_class_ids) == 0:\n                        continue\n                    stats.append(\n                        (\n                            np.zeros((0, iou_thresholds.size), dtype=bool),\n                            np.zeros((0, iou_thresholds.size), dtype=bool),\n                            np.zeros((0,), dtype=np.float32),\n                            np.zeros((0,), dtype=int),\n                            target_class_ids,\n                        )\n                    )\n\n                else:\n                    if predictions.confidence is None:\n                        raise ValueError(\n                            \"F1Score metric requires `confidence` on predictions.\"\n                        )\n                    prediction_class_ids = np.asarray(\n                        predictions.class_id, dtype=np.int32\n                    )\n                    target_class_ids = np.asarray(targets.class_id, dtype=np.int32)\n                    prediction_confidence = np.asarray(\n                        predictions.confidence, dtype=np.float32\n                    )\n                    if self._metric_target == MetricTarget.BOXES:\n                        # BOXES target never yields CompactMask; narrow for mypy.\n                        iou = box_iou_batch(\n                            cast(npt.NDArray[np.number], target_contents),\n                            cast(npt.NDArray[np.number], prediction_contents),\n                        )\n                    elif self._metric_target == MetricTarget.MASKS:\n                        iou = mask_iou_batch(target_contents, prediction_contents)\n                    elif self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/metrics/f1_score.py#L219-L255","documentation":"Raised by F1Score.update() when an image has both predictions and targets, but the predictions carry no confidence array. Confidence is used to rank predictions when computing precision-recall curves and picking the operating point for F1. Without it the metric cannot order detections, so it refuses rather than returning misleading numbers.","triggerScenarios":"Calling F1Score().update() where len(predictions) > 0, len(targets) > 0, predictions.class_id is set, but predictions.confidence is None.","commonSituations":"Detections built from non-probabilistic sources (manual annotation, geometric detection) that omit scores; tracker outputs stripped of confidence; fixtures copied from examples that only set xyxy and class_id.","solutions":["Attach a confidence array to predictions: Detections(..., confidence=np.full(len(xyxy), 1.0, dtype=np.float32)) when no real score exists","If scores come from your model, propagate them instead of dropping them in post-processing","Filter out scoreless detections before evaluation when a dummy 1.0 confidence would distort results"],"exampleFix":"# before\npreds = sv.Detections(xyxy=boxes, class_id=ids)  # no confidence\nf1.update(targets=targets, predictions=preds)\n\n# after\npreds = sv.Detections(\n    xyxy=boxes,\n    class_id=ids,\n    confidence=np.full(len(boxes), 1.0, dtype=np.float32),\n)\nf1.update(targets=targets, predictions=preds)","handlingStrategy":"validation","validationCode":"if len(predictions) > 0 and len(targets) > 0 and predictions.confidence is None:\n    predictions = sv.Detections(\n        xyxy=predictions.xyxy,\n        class_id=predictions.class_id,\n        confidence=np.ones(len(predictions), dtype=np.float32),\n    )\nf1.update(targets=targets, predictions=predictions)","typeGuard":"def has_confidence(dets: sv.Detections) -> bool:\n    \"\"\"True when confidence is populated.\"\"\"\n    return dets.confidence is not None","tryCatchPattern":"try:\n    f1.update(targets=targets, predictions=predictions)\nexcept ValueError as e:\n    if 'confidence on predictions' in str(e):\n        predictions.confidence = np.ones(len(predictions), dtype=np.float32)\n    else:\n        raise","preventionTips":["Propagate model scores into Detections at construction time; never drop confidence in post-processing","For scoreless sources, set confidence=1.0 explicitly and document that PR ordering is degenerate"],"tags":["metrics","f1-score","validation","confidence"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}