coco和yolov5 map计算结果不一致的问题

文章详细比较了COCO评估工具和YOLOv5在计算平均精度(AP)时的不同方法。COCO使用np.searchsorted()进行近似插值,而YOLOv5则采用np.interp()进行精确插值并积分计算AP。此外,COCO直接取均值,YOLOv5则通过积分求解。这两种方法导致了计算结果的轻微差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在测试检测benchmark时发现使用coco和yolov5计算出的map结果不一致, yolov5的指标要略高一点, 好奇他们都是如何计算的, 通过阅读源码, 发现了一些端倪, 如有纰漏, 还望指出.

ap概念及计算方式

先说ap(average precision), 翻译过来为平均精度, 顾名思义, 就是精度的平均值. 通常来讲, 一个算法任务在数据集上的测试输出的结果是固定的(TP, FP是固定的), 也就是说, 精度值就一个, 那么何来平均精度一说呢?
事实上, 当正负样本差别较大时, 使用单一指标, 如精度(查准率), 还是召回率(查全率), 都无法评价模型的好坏(想象一下正样本99, 负样本1, 模型将所有目标都预测为正例, 此时tp=99, fp=1, p=0.99, r=1能说明模型很好吗? ), 于是需要一个综合指标来衡量模型好坏, ap就是这个综合指标, 计算方式也很简单, 就是计算平均精度, 问题是, 如何构造多个精度呢?

假设某单一目标检测任务(只有1类)在数据集(假设只有3张图片, 每个图片一个目标)有三个预测结果(每个图片一个, 事实上,计算map时跟几张图片没关系), 每一个预测结果 都有置信度, 将所有结果按照置信度从大到小排列:
在这里插入图片描述
对于整个数据集:
置信度为0.9时, tp = 1, fp=0;
置信度为0.7时, tp = 1, fp=1;
置信度为0.4时, tp = 2, fp=1
由以上结果, 可以得到下表:
在这里插入图片描述
于是, 我们便得到了一系列的precision.
但是有几个细节需要我们注意:

  • 问: 我们如何确定检测结果是tp还是fp?
    答: 靠iou, 预测框和gt框iou>iou_th为tp, 否则为fp, ap50就是在iou_th=0.5的条件下计算得到的map.
  • 问: ap和map什么关系?
    答: 通常来讲, ap是某一类的平均精度, map是所有类别的平均精度的平均, 但在coco中, 就没map这个说法, coco中的ap就是map
    在这里插入图片描述
    值得注意的是, 在coco意义下,
    ap(map)是iou_th从0.5取到0.95共10个iou level计算得到的ap的平均值(由每一类ap得到最终ap, 一共需要取两次均值, 一次是所有的iou level, 一次是所有的class)
    ap50是iou_th取0.5计算得到的map
  • 问: 得到了p和r, 接下来ap如何计算呢? 是不是将p取平均就行了?
    答: 这个问题很关键, coco, yolov5的区别就在这

coco和yolov5的差别

在得到p, r列表后, 可以画出p, r图像(画图只是为了方便理解, 实际计算时, 只要得到p, r列表就行了)
在这里插入图片描述
通常, 在得到以上图像后, 需要将x轴(即r)分为101份(0~1, 0.01)进行插值来计算最终的ap, coco和yolov5的差别体现在得到p, r 列表后, 如何插值以及如何计算ap上.

  • 先看下coco(pycocotools.cocoeval.COCOeval):
    coco的插值方法:
# cocoeval.py, line 402
inds = np.searchsorted(rc, p.recThrs, side='left')

其中, recThrs就是recall的101个点, 熟悉np.searchsorted()的小伙伴应该发现了, 其实coco在计算时并没有真的进行插值, 只是按照101个recall的位置, 从p列表中取对应位置的p而已, 对应图像是这样:
在这里插入图片描述

细心的小伙伴会发现np.searchsorted()得到的索引可能超过p的长度, 那超过r最大值的点(右侧红框部分)怎么取呢?答案是直接置为0, 也就是说"插值坐标点"取到r能取到的最大索引后就结束了, 代码对应于这部分:

# cocoeval.py, line 402~408
try:
     for ri, pi in enumerate(inds):
         q[ri] = pr[pi]
         ss[ri] = dtScoresSorted[pi]
except:
    pass

q初始化的时候都是0, 当pi超过pr的索引时会发生错误, 于是超过索引的p都是0.
再来看最终如何计算ap值, coco直接简单粗暴, 取均值:

# cocoeval.py, line 455
mean_s = np.mean(s[s>-1])
  • 再看yolov5
    首先, yolov5是老老实实对p列表进行了101个点的插值, 插值完大概是这样:
    在这里插入图片描述

对应代码:

# Append sentinel values to beginning and end
mrec = np.concatenate(([0.0], recall, [1.0]))
mpre = np.concatenate(([1.0], precision, [0.0]))

# Compute the precision envelope
mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))

# Integrate area under curve
method = 'interp'  # methods: 'continuous', 'interp'
if method == 'interp':
   x = np.linspace(0, 1, 101)  # 101-point interp (COCO)
   ap = np.trapz(np.interp(x, mrec, mpre), x)  # integrate

从代码可以看出, 首先在p, r列表左右两侧各查了0,1两个值, 方便后面查值, 之后通过np.interp()插101个点, 最后通过
np.trapz 积分得到最终的ap
, 大概就是这样:
在这里插入图片描述

结论

coco和yolov5在计算map(coco叫ap)的不同之处有两点:

  • coco插值用的是np.searchsorted(), yolov5用的是np.interp();
  • coco用平均得到ap, yolov5积分得到ap;
### 计算YOLOv7模型的mAP 对于多类别问题,在计算平均精度(AP)平均精度均值(mAP)时,首先需要理解这些概念的基础。 #### 平均精度(AP) AP是针对单个类别的评估指标。其定义为精确率-召回率曲线下的面积。为了获得这个数值,通常会按照置信度降序排列所有的预测结果,并逐步增加阈值来构建同状态下的混淆矩阵,从而得到一系列的精确率召回率值[^1]。 具体来说: - 对于每一个测试样本,如果该样本属于某个特定类别,则将其标记为正例;否则视为负例。 - 随着决策分数的变化,记录下每次变化后的真阳性假阳性数量。 - 使用上述数据绘制PR曲线,并求取曲线下方面积即得AP。 #### mAP 的计算方法 当涉及到多个类别时,就需要考虑整体表现而仅仅是单一类别上的性能。因此引入了mAP这一综合评价标准。mAP是对各个类别上所取得的AP做进一步处理的结果——简单地说就是所有类别AP值得到的一个加权平均数或者是简单的算术平均数取决于具体的实现方式[^2]。 以下是详细的步骤说明: 1. **获取每个类别的 AP** - 根据同的 IoU 阈值设定(通常是0.5至0.95之间),分别统计各类别下的 TP FP 数量; - 利用这些统计数据建立 PR 曲线并据此得出对应类目的 AP 值。 2. **汇总各分类器的表现** - 将第一步中所得的所有类别对应的 AP 进行累加求; - 如果采用的是 COCO 数据集的标准做法,则会对这 10 种同 IoU 下的情况再次取平均作为最终得分。 3. **计算总体 mAP** - 当存在 N 类物品时,mAP 可以表示成 \( \text{mAP} = (\sum_{i=1}^{N}\text{AP}_i)/N \) ```python def calculate_map(ap_per_class, num_classes): """ Calculate the mean average precision given a list of per-class APs. Args: ap_per_class (list): List containing AP values for each class. num_classes (int): Total number of classes. Returns: float: Mean Average Precision across all classes. """ total_ap = sum(ap_per_class) map_value = total_ap / num_classes return map_value ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值