
Boli le samo prvi put!
Zdravo svima! Dragi prijatelji, u ovom članku želim da podelim svoje iskustvo korišćenja TensorRT-a, RetinaNet baziranog na repozitorijumu (to je fork zvanične repozitorije od , koji omogućava brzo korišćenje optimizovanih modela u produkciji). Dok pretražujem poruke u zajednici , suočavam se sa pitanjima o korišćenju TensorRT-a, a većina pitanja se ponavlja, pa sam odlučio da napišem što potpunije vodič o korišćenju brzog inferensa zasnovanog na TensorRT-u, RetinaNet-u, Unet-u i docker-u.
Opis naloge
Prvo, postavićemo zadatak: potrebno je da označimo dataset, obučimo mrežu RetinaNet/Unet na Pytorch-u 1.3+, pretvorimo dobijene težine u ONNX, zatim ih konvertujemo u TensorRT engine i sve to pokrenemo u docker-u, poželjno na Ubuntu 18 i izrazito poželjno na ARM (Jetson)* arhitekturi, čime minimiziramo ručno postavljanje okruženja. Na kraju dobijamo kontejner spreman ne samo za izvoz i obučavanje RetinaNet/Unet-a, već i za kompletnu razvoj i obuku klasifikacije, segmentacije sa svom potrebnom opremom.
Faza 1. Priprema okruženja
Važno je napomenuti da sam u poslednje vreme potpuno prestao sa korišćenjem i postavljanjem bilo kojih biblioteka na desktop mašini, kao i na devbox-u. Jedino što treba da se kreira i instalira je python virtual environment i cuda 10.2 (može se ograničiti na samo nvidia drajver) iz deb.
Pretpostavimo da imate sveže instaliran Ubuntu 18. Instalirajmo cuda 10.2 (deb), neću se zadržavati na procesu instalacije, zvanična dokumentacija je sasvim dovoljno.
Sada instalirajmo docker, uputstvo za instalaciju dockera lako možete pronaći, evo primera , verzija 19+ je već dostupna — instalirajte je. I ne zaboravite omogućiti korišćenje dockera bez sudo-a, biće praktičnije. Nakon što je sve uspelo, uradite ovako:
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
Možda čak nećete morati da zavirujete u zvanični repozitorijum .
Sada radimo git clone .
Ostalo je samo malo da počnemo koristiti docker s nvidia slikom, potrebno je da se registriramo na NGC Cloud i prijavimo. Idemo ovdje , registriramo se i nakon što uđemo u NGC Cloud, kliknemo na SETUP u gornjem lijevom kutu ekrana ili odlazimo na ovu vezu . Kliknite „generiraj ključ“. Preporučujem da ga sačuvate, inače ćete ga sljedeći put morati ponovo generirati i, prema tome, prilikom postavljanja na novom uređaju ponovo izvesti ovu operaciju.
Izvršimo:
docker login nvcr.io
Username: $oauthtoken
Password: - generirani ključ
Username jednostavno kopiramo. Pa, to bi bilo to, okruženje je postavljeno!
Faza 2. Izrada docker kontejnera
U drugoj fazi našeg rada sastavit ćemo docker i upoznati se s njegovim unutrašnjostima.
Prelazimo u korijensku mapu u odnosu na projekt retina-examples i izvršavamo
docker build --build-arg USER=$USER --build-arg UID=$UID --build-arg GID=$GID --build-arg PW=alex -t retinanet:latest retinanet/
Sastavljamo docker prosljeđujući trenutačnog korisnika — to je vrlo korisno ako ćete pisati na montiranom VOLUME s pravima trenutnog korisnika, inače će biti root i bolno.
Dok se docker sastavlja, razmotrimo 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"]
Kot se vidi iz besedila, vzamemo vse naše najljubše knjižnice, skompiliramo retinanet, dodamo osnovna orodja za delo z Ubuntu in nastavimo strežnik openssh. Prva vrstica je pravzaprav dedovanje slike nvidia, za katero smo se prijavili v NGC Cloud in vsebuje Pytorch1.3, TensorRT6.x.x.x in še kup knjižnic, ki omogočajo kompajliranje cpp izvornih datotek našega detektorja.
Faza 3. Zagon in odkrivanje napak v Docker kontejnerju
Poglejmo glavni primer uporabe kontejnerja in razvojnega okolja, najprej zaženimo nvidia docker. Izvedite:
docker run --gpus all --net=host -v /home/:/workspace/mounted_vol -d -P --rm --ipc=host -it retinanet:latestZdaj je kontejner dostopen preko ssh @localhost. Po uspešnem zagonu odpiramo projekt v PyCharm. Nato odpiramo
Nastavitve->Interpreter projekta->Dodaj->Ssh Interpreter Korak 1

Korak 2

Korak 3

Izberemo vse kot na posnetkih zaslona,
Interpreter -> /opt/conda/bin/python— to bo ln na Python3.6 in
Sync folder -> /workspace/retinanetPritisnemo zaključek, počakamo na indeksacijo, in vse, okolje je pripravljeno za uporabo!
POMEMBNO!!! Takoj po indeksaciji prenesite iz dockerja skompilirane datoteke za Retinanet. V kontekstnem meniju na korenu projekta izberite možnost
Namestitev->PrenesiPojavili se bodo ena datoteka in dve mapi build, retinanet.egg-info in _С.so

Če vaš projekt izgleda tako, potem okolje vidi vse potrebne datoteke in smo pripravljeni na usposabljanje RetinaNet.
Faza 4. Označimo podatke in usposabljamo detektor
Za oznake večinoma uporabljam — prijetna in uporabna orodja, v zadnjem času so odpravili veliko napak in deluje bistveno bolje.
Recimo, da ste označili nabor podatkov in ga prenesli, vendar ga takoj uporabiti v našem RetinaNet ne bo šlo, ker je v svojem formatu in zato ga moramo pretvoriti v COCO. Orodje za pretvorbo se nahaja v:
markup_utils/supervisly_to_coco.pyUpoštevajte, da je Kategorija v skriptu primer in morate vstaviti svoje (kategorija background se ne dodaja)
categories = [{'id': 1, 'name': '1'},
{'id': 2, 'name': '2'},
{'id': 3, 'name': '3'},
{'id': 4, 'name': '4'}] Avtorji izvornega repozitorija so nekako sklenili, da ne boste izobraževali ničesar razen COCO/VOC za detekcijo, zato je bilo potrebno nekoliko popraviti izvorno datoteko
retinanet/dataset.pyDodaj tudi naše najljubše augmentacije in odstranite trdo vgrajene kategorije iz COCO. Prav tako imate možnost, da obrežete velike območja zaznavanja, če iščete majhne objekte na velikih slikah, imate majhen niz podatkov =) in nič ne deluje, vendar o tem drugič.
Na splošno je tudi train loop šibek, sprva ni shranjeval kontrolnih točk, uporabljal je nek grozen scheduler itd. Ampak zdaj vam je ostalo le še, da izberete backbone in izvedete
/opt/conda/bin/python retinanet/main.pyz 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
V konzoli boste videli:
Загрузка модели...
модель: RetinaNet
основа: ResNet18FPN
классы: 2, якоря: 9
Выбран уровень оптимизации O0: Чистое FP32 обучение.
Значения по умолчанию для этого уровня оптимизации:
включено : True
уровень_оптимизации : O0
тип_модели_приведения : torch.float32
патч_функций_pytorch : False
сохранять_batchnorm_fp32 : None
основные_веса : False
масштаб_потерь : 1.0
Обработка пользовательских переопределений (дополнительные аргументы, которые не равны None)...
После обработки переопределений параметры оптимизации:
включено : True
уровень_оптимизации : O0
тип_модели_приведения : torch.float32
патч_функций_pytorch : False
сохранять_batchnorm_fp32 : None
основные_веса : False
масштаб_потерь : 128.0
Подготовка набора данных...
загрузчик: pytorch
изменение_размера: [1024, 1280], максимум: 1280
устройство: 4 gpu
пакет: 4, точность: смешанная
Обучение модели в течение 20000 итераций...
[ 1/20000] фокальная потеря: 0.95619, потеря коробки: 0.51584, 4.042с/4-пакет (вперед: 0.698с, назад: 0.459с), 1.0 им/с, lr: 0.0001
[ 12/20000] фокальная потеря: 0.76191, потеря коробки: 0.31794, 0.187с/4-пакет (вперед: 0.055с, назад: 0.133с), 21.4 им/с, lr: 0.0001
[ 24/20000] фокальная потеря: 0.65036, потеря коробки: 0.30269, 0.173с/4-пакет (вперед: 0.045с, назад: 0.128с), 23.1 им/с, lr: 0.0001
[ 36/20000] фокальная потеря: 0.46425, потеря коробки: 0.23141, 0.178с/4-пакет (вперед: 0.047с, назад: 0.131с), 22.4 им/с, lr: 0.0001
[ 48/20000] фокальная потеря: 0.45115, потеря коробки: 0.23505, 0.180с/4-пакет (вперед: 0.047с, назад: 0.133с), 22.2 им/с, lr: 0.0001
[ 59/20000] фокальная потеря: 0.38958, потеря коробки: 0.25373, 0.184с/4-пакет (вперед: 0.049с, назад: 0.134с), 21.8 им/с, lr: 0.0001
[ 71/20000] фокальная потеря: 0.37733, потеря коробки: 0.23988, 0.174с/4-пакет (вперед: 0.049с, назад: 0.125с), 22.9 им/с, lr: 0.0001
[ 83/20000] фокальная потеря: 0.39514, потеря коробки: 0.23878, 0.181с/4-пакет (вперед: 0.048с, назад: 0.133с), 22.1 им/с, lr: 0.0001
[ 94/20000] фокальная потеря: 0.39947, потеря коробки: 0.23817, 0.185с/4-пакет (вперед: 0.050с, назад: 0.134с), 21.6 им/с, lr: 0.0001
[ 105/20000] фокальная потеря: 0.37343, потеря коробки: 0.20238, 0.182с/4-пакет (вперед: 0.048с, назад: 0.134с), 22.0 им/с, lr: 0.0001
[ 116/20000] фокальная потеря: 0.19689, потеря коробки: 0.17371, 0.183с/4-пакет (вперед: 0.050с, назад: 0.132с), 21.8 им/с, lr: 0.0001
[ 128/20000] фокальная потеря: 0.20368, потеря коробки: 0.16538, 0.178с/4-пакет (вперед: 0.046с, назад: 0.131с), 22.5 им/с, lr: 0.0001
[ 140/20000] фокальная потеря: 0.22763, потеря коробки: 0.15772, 0.176с/4-пакет (вперед: 0.050с, назад: 0.126с), 22.7 им/с, lr: 0.0001
[ 148/20000] фокальная потеря: 0.21997, потеря коробки: 0.18400, 0.585с/4-пакет (вперед: 0.047с, назад: 0.144с), 6.8 им/с, lr: 0.0001
Средняя точность (AP) @[ IoU=0.50:0.95 | область= вся | максимальные детекторы=100 ] = 0.52674
Средняя точность (AP) @[ IoU=0.50 | область= вся | максимальные детекторы=100 ] = 0.91450
Средняя точность (AP) @[ IoU=0.75 | область= вся | максимальные детекторы=100 ] = 0.35172
Средняя точность (AP) @[ IoU=0.50:0.95 | область= маленькая | максимальные детекторы=100 ] = 0.61881
Средняя точность (AP) @[ IoU=0.50:0.95 | область=средняя | максимальные детекторы=100 ] = -1.00000
Средняя точность (AP) @[ IoU=0.50:0.95 | область= большая | максимальные детекторы=100 ] = -1.00000
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область= вся | максимальные детекторы= 1 ] = 0.58824
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область= вся | максимальные детекторы= 10 ] = 0.61765
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область= вся | максимальные детекторы=100 ] = 0.61765
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область= маленькая | максимальные детекторы=100 ] = 0.61765
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область=средняя | максимальные детекторы=100 ] = -1.00000
Средняя отзывчивость (AR) @[ IoU=0.50:0.95 | область= большая | максимальные детекторы=100 ] = -1.00000
Сохранение модели: 148Чтобы ознакомиться со всеми параметрами, обратите внимание на
retinanet/main.pyВ целом, они стандартные для детекции и имеют свое описание. Запустите обучение и дождитесь результатов. Пример инференса можно найти в:
retinanet/infer_example.pyили выполните команду:
/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
V repozitoriju je že vgrajen Focal Loss in več backbonov, poleg tega je enostavno dodati svoje.
retinanet/backbones/*.pyV tabeli avtorji navajajo nekatere značilnosti:

Na voljo sta tudi backbone ResNeXt50_32x4dFPN in ResNeXt101_32x8dFPN, vzeta iz torchvision.
Upam, da smo se malo razjasnili glede detekcije, vendar je nujno prebrati uradno dokumentacijo, da razumemo načine izvoza in logiranja..
Korak 5. Izvoz in inferenca modelov Unet z encoderjem Resnet.
Kot ste verjetno opazili, so bile v Dockerfile nameščene knjižnice za segmentacijo in med njimi odlična knjižnica . V paketu Unet lahko najdete primere inference in izvoza pytorch kontrolnih točk v engine TensorRT.
Glavna težava pri izvozu modelov Unet-like iz ONNX v TensorRT je potreba po določanju fiksne velikosti Upsample ali uporabi ConvTranspose2D:
import torch.onnx.symbolic_opset9 as onnx_symbolic
def upsample_nearest2d(g, input, output_size):
# Trenutno, TRT 5.1/6.0 ONNX Parser ne podpira vseh ONNX operacij
# potrebnih za podporo dinamičnemu upsampling ONNX formulaciji
# Tukaj strogo določimo scale=2 kot začasno rešitev
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
S tem pretvorbo je to mogoče avtomatizirati pri izvozu v ONNX, vendar je v 7. različici TensorRT ta težava rešena, in ostalo nam je le še malo počakati.
Zaključek
Ko sem začel uporabljati docker, sem imel dvome o njegovi zmogljivosti za moje naloge. V enem od mojih agregatorjev zdaj ustvarjam precejšen omrežni promet z več kamerami.

Različni testi na spletu so govorili o razmeroma velikem overheadu za omrežno interakcijo in zapisovanje na VOLUME, poleg tega pa je neznani in strašni GIL, saj je zajemanje okvirjev, delovanje gonilnika in prenos okvirjev po omrežju atomarna operacija v načinu hard real-time, zamude v omrežju so zame zelo kritične.
A vse je šlo dobro =)
P.S. Ne pozabite dodati svojega najljubšega train loop za segmentacijo in v produkcijo!
Zahvale
Hvala skupnosti , brez nje ni mogoče napredovati! Ogromno hvala , ki me je spodbudil, da se ukvarjam z DL, za njegove neprecenljive nasvete in izjemno profesionalnost!
Uporabite optimizirane modele v produkciji!
Aurorai, llc
Izvor: habr.com
