Jetson Nano + ROS Melodic + DeepLearning

NVIDIA Jetson Nano Cuda Samples
NVIDIA CUDA Samples

In diesem Beitrag möchte ich mein Jetson Nano B01 mit DeepLearning-Algorithmen ausreizen. Bisher habe ich ein Custom Image mit XUBUNTU auf dem Jetson Nano verwendet. Leider wird das genannte Image von NVIDA offiziell nicht unterstützt. Dabei entstehen Kompatibilitätsprobleme mit anderen Software-Bibliotheken. Beispielsweise verhindert eine ROS Noetic-Installation auf einem Custom Image mit Ubuntu 20.04 die ordentliche Installation von deepface, welches auf eine andere OpenCV-Version besteht. Mit Ubuntu 18 bekommt man deepface und ROS Melodic ohne Konflikte auf einem Jetson-System zum Laufen.

Das deepface-Projekt ist ein Python-Framework für Gesichts-erkennung, -wiedererkennung und Klassifizierung. Genau genommen kann es Gesichter in einem Bild erkennen, Personen wiedererkennen und deren Geschlecht, Alter, Gefühlslage und Rasse bestimmen. Dazu verwendet es fortschrittliche DeepLearning-Modelle wie VGG-Face, Google FaceNet, OpenFace, Facebook DeepFace, DeepID-Net, ArcFace und Dlib. Durch eine Abfrage aller genannten Modelle im sogenannten Ensemble-Modus erreicht das Framework eine Trefferquote (ca. 98%), die besser ist als beim Mensch (ca. 97%). DeepLearning-Modelle können sehr wuchtig sein, da sie auf Millionen von Bildern oder Bildern in Videos trainiert werden. Ein Programm zur Gesichtserkennung mit deepface stellt somit auch eine Performance-Messung des darunterliegenden Computersystems dar.

Fazit

Das Fazit vorab hatte ich mit deepface einen reduzierten Erfolg auf dem Jetson Nano. Die DeepLearning-Modelle sind so groß, dass ohne Auslagerung der Arbeitsspeicher überfüllt und die Programme ständig eine „Killed“-Meldung bringen, oder gänzlich abstürzen. Schließlich habe ich eine Swap-Datei mit 8GB angelegt, um dann festzustellen, dass die GPU eines NVIDIA Jetson Nano nicht auslagert, also wieder abstürzt. Schlussendlich habe ich mit nachfolgender CUDA-Einstellung in der Unit-Test-Datei von deepface und ausschließlich CPU-Rechenpower die deepface-Funktionen zum größten Teil ausführen können.

os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

Die Auslagerung betrug maximal ca. 3,4GB. In der Summe wären das 4GB RAM + 3,4GB Swap = 7,4GB. Ein Jetson mit 8GB müsste also deepface, wenn es im sogenannten Ensemble-Modus arbeitet, in der GPU ausführen können. Eine Vergleichstabelle mit den verschiedenen Jetson-Modulen befindet sich hier: https://developer.nvidia.com/embedded/jetson-modules.

Wer mit deepface sparsam umgeht, kann sehr vielversprechende Funktionen nutzen, für die bislang viel Aufwand betrieben werden musste. Das deepface-Framework stellt die gesamte Pipeline (Erkennen, Ausrichten, Analysieren, Klassifizieren) in der Bildverarbeitung für erkennende Systeme in einem einzigen Werkzeug bereit.

Systemvoraussetzung

Unser Setup wird aus folgenden Komponenten bestehen:

1. NVIDIA Jetson Nano B01
2. 5V 4A Stromversorgung an Hohlsteckeranschluss
3. Micro SD-Karte mindestens 64GB
4. NVIDIA Jetson Nano im Powermodus Max 10W (Nur mit 5 Ampere Strom möglich)
5. Ubuntu 18 (jetson-nano-jp451-sd-card-image.zip)
6. ROS Melodic
7. deepface (master branch)

In einem anderen Beitrag (hier: Jetson Nano + ROS Noetic) haben wir bereits gesehen, wie man ein Jetson Nano mit Ubuntu bespielt und anschließend ROS installiert. Allerdings bin ich bei der Reihenfolge für diesen Beitrag folgendermaßen vorgegangen:

1. Ubuntu 18 (jetson-nano-jp451-sd-card-image.zip) installieren
2. NVIDIA JetPack installieren
3. NVIDIA TensorFlow installieren
4. deepface installieren
5. ROS Melodic installieren

Es sollte zwar keinen Unterschied machen, wenn man nach der Installation von Ubuntu und den NVIDIA-Paketen zuerst ROS installiert und zum Schluss deepface, aber für den Fall, dass die Installation von ROS Melodic Pakete in einen bestimmten Versionszustand einfriert, gehen wir in der oben genannte Reihenfolge vor. Die Installation von Ubuntu und ROS werden hier nicht näher erläutert, da es ausreichend Material und Hilfe im Internet gibt. Für Ubuntu gibt es von NVIDIA eine ausführliche Anleitung hier: https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit.

NVIDIA JetPack Installation

Nachdem Ubuntu 18 auf dem Jetson Nano installiert ist und mit apt update bzw. upgrade das System aktualisiert wurde, installieren wir JetPack. Das JetPack liefert uns in erster Linie wichtige Treiber, nützliche Werkzeuge, Bibliotheken und Dokumente für die Arbeit im Bereich DeepLearning. Eine Installationsanleitung befindet sich hier: https://docs.nvidia.com/jetson/jetpack/install-jetpack/index.html. Wer das JetPack zum ersten Mal auf seinem Ubuntu-System installiert, führt lediglich folgenden Befehl aus:

apt install nvidia-jetpack

NVIDIA TensorFlow Installation

DeepLearning-Programme basieren meist auf Frameworks, die das Erstellen, Bearbeiten, Exportieren, Experimentieren und Validieren von Neuronalen Netzwerken ermöglichen. TensorFlow ist ein sehr beliebtes Beispiel für ein DeepLearning-Framework. Die trainierten Modelle können Gesichter erkennen, Objekte klassifizieren, Sprache erkennen, Fahrzeuge manövrieren und vieles mehr. Das Training bezeichnet einen Prozess, bei dem ein neuronales Netzwerk mit ausreichend Daten gefüttert wird, um das Problem, wozu es erstellt wurde, mit hoher Wahrscheinlichkeit zu lösen. Es werden oft große Mengen an Daten verarbeitet, was dazu führen kann, dass das Training mehrere Tage und Wochen die Rechenleistung eines durchschnittlichen Computers beansprucht. Zum Glück verwenden wir bereits fertig trainierte Modelle, die bei deepface mitgeliefert werden. Bevor TensorFlow installiert werden kann, müssen die Abhängigkeiten vorhanden sein. Die nächsten Schritte sind auch auf NVIDIAs Hilfeseiten (https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html) beschrieben:

apt install python3-pip
sudo apt install libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev liblapack-dev libblas-dev gfortran
pip3 install testresources setuptools==49.6.0
pip3 install numpy==1.19.4 future==0.18.2 mock==3.0.5 h5py==2.10.0 keras_preprocessing==1.1.1 keras_applications==1.0.8 gast==0.2.2 futures protobuf pybind11
pip3 install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v45 tensorflow

Es ist hilfreich nach der Installation einen Test durchzuführen, ob auch alles da ist. Im Fall von TensorFlow mache ich das mithilfe von Python in einem Terminal.

python3
>> import tensorflow

Wenn der import-Befehl ohne Fehlermeldung in die nächste Zeile springt, dann haben wir TensorFlow auf unserem System bereitgestellt.

deepface Installation

Die Installation von deepface ist mit einer einzigen Zeile getan. Während der Installation kann es wegen fehlender Bibliotheken zu einem Abbruch kommen. Beispielsweise ist bei mir die automatische Installation von Dlib fehlgeschlagen. Die manuelle Installation von Dlib sollte mit pip erfolgreich abschließen. Anschließend kann die deepface-Installation mit folgendem Befehl fortgesetzt werden.

pip install deepface

Auch hier ist es hilfreich nach der Installation einen Test durchzuführen, ob deepface korrekt installiert wurde. Im vorhergehenden Kapitel ist eine Beschreibung für die Prüfung der TensorFlow-Bibliothek. Dementsprechend kann man auch hier wie oben beschrieben verfahren.

Zuletzt laden wir das zugehörige git-Repositorium in ein Verzeichnis unserer Wahl. Darin werden unter anderem die Unit-Tests sowie Beispiele zur Nutzung des deepface-Frameworks bereitgestellt. Der folgende Befehl lädt die neueste Version von deepface in das aktuelle Verzeichnis:

git clone https://github.com/serengil/deepface.git

Swap-Datei

Zu Beginn dieses Artikels habe ich Probleme mit deepface benannt, die in den nächsten Kapiteln auftreten könnten. Dazu zählt in erster Linie eine Swap-Datei, da eine ganze Menge Arbeitsspeicher ausgelagert wird, sobald wir die Unit-Tests starten. Ich habe folgenden Hack mit Standardeinstellungen verwendet:

git clone https://github.com/JetsonHacksNano/installSwapfile
cd installSwapfile/
./installSwapfile.sh

Im nächsten Kapitel wird die unit_tests.py verwendet. Bevor wir die Tests mit CUDA erfolglos starten, weisen wir das Betriebssystem an, CUDA nicht zu verwenden. Dazu editieren wir die unit_tests.py und fügen in den ersten Zeilen folgendes ein:

os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

Die Bibliothek „os“ muss vorher importiert werden, wenn der Import nicht bereits aufgeführt ist.

deepface Unit-Tests

Nachdem wir deepface mit pip erfolgreich installiert haben, testen wir seine Funktionalitäten mit der Python-Datei unit_tests.py im tests-Verzeichnis. Im Ordner deepface des git-Repositoriums befindet sich ein Unterordner tests. Darin führen wir den nächsten Befehl aus:

python3 unit_tests.py

Hinweis: Schwierig wird es an dem Punkt, wo die Modelle aus einem Google-Drive-Laufwerk heruntergeladen werden sollen. An so einer Stelle kann es passieren, dass der Download wegen zu vielen Zugriffen nicht möglich ist. Der Entwickler von deepface weist zwar darauf hin, dass man es in 24 Stunden erneut probieren soll, aber für uns bedeutet ein Abbruch den Neustart der Unit-Tests. Leider ist der Jetson Nano nicht schnell genug, und deshalb ist ein Neustart alles andere als erträglich. Ich empfehle als Alternative, die Installation von deepface auf einem anderen, schnelleren Rechner durchzuführen, um die Google-Drive-Modelle von dort zu beziehen. Auf einem Linux-System werden die Modelle in das folgende Verzeichnis automatisch heruntergeladen, wenn man die unit_tests.py ausführt:

~/.deepface/weights

Wenn es denn Mal endlich losgeht mit dem Python-Programm unit_tests.py, dann sieht es folgendermaßen auf der Konsole aus:

Running unit tests for TF 2.4.0
Function returned 2622 dimensional vector
VGG-Face is built
Represent function returned 2622 dimensional vector
Face detectors test
ssd detector
Verification: 100%|███████████████████████████████| 2/2 [00:14<00:00, 7.07s/it]
{'pair_1': {'verified': True, 'distance': 0.27857738733291626, 'max_threshold_to_verify': 0.4, 'model': 'VGG-Face', 'similarity_metric': 'cosine'}, 'pair_2': {'verified': True, ' distance': 0.28389161825180054, 'max_threshold_to_verify': 0.4, 'model': 'VGG-Face', 'similarity_metric': 'cosine'}}
opencv detector
Verification: 100%|███████████████████████████████|2/2 [00:20<00:00, 10.31s/it]
{'pair_1': {'verified': True, 'distance': 0.2708548903465271, 'max_threshold_to_verify': 0.4, 'model': 'VGG-Face', 'similarity_metric': 'cosine'}, 'pair_2': {'verified': True, 'distance': 0.28633153438568115, 'max_threshold_to_verify': 0.4, 'model': 'VGG-Face', 'similarity_metric': 'cosine'}}
dlib detector

Die Unit-Tests laufen ausschließlich auf der CPU. Sobald die Tests mit CUDA-Bibliotheken dran sind, bricht das Python-Programm ab.

deepface video stream

deepface stream candidates

Als nächstes werden wir eine WebCam verwenden, um Gesichter zu erkennen und bestimmten Personen zuzuordnen. Die Struktur für dieses Vorhaben besteht aus einem Verzeichnis mit den Gesichtern und einem Python-Script im Ordner tests. Es handelt sich hierbei um dasselbe tests-Verzeichnis, in dem wir in den vorhergehenden Kapiteln gearbeitet haben. Der Ordner für die Gesichter heißt bei mir dataset2, da es bereits einen Ordner dataset im tests-Verzeichnis gibt. Die Bilddateien mit den Gesichtern habe ich mit den realen Namen der Personen benannt. Wenn mehrere Bilder dieselbe Person abbilden, habe ich eine fortlaufende Nummer angefügt. Mehrere Bilder derselben Person sollten in Unterordnern gruppiert werden.

Das Python-Script hat folgenden Inhalt:

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
from deepface import DeepFace
DeepFace.stream("/home/<benutzername>/deepface/tests/dataset2")

Das Script muss so angepasst werden, dass die stream-Methode der DeepFace-Klasse auf das korrekte Verzeichnis zugreift. Es erscheint nämlich keine Fehlermeldung, wenn der Pfad falsch angegeben ist. Wenn alle Voraussetzungen vorbereitet sind, starten wir das Python-Script mit python3.

Finding embedding for Murat_Calis_4.jpg: 100% |███████████████████████████████| 8/8 [01:10<00:00, 8.77s/it]
Embeddings found for given data set in 143.42 seconds

Im Verlauf des Python-Scripts werden sogenannte Embeddings gesucht.  Embeddings sind die numerische Repräsentation eines Gesichts. Es ist der Output, den man erhält, wenn man ein Gesicht als Input für ein Gesichtserkennungsmodell eingibt. Der Output am Ende eines Convolutional Neural Networks ist ein Vektor aus Zahlen, der das Gesicht numerisch repräsentiert. Ein Treffer zwischen zwei Gesichtern wird anhand einer Distanzmessung beider Vektoren vorgenommen. Je näher sich die Vektoren sind, desto ähnlicher sind die Gesichter. Eine Vektor-Repräsentation für ein Gesicht erhält man beispielsweise mit folgender Zeile:

embedding = DeepFace.represent("gesicht.jpg")

Wenn wir weiter oben die Zeit betrachten, die das Finden der Embeddings benötigt hat, wird schnell klar, dass diese Embeddings irgendwann irgendwo gespeichert werden sollten. Andernfalls würde der Start der stream-Erkennung einfach zu viel Zeit beanspruchen, denn bei jedem Start müssen zunächst die Embeddings gefunden werden. Das Thema „Embeddings speichern“ wird Teil eines zukünftigen Artikels. Darin verwenden wir ElasticSearch als Backend um Millionen von Gesichter-Repräsentationen zu speichern und performant abfragen zu können.

Und zum Abschluss ein Schnappschuss dessen, was die Webcam an deepface geliefert hat – mit entsprechenden Analysedaten.

deepface webcam stream analyse snapshot

Die Gesichtserkennung wird in ihrem Porgrammablauf mit den Parametern time_threshold und frame_threshold unterbrochen. Während time_threshold eine Pause in Sekunden einlegt, erzwingt frame_threshold eine Mindestzahl an Gesichtern, die erkannt werden müssen, bevor eine Analyse startet. Wer den Programmablauf in Echtzeit halten möchte, muss in der Datei /lib/python3.6/site-packages/deepface/DeepFace.py in Zeile ca. 798 die beiden Parameter hart kodieren bzw. auf Null setzen.

Schreibe einen Kommentar