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:
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:
cada archivo se ve así:
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):
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:
- Marco de Detectron de la investigación de Facebook
- ssd.pytorch reproducido por amdegroot con PyTorch
- La parte VOC del código oficial de CenterNet (Objects as Points)
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)
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:
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!