Blog de Amazon Web Services (AWS)

Escalando aplicaciones de reconocimiento de imágenes con Amazon Rekognition

Por Gabriel Paredes, Arquitecto de Soluciones en Amazon Web Services para Sector Público

 

Ante las medidas de control de COVID-19 emitidas por múltiples Gobiernos del mundo, la oportunidad para implementar aplicaciones apalancadas por Inteligencia Artificial y Reconocimiento de imágenes se ha incrementado soportando casos de uso como Virtual Proctoring (Educación), Conteo de Personas, Autenticación de procesos no presenciales con reconocimiento facial, escaneo de documentos (DNI, Cédulas, Licencias de Conducir), entre otros.

AWS ofrece el conjunto más amplio y profundo de servicios de Machine Learning e infraestructura en la nube, colocándolo en manos de desarrolladores, científicos de datos y profesionales expertos. Nombrado como líder en servicios de Inteligencia Artificial en la nube para desarrolladores según Gartner. Dentro de este conjunto de servicios se encuentran servicios de Machine Learning, Frameworks para Deep Learning y Servicios de Inteligencia Artificial. Con los servicios de Inteligencia Artificial previamente entrenados, agregamos fácilmente inteligencia a las aplicaciones y flujos de trabajo sin la necesidad de ninguna experiencia previa en Machine Learning.

Amazon Rekognition como parte de los servicios de Inteligencia Artificial de AWS, facilita la inclusión de análisis de imagen y video a sus aplicaciones mediante el uso de una tecnología probada, escalable y de deep learning que no requiere experiencia previa en Machine Learning para usarla. Con Amazon Rekognition podemos identificar objetos, personas, texto, escenas y actividades en imágenes y videos, además de detectar cualquier contenido inapropiado. Amazon Rekognition también permite realizar análisis facial de alta precisión y capacidades de búsqueda que puede usar para detectar, analizar y comparar rostros.

Uno de los fundamentos del pilar de Reliability de nuestro Well Architected Framework es el de planificar y manejar los Service Quotas o Service Limits. Estas existen con el propósito de prevenir que accidentalmente se aprovisionen más recursos de los requeridos, controlar la cantidad de requests de operaciones a un API específico y proteger los servicios de uso abusivo o indebido. En el caso de Amazon Rekognition, los Service Quotas están principalmente orientados a la cantidad de Transacciones por Segundo (TPS) que se pueden solicitar a los diferentes API, tales como CompareFaces, DetectFaces, DetectLabels, entre otros. También es importante destacar que esta asignación se realiza por Región y por Cuenta, y los valores pueden variar según la región que deseemos usar (https://docs.thinkwithwp.com/general/latest/gr/rekognition_region.html).

El objetivo del presente artículo es realizar una revisión detallada de estrategias y una solución para incorporar a las aplicaciones de alta demanda en el uso del API de Amazon Rekognition (específicamente DetectFaces) que requieran exceder temporalmente la cuota de servicio sin incurrir en errores del lado de la aplicación, así como recomendaciones en el manejo de errores en llamadas de API.

 

Solución Planteada

 

Figura 1: Arquitectura de procesamiento intensivo con Amazon Rekognition

 

La Figura 1 ilustra una arquitectura que permitirá escalar las llamadas hacia el API de Amazon Rekognition. En primera instancia la aplicación puede cargar la imagen en un Bucket de Amazon S3 a la cual se requiere realizar el análisis aplicando la función de DetectLabels (Identifica entidades del mundo real tales como flores, carros, arboles, fiestas, entre otros.) o DetectFaces (Identifica atributos de hasta 100 rostros dentro de una imagen). Por cada una de las imágenes que sean cargadas en el Bucket, será generado un evento de notificación de S3 dirigido hacia una cola de AWS SQS (Denominaremos esta cola Process_Queue), el cual corresponde a la descripción del evento PutItem con que contiene la información necesaria para posteriormente procesar la imagen.

Seguido a esto, por cada nuevo mensaje que ingrese al Process_Queue (SQS), se intentará generar un evento de AWS Lambda con la información contenida en el mensaje de evento original del Bucket de carga de imágenes. Es en está función Lambda donde está contenido el código necesario para manejar las llamadas hacia el endpoint de Amazon Rekognition así como el manejo de errores y reintentos. Esto se detallará en la sección de Uso de Retries con Exponencial Backoff + Jitter. En este punto hay dos variables que permiten regular la cantidad de llamadas al API de Amazon Rekognition sin sobrepasar la quota de servicio asignada. La primera, mediante la propiedad de BatchSize que indica cuantos mensajes de la cola se pueden enviar en un solo evento de AWS Lambda, éste valor va en una escala de entre 1 a 10. La segunda es mediante el Reserved Concurrency, con el se controla la cantidad máxima de invocaciones concurrentes de una función lambda, con el producto de estas dos variables se puede ajustar la cantidad de TPS que serán invocadas hacia el API de Amazon Rekognition. Para simplificar la solución, se plantea usar un BatchSize de 1 y aproximar la cantidad de TPS con la Cantidad de Reserved Concurrency.

{ "FaceDetails": [ { "BoundingBox": { "Width": 0.20062141120433807, "Height": 
0.40441733598709106, "Left": 0.15825751423835754, "Top": 0.1316724270582199 }, 
"AgeRange": { "Low": 21, "High": 33 }, "Smile": { "Value": true, "Confidence": 
92.25345611572266 }, "Eyeglasses": { "Value": true, "Confidence": 
99.9189682006836 }, "Sunglasses": { "Value": true, "Confidence":
99.63880157470703 }, "Gender": { "Value": "Female", "Confidence":
99.3447036743164 }, "Beard": { "Value": false, "Confidence": 99.50582122802734
}, "Mustache": { "Value": false, "Confidence": 99.86498260498047 }, "EyesOpen": 
{ "Value": true, "Confidence": 99.99996948242188 }, "MouthOpen": { "Value": 
true, "Confidence": 99.09195709228516 }, "Emotions": [ { "Type": "HAPPY", 
"Confidence": 97.60749053955078 }, { "Type": "SURPRISED", "Confidence": 
0.7658454179763794 }, { "Type": "CONFUSED", "Confidence": 0.5557805895805359 }, 
{ "Type": "ANGRY", "Confidence": 0.3515093922615051 }, { "Type": "DISGUSTED", 
"Confidence": 0.34102049469947815 }, { "Type": "CALM", "Confidence": 
0.19356855750083923 }, { "Type": "FEAR", "Confidence": 0.13799777626991272 },
{ "Type": "SAD", "Confidence": 0.046782251447439194 } ],…

Ejemplo 1: Extracto de respuesta de DetectFaces API de Amazon Rekognition

 

El ejemplo 1 corresponde al extracto de una respuesta exitosa del API DetectFaces de Amazon Rekognition con la propiedad de DetectLabels = “ALL”. Esta se encuentra en formato JSON y tiene múltiples campos que permiten identificar atributos de los rostros presentes en la imagen, tales como emociones, posición de ojos, boca, nariz, género, accesorios, entre otros. Todos estos pueden ser utilizados por los consumidores de downstream para realizar análisis avanzados. En este link se encuentra un listado de todas las etiquetas que pueden ser reconocidas con esta llamada de API. Esta respuesta es enviada por la función lambda como mensaje hacia una segunda cola de SQS denominada Response_Queue, que tiene el objetivo de mantener el mensaje de forma temporal hasta que sea consumida por alguna aplicación o nodo de tarea.

A efecto de la solución propuesta, lo siguiente que ocurre es generar un evento de invocación de AWS Lambda por cada mensaje que se reciba en el Response_Queue y posteriormente volcar los resultados con una operación de PutItem en una tabla de Amazon DynamoDB, que en ultima instancia será consultada por la aplicación que ha cargado la imagen al Bucket de S3. Esta parte final de la arquitectura planteada puede variar para ajustarse al caso de uso o del sistema de downstream que se quiera acoplar para realizar el análisis de la respuesta de Rekognition. Hacer este cambio sólo implica cambiar el consumidor de la cola Response_Queue por el sistema o aplicación que se necesite sin impactar ninguno de los componentes anteriores.

 

Uso de Retries con Exponencial Backoff + Jitter

Cuando una aplicación consume cualquier tipo de servicio mediante llamadas de API es muy importante implementar estrategias para el manejo de errores y reintento. Primero, mediante el uso de retries, se puede manejar situaciones de Time Out o de errores transitorios en el API que se está empleando. De esta manera el código que re-envía la petición nuevamente hasta un número máximo de ocasiones en caso de presentarse un error. Sin embargo, esta técnica es un tanto “egoísta” desde la perspectiva del servidor, en otras palabras, el código simplemente va a utilizar mayor cantidad de recursos del servidor para obtener una mayor probabilidad de éxito. Esto no presenta un problema para fallas transitorias, sin embargo, cuando la falla del lado del servidor es por una sobrecarga de peticiones, simplemente se estará aumentando la carga.

Do some sync/async operation.

retries = 0

WHILE (retry AND (retries < MAX_RETRIES))

    wait for (100 * 2^retries) milliseconds + random(0, 1000) milliseconds

    status = Get the result of the asynchronous operation.

    IF status = SUCCESS
        retry = false
    ELSE IF status = NOT_READY
        retry = true
    ELSE IF status = THROTTLED
        retry = true
    ELSE
        Some other error occurred, so stop calling the API.
        retry = false
    END IF

    retries = retries + 1

Ejemplo 2: Pseudocódigo de una implementación de Retries con Exponencial Backoff + Jitter

 

Es por esto que es recomendable incluir, aparte de los retries, un algoritmo de Backoff Exponencial. A cambio de realizar el reintento tan pronto se detecta el error, se agrega un corto período de espera en el código de la aplicación que va a llevar una progresión exponencial hasta alcanzar una condición de máximo tiempo o de máxima cantidad de reintentos. Con esto se garantiza que los sucesivos intentos se realicen espaciados en el tiempo, entre mayor sea el número de reintentos, más tiempo se debe esperar para realizar una petición. Parcialmente solucionado el problema, ahora se deberán tener en cuenta las condiciones de Overload, dado que un grupo de las peticiones que fallaron en un T1 van a ser nuevamente ejecutadas en un T1 + 1. Se incluye así la tercera técnica de Jitter, que consiste en agregar un valor aleatorio al tiempo de espera calculada, minimizando la cantidad de colisiones en los reintentos.

Para el caso de Amazon Rekognition, la documentación indica los tipos de errores que se pueden encontrar y como manejarlos, especialmente los ThrottlingException y ProvisionedThroughputExceededException son dos candidatos para aplicar las técnicas descritas en esta sección a medida que se alcanzan los topes de los Service Quotas asignados. Un ejemplo de esta implementación esta disponible en este link.

En los siguientes links van a encontrar dos artículos con una explicación mas detallada de estos tópicos:

 

Despliegue

Esta solución se ha construido usando AWS Cloud Development Kit (AWS CDK) para ser desplegada en una cuenta de AWS. Se puede hacer uso de esta, descargando el código en este repositorio y posteriormente aplicando los siguientes pasos

Prerrequisitos

Para desplegar esta solución, es necesario contar con los siguientes prerrequisitos:

  • Una cuenta de AWS
  • AWS CDK instalado
  • 6+
  • AWS CLI configurado con credenciales de acceso
  • Permisos para desplegar los componentes listados en esta solución
  • Puede existir un cobro por el uso de los servicios

Configuración del proyecto con AWS CDK

1 – Clonar el código de este repositorio – https://github.com/aws-samples/amazon-rekognition-large-scale-processing

2 – Ejecutar los siguientes comandos en la terminal

$ npm install -g aws-cdk
$ cd amazon-rekognition-large-scale-processing
$ python3 -m venv .env
$ source .env/bin/activate
$ pip install -r requirements.txt
$ cdk deploy

3 – Validación del despliegue: como resultado del paso anterior se observará en consola el nombre del bucket de Amazon S3 donde se pueden empezar a cargar las imágenes, los resultados serán registrados en la tabla de AWS DynamoDB detect_faces_results

Pruebas de carga

Para probar la escalabilidad, confiabilidad y estabilidad de la solución, se tomo un subconjunto de 30.000 imágenes del Flickr-Faces-HQ Dataset (FFHQ). Este contiene 70.000 imágenes de rostros humanos de alta calidad y resolución y es comúnmente utilizado para realizar benchmarks de generative adversarial networks (GAN). Se hicieron tres pruebas así: carga paralela de una aplicación a 25 imágenes/segundo, 50 imágenes/segundo y 100 imágenes/segundo. A continuación, veremos el comportamiento para cada uno de los escenarios y cual es el comportamiento de cada capa de la arquitectura bajo las siguientes premisas:

  • Service Quota de Amazon Rekognition de 50TPS para el API de DetectFaces
  • AWS Lambda con un Reserved Concurrency de 50 invocaciones

Simulación 1: @ 25TPS

 

Imagen 1: CloudWatch: S3 / SQS / Lambda @ 25TPS

 

Imagen 2: CloudWatch: Rekognition @ 25TPS

 

Las 30.000 imágenes se han procesado en un lapso de 20 minutos, como se detalla en las imágenes 1 y 2, por cada evento de PutObject en el bucket de imágenes de S3 se genera un mensaje hacia el Proces_Queue, debido a que se consume el API a una velocidad menor al Service Quota y a el Reserved Concurrency de las funciones Lambda, cada mensaje que entra en la cola es procesado de forma inmediata y no se observa que el numero de mensajes que persisten en el Process_Queue incrementen. Adicionalmente existe una aproximación casi exacta de la cantidad de imágenes que se reciben y de las procesadas por Amazon Rekognition.

Simulación 2: @ 50TPS

Imagen 3: CloudWatch: S3 / SQS / Lambda @ 50TPS

 

Imagen 4: CloudWatch: Rekognition @ 50TPS

 

En esta ocasión la misma cantidad de imágenes se procesaron en 10 minutos, haciendo uso total de la capacidad del Service Quota de Amazon Rekognition. El 90% de los mensajes son procesados de forma inmediata en la cola y solo unos pocos mensajes quedan visibles en la cola de procesamiento. En esta ocasión comienzan a incrementar el valor de los ThrottleCounts y en algunos pocos casos ServerErrorCount (Imagen 4). El código de la función lambda que procesa los llamados hacia el API cuenta con los mecanismos para manejar este tipo de evento mediante Reintento, Backoff Exponencial + Jitter, por lo que todas las imágenes logran ser procesadas con éxito.

Simulación 3: @ 100TPS

 

Imagen 5: CloudWatch: S3 / SQS / Lambda @ 100TPS

 

Imagen 6: CloudWatch: Rekognition @ 100TPS

 

Para finalizar se realizó una prueba duplicando la capacidad asignada. En este caso (Imagen 5) la carga de las imágenes finaliza en 5 minutos (Línea Violeta) y el procesamiento de las imágenes en 10 minutos (Línea Azul y Verde). En este escenario la cantidad de imágenes por segundo que se requieren procesar duplica la capacidad de procesamiento, se detalla en la imagen 5 (Línea Verde) que los mensajes en cola se incrementan, llegando a un tope de 15.6k mensajes, sin embargo, todas las peticiones son eventualmente procesadas y evacuadas de la cola a una tasa estable y consistente de 50TPS por Amazon Rekognition.

 

Limpieza

Para eliminar la solución desplegada:

1 – Ejecutar los siguientes comandos en la terminal

$ cd amazon-rekognition-large-scale-processing
$ cdk destroy

2 – Eliminar la tabla de AWS DynamoDB:

2.1 – Navegar en la consola de AWS al servicio de DynamoDB

2.2 – Seleccionar la opción de Tables, seleccionar la tabla detect_faces_results y proceder a eliminar esta tabla autorizando la confirmación.

3 – Eliminar Bucket de Amazon S3:

3.1 – Navegar en la consola de AWS al servicio de S3

3.2 – Buscar el bucket que se ha creado durante el despliegue de la solución, seleccionarlo y proceder eliminarlo autorizando la confirmación.

Conclusion

Con esta solución se aprovechan las capacidades de Amazon Rekogniton para el procesamiento de imágenes en la escala de decenas de miles de eventos incorporando servicios como Amazon SQS y AWS Lambda con las características de BatchSize y Reserved Concurrency, manejando así el flujo de llamadas invocadas hacia el API de DetectFaces. También, gracias a la implementación mediante de Reintento y Backoff Exponencial + Jitter se logra controlar los momentos de congestión y errores que se puedan presentaren en el uso del API.

 


Sobre el autor

Gabriel Paredes es Arquitecto de Soluciones en Amazon Web Services para Sector Público. Gabriel ayuda a múltiples instituciones de educación en Latinoamérica en la adopción tecnológica y mejora de sus servicios estudiantiles.

 

 

 

 

Revisores técnicos

Cristian David Romero es Arquitecto de Soluciones en Amazon Web Services para Sector Público. Cristian ayuda a múltiples instituciones de Sector Público y Privado en la adopción tecnológica de nube en América Latina.

 

 

 

 

Jhon Guzmán es Arquitecto de Soluciones en Amazon Web Services para Sector Público. Jhon ayuda a múltiples partners en la adopción tecnológica de nube y soluciones para sus clientes en América Latina.