Cómo implementar un modelo de detección de objetos con el servicio TensorFlow

Los modelos de detección de objetos son algunos de los modelos de aprendizaje profundo más sofisticados. Son capaces de localizar y clasificar objetos en tiempo real tanto en imágenes como en videos. Pero, ¿de qué sirve un modelo si no se puede utilizar para la producción?

Gracias a los maravillosos chicos de TensorFlow, tenemos un servicio de TensorFlow que es capaz de servir a nuestros modelos en producción. Hay algunos artículos realmente buenos sobre TensorFlow que sirven para comenzar, como este y este.

Este artículo se centrará en cómo podemos servir modelos de detección de objetos específicamente con TF Serving. Está motivado por la falta de un buen recurso en línea que explique cómo crear modelos de detección de objetos listos para producción y entornos de servicio TF utilizando Docker. También discutiremos cómo servir el modelo y crear un script del lado del cliente para acceder a él. Nuestra arquitectura se verá más o menos así:

En el espíritu de no reinventar la rueda, he tomado ayuda de los recursos disponibles en la API de detección de objetos para este tutorial. Supongo que ha clonado la API de detección de objetos de TensorFlow, pero si no, haga lo siguiente:

# Clon tensorlfow modelos repo
https://github.com/tensorflow/models.git
modelos de cd / research / object_detection

1. Cree un modelo listo para producción para TF-Serving

Suponiendo que ha entrenado su modelo de detección de objetos utilizando TensorFlow, tendrá los siguientes cuatro archivos guardados en su disco:

Archivos de modelos entrenados guardados en el disco

Estos archivos se pueden usar para inferencia directamente. O podemos usar el script freeze_graph.py para convertir el modelo en un gráfico congelado que consiste en la arquitectura del modelo y los pesos en un solo archivo. Esto es útil para realizar pruebas en su máquina local, pero no es adecuado para un entorno de producción.

Para crear modelos listos para servir, modificaremos el archivo exporter.py disponible en la API de detección de objetos Github. El script original disponible en el repositorio no guarda las variables que se requieren para servir. Utilice el siguiente script exporter.py en lugar del original TensorFlow.

Se han realizado los siguientes cambios en el exporter.py anterior:

  1. Cambie al método _write_saved_model. Esto es obligatorio, ya que el script original de Python no guarda las variables que se requieren para servir el modelo. Ahora, en lugar de usar frozen_graph_def, usamos el training_checkpoint_prefix que tiene los pesos del modelo como variables. (créditos a esta edición de Github)

2. Cambie la función de llamada de frozen_graph_def a training_checkpoint_prefix siguiendo:

3. Comente el código que guarda los archivos en el disco no necesarios durante la publicación:

Ahora ya está listo para crear su modelo que se puede utilizar para servir. El siguiente código puede ayudarlo a lograr eso:

Aquí hay una explicación de ese código:

  1. Cada modelo de detección de objetos tiene una configuración que debe pasarse a export_model.py. Consiste en información sobre la arquitectura del modelo. Para más información, consulte este enlace.
  2. El método get_configs_from_pipeline_file crea un diccionario a partir del archivo de configuración, y el método create_pipeline_proto_from_configs crea un objeto prototipo de búfer a partir de este diccionario.
  3. input_checkpoint es la ruta a model.ckpt del modelo entrenado.
  4. model_version_id es un número entero para la versión actual del modelo. TF-serve lo requiere para el control de versiones de modelos.
  5. object_detection.exporter guardará el modelo en el siguiente formato:
Modelo listo para ser utilizado por TF-Serving

1 / es la versión del modelo, saved_model.pb. Contiene la arquitectura del modelo, y el directorio de variables tiene los pesos para el modelo. Este modelo está listo para ser servido.

2. Cree un entorno de servicio TF utilizando Docker.

Sobre Docker

Docker es una herramienta de software que le permite empaquetar el software en unidades estandarizadas para el desarrollo, envío y despliegue. La imagen del contenedor Docker es un paquete ligero, ejecutable e independiente de un software que incluye todo lo necesario para ejecutarlo: código, tiempo de ejecución, herramientas del sistema, bibliotecas del sistema, configuraciones.

En resumen, Docker nos permite aislar su aplicación y sus dependencias en un paquete independiente que se puede usar en cualquier lugar y en cualquier momento sin tener que preocuparse por instalar el código y las dependencias del sistema.

Nuestra motivación para usar Docker para el servicio TensorFlow es que podemos enviar nuestro contenedor para que se ejecute en la nube y escalar fácilmente nuestro servicio sin tener que instalar ninguna dependencia nuevamente.

La documentación oficial del servicio TensorFlow describe cómo construirlo desde la fuente. Está bien, pero yo (y gran parte de la comunidad) tuve problemas para compilarlo en el contenedor acoplable. Así que veremos los pasos uno por uno aquí.

  1. Construye el contenedor usando la imagen oficial de docker

Suponiendo que ha clonado el repositorio de servicio oficial de TensorFlow como se describe en la última parte, puede crear la imagen del acoplador haciendo lo siguiente:

# Moverse al directorio de los archivos acoplables
cd ./serving/tensorflow_serving/tools/docker/
# Construir la imagen (CPU)
Docker build --pull -t $ USER / tensorflow-serving-devel-cpu -f Dockerfile.devel.
o
# Construye la imagen (GPU)
Docker build --pull -t $ USER / tensorflow-serving-devel-gpu -f Dockerfile.devel-gpu.

Antes de iniciar el contenedor docker, aumente la memoria (a 10–12 GB) y las CPU (a 4–6) disponibles para el contenedor en la sección de preferencias de la aplicación docker. La creación de TensorFlow es un proceso que requiere mucha memoria y los parámetros predeterminados pueden no funcionar. Una vez hecho esto, puede iniciar el contenedor de esta manera:

[PARA CPU]
docker run -it -p 9000: 9000 $ USER / tensorflow-serving-devel-cpu / bin / bash
o
[PARA GPU]
docker run -it -p 9000: 9000 $ USER / tensorflow-serving-devel-gpu / bin / bash

En el contenedor, haga lo siguiente:

[PARA CPU]
# Clone el TensorFlow que sirve el repositorio de Github en el contenedor
git clone --recurse-submodules https://github.com/tensorflow/serving
servicio de cd / tensorflow
# Configurar TensorFlow
./configure
discos compactos ..
# Construir servicio TensorFlow
Bazel build -c opt --copt = -msse4.1 --copt = -msse4.2 tensorflow_serving / ...
o
[PARA GPU]
# TensorFlow que sirve el repositorio de Github ya está presente en el contenedor # así que no necesita clonar nuevamente
# Configure TensorFlow con CUDA aceptando (-y) -
# with_CUDA_support flag
servicio de cd / tensorflow
./configure
# Construir TensorFlow sirviendo con CUDA
Bazel build -c opt --copt = -msse4.1 --copt = -msse4.2 --copt = -mavx --copt = -mavx2 --copt = -mfma --copt = -O3 --copt = / usr / local / cuda tensorflow_serving / ...

El proceso de compilación puede demorar hasta una hora, dependiendo del sistema host y la configuración del acoplador. Una vez que finalice la compilación sin ningún error, puede probar si el servidor modelo se está ejecutando:

bazel-bin / tensorflow_serving / model_servers / tensorflow_model_server

La salida debería verse así:

Banderas
--port = puerto int32 8500 para escuchar
--enable_batching = false bool habilita el procesamiento por lotes
--batching_parameters_file = "" string Si no está vacío, lea un protobuf ascii BatchingParameters del nombre del archivo proporcionado y use los valores contenidos en lugar de los valores predeterminados.
--model_config_file = "" string Si no está vacío, lea un protobuf ascii ModelServerConfig del nombre de archivo proporcionado y sirva los modelos en ese archivo. Este archivo de configuración se puede utilizar para especificar varios modelos para servir y otros parámetros avanzados, incluida la política de versión no predeterminada. (Si se usa, --model_name, --model_base_path se ignoran).
--model_name = nombre de cadena "predeterminado" del modelo (ignorado si se establece el indicador --model_config_file
--model_base_path = "" ruta de cadena para exportar (se ignora si se establece --model_config_file flag, de lo contrario se requiere)
--file_system_poll_wait_seconds = 1 intervalo int32 en segundos entre cada sondeo del sistema de archivos para la nueva versión del modelo
--tensorflow_session_parallelism = 0 int64 Número de hilos a utilizar para ejecutar una sesión de Tensorflow. Autoconfigurado por defecto. Tenga en cuenta que esta opción se ignora si --platform_config_file no está vacío.
--platform_config_file = "" string Si no está vacío, lea un protobuf ascii PlatformConfigMap del nombre de archivo suministrado y use esa configuración de plataforma en lugar de la plataforma Tensorflow. (Si se usa, --enable_batching se ignora).

Su entorno de servicio ahora está listo para ser utilizado. Salga del contenedor y confirme los cambios en el contenedor a una imagen. Puedes hacer esto así:

  • Presionando [Cltr-p] + [Cltr-q] para salir del contenedor
  • Encuentra el ID del contenedor:
# Encuentra el ID del contenedor
docker ps
ID DE CONTENEDOR MANDO DE IMAGEN ESTADO CREADO NOMBRES DE PUERTOS
  • Cometer los cambios:
# Compromete los cambios
[PARA CPU]
docker commit $ {ID DE CONTENEDOR} $ USER / tensorflow-serving-devel-cpu
o
[PARA GPU]
docker commit $ {ID DE CONTENEDOR} $ USER / tensorflow-serving-devel-gpu
  • Vuelva a ingresar al contenedor:
docker exec -it $ {ID DE CONTENEDOR} / bin / bash

Nota: Para que el contenedor de servicio TensorFlow acceda a las GPU en su sistema host, debe instalar nvidia-docker en su sistema y ejecutar el contenedor de esta manera:

nvidia-docker docker run -it -p 9000: 9000 $ USER / tensorflow-serving-devel-gpu / bin / bash

Luego puede verificar el uso de su GPU dentro del contenedor utilizando el cmd nvidia-smi.

Imágenes Docker preconstruidas

Como he visto en varios problemas de Github (ver recursos), las personas no pueden compilar TensorFlow en Docker. Así que tengo imágenes de Docker preconstruidas para CPU y GPU.

Puede encontrarlos en mi página Docker Hub o puede desplegar las imágenes de esta manera:

[PARA CPU]
docker pull gauravkaila / tf_serving_cpu
o
[PARA GPU]
docker pull gauravkaila / tf_serving_gpu

3. Crear un cliente para solicitar que el servidor modelo se ejecute en el contenedor Docker para inferencia en una imagen de prueba

Introducción rápida a gRPC (llamada a procedimiento remoto de Google) y búferes de protocolo

gRPC (Llamada de procedimiento remoto de Google) es el protocolo RPC envuelto en HTTP2 de Google. Esto permite que un cliente que se ejecuta en una computadora acceda a una computadora remota, a través de la red informática, y llame a una "función" en esa computadora remota como si la función fuera local para el cliente.

El servicio TensorFlow utiliza este protocolo para servir modelos para inferencia. De acuerdo con la documentación oficial,

En gRPC, una aplicación cliente puede llamar directamente a métodos en una aplicación de servidor en una máquina diferente como si fuera un objeto local, lo que facilita la creación de aplicaciones y servicios distribuidos.
arquitectura gRPC

Aquí, el servidor gRPC es nuestro contenedor acoplable que ejecuta el servicio de servicio TensorFlow, y nuestro cliente está en Python que solicita inferencia a este servicio. Este artículo describe cómo funciona RPC de una manera muy estructurada.

gRPC usa Protocol Buffers para serializar datos estructurados, así como definir los parámetros y devolver respuestas para los métodos invocables. Es neutral en lenguaje y plataforma neutral. Tiene un lenguaje estructurado que luego compila el código de serialización de transporte en el idioma elegido para incluirlo en su proyecto. Transmite datos en formato binario, que es más pequeño y más rápido en comparación con los viejos JSON y XML.

Creando el cliente

Una solicitud de publicación de TensorFlow puede ser uno de tres tipos:

  1. Clasificación: utiliza la clasificación API RPC que acepta un tensor de entrada (por ejemplo, imagen) y genera una clase y una puntuación.
  2. Predicción y regresión: utiliza la API de predicción RPC que acepta un tensor de entrada (por ejemplo, imagen) y genera múltiples tensores como (para la detección de objetos) bounding_boxes, clases, puntajes, etc.

Como el problema en cuestión aquí es un problema de predicción, utilizaremos la API de predicción RPC. Para esto, necesitamos el protobuf de predicción disponible en el github de servicio TensorFlow, y debemos convertirlos a nuestro código específico de idioma (es decir, Python).

Puede hacerlo usted mismo o seguir el camino fácil y descargar los archivos de Python desde este repositorio de Github. Utilizaremos este objeto protobuf para crear una solicitud de predicción en nuestro cliente.

Plantilla de un cliente de predicción RPC

Stub es un fragmento de código que se utiliza para convertir parámetros durante una llamada a procedimiento remoto (RPC). Como el cliente y el servidor se sientan en diferentes espacios de direcciones, el parámetro enviado desde el cliente al servidor (y viceversa) debe convertirse para que la computadora del servidor remoto perciba el RPC como una llamada de función local. El código auxiliar utilizado aquí es el código generado a partir del protocolo de predicción como se describió anteriormente.

Lanzamiento del servicio de servicio TensorFlow

Como se describe en la parte anterior, nuestro servicio de servicio TensorFlow se ejecutará en un contenedor acoplable con puertos abiertos al mundo exterior. Suponiendo que la imagen del acoplador esté disponible, el contenedor se puede iniciar así:

$ docker run -it -d -P --name tf_serving_cpu -p 3000: 3000 gauravkaila / tf_serving_cpu

Aquí, el puerto 3000 está abierto al mundo y el cliente puede acceder al servicio de servicio TensorFlow a través de este puerto. Exporte el directorio del modelo creado en la primera parte a una carpeta dentro del contenedor:

$ docker cp / ruta / a / modelo tf_serving_cpu: / ruta / a / destino

Para ejecutar el servicio, muévase al contenedor y comience:

# Moverse al directorio de servicio /
porción de $ cd /
# Iniciar el servicio
$ bazel-bin / tensorflow_serving / model_servers / tensorflow_model_server
--port = 3000
--model_name = obj_det
--model_base_path = / path / to / dest &> obj_det &

Asegúrese de que el indicador model_name tenga el mismo nombre que el especificado en el cliente. La salida se registra en obj_det. Si todo salió bien, podrá ver la siguiente salida cuando escriba:

$ tail -f obj_det
tensorflow_serving / model_servers / main.cc: 288] Ejecutando ModelServer en 0.0.0.0:3000 ...

El modelo se está sirviendo y está listo para ser utilizado por nuestro cliente.

Visualice cuadros delimitadores en imágenes de prueba

El objetivo de un modelo de detección de objetos es visualizar los cuadros delimitadores de los objetos ubicados en la imagen. Para visualizar la imagen final con los cuadros delimitadores, utilizaremos el archivo visualization_utils.py de la API de detección de objetos TensorFlow.

Podemos acceder a las salidas individuales del resultado de esta manera:

cajas = resultado.salidas ['detección_cajas']. float_val
clases = resultado.salidas ['detección_clases']. float_val
puntajes = resultado.salidas ['detección_puntos']. float_val

Esto devuelve objetos protobuf que se pueden alimentar al archivo visualization_utils.py:

image_vis = vis_util.visualize_boxes_and_labels_on_image_array (
    {input_image},
    np.reshape (cajas, [100,4]),
    np.squeeze (clases) .astype (np.int32),
    np.squeeze (puntajes),
    index_categoría,
    use_normalized_coordinates = True,
    grosor_línea = 8)

El script final del cliente se verá así:

Salida final

Al enviar una imagen de prueba de un reloj, nuestro resultado final debería verse más o menos así. Nota: el modelo utilizado aquí es un RCNN más rápido pre-entrenado en el conjunto de datos COCO para el cual la clase número 85 corresponde a un reloj.

salidas {
clave: "detección_cajas"
valor {
dtype: DT_FLOAT
tensor_shape {
tenue {
tamaño: 1
}
tenue {
tamaño: 300
}
tenue {
tamaño: 4
}
}
float_val: 0.24750074744224548
float_val: 0.17159424722194672
float_val: 0.9083144068717957
float_val: 0.797699511051178
salidas {
clave: "clase_detección"
valor {
dtype: DT_FLOAT
tensor_shape {
tenue {
tamaño: 1
}
tenue {
tamaño: 300
}
}
float_val: 85.0
salidas {
clave: "puntuaciones_detección"
valor {
dtype: DT_FLOAT
tensor_shape {
tenue {
tamaño: 1
}
tenue {
tamaño: 300
}
}
float_val: 0.9963208436965942

Que hemos logrado

Comenzamos con un caso de uso de detección de objetos para demostrar el poder del servicio TensorFlow. Exportamos nuestro modelo entrenado a un formato esperado por el servicio TensorFlow, compilamos el servicio TF utilizando Docker y creamos un script de cliente que podría solicitar la inferencia del servidor modelo.

Qué depara el futuro

  1. Usando este caso de uso como plantilla, podemos usar el servicio TensorFlow para servir otros modelos de predicción y clasificación.
  2. Podemos aprovechar la versión de GPU de TensorFlow que sirve para lograr una inferencia más rápida.
  3. Podemos escalar nuestro servicio mediante la implementación de múltiples contenedores acoplables que ejecutan el servicio de servicio TF.
  4. Puede procesar imágenes de entrada por lotes en lugar de enviar una imagen por solicitud.

Recursos

  1. API de detección de objetos TensorFlow
  2. Este increíble blog sobre TF-Serving

Problemas de Github:

  • https://github.com/tensorflow/serving/issues/542
  • https://github.com/tensorflow/serving/issues/590
  • https://github.com/tensorflow/serving/issues/410
  • https://github.com/tensorflow/serving/issues/672
  • https://github.com/tensorflow/serving/issues/659

Sobre el autor: Gaurav es ingeniero de aprendizaje automático en The Dock, el principal centro de investigación e innovación de Accenture en Dublín, Irlanda. Sus intereses incluyen la construcción de sistemas escalables de aprendizaje profundo para aplicaciones de visión por computadora. Encuentre más en gauravkaila.com