
Fa male solo la prima volta!
Ciao a tutti! Cari amici, in questo articolo voglio condividere la mia esperienza nell'utilizzo di TensorRT, RetinaNet basato sul repository (questa è una forchetta della rapa ufficiale da , che ti consentirà di iniziare a utilizzare i modelli ottimizzati in produzione il prima possibile). Scorrimento dei messaggi nei canali della community , sto riscontrando domande sull'utilizzo di TensorRT e le domande sono per lo più ripetitive, quindi ho deciso di scrivere il più completo possibile Una guida all'utilizzo dell'inferenza rapida basata su TensorRT, RetinaNet, Unet e docker.
Descrizione del compito
Propongo di formulare il compito in questo modo: dobbiamo etichettare il dataset, addestrare la rete RetinaNet/Unet su Pytorch 1.3+, convertire i pesi ottenuti in ONNX, quindi convertirli nel motore TensorRT ed eseguire il tutto in Docker, preferibilmente su Ubuntu 18 e altamente desiderabile sull'architettura ARM (Jetson)*, minimizzando così l'implementazione manuale dell'ambiente. Il risultato finale sarà un container pronto non solo per l'esportazione e l'addestramento di RetinaNet/Unet, ma anche per lo sviluppo e l'addestramento completi di sistemi di classificazione e segmentazione, con tutto l'hardware necessario.
Fase 1. Preparazione dell'ambiente
È importante notare qui che recentemente ho completamente abbandonato l'uso e la distribuzione di almeno alcune librerie su una macchina desktop, oltre che su devbox. L'unica cosa che devi creare e installare è l'ambiente virtuale Python e cuda 10.2 (puoi limitarti a un driver nvidia) da deb.
Supponiamo che tu abbia appena installato Ubuntu 18. Installiamo CUDA 10.2 (in formato .deb). Non entrerò nei dettagli del processo di installazione, la documentazione ufficiale è più che sufficiente.
Ora installiamo la finestra mobile, la guida all'installazione della finestra mobile può essere facilmente trovata, ecco un esempio , la versione 19+ è già disponibile: installala. Bene, non dimenticare di rendere possibile l'utilizzo della finestra mobile senza sudo, sarà più conveniente. Dopo che tutto ha funzionato, facciamo questo:
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
E non devi nemmeno guardare il repository ufficiale .
Ora eseguiamo git clone .
Manca solo un po', per iniziare a utilizzare docker con un'immagine nvidia, dovremo registrarci su NGC Cloud ed effettuare il login. Andiamo qui , registrati e una volta entrato in NGC Cloud, fai clic su SETUP nell'angolo in alto a sinistra dello schermo o segui questo collegamento . Fare clic su "genera chiave". Ti consiglio di salvarlo, altrimenti la prossima volta che lo visiterai dovrai generarlo di nuovo e, di conseguenza, distribuirlo su una nuova auto e ripetere questa operazione.
Facciamo:
docker login nvcr.io
Username: $oauthtoken
Password: <Your Key> - сгенерированный ключ
Il nome utente viene semplicemente copiato. Bene, considera l'ambiente distribuito!
Fase 2: costruzione del contenitore docker
Nella seconda fase del nostro lavoro, costruiremo la finestra mobile e ne conosceremo i componenti interni.
Andiamo alla cartella principale in relazione al progetto retina-examples ed eseguiamo
docker build --build-arg USER=$USER --build-arg UID=$UID --build-arg GID=$GID --build-arg PW=alex -t retinanet:latest retinanet/
Costruiamo la finestra mobile passandovi l'utente corrente: questo è molto utile se scrivi qualcosa su un VOLUME montato con i diritti dell'utente corrente, altrimenti sarà root e dolore.
Durante la creazione del docker, esaminiamo il 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"]
Come puoi vedere dal testo, prendiamo tutte le nostre librerie preferite, compiliamo retinanet, aggiungiamo alcuni strumenti di base per facilitare il lavoro con Ubuntu e configura il server OpenSSH. La prima riga eredita l'immagine NVIDIA per la quale abbiamo creato l'accesso a NGC Cloud e che contiene Pytorch 1.3, TensorRT 6.xxx e una serie di altre librerie che ci consentono di compilare il codice sorgente C++ per il nostro rivelatore.
Fase 3: avvio e debug del contenitore Docker
Passiamo al caso principale dell'utilizzo del contenitore e dell'ambiente di sviluppo; innanzitutto, avviamo nvidia docker. Facciamo:
docker run --gpus all --net=host -v /home/<your_user_name>:/workspace/mounted_vol -d -P --rm --ipc=host -it retinanet:latestIl contenitore è ora accessibile tramite ssh @localhost. Dopo il lancio riuscito, apri il progetto in PyCharm. Successivamente apriamo
Settings->Project Interpreter->Add->Ssh Interpreter Passo 1

Passo 2

Passo 3

Selezioniamo tutto come negli screenshot,
Interpreter -> /opt/conda/bin/python- questo sarà ln in Python3.6 e
Sync folder -> /workspace/retinanetPremiamo Fine, aspettiamo l'indicizzazione e il gioco è fatto, l'ambiente è pronto per l'uso!
IMPORTANTE! Immediatamente dopo l'indicizzazione, estrarre i file compilati per Retinanet dalla finestra mobile. Nel menu contestuale nella radice del progetto, seleziona l'elemento
Deployment->DownloadAppariranno un file e due cartelle: build, retinanet.egg-info e _С.so

Se il tuo progetto assomiglia a questo, l'ambiente vede tutti i file necessari e siamo pronti per addestrare RetinaNet.
Fase 4. Etichettare i dati e addestrare il rilevatore
Per il markup utilizzo principalmente — uno strumento piacevole e conveniente, recentemente sono stati corretti un sacco di bug e si è comportato molto meglio.
Supponiamo che tu abbia contrassegnato il set di dati e lo abbia scaricato, ma non potrai inserirlo immediatamente nel nostro RetinaNet, poiché ha un formato proprio e per questo dobbiamo convertirlo in COCO. Lo strumento di conversione si trova in:
markup_utils/supervisly_to_coco.pyTieni presente che la categoria nello script è un esempio e devi inserirne una tua (non è necessario aggiungere la categoria di sfondo)
categories = [{'id': 1, 'name': '1'},
{'id': 2, 'name': '2'},
{'id': 3, 'name': '3'},
{'id': 4, 'name': '4'}] Per qualche ragione, gli autori del repository originale hanno deciso che non avresti addestrato nient'altro che COCO/VOC per il rilevamento, quindi hanno dovuto modificare leggermente il file sorgente
retinanet/dataset.pyAggiungendo qui i tuoi potenziamenti preferiti ed eliminare le categorie cablate da COCO. È anche possibile ritagliare grandi aree di rilevamento, se stai cercando piccoli oggetti in immagini grandi, hai un piccolo set di dati =), e non funziona nulla, ma ne parleremo un'altra volta.
In generale, anche il circuito del treno è debole, inizialmente non salvava i checkpoint, utilizzava una sorta di terribile programmatore, ecc. Ma ora tutto ciò che devi fare è selezionare la spina dorsale ed eseguirla
/opt/conda/bin/python retinanet/main.pycon parametri:
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
Nella console vedrai:
Initializing model...
model: RetinaNet
backbone: ResNet18FPN
classes: 2, anchors: 9
Selected optimization level O0: Pure FP32 training.
Defaults for this optimization level are:
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
Processing user overrides (additional kwargs that are not None)...
After processing overrides, optimization options are:
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
Preparing dataset...
loader: pytorch
resize: [1024, 1280], max: 1280
device: 4 gpus
batch: 4, precision: mixed
Training model for 20000 iterations...
[ 1/20000] focal loss: 0.95619, box loss: 0.51584, 4.042s/4-batch (fw: 0.698s, bw: 0.459s), 1.0 im/s, lr: 0.0001
[ 12/20000] focal loss: 0.76191, box loss: 0.31794, 0.187s/4-batch (fw: 0.055s, bw: 0.133s), 21.4 im/s, lr: 0.0001
[ 24/20000] focal loss: 0.65036, box loss: 0.30269, 0.173s/4-batch (fw: 0.045s, bw: 0.128s), 23.1 im/s, lr: 0.0001
[ 36/20000] focal loss: 0.46425, box loss: 0.23141, 0.178s/4-batch (fw: 0.047s, bw: 0.131s), 22.4 im/s, lr: 0.0001
[ 48/20000] focal loss: 0.45115, box loss: 0.23505, 0.180s/4-batch (fw: 0.047s, bw: 0.133s), 22.2 im/s, lr: 0.0001
[ 59/20000] focal loss: 0.38958, box loss: 0.25373, 0.184s/4-batch (fw: 0.049s, bw: 0.134s), 21.8 im/s, lr: 0.0001
[ 71/20000] focal loss: 0.37733, box loss: 0.23988, 0.174s/4-batch (fw: 0.049s, bw: 0.125s), 22.9 im/s, lr: 0.0001
[ 83/20000] focal loss: 0.39514, box loss: 0.23878, 0.181s/4-batch (fw: 0.048s, bw: 0.133s), 22.1 im/s, lr: 0.0001
[ 94/20000] focal loss: 0.39947, box loss: 0.23817, 0.185s/4-batch (fw: 0.050s, bw: 0.134s), 21.6 im/s, lr: 0.0001
[ 105/20000] focal loss: 0.37343, box loss: 0.20238, 0.182s/4-batch (fw: 0.048s, bw: 0.134s), 22.0 im/s, lr: 0.0001
[ 116/20000] focal loss: 0.19689, box loss: 0.17371, 0.183s/4-batch (fw: 0.050s, bw: 0.132s), 21.8 im/s, lr: 0.0001
[ 128/20000] focal loss: 0.20368, box loss: 0.16538, 0.178s/4-batch (fw: 0.046s, bw: 0.131s), 22.5 im/s, lr: 0.0001
[ 140/20000] focal loss: 0.22763, box loss: 0.15772, 0.176s/4-batch (fw: 0.050s, bw: 0.126s), 22.7 im/s, lr: 0.0001
[ 148/20000] focal loss: 0.21997, box loss: 0.18400, 0.585s/4-batch (fw: 0.047s, bw: 0.144s), 6.8 im/s, lr: 0.0001
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.52674
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.91450
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.35172
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.61881
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.00000
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.00000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.58824
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.61765
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.61765
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.61765
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.00000
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.00000
Saving model: 148Per esplorare l'intero set di parametri, guarda
retinanet/main.pyIn generale, sono standard per il rilevamento e hanno una descrizione. Inizia la formazione e attendi i risultati. Un esempio di inferenza può essere visto in:
retinanet/infer_example.pyoppure esegui il 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
Il repository ha già Focal Loss e diversi backbone integrati ed è anche facile incorporarne uno tuo
retinanet/backbones/*.pyNella tabella gli autori danno alcune caratteristiche:

Sono inoltre presenti i backbone ResNeXt50_32x4dFPN e ResNeXt101_32x8dFPN, presi da torchvision.
Spero che tu abbia capito un po' il rilevamento, ma dovresti assolutamente leggere la documentazione ufficiale in tal senso comprendere le modalità di esportazione e registrazione.
Fase 5. Esportazione e inferenza di modelli Unet con codificatore Resnet
Come probabilmente avrai notato, nel Dockerfile sono state installate le librerie per la segmentazione, e in particolare la meravigliosa lib . Nel pacchetto unitet puoi trovare esempi di inferenza ed esportazione di checkpoint pytorch sul motore TensorRT.
Il problema principale quando si esportano modelli simili a Unet da ONNX a TensoRT è la necessità di impostare una dimensione Upsample fissa o utilizzare ConvTranspose2D:
import torch.onnx.symbolic_opset9 as onnx_symbolic
def upsample_nearest2d(g, input, output_size):
# Currently, TRT 5.1/6.0 ONNX Parser does not support all ONNX ops
# needed to support dynamic upsampling ONNX forumlation
# Here we hardcode scale=2 as a temporary workaround
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
Utilizzando questa trasformazione, puoi farlo automaticamente durante l'esportazione in ONNX, ma già nella versione 7 di TensorRT questo problema è stato risolto e dobbiamo aspettare un bel po'.
conclusione
Quando ho iniziato a utilizzare docker, avevo dei dubbi sulle sue prestazioni per le mie attività. Una delle mie unità attualmente ha molto traffico di rete generato da diverse telecamere.

Vari test su Internet hanno parlato di un sovraccarico relativamente grande per l'interazione di rete e la registrazione su VOLUME, oltre allo sconosciuto e terribile GIL, e poiché catturare un frame, azionare il driver e trasmettere il frame sulla rete è un'operazione atomica nella modalità difficile in tempo reale, i ritardi della rete sono molto critici per me.
Ma è andato tutto bene =)
PS Non resta che aggiungere il tuo circuito ferroviario preferito per la segmentazione e la produzione!
Grazie
Grazie alla comunità , senza di essa è impossibile svilupparsi! Molte grazie , che mi ha incoraggiato a fare DL, per i suoi preziosi consigli e la sua estrema professionalità!
Utilizza modelli ottimizzati in produzione!
Aurorai, LLC
Fonte: habr.com
