TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)
¡Solo duele la primera vez!

¡Hola a todos! Queridos amigos, en este artículo quiero compartir mi experiencia utilizando TensorRT y RetinaNet basada en el repositorio github.com/aidonchuk/retinanet-examples (este es un fork del repositorio oficial de nvidia, que permitirá empezar a utilizar modelos optimizados en producción en el menor tiempo posible). Al revisar los mensajes en los canales de la comunidad ods.ai, me encuentro con preguntas sobre el uso de TensorRT, y la mayoría de las preguntas se repiten, por lo que decidí escribir una guía lo más completa posible sobre cómo utilizar la inferencia rápida basada en TensorRT, RetinaNet, Unet y docker.

Descripción de la tarea

Propongo abordar la tarea de la siguiente manera: necesitamos etiquetar el conjunto de datos, entrenar una red RetinaNet/Unet en Pytorch1.3+, transformar los pesos obtenidos a ONNX, luego convertirlos a un engine de TensorRT y ejecutar todo esto en docker, preferiblemente en Ubuntu 18 y muy preferiblemente en arquitectura ARM(Jetson)*, minimizando así el despliegue manual del entorno. Al final obtendremos un contenedor listo no solo para la exportación y entrenamiento de RetinaNet/Unet, sino también para el desarrollo completo y aprendizaje de clasificación y segmentación con todos los accesorios necesarios.

Fase 1. Preparación del entorno

Aquí es importante notar que en los últimos tiempos he dejado de usar y desplegar cualquier biblioteca en la máquina de escritorio, así como en el devbox. Lo único que tengo que crear e instalar es un entorno virtual de python y CUDA 10.2 (se puede limitar a solo el controlador de NVIDIA) desde deb.

Supongamos que tienes Ubuntu 18 recién instalado. Instalemos CUDA 10.2 (deb), no me detendré en el proceso de instalación, la documentación oficial es más que suficiente.

Ahora instalemos docker, se puede encontrar fácilmente una guía para la instalación de docker, aquí hay un ejemplo www.digitalocean.com/community/tutorials/docker-ubuntu-18-04-1-es, ya está disponible la versión 19+, así que la instalamos. Y no olvides permitir el uso de docker sin sudo, será más conveniente. Después de que todo funcione, hacemos lo siguiente:

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker

Y ni siquiera es necesario mirar el repositorio oficial github.com/NVIDIA/nvidia-docker.

Ahora hacemos git clone github.com/aidonchuk/retinanet-examples.

Queda muy poco para comenzar a usar Docker con la imagen de NVIDIA, necesitamos registrarnos en NGC Cloud y hacer login. Vamos aquí ngc.nvidia.com, nos registramos y después de ingresar a NGC Cloud, hacemos clic en SETUP en la esquina superior izquierda de la pantalla o seguimos este enlace ngc.nvidia.com/setup/api-key. Hacemos clic en 'generar clave'. Te recomiendo guardarla, de lo contrario, la próxima vez que ingreses, tendrás que generarla nuevamente y, por lo tanto, al desplegar en una nueva máquina, deberás repetir esta operación.

Ejecutemos:

docker login nvcr.io
Username: $oauthtoken
Password:  - clave generada

Simplemente copiamos el nombre de usuario. Bueno, eso es, la configuración ya está lista.

Etapa 2. Construcción del contenedor Docker

En la segunda etapa de nuestro trabajo, construiremos Docker y nos familiarizaremos con sus interiores.
Vamos a la carpeta raíz en relación al proyecto retina-examples y ejecutamos

docker build --build-arg USER=$USER --build-arg UID=$UID --build-arg GID=$GID --build-arg PW=alex -t retinanet:latest retinanet/

Estamos construyendo Docker pasando el usuario actual, lo cual es muy útil si vas a escribir en un VOLUME montado con los derechos del usuario actual, de lo contrario será root y complicado.

Mientras Docker se construye, vamos a estudiar el Dockerfile:

FROM nvcr.io/nvidia/pytorch:19.10-py3

ARG USER=alex
ARG UID=1000
ARG GID=1000
ARG PW=alex
RUN useradd -m ${USER} --uid=${UID} && echo "${USER}:${PW}" | chpasswd

RUN apt-get -y update && apt-get -y upgrade && apt-get -y install curl && apt-get -y install wget && apt-get -y install git && apt-get -y install automake && apt-get install -y sudo && adduser ${USER} sudo
RUN pip install git+https://github.com/bonlime/pytorch-tools.git@master

COPY . retinanet/
RUN pip install --no-cache-dir -e retinanet/
RUN pip install /workspace/retinanet/extras/tensorrt-6.0.1.5-cp36-none-linux_x86_64.whl
RUN pip install tensorboardx
RUN pip install albumentations
RUN pip install setproctitle
RUN pip install paramiko
RUN pip install flask
RUN pip install mem_top
RUN pip install arrow
RUN pip install pycuda
RUN pip install torchvision
RUN pip install pretrainedmodels
RUN pip install efficientnet-pytorch
RUN pip install git+https://github.com/qubvel/segmentation_models.pytorch
RUN pip install pytorch_toolbelt

RUN chown -R ${USER}:${USER} retinanet/

RUN cd /workspace/retinanet/extras/cppapi && mkdir build && cd build && cmake -DCMAKE_CUDA_FLAGS="--expt-extended-lambda -std=c++14" .. && make && cd /workspace

RUN apt-get install -y openssh-server && apt install -y tmux && apt-get -y install bison flex && apt-cache search pcre && apt-get -y install net-tools && apt-get -y install nmap
RUN apt-get -y install libpcre3 libpcre3-dev && apt-get -y install iputils-ping

RUN mkdir /var/run/sshd
RUN echo 'root:pass' | chpasswd
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed 's@sessions*requireds*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
CMD ["/usr/sbin/sshd", "-D"]

Como se puede ver en el texto, recopilamos todas nuestras librerías favoritas, compilamos retinanet, añadimos herramientas básicas para trabajar cómodamente con Ubuntu y configuramos el servidor openssh. La primera línea es precisamente la herencia de la imagen nvidia, para la cual hicimos login en NGC Cloud y que contiene Pytorch1.3, TensorRT6.x.x.x y un montón de librerías que permiten compilar el código fuente cpp de nuestro detector.

Etapa 3. Ejecución y depuración del contenedor docker

Pasemos al caso principal de uso del contenedor y del entorno de desarrollo, primero ejecutaremos nvidia docker. Ejecutamos:

docker run --gpus all --net=host -v /home/:/workspace/mounted_vol -d -P --rm --ipc=host -it retinanet:latest

Ahora el contenedor está disponible por ssh @localhost. Después de un inicio exitoso, abrimos el proyecto en PyCharm. A continuación, abrimos

Configuración->Intérprete del proyecto->Añadir->Intérprete Ssh

Compra una suscripción
TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

Paso 2
TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

Paso 3
TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

Seleccionamos todo como en las capturas de pantalla,

Intérprete -> /opt/conda/bin/python

— esto será un enlace en Python3.6 y

Carpeta de sincronización -> /workspace/retinanet

Hacemos clic en finalizar, esperamos la indexación, ¡y listo, el entorno está listo para usarse!

¡IMPORTANTE! Inmediatamente después de la indexación, extraer los archivos compilados de docker para Retinanet. En el menú contextual en la raíz del proyecto, seleccionamos la opción

Despliegue->Descargar

Aparecerá un archivo y dos carpetas build, retinanet.egg-info y _С.so

TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

Si su proyecto se ve así, entonces el entorno ve todos los archivos necesarios y estamos listos para el entrenamiento de RetinaNet.

Etapa 4. Etiquetamos los datos y entrenamos el detector

Para la etiquetación, generalmente uso supervise.ly — una herramienta agradable y conveniente, últimamente han corregido muchos errores y ha mejorado significativamente su funcionamiento.

Supongamos que ha etiquetado el conjunto de datos y lo ha descargado, pero no se puede meter inmediatamente en nuestro RetinaNet ya que está en su propio formato y necesitamos convertirlo a COCO. La herramienta para la conversión se encuentra en:

markup_utils/supervisly_to_coco.py

Tenga en cuenta que Category en el script es un ejemplo y debe insertar los suyos (no es necesario agregar la categoría de fondo)

categories = [{'id': 1, 'name': '1'}, 
                  {'id': 2, 'name': '2'}, 
                  {'id': 3, 'name': '3'},
                  {'id': 4, 'name': '4'}] 

Los autores del repositorio original decidieron, de alguna manera, que no entrenarían nada más que COCO/VOC para detección, así que tuve que editar un poco el archivo fuente

retinanet/dataset.py

Agregando aquí mis aumentaciones favoritas albumentations.readthedocs.io/en/latest y eliminar las categorías incrustadas de COCO. También hay la posibilidad de recortar grandes áreas de detección si está buscando pequeños objetos en imágenes grandes, tiene un conjunto de datos pequeño =), y nada funciona, pero de esto hablaremos en otra ocasión.

En general, el bucle de entrenamiento también es débil, inicialmente no guardaba puntos de control, utilizaba un programador horrible, etc. Pero ahora solo les queda elegir el backbone y ejecutar.

/opt/conda/bin/python retinanet/main.py

con los parámetros:

train retinanet_rn34fpn.pth
--backbone ResNet34FPN
--classes 12
--val-iters 10
--images /workspace/mounted_vol/dataset/train/images
--annotations /workspace/mounted_vol/dataset/train_12_class.json
--val-images /workspace/mounted_vol/dataset/test/images_small
--val-annotations /workspace/mounted_vol/dataset/val_10_class_cropped.json
--jitter 256 512
--max-size 512
--batch 32

Verás en la consola:

Inicializando el modelo...
     modelo: RetinaNet
  backbone: ResNet18FPN
   clases: 2, anclas: 9
Nivel de optimización seleccionado O0: Entrenamiento puro FP32.

Los valores predeterminados para este nivel de optimización son:
enabled                : True
opt_level              : O0
cast_model_type        : torch.float32
patch_torch_functions  : False
keep_batchnorm_fp32    : None
master_weights         : False
loss_scale             : 1.0
Procesando sobrecargas de usuario (argumentos adicionales que no son None)...
Después de procesar las sobrecargas, las opciones de optimización son:
enabled                : True
opt_level              : O0
cast_model_type        : torch.float32
patch_torch_functions  : False
keep_batchnorm_fp32    : None
master_weights         : False
loss_scale             : 128.0
Preparando el conjunto de datos...
    cargador: pytorch
    redimensionar: [1024, 1280], máximo: 1280
    dispositivo: 4 gpus
    lote: 4, precisión: mixta
Entrenando el modelo por 20000 iteraciones...
[    1/20000] pérdida focal: 0.95619, pérdida de caja: 0.51584, 4.042s/4-batch (fw: 0.698s, bw: 0.459s), 1.0 im/s, lr: 0.0001
[   12/20000] pérdida focal: 0.76191, pérdida de caja: 0.31794, 0.187s/4-batch (fw: 0.055s, bw: 0.133s), 21.4 im/s, lr: 0.0001
[   24/20000] pérdida focal: 0.65036, pérdida de caja: 0.30269, 0.173s/4-batch (fw: 0.045s, bw: 0.128s), 23.1 im/s, lr: 0.0001
[   36/20000] pérdida focal: 0.46425, pérdida de caja: 0.23141, 0.178s/4-batch (fw: 0.047s, bw: 0.131s), 22.4 im/s, lr: 0.0001
[   48/20000] pérdida focal: 0.45115, pérdida de caja: 0.23505, 0.180s/4-batch (fw: 0.047s, bw: 0.133s), 22.2 im/s, lr: 0.0001
[   59/20000] pérdida focal: 0.38958, pérdida de caja: 0.25373, 0.184s/4-batch (fw: 0.049s, bw: 0.134s), 21.8 im/s, lr: 0.0001
[   71/20000] pérdida focal: 0.37733, pérdida de caja: 0.23988, 0.174s/4-batch (fw: 0.049s, bw: 0.125s), 22.9 im/s, lr: 0.0001
[   83/20000] pérdida focal: 0.39514, pérdida de caja: 0.23878, 0.181s/4-batch (fw: 0.048s, bw: 0.133s), 22.1 im/s, lr: 0.0001
[   94/20000] pérdida focal: 0.39947, pérdida de caja: 0.23817, 0.185s/4-batch (fw: 0.050s, bw: 0.134s), 21.6 im/s, lr: 0.0001
[  105/20000] pérdida focal: 0.37343, pérdida de caja: 0.20238, 0.182s/4-batch (fw: 0.048s, bw: 0.134s), 22.0 im/s, lr: 0.0001
[  116/20000] pérdida focal: 0.19689, pérdida de caja: 0.17371, 0.183s/4-batch (fw: 0.050s, bw: 0.132s), 21.8 im/s, lr: 0.0001
[  128/20000] pérdida focal: 0.20368, pérdida de caja: 0.16538, 0.178s/4-batch (fw: 0.046s, bw: 0.131s), 22.5 im/s, lr: 0.0001
[  140/20000] pérdida focal: 0.22763, pérdida de caja: 0.15772, 0.176s/4-batch (fw: 0.050s, bw: 0.126s), 22.7 im/s, lr: 0.0001
[  148/20000] pérdida focal: 0.21997, pérdida de caja: 0.18400, 0.585s/4-batch (fw: 0.047s, bw: 0.144s), 6.8 im/s, lr: 0.0001
 Precisión promedio  (AP) @[ IoU=0.50:0.95 | área=   todas | maxDets=100 ] = 0.52674
 Precisión promedio  (AP) @[ IoU=0.50      | área=   todas | maxDets=100 ] = 0.91450
 Precisión promedio  (AP) @[ IoU=0.75      | área=   todas | maxDets=100 ] = 0.35172
 Precisión promedio  (AP) @[ IoU=0.50:0.95 | área= pequeña | maxDets=100 ] = 0.61881
 Precisión promedio  (AP) @[ IoU=0.50:0.95 | área=media | maxDets=100 ] = -1.00000
 Precisión promedio  (AP) @[ IoU=0.50:0.95 | área= grande | maxDets=100 ] = -1.00000
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área=   todas | maxDets=  1 ] = 0.58824
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área=   todas | maxDets= 10 ] = 0.61765
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área=   todas | maxDets=100 ] = 0.61765
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área= pequeña | maxDets=100 ] = 0.61765
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área=media | maxDets=100 ] = -1.00000
 Recall promedio     (AR) @[ IoU=0.50:0.95 | área= grande | maxDets=100 ] = -1.00000
Guardando modelo: 148

Para ver todo el conjunto de parámetros, consulte

retinanet/main.py

En general, son estándar para la detección y tienen una descripción. Inicie el entrenamiento y espere los resultados. Puede ver un ejemplo de inferencia en:

retinanet/infer_example.py

o ejecute el comando:

/opt/conda/bin/python retinanet/main.py infer retinanet_rn34fpn.pth 
--images /workspace/mounted_vol/dataset/test/images 
--annotations /workspace/mounted_vol/dataset/val.json 
--output result.json 
--resize 256 
--max-size 512 
--batch 32

El repositorio ya incluye Focal Loss y varios backbones, así como la posibilidad de integrar fácilmente los propios.

retinanet/backbones/*.py

En la tabla, los autores presentan algunas características:

TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

También hay backbone ResNeXt50_32x4dFPN y ResNeXt101_32x8dFPN, extraídos de torchvision.
Espero que hayamos aclarado un poco sobre la detección, pero es fundamental leer la documentación oficial para entender los modos de exportación y registro..

Etapa 5. Exportación e inferencia de modelos Unet con el codificador Resnet.

Como habrán notado, en el Dockerfile se instalaron bibliotecas para segmentación, incluyendo una maravillosa librería github.com/qubvel/segmentation_models.pytorch.En el paquete Unet, se pueden encontrar ejemplos de inferencia y exportación de puntos de control de pytorch en el motor TensorRT.

El principal problema al exportar modelos similares a Unet de ONNX a TensorRT es la necesidad de especificar un tamaño fijo para Upsample o utilizar ConvTranspose2D:

import torch.onnx.symbolic_opset9 as onnx_symbolic
        def upsample_nearest2d(g, input, output_size):
            # Actualmente, el Parser ONNX de TRT 5.1/6.0 no soporta todas las operaciones ONNX
            # necesarias para soportar la formulación dinámica de upsampling
            # Aquí codificamos a mano scale=2 como una solución temporal
            scales = g.op("Constant", value_t=torch.tensor([1., 1., 2., 2.]))
            return g.op("Upsample", input, scales, mode_s="nearest")

        onnx_symbolic.upsample_nearest2d = upsample_nearest2d

Con esta transformación, se puede hacer automáticamente al exportar a ONNX, pero en la versión 7 de TensorRT se resolvió este problema, y solo nos queda esperar un poco más.

Conclusión

Cuando empecé a utilizar Docker, tenía dudas sobre su rendimiento para mis tareas. En uno de mis agregados, ahora hay un tráfico de red bastante grande generado por varias cámaras.

TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación)

Diferentes pruebas en internet hablaban de un overhead relativamente alto en la interacción de red y la grabación en VOLUME, además del temido y misterioso GIL, y dado que la toma de un cuadro, el funcionamiento del controlador y la transmisión de un cuadro por la red son operaciones atómicas en modo hard real-time, los retardos en la red son muy críticos para mí.

Pero todo salió bien =)

P.D. Solo queda añadir su bucle de entrenamiento favorito para segmentación y ¡a producción!

Agradecimientos

Gracias a la comunidad ods.ai, sin la cual no sería posible desarrollarse. Muchas gracias n01z3, quien me inspiró a involucrarme en DL, por sus valiosos consejos y su extraordinario profesionalismo.

¡Utilicen modelos optimizados en producción!

TensorRT 6.x.x.x — inferencia de alto rendimiento para modelos de aprendizaje profundo (Detección de Objetos y Segmentación) Aurorai, llc

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster