天天看點

map平均準确率_mAP

最近在處理檢測問題的時候,需要對網絡參數進行評估,之前對mAP的詳細細節沒有深入了解,這裡記錄一下

-----------------------

準确率(accuracy),精确率(precision),召回率(recall)

MeanAverage Precision(平均精确率),即mAP

Precision=TP/(TP+FP)

 TP(檢測為P, 實際為p ,檢測正确,P->P)  TruePostive

 FP(檢測為P,實際為N,檢測錯誤,N->P)   False Postive

即Precision 為檢測認為是P的樣本裡,真實為P的機率,即檢測結果哩的真實率  

Recall =TP/(TP+FN)   

 TP(檢測為P,檢測正确,實際為正,P->P) True Postive

 FN(檢測為N,檢測錯誤,說明實際為正,被檢測為負,即漏掉了P->N) False Negive

即Recall 為檢測認為是P的樣本哩占全部真實為P的機率,即反映了查全率

假如原圖中有10張人臉,檢測到12張人臉,其中正确的人臉為8張,錯誤的為4張,則漏檢的2張。

TP= 8, FP=4, Precision=66.7%  期望Precision越高越好,即錯檢測的盡可能少

TP=8,  FN=2, Recall =80% 即檢測的查全率為80% 希望越多越好。

Precision:根據上述方法,對于某個類别A,我們先計算每張圖檔中A類别TP和FP的數量,并周遊所有圖像對其進行累加,即可得到類别A在整個資料集中TP和FP的數量,計算TP/(TP+FP)即可得到類别A的Precision (計算Precision的時候隻需要用到TP和FP),但是會發現Precision的數值是受模型預測出的邊界框的數量(上述計算式的分母部分,即我們認為的檢測出的Postive的樣本的數量)影響的,如果我們控制模型輸出預測框的數量,就可以得到不同的Precision,是以我們可以設定不同的score門檻值,最終得到不同數量的TP和FP。

Recall:對于某個類别A,按上述方法進行累加TP的數量,計算TP/(n_gt)即可得到Recall,其中n_gt表示類别A在所有圖檔中gt的數量之和。同理,如果控制模型輸出的預測框的數量,就會改變TP的數量,也就會改變Recall的值

綜上,想要得到PR曲線,可以通過改變score的門檻值來控制模型輸出的預測框數量,進而得到不同的TP、FP、FN。不過在實際操作中,并不需要手動來設定score的門檻值,因為每個預測框都有一個score,我們隻需要将其按從小到大進行排序,然後每次選擇最後一個score作為門檻值即可,這樣如果類别A在所有圖檔中的預測框數量之和有100個,就可以計算100組類别A的Precision-Recall值。

假設現在資料集中一共有5張圖檔,

①第1張圖檔中有2個A類别的gt,有三個A類别的預測框,score分别為(0.3, 0.5, 0.9),按照上述計算TP的方法(按score從大到小的順序比對)發現score為0.3和0.9的與gt相比對,則将這兩個記為TP。建立用于計算PR曲線的數組metric和記錄A類别gt總數的變量ngt,向數組中加入(0.3, 1), (0.5, 0), (0.9, 1)三組

資料(每組資料的第一個代表預測框的score,第二個代表這個預測框是否是TP),并将n_gt累加2;使用相同的方法計算接下來的幾張圖檔,

(0.3,1)(0.5, 0)(0.9,1)     gt=2

②第2張圖檔中沒有A類别的物體(gt數量為0),則n_gt+=0,但有一個關于A類别的預測框,score為0.45,則向metric中加入(0.45, 0);

(0.3,1)(0.5, 0)(0.9,1)(0.45, 0)     gt=2

③第3張圖檔中有1個A類别的物體,但沒有預測框,則n_gt+=1;

(0.3,1)(0.5, 0)(0.9,1)(0.45, 0)     gt=3

④第4張圖檔中有3個A類别的物體,有5個預測框,其中有3個與gt相比對,最終n_gt+=3,metric中加入(0.85, 1), (0.8, 1), (0.7, 1), (0.35, 0), (0.1, 0);

(0.3,1)(0.5, 0)(0.9,1)(0.45, 0) (0.85, 1), (0.8, 1), (0.7, 1), (0.35, 0), (0.1, 0)   gt=6

⑤第5張圖檔中沒有A類别的物體,也沒有A類别的預測框。

(0.3,1)(0.5, 0)(0.9,1)(0.45, 0) (0.85, 1), (0.8, 1), (0.7, 1), (0.35, 0), (0.1, 0)   gt=6

對score進行從大到小排序,之是以使用由大向小排列是因為判斷條件為>=score,是因為小的門檻值向上相容大的門檻值

       accTP    accFP      precition    recall

(0.9,  1)        1                0        1/1        1/6 

    #score  =0.9   正确分類1個(0.9,  1),錯分0個.  

     precision=1/(1+0)=1,  recall =1/6

(0.85,1)        2        0        2/2        2/6 

    #score  =0.85   正确分類2個(0.85,1)(0.9,1),錯分0個. 

    precision=2/(2+0)=1,  recall =2/6

(0.8,  1)        3        0        3/3        3/6 

    #score  =0.80   正确分類3個(0.85,1)(0.9,1),(0.8,1),錯分0個.

     precision=3/(3+0)=1,  recall =3/6

(0.7,  1)        4        0        4/4        4/6 

     #score  =0.70   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1),錯分0個.

     precision=4/(4+0)=1,  recall =4/6

(0.5,  0)        4        1        4/5        4/6 

     #score  =0.50   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1),錯分1個

     (0.5, 0).  precision=4/(4+1)=4/5,  recall =4/6

(0.45,0)        4        2        4/6        4/6 

     #score  =0.45   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1),錯2個

     (0.5,0),(0.45,0).  precision=4/(4+2)=4/6,  recall =4/6

(0.35,0)        4        3        4/7        4/6 

     #score  =0.35   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1),錯3個

     (0.5,0),(0.45,0),(0.35,0). precision=4/(4+3)=4/7,  recall=4/6

(0.3,  1)        5        3        5/8        5/6 

     #score  =0.3   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1)(0.3,1),錯3個

     (0.5,0),(0.45,0),(0.35,0). precision=5/(5+3)=5/8,  recall=5/6

(0.1   0)        5        4        5/9        5/6  

      #score  =0.1   正确分類4個(0.85,1)(0.9,1),(0.8,1)(0.7,1)(0.3,1),錯3個

      (0.5,0),(0.45,0),(0.35,0),(0.1,0). precision=5/(5+4)=5/9,  recall=5/6

在計算面積的時候,我們使用均勻分片的方法,用樣條面積相加生成最終的面積,是以,合适的樣條,以Recall固定間隔進行采樣,以上面資料為例子則分别為

[0-1/6],[1/6-2/6],[2/6-3/6],[3/6-4/6],[4/6-5/6],[5/6-6/6], 由于是離散的點,則分别取recall=1/6, 2/6, 3/6, 4/6, 5/6, 6/6的對應的對應得precision,由于計算得是面積,

是以取每個Recall值對應的最大Precision進行計算即可

map平均準确率_mAP

因為A類别一共有6個gt,是以Recall的值應該是從1/6~6/6共6個,也就是要取6組PR值計算平均Precision,因為這個例子中沒有出現Recall=6/6的情況,

是以R=6/6時的Precision算作0,即類别A的AP=(1/1 + 2/2 + 3/3 + 4/4+ 5/8 + 0) / 6 = 0.7708

def ap_per_class(tp, conf, pred_cls, target_cls):

    """ Compute the average precision, given the recall and precision curves.

    Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.

    # Arguments

        tp:    True positives (list).

        conf:  Objectness value from 0-1 (list).

        pred_cls: Predicted object classes (list).

        target_cls: True object classes (list).

    # Returns

        The average precision as computed in py-faster-rcnn.

    """

    # Sort by objectness

    i = np.argsort(-conf)#np.argsort為從小到大排序,使用-conf,即等于對conf從大到小排序

    tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]#基于conf從大到小的排序結果對 tp, conf,pred_cls進行調整順序

    # Find unique classes

    unique_classes = np.unique(target_cls) #對類别進行去重,并按從小到大排序,因為計算AP是針對每類進行

    # Create Precision-Recall curve and compute AP for each class

    ap, p, r = [], [], []

    for c in tqdm.tqdm(unique_classes, desc="Computing AP"):

        i = pred_cls == c #對于類别c,找到預測類别為c的所有的索引,索引記為i(i的0,1組成的序列)

        n_gt = (target_cls == c).sum()  # Number of ground truth objects 真實的類别為c的數目,即c類的gt數目

        n_p = i.sum()  # Number of predicted objects 即預測為c類的總的數目 n_p 即為fp和np的和

        if n_p == 0 and n_gt == 0: #本身沒有c類,也沒有被預測為c類的樣本,則跳過該類别

            continue

        elif n_p == 0 or n_gt == 0:

            ap.append(0) 

            r.append(0) #r為recall的容器

            p.append(0) #p為precision的容器

            #本身沒有c類,或者沒有被預測為c類,兩者必有之一,

            #如果沒有c類,則p=0, r=0,ap=0

            #如果沒有被預測到c類,則tp一定為0,則p=0,r=0,ap=0

        else:

            # Accumulate FPs and TPs

            fpc = (1 - tp[i]).cumsum()

            #i的值為(1,0),清單記錄着索引對應位置是否是c類别框,i為1 則表示該點被預測為c,tp裡面儲存的為是否true-postive, tp[i]=0,則為false-postive, fpc則為計算累加的fp的總數目

            tpc = (tp[i]).cumsum()

            #i的值為(1,0),清單記錄着索引對應位置是否是c類别框,i為1 則表示該點被預測為c,tp裡面儲存的為是否true-postive, tp[i]=1,則為true-postive, tpc則為計算累加的tp的總數目

            # Recall

            recall_curve = tpc / (n_gt + 1e-16)#計算recall的值

            r.append(recall_curve[-1])#recall值放入到r中,r為recall的容器

            # Precision

            precision_curve = tpc / (tpc + fpc)#計算precision值

            p.append(precision_curve[-1])#precision值放入到p中,p為precision的容器

            # AP from recall-precision curve

            ap.append(compute_ap(recall_curve, precision_curve))

    # Compute F1 score (harmonic mean of precision and recall)

    p, r, ap = np.array(p), np.array(r), np.array(ap)

    f1 = 2 * p * r / (p + r + 1e-16)

    return p, r, ap, f1, unique_classes.astype("int32")

def compute_ap(recall, precision):

    """ Compute the average precision, given the recall and precision curves.

    Code originally from https://github.com/rbgirshick/py-faster-rcnn.

    # Arguments

        recall:    The recall curve (list).

        precision: The precision curve (list).

    # Returns

        The average precision as computed in py-faster-rcnn.

    """

    # correct AP calculation

    # first append sentinel values at the end

    mrec = np.concatenate(([0.0], recall, [1.0]))

    mpre = np.concatenate(([0.0], precision, [0.0]))

    # compute the precision envelope

    for i in range(mpre.size - 1, 0, -1):

        mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])

    # to calculate area under PR curve, look for points

    # where X axis (recall) changes value

    i = np.where(mrec[1:] != mrec[:-1])[0]

    # and sum (\Delta recall) * prec

    ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])

    return ap