首页
/ supervision Legacy 评估 API 详解:ConfusionMatrix 与 MeanAveragePrecision 的原理、用法与迁移指南

supervision Legacy 评估 API 详解:ConfusionMatrix 与 MeanAveragePrecision 的原理、用法与迁移指南

2026-09-05 12:01:28作者:乔或婵

本篇聚焦 supervision 文档中的 Legacy Metrics(遗留评估 API),即 src/supervision/metrics/detection.py 中实现的 ConfusionMatrixMeanAveragePrecision 两个对象检测评估类。文章覆盖二者的完整属性、构建方式、贪心匹配与 COCO 101 点插值等底层实现细节,以及自 0.23.0 起引入的新 metrics 模块与遗留 API 的关系和迁移路径。读完后可独立完成:安装 metrics 依赖、用 from_detections / from_tensors / benchmark 三种入口计算混淆矩阵与 mAP,理解 TP/FP/FN 归属逻辑,并判断何时应改用新版指标模块。

Legacy Metrics 的定位与安装

自 supervision 0.23.0 起,项目引入了全新的 metrics 模块(src/supervision/metrics/init.py 导出的 MeanAveragePrecisionF1ScorePrecisionRecallMeanAverageRecall 等)。sv.ConfusionMatrix 和顶层 sv.MeanAveragePrecision(来自 supervision.metrics.detection)则属于遗留评估 API,文档明确说明其"will be deprecated in the future"(见 docs/detection/metrics.md)。

使用本页 API 前,需要安装 metrics 可选依赖:

pip install "supervision[metrics]"

pyproject.toml 可以看到,该 extra 目前只额外安装了 pandas>=2。这两个类通过 src/supervision/init.py 暴露在顶层命名空间,因此可直接以 sv.ConfusionMatrixsv.MeanAveragePrecision 的方式引用。

需要注意两者的废弃状态并不相同(以当前仓库源码为准,当前开发版本为 0.31.0.dev0):

废弃标记(源码事实)
sv.ConfusionMatrix 未加 @deprecated_class 装饰器,但被文档归为 legacy API
sv.MeanAveragePrecision @deprecated_class(deprecated_in="0.27.0", remove_in="0.31.0"),且 docstring 明确提示其结果与 pycocotools 不一致,推荐使用 supervision.metrics.mean_average_precision.MeanAveragePrecision

ConfusionMatrix:按类统计 TP / FP / FN

ConfusionMatrix 是一个 dataclass,定义于 src/supervision/metrics/detection.py,用于对象检测任务的混淆矩阵统计。其核心属性如下:

属性 类型 说明
matrix np.ndarray[np.int32] 形状为 (len(classes) + 1, len(classes) + 1) 的二维矩阵,最后多出的行列用于汇总 FP / FN
classes list[str] 模型类别名列表
conf_threshold float 置信度阈值(0~1),低于该值的预测不计入矩阵
iou_threshold float IoU 阈值(0~1),低于该值的预测-真值对不会被匹配,预测记为 FP
metric_target MetricTarget IoU 计算所用坐标类型:BOXES(默认)或 ORIENTED_BOUNDING_BOXESMASKS 不受支持

MetricTarget 枚举定义在 src/supervision/metrics/core.py,取值 BOXES(xyxy 框)、MASKS(掩码)、ORIENTED_BOUNDING_BOXES(OBB 旋转框)。ConfusionMatrix 仅支持前两者之外的 BOXES 与 OBB 两种——传入 MetricTarget.MASKS 会抛出 ValueError(见 detection.py 中的 _assert_supported_target)。

三种构建方式

1. from_detections:从 sv.Detections 列表构建

import numpy as np
import supervision as sv

targets = [
    sv.Detections(
        xyxy=np.array([[0, 0, 10, 10], [50, 50, 60, 60]]),
        class_id=np.array([0, 0]),
    )
]
predictions = [
    sv.Detections(
        xyxy=np.array([[0, 0, 10, 10], [100, 100, 110, 110]]),
        class_id=np.array([0, 0]),
        confidence=np.array([0.9, 0.8]),
    )
]

confusion_matrix = sv.ConfusionMatrix.from_detections(
    predictions=predictions,
    targets=targets,
    classes=["person"],
    conf_threshold=0.3,   # 默认值 0.3
    iou_threshold=0.5,   # 默认值 0.5
)
print(confusion_matrix.matrix)
# array([[1, 1],
#        [1, 0]], dtype=int32)

from_detections 内部先把每组 Detections 转换为张量再调用 from_tensors。转换规则由 detections_to_tensor 实现(src/supervision/metrics/detection.py):

  • MetricTarget.BOXES:预测张量行格式 (x_min, y_min, x_max, y_max, class_id, confidence),即 (M, 6);真值无 confidence,为 (N, 5)
  • MetricTarget.ORIENTED_BOUNDING_BOXES:要求 detections.data[ORIENTED_BOX_COORDINATES] 中存有 float32 的 OBB 坐标,形状 (N, 8)(扁平)或 (N, 4, 2)sv.Detections.from_ultralytics 的存储形式),内部统一规整为 (N, 8);对应张量行为 (x1, y1, x2, y2, x3, y3, x4, y4, class_id [, confidence]),即预测 (M, 10)、真值 (N, 9)
  • class_idNone 会报错;with_confidence=TrueconfidenceNone 也会报错。

2. from_tensors:直接从 numpy 张量列表构建

import numpy as np
import supervision as sv

targets = [
    np.array([
        [0.0, 0.0, 3.0, 3.0, 0],
        [2.0, 2.0, 5.0, 5.0, 0],
        [6.0, 1.0, 8.0, 3.0, 1],
    ])
]
predictions = [
    np.array([
        [0.0, 0.0, 3.0, 3.0, 0, 0.9],
        [0.1, 0.1, 3.0, 3.0, 0, 0.9],
        [6.0, 1.0, 8.0, 3.0, 1, 0.8],
    ])
]

confusion_matrix = sv.ConfusionMatrix.from_tensors(
    predictions=predictions,
    targets=targets,
    classes=["person", "dog"],
)
print(confusion_matrix.matrix)
# array([[1, 0, 1],
#        [0, 1, 0],
#        [1, 0, 0]], dtype=int32)

_validate_input_tensors 会校验:预测与真值列表长度一致、元素必须是 numpy 数组、列数符合 metric_target 的期望(BOXES 为 6/5 列,OBB 为 10/9 列)。

3. benchmark:数据集 + 回调函数一步到位

import supervision as sv

dataset = sv.DetectionDataset.from_yolo(
    images_directory_path=".../test/images",
    annotations_directory_path=".../test/labels",
    data_yaml_path=".../data.yaml",
)

def callback(image: np.ndarray) -> sv.Detections:
    return model.predict(image[:, :, ::-1])

confusion_matrix = sv.ConfusionMatrix.benchmark(
    dataset=dataset,
    callback=callback,
    conf_threshold=0.3,
    iou_threshold=0.5,
    save_directory_path="./results",  # 可选
)
print(confusion_matrix.matrix)

benchmark 遍历 DetectionDataset(每轮产出 image_name, image, annotation),调用 callback 得到预测后汇总。可选参数 save_directory_path(关键参数,仅 benchmark 支持)会在该目录中为每张图写出一张 2x2 结果拼图,按原图文件名直接落盘:四个面板分别为 Ground TruthTrue PositivesFalse PositivesFalse Negatives。从源码看(_save_detection_validation_visualizationsrc/supervision/metrics/detection.py),该拼图通过 _split_detections_by_outcome 复用与 evaluate_detection_batch 相同的匹配逻辑划分 TP/FP/FN,并用 BoxAnnotator / LabelAnnotator 按类别着色绘制;若目录中已存在同名文件会发出 UserWarning 后覆盖。完整的基准测试工作流可参考 docs/how_to/benchmark_a_model.md

匹配算法:TP / FP / FN 如何归属

单张图的矩阵累加由静态方法 evaluate_detection_batch 完成,流程可从源码逐段印证:

  1. 形状校验:预测 (M, 6)(或 OBB 下 (M, 10)),真值 (N, 5)(或 (N, 9))。
  2. 置信度过滤predictions[confidence >= conf_threshold] 留下参与匹配的预测。
  3. 边界短路:无有效预测时,所有真值计入 matrix[gt_class, num_classes](FN 汇总列);真值为空时,所有有效预测计入 matrix[num_classes, det_class](FP 汇总行)。
  4. IoU 矩阵:BOXES 用 box_iou_batch,OBB 用 oriented_box_iou_batch(均来自 src/supervision/detection/utils/iou_and_nms.py)。
  5. 贪心匹配:取所有 iou > iou_threshold 的候选对,用 np.lexsort 按"同类优先、IoU 降序"排序后逐一贪心分配,每个真值与每个预测最多匹配一次
  6. 跨类空间匹配的特殊处理:两个框空间重叠但类别不同时,matrix[gt_class, det_class] += 1——即该预测对目标类别是 FP(错检),对预测类别是 FN(漏检),同一笔错检同时体现在两个位置。
  7. 汇总:未匹配真值累加到 FN 列,未匹配预测累加到 FP 行。

矩阵语义因此是:matrix[i, j]i != j 且均在类索引范围内)= 真值为类 i 但被预测成类 j 的数量;对角线 = TP;最后一列 = 各类 FN;最后一行 = 各类 FP。

plot:热力图可视化

fig = confusion_matrix.plot(
    save_path=None,          # 给路径则保存为 250 dpi 透明背景 PNG
    title="Corgi benchmark", # 可选标题
    classes=None,            # 自定义显示类别,None 则显示全部
    normalize=False,         # True 时按列归一化
    fig_size=(12, 10),      # 画布尺寸
)

实现细节(plot 方法):矩阵先转 float64normalize=True 时按列求和归一化;小于 0.005 的单元格置为 NaN 以隐藏噪点;坐标轴刻度默认显示类名并追加 FN / FP 两个汇总刻度;格子数少于 30 个时会在每个单元格内标注数值,颜色随数值大小在黑/白之间切换。

MeanAveragePrecision(遗留版):mAP@50:95 的计算

sv.MeanAveragePrecision(frozen dataclass,定义于 src/supervision/metrics/detection.py)的四个属性为:

属性 含义
map50_95 IoU 阈值 0.50~0.95(步长 0.05)十个档位上的 mAP 均值
map50 仅 IoU = 0.50 时的 mAP
map75 仅 IoU = 0.75 时的 mAP
per_class_ap50_95 每个类在 10 个 IoU 档位上的 AP 数组,形状 (num_classes, 10)

再次强调(源码 docstring 中的废弃提示):该实现自 0.27.0 起被标记 deprecated,计划于 0.31.0 移除;官方理由是"deprecated implementation provides results that are inconsistent with pycocotools",建议改用新版 supervision.metrics.mean_average_precision.MeanAveragePrecision(该新实现与 pycocotools 结果一致)。如果你的目标是与 COCO 评测对齐,请优先走新模块;下述内容用于理解遗留实现本身及已有代码。

计算入口

import supervision as sv

# 方式一:从 Detections 列表
mAP = sv.MeanAveragePrecision.from_detections(
    predictions=predictions_list,  # list[sv.Detections]
    targets=targets_list,          # list[sv.Detections]
)

# 方式二:从张量列表(每图 (M,6) / (N,5))
mAP = sv.MeanAveragePrecision.from_tensors(
    predictions=prediction_tensors,
    targets=target_tensors,
)

# 方式三:数据集 + 回调
mAP = sv.MeanAveragePrecision.benchmark(
    dataset=dataset,
    callback=callback,
)

print(mAP.map50_95, mAP.map50, mAP.map75)

一个最小示例:单图单框完全重合且类别一致时,map501.0;若真值为空的背景图上存在预测,这些预测全部计为 FP,会压低 AP(背景图语义在 from_tensors 的 docstring 中有明确说明)。

实现原理:IoU 档位、贪心匹配与 101 点插值

from_tensors 的核心计算链可以从源码拆解为三步:

  1. 多档 IoU 匹配_match_detection_batch):IoU 档位为 np.linspace(0.5, 0.95, 10),即 [0.50, 0.55, ..., 0.95] 共 10 档。对每一档,用 box_iou_batch 算整图 IoU 矩阵,要求 iou >= 档位值 且类别一致,再经 _greedy_match(来自 src/supervision/metrics/utils/matching.py)保证每个真值与预测各只匹配一次。最终得到每图每档的 TP 布尔矩阵。
  2. 按置信度排序累计 P/R_average_precisions_per_class):所有图的匹配结果、预测置信度与类别被拼接后,按预测置信度全局降序排列;对每个类分别累加 true_positives / false_positives,得到 recall 与 precision 曲线。注意此处只统计至少在一个真值图中出现的类——从未出现在 GT 中的类会被跳过。
  3. COCO 101 点插值compute_average_precision):将 precision 做从尾部起的最大值累积(单调包络),再在 recall = 0, 0.01, ..., 1.0 的 101 个取整点采样求均值,即标准 COCO AP 定义。

边界情况同样有源码背书:若所有图都没有真值,函数返回 0.0 而非 NaNmap50 / map75 / map50_95 分别取平均精度数组的第 1 列、第 6 列(0.75 档)与全体均值。

与新版指标的差异提示:新版 supervision.metrics 中的指标采用 update(...).compute() 的两段式 API(基类 Metric 定义于 src/supervision/metrics/core.py),支持 MetricTarget.BOXES / MASKS / ORIENTED_BOUNDING_BOXESAveragingMethod.MACRO / MICRO / WEIGHTED 三种平均方式,并额外提供按对象尺寸(small / medium / large)的细分结果;而遗留版 MeanAveragePrecision 仅支持 xyxy 框、固定按类宏平均。

从 Legacy 迁移到新指标模块的对照

结合 src/supervision/metrics/init.py 的导出与 docs/metrics/ 下的文档,迁移对照关系如下:

Legacy(本页) 新模块推荐替代 关键差异
sv.MeanAveragePrecisionsupervision.metrics.detection supervision.metrics.mean_average_precision.MeanAveragePrecision 新版结果与 pycocotools 一致,支持 MASKS / OBB、尺寸细分
sv.ConfusionMatrix.from_detections(...) 新模块未提供同名类,可保留使用 目前 ConfusionMatrix 源码中无废弃装饰器,但仍属 legacy 页面范畴,建议关注后续版本
一次性 from_tensors metric.update(predictions, targets).compute() 新版支持流式累积,便于大图集分批评估

新模块各指标的详细说明可查阅仓库内文档:mAPF1 ScorePrecisionRecallMAR常用数值

小结与适用前提

  • 本仓库当前开发版本为 0.31.0.dev0(见 pyproject.toml),sv.MeanAveragePrecision 携带"0.31.0 移除"的废弃标记,新增代码不应再依赖它;sv.ConfusionMatrix 则暂无源码级废弃标记,但仍位于 legacy 文档页,使用时应留意版本演进。
  • 使用任一 API 前先执行 pip install "supervision[metrics]"ConfusionMatrix 要求预测携带 confidencebenchmark / from_detections 路径)、两者都要求 class_id 非空。
  • conf_thresholdiou_threshold 默认值分别为 0.30.5,直接决定 TP 判定口径,跨配置比较指标时必须保持一致。
  • 若需要逐图定位错检/漏检原因,sv.ConfusionMatrix.benchmark(..., save_directory_path=...) 会产出 GT/TP/FP/FN 四宫格拼图,是与数值指标配合的最快排障手段。
登录后查看全文
热门项目推荐
相关项目推荐