[TOC]
# Ejemplo: Evaluación del SNMB con el Megadetector
###### tags: `Evaluation Examples`
## Motivación
- **Persona u organización que desarrolla el experimento**: Equipo de Ecoinformática de la CONABIO.
- **Resumen**: Nuestra objetivo es probar el [Megadetector](https://github.com/microsoft/CameraTraps) en varios conjuntos de fotos de cámaras trampa para ver cómo funciona para una variedad de ecosistemas y evaluar si es una buena opción para ser el modelo de referencia en la tarea de filtrar fotos vacías. En este experimento utilizamos este modelo para evaluar todas las imágenes etiquetadas con animales, personas y todas las vacías de la colección del SNMB.
- **Propuesta de solución**: Dado que la versión actual de Megadetector solo tiene dos clases: `Animal` y `Person`, filtramos el conjunto de fotos del SNMB para estas dos categorías, además de imágenes vacías, y evaluamos ~240,000 imágenes usando el conjunto de métricas PASCAL VOC, pero realizando la evaluación a la manera de los métodos de clasificación, es decir, solo verificando si el modelo encuentra algún animal o persona en la imagen, sin tener en cuenta la localización.
- **Tipo de problema**: Detección de objetos y clasificación multietiqueta.
## Información del dataset
- **Colección**: SNMB
- **Versión**: 2019, detección
- **URL**: https://snmb.conabio.gob.mx/conabio_ml_collections/images/snmb_2019_detection.tar.gz
### Información de preprocesamiento
- Operaciones de preprocesamiento antes del entrenamiento/evaluación: resize (800, 600)
### Distribución del dataset
31,753 imágenes con etiquetas a nivel de objeto de `Animal` ó `Person` y 206,888 imágenes vacías.
| Animal | Person | Empty |
| ------ | ------ | ------ |
| 28,535 | 4,296 | 206,888 |

### Particiones del dataset
El dataset fue particionado con la siguiente distribución:
| Train | Test | Validation |
| ----- | ---- |:---------- |
| 70% | 20% | 10% |

Las clases quedan distribuídas de la siguiente forma de acuerdo a las particiones:
| | Train | Test | Validation |
| ------ | ------- | ------ |:---------- |
| Empty | 144,821 | 41,377 | 20,688 |
| Animal | 19,974 | 5,707 | 2,854 |
| Person | 3,007 | 860 | 429 |

#### Criterios adicionales
- **Criterios de agrupamiento**: El dataset se particionó agrupando en secuencias aquéllas fotos que pertenecen a la misma localidad y fueran tomadas, como mucho, por **2 segundos** de diferencia. Las fotos de una misma secuencia sólo pueden pertenecer a una partición.
## Datos del modelo
- **Tipo de modelo**: Faster-RCNN con una red InceptionResNetv2.
- **Datos de entrada**: Imágenes de 800 x 600 pixeles.
- **Salida**:
- Etapa 1 : Entrega un conjunto de detecciones (hasta 100) para cada imagen. Cada detección tiene asociados: una categoría, un bounding box dado en coordenadas relativas y un score o nivel de certeza de la detección.
- Etapa 2: Se seleccionan las imágenes más confiables por categoría (no excluyentes) y se clasifican para las clases `Animal`, `Person` y `Empty`.
### Notas adicionales
- El modelo Megadetector fue entrenado con el API de detección de objetos de TensorFlow. La versión del modelo utilizada se entrenó primero con el conjunto de datos COCO y luego se volvió a entrenar usando varias colecciones de fotos de cámaras trampa, incluidas [Caltech](http://lila.science/datasets/caltech-camera-traps) y [Serengeti](http://lila.science/datasets/snapshot-serengeti)
- **Usuarios principales**: Los investigadores dedicados al monitoreo de la vida silvestre a través de fotografías de cámaras trampa, que deseen filtrar aquéllas que no contienen animales para etiquetar manualmente el resto.
- **Usos principales**: Detección de fotos de cámaras trampa que contienen animales.
- **Usos no previstos**
- Clasificación taxonómica más específica de los animales detectados.
- Detección de animales en fotografías que no son similares a las de cámaras trampa. Para las fotografías de cámaras trampa de ecosistemas que son muy distintos de aquéllos en que se tomaron las fotos utilizadas en el proceso de entrenamiento puede resultar difícil la detección de animales.
## Metodología
El proceso se realiza en dos etapas, que definimos como sigue:
- **Detección**: Se consideran hasta 100 detecciones producidas por el modelo de detección.
- **Clasificación**: Se genera una lista con las categorías para las que haya detecciones que superen un umbral de score. Después se crea una etiqueta de las categorías presentes en la lista, y en caso de que la lista esté vacía, se crea una etiqueta de la categoría `Empty`.

A continuación se detallan ambos métodos.
### Método de Detección
En este proceso se toma la salida del modelo de detección, que produce un conjunto de detecciones y se consideran hasta un máximo de 100 por cada imagen. Estas detecciones son pasadas a la etapa de Clasificación.
#### Umbrales de decisión usados en la detección
- Máximo de detecciones por imagen: 100
### Método de Clasificación
Para las clases `Animal` y `Person`, la clasificación se define como:
$C_{animal} = 1$ si $\max\{D_{animal}\}$ > umbral_clasificacion, 0 en otro caso
$C_{person} = 1$ si $\max\{D_{person}\}$ > umbral_clasificacion, 0 en otro caso
Donde:
$\max\{D_{class}\}$: Score máximo para la deteccion de la categoría.
*Las clases no son mutuamente excluyentes.*
Y para la clase vacía:
$C_{empty}$ = $C_{animal} \lor C_{person}$
#### Umbrales de decisión para clasificación
- Umbral de clasificación: 0.2
## Evaluación
### Resultados
#### Evaluación de detección: Curvas de Precision x Recall
A continuación se muestran las curvas de precisión x recall resultantes para cada clase:

En la siguiente gráfica resumimos los resultados de Precisión promedio y mAP:

En la gráfica anterior se muestran las curvas para las 2 clases que estamos analizando, además del área bajo cada una de ellas, que se aproxima al valor de la precisión promedio (AP) para esa clase. El área bajo la curva macro-promedio se aproxima al valor de la media de la precisión promedio (mAP) del modelo.
#### Evaluación multietiqueta
Para el desempeño general del modelo, se tienen los siguientes resultados:

(Los valores exactos están presentados en la sección<a href="#Tablas-de-resultados"> resultados multietiqueta</a>)
Ya que el conjunto de datos tiene un claro desbalance se utilizará el promediado **macro** ([Vease el siguiente](https://hackmd.io/H1_tl8eeT-ecdLeEDfstWg) para algunas consideraciones del proceso)
El modelo en general tiene buen desempeño en cuanto a recuperación y pérdida, con `0.86342568` y `0.1603153`, respectivamente.
Adicionalmente, se presentan los valores de comparación por clase.
Se puede ver que la clase `Animal` tiene la mayor contribución en clasificaciones erróneas, reporta un pérdida de `0.31209` y una precisión de `0.257609`.

En contraste, la pérdida para la clase `Person` es muy baja, lo que implica una buena clasificación y que contribuye a la mejora general del desempeño, con un valor de `0.00853281`.

#### Clase vacía
Finalmente, debido a la necesidad de entender el comportamiento de clase `empty/vacía`.
Se utilizará una metología similar la evaluación adicional anterior, la evaluación binaria a nivel de clase.

(Los valores exactos están presentados en la sección<a href="##Tablas-de-resultados"> resultados multietiqueta</a>)
De igual manera se puede ver un mejor desempeño en **exhaustividad** que en **precisión**, mismo que el comportamiento global.
### Tablas de resultados
#### Resultados muticlase
| | Average Precision |
| ------ | ----------------- |
| Animal | 0.78 |
| Person | 0.85 |
| mAP | 0.82 |
#### Evaluación multietiqueta
**Global**
| | Metric |
| ------ | ---------- |
| precision | 0.49221256 |
| recall | 0.86342568 |
| f1_score | 0.58967516 |
| hamming_loss | 0.1603153 |
**Animal**
| | Metric |
| ------ | ---------- |
| precision | 0.25760958 |
| recall | 0.88446386 |
| f1_score | 0.39900475 |
| hamming_loss | 0.31209779 |
**Person**
| | Metric |
| ------ | ---------- |
| precision | 0.72681553 |
| recall | 0.84238750 |
| f1_score | 0.78034557 |
| hamming_loss | 0.00853281 |
#### Resultados para clase vacía
| | Metric |
| ------ | ---------- |
| precision | 0.292639943 |
| recall | 0.897971304 |
| f1_score | 0.441424137 |
### Métricas de evaluación utilizadas
**Para detección:**
- Métricas de la competencia PASCAL VOC.
Curva de Precisión x Recall y la precisión promedio (AP) para cada categoría, así como la media de la precisión promedio (mAP) para el modelo en general. Se realiza modificación para evaluación solo con preencia de imagen, sin tomar en cuanta la localización.
La metodología para el cálculo de precisión promedio (AP) se detalla en [el siguiente enlace](#Metodolog%C3%ADa-para-el-c%C3%A1lculo-de-la-curva-de-Precisi%C3%B3n-x-Recall).
**Para clasificación:**
Se presentan los siguientes escenarios:
1. Métricas de clasificación multietiqueta (*precision, recall, f1_score y hamming loss*) con promediado **macro** para desempeño global.
2. Métricas de clasificación (*precision, recall, f1_score y hamming loss*) a nivel de clase para `Animal` y `Person`.
3. Métricas de clasificación (*precision, recall y f1_score*) a nivel de clase para etiqueta vacía.
Véase la sección de <a href="#Metodología-de-evaluación-multietiqueta"> evaluación correspondiente</a> para un análisis más detallado del proceso.
### Análisis de resultados
A continuación se presenta un análisis más detallado de los resultados presentados anteriormente.
#### Análisis de la curva de la categoría Animal
Podemos ver para la clase Animal que a niveles de recall bajos la precisión es muy alta, lo que indica que para niveles de score altos no hay tantos falsos positivos en las fotos vacías. Sin embargo, este nivel comienza a disminuir bastante rápido para un valor de recall de 0.6, lo que indica que hay muchos falsos positivos en esta categoría para estos valores de score. Finalmente, se logra una recuperación del 100% con una precisión cercana al 12%, que es igual al porcentaje de fotos en el conjunto de datos, lo que indica que en los niveles de score más bajos, el modelo no está ayudando en nada a acelerar el proceso de etiquetado, ya que devuelve prácticamente todas las fotos del conjunto de datos.
#### Análisis de la curva de la categoría Person
Podemos ver para la clase `Person` que a niveles de recall muy bajos la precisión es muy alta, pero cerca del 10% de recall la precisión comienza a disminuir un poco, lo que indica que para estos niveles de score hay un cierto porcentaje de detecciones falsas de la clase `Person` en las fotos vacías y en las que solo contienen animales. Este nivel de precisión se mantiene hasta alrededor del 75% de recall, donde comienza a disminuir rápidamente, alcanzando el 100% de recall con una precisión de ~2%, lo que indica, al igual que en la clase Animal, que el modelo no está ayudando para filtrar fotos que contienen personas, ya que devuelve prácticamente todas las imágenes en el conjunto de datos.
## Conclusiones
Las siguientes conclusiones son inferidas del desempeño general del modelo:
- Para la clase Animal el modelo es capaz de recuperar el 80% de las fotos con animales teniendo una precisión del 50%. Es decir, si tenemos un conjunto de 1 millón de fotos, donde es probable que sólo 100,000 tengan animales (10%), el modelo será capaz de recuperar 80,000 de ellas (80%), teniendo que revisar para ello 160,000. Sin utilizar el modelo, se habrían tenido que revisar 800,000 fotos para encontrar 80,000, por lo que se estaría reduciendo el trabajo en un factor de 5.
- Si quisiéramos tener un 90% de recuperación de los animales, esto sería asumiendo que la precisión es del ~25%, y se podrían recuperar 90,000 fotos con animales teniendo que revisar 360,000, reduciendo el trabajo en un factor de 2.5 si no se utilizara el modelo.
- Si quisiérmos tener un 100% de recuperación de los animales, esto sería asumiendo que la precisión es del ~10%, por lo que tendríamos que revisar 1 millón de fotos para recuperar las 100,000 con animal, y en ese caso el modelo no estaría ayudando a reducir el trabajo de etiquetado.
- Si es suficiente con tener una recuperación del 60%, ésta se puede alcanzar con una precisión del 90%, por lo que podríamos recuperar 60,000 fotos con animal teniendo que revisar únicamente 66,666. Sin el modelo se habrían tenido que revisar 600,000, por lo que el trabajo se redujo en un factor de 9.
- Para la clase `Person` es algo similar, teniendo un 90% de recuperación con un 50% de precisión.
En caso de requerir un nivel de recuperación del 100% mejorando la precisión del 10% que presenta el modelo, se puede usar alguno de los esquemas de detección de movimiento en secuencias de fotos, como se describe [aquí](https://hackmd.io/KqOleR0TQXGAbQ8NsBuzuA#Esquemas-de-detecci%C3%B3n-de-movimiento), para tratar de reducir las falsas detecciones generadas y que son casi idénticas en todas las fotos que pertenecen a una secuencia.
Alternativente, para el proceso de clasificación. Se muestra que la principal penalización al desempeño global (las métricas de `hamming loss` y `métricas de recuperación de información`) estan correlacionadas a la clasificación errónea de la clase `Animal`.
Esto puede estar relacionado al umbral de clasificación seleccionado. Para ajustar este umbral, algunas metodologías pueden utilizarse:
- Desarrollar un análisis de el `umbral/threshold` a utilizar, para mitigar la clasificación errónea de ciertas clases.
- Utilizar una método de optimización para el proceso de clasificación.
### Ejemplos de clasificación
#### Muestras mejor clasificadas



#### Muestras peor clasificadas







#### Muestras en el umbral entre las dos clases




## Anexos
### Metodología de evaluación multietiqueta
Para una muestra del tipo.
| | Animal | Person | empty |
| ------| ----------| -------- | ---------- |
| image_id | ${0, 1}$ |${0, 1}$ | $Animal \lor Person$|
Los escenarios de evaluación son los siguientes:
1. Global multiclase
Se analiza el desempeño de ambas clases `animal` y `person`, ejemplo:
| Y_true | Y_pred |
| ------ | -------- |
|  | 
Se pueden utilizar varias métricas, en el ejemplo se utilizará **precision**.
**Utilizando el promediado macro**, para un muestra pequeña, se tiene:
Clase Animalia
$y_{true} = [0,0,0,0,1]$
$y_{pred} = [1,1,0,0,1]$
$TP = 1, FP = 2: precision=\frac{1}{1+2}=1/3$
Clase Person
$y_{true} = [0,0,0,0,0]$
$y_{pred} = [0,0,0,0,0]$
$TP = 0, FP = 0: precision=\frac{0}{0+0}=1$
$Precision_{macro}= \frac{1}{2}(1/3 +1) = 4/6$
2. Además del panorama general, se entrega la evaluación binaria por clase (`Animalia`, `Person`), así como para la etiqueta vacía (`empty`).
El cálculo de **precision**, para la muestra presentada previamente, tendrá tendran 2 valores:
Para Animalia:
$y_{true} = [0,0,0,0,1]$
$y_{pred} = [1,1,0,0,1]$
$TP = 1, FP = 2: precision=\frac{1}{1+2}=1/3$
Para Person:
$y_{true} = [0,0,0,0,0]$
$y_{pred} = [0,0,0,0,0]$
$TP = 0, FP = 0: precision=\frac{0}{0+0}=1$
### Conceptos de las métricas de evaluación de detección de objetos
Un modelo suele producir un conjunto de unas 100 detecciones por imagen, donde por lo general, la mayoría tienen un valor de score muy pequeño (<0.1) y es muy probable que se trate de detecciones falsas. Por lo tanto, es necesario poner un umbral de score y considerar sólo aquéllas detecciones que superen este valor.
Para el caso de las fototrampas, configurar este umbral demasiado alto puede suponer que sea difícil encontrar algunas especies y se pierda una parte considerable de las fotos con animales. Por otro lado, si se pone el umbral muy bajo, se pueden tener demasiadas detecciones falsas y el modelo no estaría ayudando a reducir el esfuerzo requerido para encontrar las fotos que nos interesan.
La relación entre la cantidad de fotos con animales que el modelo es capaz de recuperar y la cantidad de fotos que tenemos que revisar para ello, se puede medir con la **precisión** y la **recuperación** (recall).

Un modelo ideal recuperará todos los objetos verdaderos (recall=1) sin generar detecciones falsas (precisión=1).
Una curva de Precisión x Recall es una forma de medir la relación entre la precisión y la recuperación de un modelo. La idea detrás de este método es la de estimar el **impacto** que tendrá tomar únicamente las detecciones con un score mayor a un cierto valor de umbral.
Aunque las métricas tradicionales para evaluar los modelos de detección de objetos evalúan que la localización de los recuadros encontrados sea muy similar a la de los recuadros de las anotaciones manuales, en este caso no es necesario evaluar esto, por lo que simplemente se analizan los nivel de score de las detecciones del modelo para la construcción de las curvas de Precisión x Recall.
[En el siguiente enlace](https://hackmd.io/@api-conabio-ml/BktTPdhQL) se detallan los conceptos de los modelos de detección de objetos.
### Metodología para el cálculo de la curva de Precisión x Recall
Esta metodología no toma en cuenta la localización de los bounding boxes de las detecciones.
Primero se crea una lista `TP_FP` donde se van acumulando los Verdaderos Positivos (TP) y Falsos Positivos (FP) de todas las imágenes, de la siguiente manera:
- Para cada imagen `image`:
- Para cada categoría `cat`:
- Se obtienen los recuadros de las etiquetas reales (`groundtruth_boxes`), los recuadros de las detecciones (`detected_boxes`) y los scores de las detecciones (`detected_scores`) de la categoría `cat` en la imagen `image`.
- Descartamos todos los recuadros de `detected_boxes` excepto el que tenga el score más alto, y tomamos uno de `groundtruth_boxes` (en caso de haber), para la categoría `cat`.
- Si el `detected_box` corresponde con el `groundtruth_box`, la detección es acumulada como TP (se inserta un `True`) en `TP_FP[cat]`. Si no hubo al menos un `groundtruth_box` para la categoría, se acumula un FP (se inserta un `0`) en `TP_FP[cat]`, en caso contrario, se suma 1 a `num_total_gt[cat]`, que servirá posteriormente para el cálculo de los FN de la categoría `cat`.
Después se calcula la precisión y recall sobre la lista de `TP_FP` de cada categoría:
- Para cada categoría `cat`:
- Se ordena `TP_FP[cat]` de forma descendente a partir del score de las detecciones.
- Se realiza una suma acumulada de True Positives y False Positives. P.e., si tenemos 10 detecciones y únicamente la segunda es un True Positive, tendríamos lo siguiente:
`true_positives` => `[False, True, False, False, False, False, False, False, False, False]`
`false_positives` => `[True, False, True, True, True, True, True, True, True, True]`
`cum_true_positives` => `[0, 1, 1, 1, 1, 1, 1, 1, 1, 1]`
`cum_false_positives` => `[ 1., 1., 2., 3., 4., 5., 6., 7., 8., 9.]`
- Se realiza el cálculo de la precisión y el recall con estos arreglos "acumulados" y el número de anotaciones de la categoría `cat` con las siguientes fórmulas:
`precision = cum_true_positives / (cum_true_positives + cum_false_positives)`
`recall = cum_true_positives / num_gt`
Obteniendo:
`precision` => `[0., 0.5, 0.33, 0.25, 0.2, 0.167, 0.14, 0.125, 0.11, 0.1]`
`recall` => `[0., 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]`
[En el siguiente enlace](https://hackmd.io/lpUAQgeWQLe55GQ46KhK-w?view#Ejemplo-ilustrativo) se muestra un ejemplo ilustrativo del procedimiento anterior.