MAP的计算

AP以及MAP概述

AP和MAP是用于评估深度学习目标检测模型性能的常用指标。AP是指对于每个类别,计算检测结果与真实标签匹配时的精度,然后将其平均化;而MAP则是对所有类别的AP进行平均。因此,AP和MAP都是越高越好,表示模型在目标检测任务中表现越优秀。

AP以及MAP的计算

计算AP和MAP需要经过以下几个步骤:

**特征提取:**利用深度学习模型提取输入图像的特征,以便进行目标检测。

**分类器训练:**使用训练数据集训练一个分类器,用于区分不同的目标类别。

**检测与排序:**利用分类器对测试集进行目标检测,将检测结果按照置信度从高到低排序。

**截断与计分:**根据预设的截断阈值,将低置信度的检测结果舍弃,并对剩余的检测结果按照实际正确率进行计分。

**计算AP:**对于每个类别,计算其AP值,即将每个类别的计分结果与该类别的实际正确率进行比较,得出该类别的精确度-召回率曲线下的面积。

**计算MAP:**将所有类别的AP值进行平均,得到MAP值。

计算的过程

'''该函数共4个参数。其中,pred_bboxes 代表所有预测框,true_boxes代表所有真实框,iou_threshold代表设定的IoU阈值,num_classes是总类别数。

pred_bboxes 和true_boxes都包含多条数据。

pred_bboxes中的每条数据格式如下:

[train_idx,class_pred,prob_score,x1,y1,x2,y2]
train_idx:指示图片编号,用于区分不同的图片

class_pred:预测的类别

prob_score:置信度

true_boxes中每条数据的格式与之类似,只不过它是确定的。'''
import torch
from collections import Counter
 
def mean_average_precision(pred_bboxes,true_boxes,iou_threshold,num_classes=20):
    
    #pred_bboxes(list): [[train_idx,class_pred,prob_score,x1,y1,x2,y2], ...]
    
    average_precisions=[]#存储每一个类别的AP
    epsilon=1e-6#防止分母为0
    
    #对于每一个类别
    for c in range(num_classes):
        detections=[]#存储预测为该类别的bbox
        ground_truths=[]#存储本身就是该类别的bbox(GT)
        
        for detection in pred_bboxes:
            if detection[1]==c:
                detections.append(detection)
        
        for true_box in true_boxes:
            if true_box[1]==c:
                ground_truths.append(true_box)
                
        #img 0 has 3 bboxes
        #img 1 has 5 bboxes
        #就像这样:amount_bboxes={0:3,1:5}
        #统计每一张图片中真实框的个数,train_idx指示了图片的编号以区分每张图片
        amount_bboxes=Counter(gt[0] for gt in ground_truths)
        
        for key,val in amount_bboxes.items():
            amount_bboxes[key]=torch.zeros(val)#置0,表示这些真实框初始时都没有与任何预测框匹配
        #此时,amount_bboxes={0:torch.tensor([0,0,0]),1:torch.tensor([0,0,0,0,0])}
        
        #将预测框按照置信度从大到小排序
        detections.sort(key=lambda x:x[2],reverse=True)
        
        #初始化TP,FP
        TP=torch.zeros(len(detections))
        FP=torch.zeros(len(detections))
        
        #TP+FN就是当前类别GT框的总数,是固定的
        total_true_bboxes=len(ground_truths)
        
        #如果当前类别一个GT框都没有,那么直接跳过即可
        if total_true_bboxes == 0:
            continue
        
        #对于每个预测框,先找到它所在图片中的所有真实框,然后计算预测框与每一个真实框之间的IoU,大于IoU阈值且该真实框没有与其他预测框匹配,则置该预测框的预测结果为TP,否则为FP
        for detection_idx,detection in enumerate(detections):
            #在计算IoU时,只能是同一张图片内的框做,不同图片之间不能做
            #图片的编号存在第0个维度
            #于是下面这句代码的作用是:找到当前预测框detection所在图片中的所有真实框,用于计算IoU
            ground_truth_img=[bbox for bbox in ground_truths if bbox[0]==detections[0]]
            
            num_gts=len(ground_truth_img)
            
            best_iou=0
            for idx,gt in emnumerate(ground_truth_img):
                #计算当前预测框detection与它所在图片内的每一个真实框的IoU
                iou=insert_over_union(torch.tensor(detection[3:]),torch.tensor(gt[3:]))
                if iou >best_iou:
                    best_iou=iou
                    best_gt_idx=idx
            if best_iou>iou_threshold:
                #这里的detection[0]是amount_bboxes的一个key,表示图片的编号,best_gt_idx是该key对应的value中真实框的下标
                if amount_bboxes[detection[0]][best_gt_idx]==0:#只有没被占用的真实框才能用,0表示未被占用(占用:该真实框与某预测框匹配【两者IoU大于设定的IoU阈值】)
                    TP[detection_idx]=1#该预测框为TP
                    amount_bboxes[detection[0]][best_gt_idx]=1#将该真实框标记为已经用过了,不能再用于其他预测框。因为一个预测框最多只能对应一个真实框(最多:IoU小于IoU阈值时,预测框没有对应的真实框)
                else:
                    FP[detection_idx]=1#虽然该预测框与真实框中的一个框之间的IoU大于IoU阈值,但是这个真实框已经与其他预测框匹配,因此该预测框为FP
            else:
                FP[detection_idx]=1#该预测框与真实框中的每一个框之间的IoU都小于IoU阈值,因此该预测框直接为FP
                
        TP_cumsum=torch.cumsum(TP,dim=0)
        FP_cumsum=torch.cumsum(FP,dim=0)
        
        #套公式
        recalls=TP_cumsum/(total_true_bboxes+epsilon)
        precisions=torch.divide(TP_cumsum,(TP_cumsu+FP_cumsum+epsilon))
        
        #把[0,1]这个点加入其中
        precisions=torch.cat((torch.tensor([1]),precision))
        recalls=torch.cat((torch.tensor([0]),recalls))
        #使用trapz计算AP
        average_precisions.append(torch.trapz(precisions,recalls))
        
    return sum(average_precisions)/len(average_precisions) 
 
 
def insert_over_union(boxes_preds,boxes_labels):
    
    box1_x1=boxes_preds[...,0:1]
    box1_y1=boxes_preds[...,1:2]
    box1_x2=boxes_preds[...,2:3]
    box1_y2=boxes_preds[...,3:4]#shape:[N,1]
    
    box2_x1=boxes_labels[...,0:1]
    box2_y1=boxes_labels[...,1:2]
    box2_x2=boxes_labels[...,2:3]
    box2_y2=boxes_labels[...,3:4]
    
    x1=torch.max(box1_x1,box2_x1)
    y1=torch.max(box1_y1,box2_y1)
    x2=torch.min(box1_x2,box2_x2)
    y2=torch.min(box1_y2,box2_y2)
    
    
    #计算交集区域面积
    intersection=(x2-x1).clamp(0)*(y2-y1).clamp(0)
    
    box1_area=abs((box1_x2-box1_x1)*(box1_y1-box1_y2))
    box2_area=abs((box2_x2-box2_x1)*(box2_y1-box2_y2))
    
    return intersection/(box1_area+box2_area-intersection+1e-6)
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值