Cálculo de mAP en Pascal VOC

De hecho, nuestra parte de entrenamiento es nuestra propia propuesta de varios algoritmos mejorados, y obtenemos un resultado propio, pero la parte de verificación es básicamente la misma, o siempre usamos métodos de verificación oficiales, para evaluar correctamente los resultados de nuestro modelo. , todavía tenemos que llegar a ser claro acerca de lo que la parte de verificación del código nos necesita para ofrecer, y lo que se calcula , por favor, tenga en cuenta que este es nuestro objetivo final .

Aquí hay un resumen del cálculo de mAP en el conjunto de datos de Pascal VOC Finalmente, resumiré cómo usar este código. Pascal VOC es un conjunto de datos estándar comúnmente utilizado en la detección temprana de objetivos (por ejemplo, Fast / er RCNN, SSD).


1. ¿Qué es mAP?

Dado que desea comprender el código para calcular mAP, primero debe saber qué es mAP. No puede ir en contra de las leyes de la cognición humana. Por supuesto, aquellos que ya están familiarizados con la detección de objetivos pueden omitir esta parte.
Mucha gente ha escrito sobre esto. Te sugiero que leas este blog:
Deep Learning Notes (8): Índice de Evaluación del Desempeño de Detección de Objetivos (mAP, IOU ...).
Aquí también se da que hay que prestar atención después de leer estos blogs. lugar para ser como yo blanco Algunas dudas:
Explicación de relaciones públicas de Wikipedia
1 , en el mapa de Wikipedia para la precisión de la imagen y el recuerdo de la interpretación, la precisión , la comprensión literal, la precisión es lo que predijeron Clase positiva ¿Cuántos de ellos tienen un positivo La proporción de clases (yo también tengo una pregunta aquí al principio, ¿por qué todas nuestras predicciones se consideran clases positivas ? De hecho, no es difícil de entender si lo pensamos detenidamente. Al igual que la detección de objetivos, las casillas que elegimos son, naturalmente, lo que pensamos que son el primer plano objetivos. ¿Cómo podemos ir a enmarcar un trasfondo del mismo, prestar atención a dónde pensamos nuestra caja, el actual marco totalmente posible al fondo); recordar la comprensión literal, recordar es que en realidad hay tantas clases positivas, que eligió una proporción de el número de Si no comprende, puede leer el diagrama anterior varias veces para ayudarlo a comprender, lo que afectará la comprensión del dibujo de la curva PR a continuación.
2. Una curva PR corresponde a un umbral ¿Qué significa este umbral? Por ejemplo, en VOC, este umbral significa que cuando el IoU de Ground Truth bbox es mayor que este umbral, se considerará como una categoría positiva ( como en la línea 157 del código a continuación)ovthresh , pero debe pasar ciertos criterios antes de se puede clasificar como TP (verdadero positivo) Todavía tengo dos preguntas aquí : 1. ¿Cómo se dibuja la curva PR? 2. ¿Por qué R aumenta y P disminuirá?, Lo explicaré en detalle a continuación junto con la parte del código, y le daré la curva PR que dibujé en la práctica.
3. Por lo general, la gente dirá: el área encerrada por la curva PR es el valor AP, pero lo que quiero decir es que cuando lo entiendes, puedes entenderlo intuitivamente. Cuando lo calculas, todavía miras la forma en que El código está implementado Por supuesto, el cálculo real no puede ser muy Es preciso solo para el área debajo de la curva PR.

2. ¿Cómo dibujar la curva PR y calcular AP?

Entonces, ¿cómo dibujamos la curva PR y calculamos el AP? La curva PR no es más que un montón de pares (recordemos, precisión) conectados ¿No sabía de dónde venían estos pares de puntos al principio ? De hecho, volvemos a nuestra pregunta original, ¿cuál es nuestra aportación ? Nuestra entrada es el valor pronosticado (incluido el índice de imagen, la puntuación de confianza del cuadro y las cuatro coordenadas) dado a una categoría determinada (como "automóvil") después de que atravesamos todas las imágenes de prueba. Este valor pronosticado generalmente es procesado por NMS es para asegurar que cada marco de imagen no tenga demasiado, porque después de analizar el código, demasiados cuadros reducirán el AP ), por ejemplo, la siguiente imagen es lo que ejecuté CenterNet generado en el conjunto de datos de Pascal VOC 20 El resultado de la clase:
resultado de evaluación
cada archivo se ve así:
Inserte la descripción de la imagen aquí

El siguiente análisis específico

Los archivos de resultados anteriores corresponden a la [ Línea 107 del código a continuacióndetfile = detpath.format(classname) ], porque los consideramos positivos, por lo que a cada cuadro predicho aquí se le asignará un TP o FP (o de acuerdo con las reglas oficiales, para difícil = 1 El objeto no está involucrado en el cálculo de AP), por lo que hay tantas líneas en el archivo anterior, habrá una lista tan larga de TP y FP, y cada vez que se ingrese un cuadro de predicción, se puede calcular un par (recuperación, precisión), luego se puede dibujar puntos muy finos, por supuesto, con el aumento de los cuadros de entrada, la recuperación debe aumentar, porque el número real de positivos es fijo, con el aumento de los cuadros de entrada, estos valores reales deben ser cubiertos, de acuerdo con el recuerdo La definición estará cerca de 1; y en este momento, cuando el siguiente no se ha predicho correctamente, es decir, cuando el TP en el diagrama wiki anterior permanece sin cambios, el número de FP del hemisferio rojo aumenta, entonces la precisión definitivamente disminuirá , y solo las predicciones media e inferior. Cuando hay un valor verdadero, aumentará repentinamente, por lo que la curva PR no está disminuyendo. La siguiente es la curva de RP que dibujé en el proceso de práctica. Si miras de cerca, habrá una tendencia alcista parcial (siento que no es suficiente con entenderlo, y finalmente hay un ejemplo de referencia a otras personas blogs en proceso de comprensión):
Curva PR

Veamos el código específico

En este punto, hemos completado todos los conocimientos preliminares, ¿necesitamos analizar cómo se implementa el código? Marqué la parte necesaria del código con un comentario en chino:
El siguiente es el código para calcular la versión python de mAP en Pascal VOC, que está tomado del código en Faster RCNN de Ross Girshick . Posteriormente, mucha gente usa este código , tal como:

def voc_ap(rec, prec, use_07_metric=False):
    """ ap = voc_ap(rec, prec, [use_07_metric])
    Compute VOC AP given precision and recall.
    If use_07_metric is true, uses the
    VOC 07 11 point method (default:False).
    """
    if use_07_metric:  #VOC在2010之后换了评价方法,所以决定是否用07年的
        # 11 point metric
        ap = 0.
        for t in np.arange(0., 1.1, 0.1):  #  07年的采用11个点平分recall来计算
            if np.sum(rec >= t) == 0:
                p = 0
            else:
                p = np.max(prec[rec >= t])  # 取一个recall阈值之后最大的precision
            ap = ap + p / 11.  # 将11个precision加和平均
    else:  # 这里是用2010年后的方法,取所有不同的recall对应的点处的精度值做平均,不再是固定的11个点
        # correct AP calculation
        # first append sentinel values at the end
        mrec = np.concatenate(([0.], rec, [1.]))  #recall和precision前后分别加了一个值,因为recall最后是1,所以
        mpre = np.concatenate(([0.], prec, [0.])) # 右边加了1,precision加的是0

        # compute the precision envelope
        for i in range(mpre.size - 1, 0, -1):
            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])  #从后往前,排除之前局部增加的precison情况

        # to calculate area under PR curve, look for points
        # where X axis (recall) changes value
        i = np.where(mrec[1:] != mrec[:-1])[0]  # 这里巧妙的错位,返回刚好TP的位置,
                                                                                      # 可以看后面辅助的例子

        # and sum (\Delta recall) * prec   用recall的间隔对精度作加权平均
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap
# 计算每个类别对应的AP,mAP是所有类别AP的平均值
def voc_eval(detpath,
             annopath,
             imagesetfile,
             classname,
             cachedir,
             ovthresh=0.5,
             use_07_metric=False):
    """rec, prec, ap = voc_eval(detpath,
                                annopath,
                                imagesetfile,
                                classname,
                                [ovthresh],
                                [use_07_metric])
    Top level function that does the PASCAL VOC evaluation.
    detpath: Path to detections
        detpath.format(classname) should produce the detection results file.
    annopath: Path to annotations
        annopath.format(imagename) should be the xml annotations file.
    imagesetfile: Text file containing the list of images, one image per line.
    classname: Category name (duh)
    cachedir: Directory for caching the annotations
    [ovthresh]: Overlap threshold (default = 0.5)
    [use_07_metric]: Whether to use VOC07's 11 point AP computation
        (default False)
    """
    # assumes detections are in detpath.format(classname)
    # assumes annotations are in annopath.format(imagename)
    # assumes imagesetfile is a text file with each line an image name
    # cachedir caches the annotations in a pickle file

    # first load gt
    if not os.path.isdir(cachedir):
        os.mkdir(cachedir)
    cachefile = os.path.join(cachedir, 'annots.pkl')
    # read list of images
    with open(imagesetfile, 'r') as f:
        lines = f.readlines()
    imagenames = [x.strip() for x in lines]

    if not os.path.isfile(cachefile):
        # load annots 
        # 这里提取的是所有测试图片中的所有object gt信息, 07年的test真实标注是可获得的,12年就没有了
        recs = {
    
    }
        for i, imagename in enumerate(imagenames):
            recs[imagename] = parse_rec(annopath.format(imagename))
            if i % 100 == 0:
                print 'Reading annotation for {:d}/{:d}'.format(
                    i + 1, len(imagenames))
        # save
        print 'Saving cached annotations to {:s}'.format(cachefile)
        with open(cachefile, 'w') as f:
            cPickle.dump(recs, f)
    else:
        # load
        with open(cachefile, 'r') as f:
            recs = cPickle.load(f)

    # extract gt objects for this class 从上面的recs提取我们要判断的那类标注信息
    class_recs = {
    
    }
    npos = 0
    for imagename in imagenames:
        R = [obj for obj in recs[imagename] if obj['name'] == classname]
        bbox = np.array([x['bbox'] for x in R])
        difficult = np.array([x['difficult'] for x in R]).astype(np.bool)
        det = [False] * len(R) # 该图片中该类别对应的所有bbox的是否已被匹配的标志位
        npos = npos + sum(~difficult) #累计所有图片中的该类别目标的总数,不算diffcult
                                                                     # 这里计算还是很巧妙的,npos=TP+FN
        class_recs[imagename] = {
    
    'bbox': bbox,
                                 'difficult': difficult,
                                 'det': det}

    # read dets
    detfile = detpath.format(classname)
    # 读取相应类别的检测结果文件,每一行对应一个检测目标
    with open(detfile, 'r') as f:
        lines = f.readlines()

    splitlines = [x.strip().split(' ') for x in lines]
    image_ids = [x[0] for x in splitlines]
    confidence = np.array([float(x[1]) for x in splitlines])
    BB = np.array([[float(z) for z in x[2:]] for x in splitlines])

    # sort by confidence 按置信度由大到小排序
    sorted_ind = np.argsort(-confidence)
    sorted_scores = np.sort(-confidence)
    BB = BB[sorted_ind, :]
    image_ids = [image_ids[x] for x in sorted_ind]

    # go down dets and mark TPs and FPs
    nd = len(image_ids) # 检测结果文件的行数
    tp = np.zeros(nd) # 用于标记每个检测结果是tp还是fp
    fp = np.zeros(nd)
    for d in range(nd):
       # 取出该条检测结果所属图片中的所有ground truth
        R = class_recs[image_ids[d]]
        bb = BB[d, :].astype(float)
        ovmax = -np.inf
        BBGT = R['bbox'].astype(float)

        if BBGT.size > 0:
            # compute overlaps  计算与该图片中所有ground truth的最大重叠度
            # intersection
            ixmin = np.maximum(BBGT[:, 0], bb[0])
            iymin = np.maximum(BBGT[:, 1], bb[1])
            ixmax = np.minimum(BBGT[:, 2], bb[2])
            iymax = np.minimum(BBGT[:, 3], bb[3])
            iw = np.maximum(ixmax - ixmin + 1., 0.)
            ih = np.maximum(iymax - iymin + 1., 0.)
            inters = iw * ih

            # union
            uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +
                   (BBGT[:, 2] - BBGT[:, 0] + 1.) *
                   (BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)

            overlaps = inters / uni
            ovmax = np.max(overlaps)
            jmax = np.argmax(overlaps)
        # 这里就是具体的分配TP和FP的规则了
        if ovmax > ovthresh:  # 如果最大的重叠度大于一定的阈值
            if not R['difficult'][jmax]: # 如果最大重叠度对应的ground truth为difficult就忽略,
                                                               # 因为上面npos就没算
                if not R['det'][jmax]: # 如果对应的最大重叠度的ground truth以前没被匹配过则匹配成功,即tp
                    tp[d] = 1.
                    R['det'][jmax] = 1
                else:  # 若之前有置信度更高的检测结果匹配过这个ground truth,则此次检测结果为fp
                    fp[d] = 1.
        else:
            # 该图片中没有对应类别的目标ground truth或者与所有ground truth重叠度都小于阈值
            fp[d] = 1.

    # compute precision recall
    fp = np.cumsum(fp) # 累加函数np.cumsum([1, 2, 3, 4]) -> [1, 3, 6, 10]
    tp = np.cumsum(tp)
    rec = tp / float(npos)
    # avoid divide by zero in case the first detection matches a difficult
    # ground truth
    prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)
    ap = voc_ap(rec, prec, use_07_metric)

    return rec, prec, ap

Ejemplos para ayudar a comprender

El siguiente ejemplo proviene de otro blog: Índice de evaluación de detección de objetivos COCO , los datos en el interior son así: El método de cálculo de AP en esta imagen es el tipo posterior a 2010. En este ejemplo, hay un total de 20 pruebas. Pero hay solo 6 muestras de prueba positivas, por lo que aquí se divide en 6 intervalos en lugar de 11. Si consideramos cada color (de hecho, este bloque de color se divide según el valor diferente de recuerdo) como una etapa, 2010 La precisión tomada por el método después de años es el más grande en la etapa posterior y esta etapa (correspondiente a la oración en la tabla [Precisión máxima para cualquier recuerdo r '> = r], tenga en cuenta que es mayor o igual que no mayor que)
ejemplo
La siguiente es mi Comprensión de 2010 Los diagramas realizados en los métodos de evaluación posteriores se comprenderán rápidamente cuando se mire, y también se puede encontrar el ingenio de la desalineación del código. Los datos provienen de la imagen de arriba:
Ejemplos de métodos de evaluación después de 2010

para resumir

Así que volvemos al objetivo final al principio. Una vez que entendemos el código, solo necesitamos aprender a llamarlo. Para el conjunto de datos de Pascal VOC:

  • Qué necesitamos proporcionar: los 20 archivos de resultados de predicción, como se ve en la segunda imagen más grande, y el archivo de anotaciones;
  • El calculado es: ¡Por supuesto que es mAP!

Supongo que te gusta

Origin blog.csdn.net/laizi_laizi/article/details/103887550
Recomendado
Clasificación