
Durerea se simte doar prima dată!
Salutare tuturor! Dragi prieteni, în acest articol vreau să împărtășesc experiența mea de utilizare a TensorRT, RetinaNet bazat pe repository-ul (acesta este un fork al repo-ului oficial de la , care va permite să începeți să utilizați modele optimizate în producție într-un timp scurt). Răsfoind mesajele din canalele comunității , mă întâlnesc cu întrebări despre utilizarea TensorRT, iar întrebările în principal se repetă, așa că am decis să scriu un ghid cât mai complet pentru utilizarea inferenței rapide pe baza TensorRT, RetinaNet, Unet și docker.
Descrierea sarcinii
Îți propun să formulăm sarcina astfel: trebuie să etichetem un set de date, să antrenăm o rețea RetinaNet/Unet pe Pytorch1.3+, să transformăm greutățile obținute în ONNX, apoi să le convertim în engine TensorRT și să rulăm totul în docker, de preferat pe Ubuntu 18 și, foarte ideal, pe arhitectura ARM(Jetson)*, minimizând astfel desfășurarea manuală a mediului. În final, vom obține un container pregătit nu doar pentru export și antrenare RetinaNet/Unet, ci și pentru dezvoltarea și antrenarea completă a clasificării, segmentării cu tot suportul necesar.
Pasul 1. Pregătirea mediului
Este important de menționat că, în ultima vreme, m-am abținut complet de la utilizarea și desfășurarea oricăror biblioteci pe mașina desktop, la fel ca și pe devbox. Singurul lucru pe care trebuie să-l creez și să-l instalez este un mediu virtual python și cuda 10.2 (se poate limita la un singur driver nvidia) din deb.
Să presupunem că aveți o instalare proaspătă de Ubuntu 18. Să instalăm cuda 10.2(deb), nu voi detalia procesul de instalare, documentația oficială este destul de suficientă.
Acum să instalăm docker, un ghid pentru instalarea docker-ului poate fi găsit cu ușurință, iată un exemplu , deja este disponibilă versiunea 19+ — o instalăm. Și nu uitați să permiteți utilizarea docker fără sudo, va fi mai convenabil. După ce totul a funcționat, facem așa:
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
Și nu trebuie nici măcar să aruncăm o privire în depozitul oficial .
Acum facem git clone .
Mai este foarte puțin, pentru a începe să folosești docker cu imaginea nvidia, va trebui să te înregistrezi în NGC Cloud și să te loghezi. Mergem aici , ne înregistrăm și după ce ajungem în NGC Cloud, apăsăm SETUP în colțul din stânga sus al ecranului sau putem merge pe acest link . Apăsăm „generați cheie”. Îți recomand să o salvezi, altfel, la următoarea vizită va trebui să o generezi din nou și, în consecință, având o nouă mașină, va trebui să repeți această operație.
Executăm:
docker login nvcr.io
Username: $oauthtoken
Password: - cheia generată
Username îl copiem pur și simplu. Așa că, consideră că mediul este desfășurat!
Etapa 2. Construirea containerului docker
În a doua etapă a muncii noastre, vom construi docker și ne vom familiariza cu interiorul său.
Vom merge în folderul rădăcină în raport cu proiectul retina-examples și vom executa
docker build --build-arg USER=$USER --build-arg UID=$UID --build-arg GID=$GID --build-arg PW=alex -t retinanet:latest retinanet/
Construim docker transmitându-i utilizatorul curent — este foarte util dacă vei scrie ceva pe un VOLUME montat cu permisiunile utilizatorului curent, altfel va fi root și va fi complicat.
În timp ce se construiește docker, să studiem 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"]
După cum se poate observa din text, luăm toate bibliotecile noastre preferate, compilăm retinanet, adăugăm uneltele de bază pentru a facilita lucrul cu Ubuntu și configurăm serverul openssh. Prima linie se referă la moștenirea imaginii nvidia, pentru care ne-am autentificat în NGC Cloud și care conține Pytorch1.3, TensorRT6.x.x.x și multe alte biblioteci necesare pentru a compila codul sursă cpp al detectorului nostru.
Etapa 3. Pornirea și depanarea containerului docker
Să trecem la cazul principal de utilizare al containerului și mediului de dezvoltare, începând prin a porni nvidia docker. Să executăm:
docker run --gpus all --net=host -v /home/:/workspace/mounted_vol -d -P --rm --ipc=host -it retinanet:latestAcum containerul este accesibil prin ssh @localhost. După ce a fost pornit cu succes, deschidem proiectul în PyCharm. Apoi, deschidem
Settings->Project Interpreter->Add->Ssh Interpreter Pasul 1

Pasul 2

Pasul 3

Selectăm totul ca în capturile de ecran,
Interpreter -> /opt/conda/bin/python— aceasta va fi o legătură la Python3.6 și
Sync folder -> /workspace/retinanetFacem clic pe final, așteptăm indexarea, și gata, mediul este pregătit pentru utilizare!
IMPORTANT!!! Imediat după indexare, extrageți din docker fișierele compilate pentru Retinanet. În meniul contextual din directorul rădăcină al proiectului, selectăm opțiunea
Deployment->DownloadVor apărea un fișier și două foldere build, retinanet.egg-info și _C.so

Dacă proiectul dvs. arată astfel, atunci mediu vede toate fișierele necesare și suntem gata pentru antrenamentul RetinaNet.
Etapa 4. Etichetăm datele și antrenăm detectorul
Pentru etichetare, eu folosesc în principal — un instrument plăcut și convenabil, recent au fost corectate multe erori și a devenit semnificativ mai bun.
Să presupunem că ați etichetat dataset-ul și l-ați descărcat, dar nu îl puteți introduce imediat în RetinaNet, deoarece este în propriul său format și trebuie să-l convertiți în COCO. Instrumentul pentru conversie se află în:
markup_utils/supervisly_to_coco.pyRețineți că Category din script este un exemplu și trebuie să inserați propriile (categoria background nu trebuie adăugată)
categories = [{'id': 1, 'name': '1'},
{'id': 2, 'name': '2'},
{'id': 3, 'name': '3'},
{'id': 4, 'name': '4'}] Autorii originali ai repozitoriului au decis, dintr-un motiv oarecare, că nu veți antrena nimic altceva decât pentru COCO/VOC, așa că a fost necesar să editez puțin fișierul sursă
retinanet/dataset.pyAdăugând aici favoritele augmentări și eliminând categoriile integrate fixate în COCO. Există de asemenea posibilitatea de a tăia zone mari de detecție, dacă căutați obiecte mici pe imagini mari, aveți un dataset mic =), și nimic nu funcționează, dar despre aceasta într-o altă dată.
În general, bucla de antrenament este, de asemenea, slabă, inițial nu salva checkpoint-uri, folosea un scheduler groaznic etc. Dar acum mai trebuie doar să alegeți backbone-ul și să executați
/opt/conda/bin/python retinanet/main.pycu parametrii:
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
În consolă veți vedea:
Initializarea modelului...
model: RetinaNet
backbone: ResNet18FPN
clase: 2, ancore: 9
Nivel de optimizare selectat O0: Antrenament FP32 pur.
Impostazele pentru acest nivel de optimizare sunt:
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
Prelucrarea suprapunerilor utilizatorului (argumente suplimentare care nu sunt None)...
După procesarea suprapunerilor, opțiunile de optimizare sunt:
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
Pregătirea setului de date...
loader: pytorch
redimensionare: [1024, 1280], max: 1280
dispozitiv: 4 gpu
lot: 4, precizie: mixt
Antrenarea modelului pentru 20000 de iterații...
[ 1/20000] pierdere focală: 0.95619, pierdere box: 0.51584, 4.042s/4-lot (fw: 0.698s, bw: 0.459s), 1.0 im/s, lr: 0.0001
[ 12/20000] pierdere focală: 0.76191, pierdere box: 0.31794, 0.187s/4-lot (fw: 0.055s, bw: 0.133s), 21.4 im/s, lr: 0.0001
[ 24/20000] pierdere focală: 0.65036, pierdere box: 0.30269, 0.173s/4-lot (fw: 0.045s, bw: 0.128s), 23.1 im/s, lr: 0.0001
[ 36/20000] pierdere focală: 0.46425, pierdere box: 0.23141, 0.178s/4-lot (fw: 0.047s, bw: 0.131s), 22.4 im/s, lr: 0.0001
[ 48/20000] pierdere focală: 0.45115, pierdere box: 0.23505, 0.180s/4-lot (fw: 0.047s, bw: 0.133s), 22.2 im/s, lr: 0.0001
[ 59/20000] pierdere focală: 0.38958, pierdere box: 0.25373, 0.184s/4-lot (fw: 0.049s, bw: 0.134s), 21.8 im/s, lr: 0.0001
[ 71/20000] pierdere focală: 0.37733, pierdere box: 0.23988, 0.174s/4-lot (fw: 0.049s, bw: 0.125s), 22.9 im/s, lr: 0.0001
[ 83/20000] pierdere focală: 0.39514, pierdere box: 0.23878, 0.181s/4-lot (fw: 0.048s, bw: 0.133s), 22.1 im/s, lr: 0.0001
[ 94/20000] pierdere focală: 0.39947, pierdere box: 0.23817, 0.185s/4-lot (fw: 0.050s, bw: 0.134s), 21.6 im/s, lr: 0.0001
[ 105/20000] pierdere focală: 0.37343, pierdere box: 0.20238, 0.182s/4-lot (fw: 0.048s, bw: 0.134s), 22.0 im/s, lr: 0.0001
[ 116/20000] pierdere focală: 0.19689, pierdere box: 0.17371, 0.183s/4-lot (fw: 0.050s, bw: 0.132s), 21.8 im/s, lr: 0.0001
[ 128/20000] pierdere focală: 0.20368, pierdere box: 0.16538, 0.178s/4-lot (fw: 0.046s, bw: 0.131s), 22.5 im/s, lr: 0.0001
[ 140/20000] pierdere focală: 0.22763, pierdere box: 0.15772, 0.176s/4-lot (fw: 0.050s, bw: 0.126s), 22.7 im/s, lr: 0.0001
[ 148/20000] pierdere focală: 0.21997, pierdere box: 0.18400, 0.585s/4-lot (fw: 0.047s, bw: 0.144s), 6.8 im/s, lr: 0.0001
Precizia medie (AP) @[ IoU=0.50:0.95 | zona= toate | maxDets=100 ] = 0.52674
Precizia medie (AP) @[ IoU=0.50 | zona= toate | maxDets=100 ] = 0.91450
Precizia medie (AP) @[ IoU=0.75 | zona= toate | maxDets=100 ] = 0.35172
Precizia medie (AP) @[ IoU=0.50:0.95 | zona= mică | maxDets=100 ] = 0.61881
Precizia medie (AP) @[ IoU=0.50:0.95 | zona=medie | maxDets=100 ] = -1.00000
Precizia medie (AP) @[ IoU=0.50:0.95 | zona= mare | maxDets=100 ] = -1.00000
Recall mediu (AR) @[ IoU=0.50:0.95 | zona= toate | maxDets= 1 ] = 0.58824
Recall mediu (AR) @[ IoU=0.50:0.95 | zona= toate | maxDets= 10 ] = 0.61765
Recall mediu (AR) @[ IoU=0.50:0.95 | zona= toate | maxDets= 100 ] = 0.61765
Recall mediu (AR) @[ IoU=0.50:0.95 | zona= mică | maxDets=100 ] = 0.61765
Recall mediu (AR) @[ IoU=0.50:0.95 | zona=medie | maxDets=100 ] = -1.00000
Recall mediu (AR) @[ IoU=0.50:0.95 | zona= mare | maxDets=100 ] = -1.00000
Salvarea modelului: 148Pentru a studia întreaga gamă de parametri, consultați
retinanet/main.pyÎn general, acestea sunt standard pentru detecție și au o descriere. Rulați antrenamentul și așteptați rezultatele. Un exemplu de inferență poate fi văzut în:
retinanet/infer_example.pysau executați comanda:
/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
În depozit este deja integrat Focal Loss și mai multe backbone, de asemenea, este ușor să implementezi propriile variante.
retinanet/backbones/*.pyÎn tabel, autorii oferă câteva caracteristici:

De asemenea, există backbone ResNeXt50_32x4dFPN și ResNeXt101_32x8dFPN, preluate din torchvision.
Sper că am clarificat un pic detecția, dar este necesar să citești documentația oficială pentru a înțelege modelele de export și de jurnalizare..
Etapa 5. Exportul și inferența modelele Unet cu encoder Resnet.
După cum probabil ați observat, în Dockerfile au fost instalate biblioteci pentru segmentare și în special o bibliotecă minunată . În pachetul Unet pot fi găsite exemple de inferență și export de checkpoint-uri pytorch în motorul TensorRT.
Principala problemă la exportarea modelelor de tip Unet din ONNX în TensoRT este necesitatea de a specifica o dimensiune fixă pentru Upsample sau de a folosi ConvTranspose2D:
import torch.onnx.symbolic_opset9 as onnx_symbolic
def upsample_nearest2d(g, input, output_size):
# În prezent, parserul ONNX pentru TRT 5.1/6.0 nu suportă toate operațiile ONNX
# necesare pentru a susține formularea dinamică a upsampling-ului ONNX
# Aici, codificăm manual scala=2 ca o soluție temporară
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
Cu această transformare, acest lucru poate fi realizat automat la exportul în ONNX, dar deja în versiunea 7 a TensorRT această problemă a fost rezolvată și nu mai trebuie să așteptăm mult.
Concluzie
Când am început să folosesc docker, am avut îndoieli în legătură cu performanța sa pentru sarcinile mele. În unul dintre agregatele mele, acum există un trafic de rețea destul de mare generat de mai multe camere.

Diverse teste pe internet indicau o supraîncărcare relativ mare în ceea ce privește interacțiunea în rețea și scrierea pe VOLUME, plus în plus necunoscutul și înfricoșătorul GIL, și deoarece capturarea unei imagini, funcționarea driverului și transmiterea imaginii prin rețea sunt operațiuni atomice în modul hard real-time, întârzierile din rețea sunt foarte critice pentru mine.
Dar totul s-a rezolvat =)
P.S. Rămâne să adăugați bucla preferată de antrenare pentru segmentare și în producție!
Mulțumiri
Mulțumesc comunității , fără de care nu este posibil să te dezvolți! Un imens mulțumesc , care m-a îndrumat să mă ocup de DL, pentru sfaturile sale neprețuite și profesionalismul extraordinar!
Folosiți modele optimizate în producție!
Aurorai, llc
Sursa: habr.com
