TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)
Boli tylko za pierwszym razem!

Cześć wszystkim! Drodzy przyjaciele, w tym artykule chcę podzielić się swoim doświadczeniem z wykorzystaniem TensorRT oraz RetinaNet bazując na repozytorium github.com/aidonchuk/retinanet-examples (to jest fork oficjalnego repozytorium od nvidia, które umożliwi szybkie wprowadzenie zoptymalizowanych modeli do produkcji). Przeglądając wiadomości w kanałach społeczności ods.ai, natrafiam na pytania dotyczące wykorzystania TensorRT, a pytania te często się powtarzają, dlatego postanowiłem napisać jak najbardziej wyczerpujące poradnik dotyczący szybkiego wnioskowania opartego na TensorRT, RetinaNet, Unet i dockerze.

Opis zadania

Proponuję postawić zadanie w ten sposób: musimy oznaczyć zbiór danych, wytrenować na nim sieć RetinaNet/Unet w Pytorch1.3+, przekształcić uzyskane wagi do ONNX, następnie skonwertować je do silnika TensorRT i uruchomić wszystko w dockerze, najlepiej na Ubuntu 18 i zdecydowanie na architekturze ARM (Jetson)*, minimalizując ręczne wdrażanie środowiska. W rezultacie uzyskamy kontener gotowy nie tylko do eksportu i szkolenia RetinaNet/Unet, ale również do kompleksowego rozwoju i szkolenia klasyfikacji oraz segmentacji z całym niezbędnym wsparciem.

Etap 1. Przygotowanie środowiska

Warto zauważyć, że w ostatnim czasie całkowicie zrezygnowałem z używania i wdrażania jakichkolwiek bibliotek na komputerze stacjonarnym, tak samo jak na devboxie. Jedynym, co trzeba stworzyć i zainstalować, jest środowisko wirtualne Pythona i cuda 10.2 (można ograniczyć się do samego sterownika nvidia) z deb.

Załóżmy, że masz świeżo zainstalowane Ubuntu 18. Zainstalujemy cuda 10.2 (deb), szczegółowo na procesie instalacji nie będę się zatrzymywał, oficjalna dokumentacja jest wystarczająca.

Teraz zainstalujemy docker, przewodnik po instalacji dockera można łatwo znaleźć, oto przykład www.digitalocean.com/community/tutorials/docker-ubuntu-18-04-1-ru, dostępna jest już wersja 19+ — instalujemy ją. Nie zapomnij również umożliwić korzystania z dockera bez sudo, będzie to wygodniejsze. Po tym, jak wszystko się udało, robimy tak:

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 można nawet nie zaglądać do oficjalnego repozytorium github.com/NVIDIA/nvidia-docker.

Teraz robimy git clone github.com/aidonchuk/retinanet-examples.

Zostało tylko kilka kroków, aby zacząć korzystać z dockera z obrazem nvidia. Musimy zarejestrować się w NGC Cloud i zalogować. Idźmy tutaj ngc.nvidia.com, rejestrujemy się, a po wejściu do NGC Cloud klikamy SETUP w lewym górnym rogu ekranu lub przechodzimy pod ten link ngc.nvidia.com/setup/api-key. Klikamy „wygenerować klucz”. Polecam go zapisać, w przeciwnym razie przy kolejnym wejściu trzeba będzie go wygenerować ponownie, a przy uruchamianiu na nowym sprzęcie powtórzyć tę operację.

Wykonaj:

docker login nvcr.io
Username: $oauthtoken
Password:  - wygenerowany klucz

Username po prostu kopiujemy. No, oto, środowisko zostało uruchomione!

Etap 2. Budowa kontenera docker

Na drugim etapie naszej pracy zbudujemy docker i zaznajomimy się z jego wnętrzem.
Przejdziemy do głównego katalogu projektu retina-examples i wykonamy

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

Budujemy docker przekazując bieżącego użytkownika – to bardzo przydatne, jeśli planujesz coś pisać na zamontowanym VOLUME z prawami obecnego użytkownika, w przeciwnym razie będzie to root i będzie to problem.

Podczas gdy docker się buduje, przyjrzyjmy się plikowi 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"]

Jak widać z tekstu, bierzemy wszystkie nasze ulubione biblioteki, kompilujemy retinanet, dodajemy podstawowe narzędzia do wygodnej pracy z Ubuntu i konfigurujemy serwer openssh. Pierwszą linią jest dziedziczenie obrazu nvidia, do którego logowaliśmy się w NGC Cloud i który zawiera Pytorch1.3, TensorRT6.x.x.x oraz wiele bibliotek, które pozwalają na skompilowanie cpp źródeł naszego detektora.

Etap 3. Uruchamianie i debugowanie kontenera docker

Przejdźmy do głównego przypadku użycia kontenera i środowiska programistycznego, na początek uruchomimy nvidia docker. Wykonujemy:

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

Teraz kontener jest dostępny przez ssh @localhost. Po pomyślnym uruchomieniu, otwieramy projekt w PyCharm. Następnie otwieramy

Ustawienia->Interpreter projektu->Dodaj->Interpreter Ssh

Krok 1
TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Krok 2
TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Krok 3
TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Wybieramy wszystko jak na zrzutach ekranu,

Interpreter -> /opt/conda/bin/python

— to będzie odniesienie do Python3.6 i

Folder synchronizacji -> /workspace/retinanet

Klikamy zakończ, czekamy na indeksowanie, i wszystko, środowisko jest gotowe do użycia!

WAŻNE!!! Tuż po indeksowaniu należy pobrać z docker skompilowane pliki dla Retinanet. W menu kontekstowym w głównym folderze projektu wybieramy opcję

Zarządzanie->Pobierz

Pojawią się jeden plik i dwa foldery build, retinanet.egg-info oraz _C.so

TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Jeśli twój projekt wygląda tak, to środowisko widzi wszystkie niezbędne pliki i jesteśmy gotowi do szkolenia RetinaNet.

Etap 4. Oznaczamy dane i trenujemy detektor

Do oznaczania zazwyczaj używam supervise.ly — przyjemne i wygodne narzędzie, w ostatnim czasie poprawiono wiele błędów i działa znacznie lepiej.

Załóżmy, że oznaczyłeś zbiór danych i go pobrałeś, ale nie można go od razu włożyć do naszego RetinaNet, ponieważ jest w własnym formacie i musimy go skonwertować do COCO. Narzędzie do konwersji znajduje się w:

markup_utils/supervisly_to_coco.py

Zwróć uwagę, że Kategoria w skrypcie to przykład i musisz wstawić swoje (kategorii background nie dodawaj)

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

Autorzy oryginalnego repozytorium postanowili, że nic poza COCO/VOC nie będziesz trenować do detekcji, więc musiałem nieco edytować oryginalny plik

retinanet/dataset.py

Dodając tutaj ulubione augmentacje albumentations.readthedocs.io/en/latest i usunąć sztywno wbudowane kategorie z COCO. Istnieje także możliwość przycięcia dużych obszarów detekcji, jeśli na dużych obrazkach szukasz małych obiektów, masz mały zbiór danych =), i nic nie działa, ale o tym innym razem.

Ogólnie pętla treningowa też jest słaba, początkowo nie zapisywała punktów kontrolnych, używała jakiegoś strasznego harmonogramu itd. Ale teraz wystarczy, że wybierzesz backbone i wykonasz

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

z parametrami:

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

W konsoli zobaczysz:

Inicjowanie modelu...
     model: RetinaNet
  backbone: ResNet18FPN
   klasy: 2, kotwice: 9
Wybrany poziom optymalizacji O0: Czyste szkolenie FP32.

Domyślne ustawienia dla tego poziomu optymalizacji to:
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
Przetwarzanie nadpisania użytkownika (dodatkowe kwargs, które nie są None)...
Po przetworzeniu nadpisania, opcje optymalizacji to:
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
Przygotowywanie zbioru danych...
    loader: pytorch
    zmiana rozmiaru: [1024, 1280], max: 1280
    urządzenie: 4 GPU
    partia: 4, precyzja: mieszana
Trenowanie modelu przez 20000 iteracji...
[    1/20000] strata ogniskowa: 0.95619, strata ramki: 0.51584, 4.042s/4-partii (fw: 0.698s, bw: 0.459s), 1.0 im/s, lr: 0.0001
[   12/20000] strata ogniskowa: 0.76191, strata ramki: 0.31794, 0.187s/4-partii (fw: 0.055s, bw: 0.133s), 21.4 im/s, lr: 0.0001
[   24/20000] strata ogniskowa: 0.65036, strata ramki: 0.30269, 0.173s/4-partii (fw: 0.045s, bw: 0.128s), 23.1 im/s, lr: 0.0001
[   36/20000] strata ogniskowa: 0.46425, strata ramki: 0.23141, 0.178s/4-partii (fw: 0.047s, bw: 0.131s), 22.4 im/s, lr: 0.0001
[   48/20000] strata ogniskowa: 0.45115, strata ramki: 0.23505, 0.180s/4-partii (fw: 0.047s, bw: 0.133s), 22.2 im/s, lr: 0.0001
[   59/20000] strata ogniskowa: 0.38958, strata ramki: 0.25373, 0.184s/4-partii (fw: 0.049s, bw: 0.134s), 21.8 im/s, lr: 0.0001
[   71/20000] strata ogniskowa: 0.37733, strata ramki: 0.23988, 0.174s/4-partii (fw: 0.049s, bw: 0.125s), 22.9 im/s, lr: 0.0001
[   83/20000] strata ogniskowa: 0.39514, strata ramki: 0.23878, 0.181s/4-partii (fw: 0.048s, bw: 0.133s), 22.1 im/s, lr: 0.0001
[   94/20000] strata ogniskowa: 0.39947, strata ramki: 0.23817, 0.185s/4-partii (fw: 0.050s, bw: 0.134s), 21.6 im/s, lr: 0.0001
[  105/20000] strata ogniskowa: 0.37343, strata ramki: 0.20238, 0.182s/4-partii (fw: 0.048s, bw: 0.134s), 22.0 im/s, lr: 0.0001
[  116/20000] strata ogniskowa: 0.19689, strata ramki: 0.17371, 0.183s/4-partii (fw: 0.050s, bw: 0.132s), 21.8 im/s, lr: 0.0001
[  128/20000] strata ogniskowa: 0.20368, strata ramki: 0.16538, 0.178s/4-partii (fw: 0.046s, bw: 0.131s), 22.5 im/s, lr: 0.0001
[  140/20000] strata ogniskowa: 0.22763, strata ramki: 0.15772, 0.176s/4-partii (fw: 0.050s, bw: 0.126s), 22.7 im/s, lr: 0.0001
[  148/20000] strata ogniskowa: 0.21997, strata ramki: 0.18400, 0.585s/4-partii (fw: 0.047s, bw: 0.144s), 6.8 im/s, lr: 0.0001
 Średnia precyzja (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.52674
 Średnia precyzja (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.91450
 Średnia precyzja (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.35172
 Średnia precyzja (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.61881
 Średnia precyzja (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.00000
 Średnia precyzja (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.00000
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.58824
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.61765
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.61765
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.61765
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.00000
 Średnia przypomnienie (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.00000
Zapisywanie modelu: 148

Aby zapoznać się z pełnym zestawem parametrów, sprawdź

retinanet/main.py

Ogólnie mówiąc, są standardowe dla detekcji i zawierają opisy. Uruchom trening i poczekaj na wyniki. Przykład inferencji można zobaczyć w:

retinanet/infer_example.py

lub wykonaj polecenie:

/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

W repozytorium zintegrowano już Focal Loss oraz kilka backbone'ów, a także łatwo można dodać własne.

retinanet/backbones/*.py

W tabeli autorzy przedstawiają niektóre charakterystyki:

TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Jest także backbone ResNeXt50_32x4dFPN oraz ResNeXt101_32x8dFPN, pobrany z torchvision.
Mam nadzieję, że udało się trochę zrozumieć detekcję, ale warto koniecznie przeczytać oficjalną dokumentację, aby zrozumieć tryby eksportu i logowania..

Krok 5. Eksport i inferencja modeli Unet z enkoderem Resnet.

Jak zapewne zauważyliście, w Dockerfile zainstalowano biblioteki do segmentacji, w tym znakomitą bibliotekę github.com/qubvel/segmentation_models.pytorch. W pakiecie YUNET można znaleźć przykłady inferencji i eksportu punktów kontrolnych pytorch do silnika TensorRT.

Głównym problemem przy eksporcie modeli podobnych do Unet z ONNX do TensorRT jest konieczność podania stałego rozmiaru Upsample lub używania ConvTranspose2D:

import torch.onnx.symbolic_opset9 as onnx_symbolic
        def upsample_nearest2d(g, input, output_size):
            # Obecnie, parser ONNX TRT 5.1/6.0 nie obsługuje wszystkich operacji ONNX
            # potrzebnych do obsługi dynamicznego upscale, ONNX forumlation
            # Tutaj w hardcodujemy skalę=2 jako tymczasowe obejście
            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

Dzięki temu przekształceniu można zrobić to automatycznie podczas eksportu do ONNX, ale w 7 wersji TensorRT ten problem został rozwiązany, a my musimy czekać już bardzo niewiele.

Podsumowanie

Kiedy zacząłem korzystać z dockera, miałem wątpliwości co do jego wydajności dla moich zadań. W jednej z moich strukturyzacji generuje się obecnie dość duży ruch sieciowy, tworzony przez kilka kamer.

TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja)

Różne testy w internecie mówiły o stosunkowo dużym narzucie na interakcji sieciowej i zapisie na VOLUME, a także o nieznanym i strasznym GIL, a ponieważ uchwycenie klatki, działanie sterownika i przesyłanie klatki przez sieć są operacjami atomowymi w trybie hard real-time, opóźnienia w sieci są dla mnie bardzo krytyczne.

Ale wszystko się udało =)

P.S. Pozostaje dodać ulubioną pętlę treningową dla segmentacji i do produkcji!

Podziękowania

Dziękuję społeczności ods.ai, bez niej rozwój jest niemożliwy! Ogromne dzięki n01z3, który namówił mnie do zajęcia się DL, za jego cenne porady i niezwykły profesjonalizm!

Używaj w produkcji zoptymalizowanych modeli!

TensorRT 6.x.x.x — wysokowydajny inferens dla modeli głębokiego uczenia (Wykrywanie Obiektów i Segmentacja) Aurorai, llc

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster