Jetson Nano z wykorzystaniem konteneryzacji

Jednym z początkowych problemów z jakimi natknąłem się podczas pracy z płytką Nvidia Jetson Nano był długi czas potrzebny do konfiguracji nowego środowiska wirtualnego w Pythonie. System operacyjny uruchamiany z karty microSD i potrzeba kompilacji poszczególnych bibliotek, które umożliwiały uruchamianie modeli uczenia maszynowego z wykorzystaniem GPU w nie należały w sumie do demonów prędkości. Trudno się dziwić, nieco inne jest zastosowanie tego typu urządzeń elektronicznych. Zacząłem się zastanawiać, czy zamiast budowy osobnych środowisk wirtualnych dla pakietu Keras z TensorFlow, dla pakietu Pytorch i pakietu Scikit-Learn, wykorzystać możliwość budowy gotowych obrazów w skonteneryzowanej postaci.

Niniejszy wpis jest próbą weryfikacji takiej konfiguracji.

 

Instalacja

 

Zdecydowałem się na zakup nowej karty microSD o wielkość 128GB.

Na początku ściągnąłem obraz

https://developer.nvidia.com/embedded/jetpack

Zainteresowało mnie jedno zdanie:

“In addition to the features listed below, JetPack 4.2.1 also introduced two beta features: NVIDIA Container Runtime with Docker integration and TensorRT support for INT-8 DLA operations.”

 

Obraz zajmuje ok 5GB. Po rozpakowaniu pliku nagrałem go na kartę za pomocą programu Balena Etcher.

Po zakończeniu wgrywania obrazu na kartę microSD nadszedł czas na uruchomienie oprogramowania.

Po pierwszym uruchomieniu systemu Ubuntu nadszedł czas na konfigurację

Przy okazji warto zaktualizować czas systemowy

Jak widać silnik konteneryzacji (Docker) jest już zainstalowany, ale do sprawnego działanie będziemy musieli zaktualizować jego wersję (do wersji 19.03 w momencie pisania tego artykułu).

W razie problemów z DNS należy do pliku /etc/systemd/resolved.conf dodać główny DNS Google lub preferowany inny

Założyłem, że wszelkie kompilacje oprogramowania wykonam na środowisku natywnym (Jetson Nano).

W tym momencie warto dodać plik wymiany, gdyż wolna pamięć 4GB może się okazać chwilami zasobem zanikającym.

Dokonujemy aktualizacji silnika Docker

Po zakończeniu instalacji mamy już wersję 19.03

 

Budowa obrazów

 

Należy uzbroić się w cierpliwość, budowa jednego obrazu to co najmniej godzina czasu. Może się to wydawać długim czasem, ale jeden z wpisów na blogu, który przeczytałem o walkach autora z instalacją Tensorflow 2.0 doprowadził do jedynego słusznego wniosku, że taki sposób rozwoju oprogramowania to droga donikąd.

Note this script would take a very long time (~40 hours) to run.

To była dodatkowa motywacja, by przygotować raz, a dobrze podstawowe zbiory pakietów dla uczenia maszynowego, czy nawet uczenia głębokiego z wykorzystaniem GPU. Wyobraźmy sobie, że karta SD, na której uruchamiamy system operacyjny ulega uszkodzeniu, co wtedy, kolejne godziny kompilacji? Co jest ważniejsze, trenowanie i rozbudowa modelu, czy wieczna konfiguracja i instalacja?  I o tym jest poniższa opowieść.

 

Dla osób, które chcą zamiast wykonywania poleceń z poziomu linii komend wykorzystać interfejs w przeglądarce polecam instalację Portainera.

Można pobrać gotowy obraz Portainera

Aplikacja uruchomiona zostanie na porcie 9000 hosta, jeśli chcemy korzystać z naszego laptopa można forwardować port 9000 przy konfiguracji łącza SSH.

 

Po utworzeniu konta administratorskiego wraz z hasłem  i zalogowaniu się należy podłączyć się do lokalnego silnika Dockera.

Od tego momentu wszystkie podane niżej operacje będzie można wykonać z poziomu przeglądarki.  Przykładowy tutorial można znaleźć np. tutaj:

ubuntu-docker-portainer

W tym momencie warto zrobić forwardowanie dla portu 8888, który będzie wykorzystywany przez Jupyter Lab/Notebook. W popularnym kliencie SSH Putty wygląda to tak:

 

Zaczynamy właściwą zabawę.

Pobieramy bazowy obraz z repozytorium NVidia

Jest to przygotowane oprogramowanie dobrze współpracujące z architekturą ARM 64-bit i procesorami GPU serii Tegra.

Prace na nowo uruchomionym systemie operacyjnym możemy rozpocząć od sklonowania repozytorium:

Kod znajduje się w podkatalogu ml-containers

Postanowiłem przygotować trzy obrazy zawierające różne grupy oprogramowania do uczenia maszynowego:

1) Scikit-learn, ale tu nie będzie wsparcia GPU zgodnie z:

https://scikit-learn.org/stable/faq.html

2) Tensorflow plus Keras z obsługą GPU

3) Pytorch z obsługą GPU.

Dodatkowo rozszerzyłem te obrazy o najbardziej popularne pakiety (scipy,numpy, pandas, folium, matplotlib, seaborn), ten wybór jest autorski i wynika z doświadczeń typowego wykorzystywana dodatkowych pakietów do wstępnego przetwarzania danych wejściowych i ich wizualizacji.

Każdy z tych obrazów można będzie dodatkowo rozszerzyć o instalację Jupyter Notebook i Jupyter Lab.

Wszystkie obrazy zostaną umieszczone publicznym repozytorium Docker Hub.

Skrypty budujące i zawartość Dockerfile oraz przykładowe pliki Pythona czy notatniki Jupytera umieściłem  w repozytorium Githuba. Kod będzie sukcesywnie rozwijany. Liczę na konstruktywne uwagi.

https://github.com/djkormo/jetson-nano/tree/master/ml-containers

 

Dla każdego z obrazów przygotowałem podkatalog zawierające przynajmniej następujące pliki

 

 A. Dockerfile – plik  zwierający instrukcję budowy naszego obrazu

Zwracam uwagę na kilka podstawowych zasad, które przyjąłem przy budowie aplikacji

  1. Korzystamy z tego samego obrazu bazowego nvcr.io/nvidia/l4t-base:r32.2
  2. Instalujemy potrzebne oprogramowanie (ten punkt był najbardziej pracochłonny, gdyż dopiero podczas etapu kompilacji pojawiały się błędy braku zależności)
  3. Dedykowane oprogramowanie (Tensorflow, Pytorch) pochodzi z repozytorium NVidia dla kart GPU Tegra
  4. Dedykowane polecenia python3 i pip3 zostaną zmapowane do python i pip. Wersja 2.7 Pythona nie będzie wykorzystywana
  5. Dodany zostanie przynajmniej jeden plik z testowym kodem (test.py)

B. build.bash – polecenia umożliwiający sprawną budowę obrazu i jego wypchnięcie do publicznego repozytorium

C. run_local.bash – polecenie uruchamiające kontener na podstawie zbudowanego obrazu

W przypadku kontenerów bazowych (bez oprogramowania Jupyter)  uruchamiam oprogramowanie w trybie interaktywnym

W przypadku kontenerów dodatkowych uruchamiam oprogramowanie z podaniem mapowania portu kontenera na port host bez trybu interaktywnego

Należy pamiętać o opcji –runtime=nvidia, inaczej nie będzie możliwości  wykorzystania procesora graficznego.

 

D. test.py – plik zawierający testowy kod weryfikujący poprawność instalacji (w szczególności wykorzystanie GPU hosta).

Ten kod należy wykonać wewnątrz kontenera.

Czasem wystarczy proste sprawdzenie, czy GPU Hosta jest widoczne w kontenerze, przykładowo dla biblioteki Pytorch:

Po uruchomieniu kontenera wystarczy wpisać polecenie

python test.py.

 

Proces budowania składa się z następujących kroków

1. Przygotowanie pliku Dockerfile

2 Przygotowanie pliku budującego build bash

Uruchomienie polecenia bash build.bash

3. Przygotowanie pliku lokalnego uruchomienia run_local.bash

Uruchomienie polecenia bash run_local.bash

Punkty od 1) do 3) mają charakter cykliczny, do czasu uzyskania działającego oprogramowania  przetestowanego odpowiednim skryptem (*.py).

Uwaga na pakiet Pytorch

W przypadku pakietu Pytorch pojawił się pewien problem, nie udało się skompilować pakietu torchvision w sposób automatyczny. Poradziłem sobie w następujący sposób.

1. Po zbudowaniu obrazu o nazwie jetson-pytorch-base:0.1.0 uruchomiłem go z opcją –runtime=nvidia

2. Wewnątrz kontenera doinstalowałem brakujący pakiet (jak zwykle należy się uzbroić w cierpliwość)

3. Po zbudowaniu pakietu skorzystałem z możliwości zatwierdzenia zmian z punktu 2)

Stworzyłem dedykowany kawałek kodu

Obrazy, które zostały umieszczone w repozytorium DockerHub.

bazowe

https://hub.docker.com/r/djkormo/jetson-scikit-base

https://hub.docker.com/r/djkormo/jetson-tensorflow-base

https://hub.docker.com/r/djkormo/jetson-pytorch-jlab

z dodatkiem Jupyter Lab

https://hub.docker.com/r/djkormo/jetson-scikit-jlab

https://hub.docker.com/r/djkormo/jetson-tensorflow-jlab

https://hub.docker.com/r/djkormo/jetson-pytorch-jlab

Demonstracja:

Korzystając  z przygotowanego obrazu uruchamiamy kontener w trybie detach z wystawionym portem 8888 hosta.

Po uruchomieniu obrazu  djkormo/jetson-pytorch-jlab:0.1.0 mamy możliwość rozpoczęcia testu na przygotowanym notatniku Jupyter. Zawiera on prosty model konwolucyjny wykorzystujący klasyczny zbiór danych Cifar10.

Ważne, żeby podczas trenowania zweryfikować, czy GPU hosta jest wykorzystywane. Warto w tym celu mieć zainstalowany  pakiet jtop.

 

Dla osób, które słyszą po raz pierwszy o pakiecie Pytorch, konkurencyjnym do TenforFlow, zwrócę uwagę na jeden szczegół. Pytorch wymaga jawnego (explicite) oprogramowania wykorzystania CUDA, TensorFlow robi to w trybie niejawnym (implicite).

Przy okazji zwracam uwagę na bazowe oprogramowanie hosta

NVIDIA Jetson NANO/TX1 – Jetpack UNKNOWN [L4T 32.2.1], które jest starsze niż bazowe oprogramowanie wykorzystywane przy budowie obrazów (!).

Można też uruchamiać skrypty Pythona z poziomu  wbudowanego terminala

 

Dla osób, które zamiast Jupyter Lab wolą notatniki, wystarczy w adresie URL zamienić /lab na /tree

http://localhost:8888/tree

Wnioski:

Czy konteneryzacja i uczenie maszynowe mogą się uzupełniać i być efektywnie wykorzystane? Myślę, że tak. Niniejszy wpis jest próbą pokazania i próbą odpowiedzi na pytanie, czy warto znać jedno i drugie? Warto, ale należy pamiętać, że optymalizacja i automatyzacja naszego kodu może dotyczyć nie tylko typowego oprogramowania biznesowego, ale również modeli uczenia maszynowego.

 

Literatura:

https://ngc.nvidia.com/catalog/containers/nvidia:l4t-base

https://medium.com/@chengweizhang2012/how-to-run-keras-model-on-jetson-nano-in-nvidia-docker-container-b52d0df07129
http://collabnix.com/why-docker-19-03-on-nvidia-jetson-nano/

https://jkjung-avt.github.io/build-tensorflow-2.0.0/

https://github.com/rbonghi/jetson_stats

https://www.howtoforge.com/tutorial/ubuntu-docker-portainer/

 

  1. mmoskit

    Fajny wpis. Do tej pory nie wykorzystywałem konteneryzacji ale może zacznę 🙂

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *