Single-Tasking-Problem
In der letzten
Serie habe ich den Intel Neural Computer Stick 2 auf den Tank gelegt und alle Berechnungen des neuronalen Netzwerks darauf geworfen, wobei ich Tensorflow und OpenCV-DNN aufgegeben habe.
Es gab ein Problem, auf das ich damals bereits gestoßen bin - die Unfähigkeit, mit NCS aus mehreren Prozessen gleichzeitig zu arbeiten. Damals war es nicht kritisch, aber jetzt ist es Zeit, es herauszufinden.
Beim Versuch, ein Modell aus dem zweiten Prozess zu laden, begann OpenVino zu schwören:
E: [ncAPI] [ 926029] resetAll:348 Failed to connect to stalled device, rc: X_LINK_ERROR E: [ncAPI] [ 933282] ncDeviceOpen:672 Failed to find suitable device, rc: X_LINK_DEVICE_NOT_FOUND
Bei der Suche im Intel Support Forum wurde ein
ähnliches Problem festgestellt
.Von dort wurden wir zu der
Dokumentation weitergeleitet, wo klar angegeben ist:
Ein einzelnes Gerät kann nicht für mehrere Prozesse freigegeben werden.
In diesem Experiment können Sie den Multiprozesszugriff minimieren und beginnen.
NCS-Service
Es ist völlig logisch, die direkte Arbeit mit NCS in einem separaten Dienst zu platzieren und die API an alle Clients zu verteilen, über die sie arbeiten werden.
Im Allgemeinen sollte dies ein Thema über den Roboter und seine neuen Errungenschaften in Bezug auf neuronale Netze sein. Es stellte sich jedoch heraus, dass das Material zur NCS-API in einem separaten Artikel enthalten ist.
NCS-API
Auf niedriger Ebene ist die NCS-API sehr einfach:
- Modell laden
- Berechnung starten
- Holen Sie sich eine Liste der Modelle
- Modelleigenschaften abrufen
Wenn beim Laden des Modells alles eindeutig ist, ist der Berechnungsauspuff ein kontextsensitiver Tensor, den der Client möglicherweise nicht alle benötigt.
Das Abrufen einer Liste von Modellen ist ebenfalls ziemlich transparent, und die Dimension des Eingangstensors fällt sofort ein - für den Menschen bedeutet dies, dass es schön wäre, die Bilder im Voraus an die Netzwerkeinstellungen anzupassen.
Darüber hinaus ist ein niedriger Pegel gut, aber wenn Sie spezielle Operationen unterstützen, vereinfacht dies die Logik und die Daten.
Daher gibt es neben der Basis eine Aufgabe, die API für die Klassifizierung, Erkennung und Segmentierung zu unterstützen.
Leider werden die interessantesten Segmentierungsmodelle vom NCS
nicht unterstützt , sodass Sie sich auf das Einfachste mit der Straße und dem Markup beschränken müssen.
Jede dieser Operationen verwendet die Grundberechnung des Modells, unterscheidet sich jedoch in der Interpretation des Ausgangstensors.
Hauptschnittstelle
Die Hauptschnittstelle enthält also Methoden:
- POST: / load - Laden Sie das Modell
- POST: / unload / $ model - Löscht das Modell (aus dem Dienst kann es nicht vom Gerät entfernt werden)
- GET: / list - Eine Liste der Modelle abrufen
- GET: / input / shape / $ model - Ermitteln Sie die Dimension des Eingangstensors
- POST: / inference / file / $ model - Berechnen Sie mit Daten aus dem Speicher
- POST: / inference / path / $ model - Berechnen Sie mit Daten im Dateisystem
Hier sind zwei Wörter zu Daten aus dem Speicher und dem Dateisystem:
Wenn der NCS-Dienst und sein Benutzer auf derselben Himbeere ausgeführt werden, ist es sinnvoll, beim Übertragen des Bildes zu speichern und stattdessen den Pfad so zu übertragen, dass der Dienst selbst die Datei liest.
Befindet sich das Bild bereits im Speicher (oder ist es im Dateisystem nicht vorhanden), übertragen wir es direkt von dort.
Tests zeigen, dass die Übertragung von Bytes aus dem Speicher erheblich langsamer ist (Messung für 1000 Versuche):
Aus dem Speicher: 87,5 Sekunden
Dateipfad: 63,3150 Sekunden
Diese beiden Optionen werden jedoch für jede Methode unterstützt, sowohl für die allgemeine Berechnung als auch für die folgenden Sonderfälle.
Im Allgemeinen nimmt die Inferenzmethode ein Bild in Form eines Numpy-Arrays als Eingabe und erzeugt einen Tensor im gleichen Format.
Die Interpretation des Auspuffs ist bereits ein Kundenproblem.
Um diese Aufgabe zu erleichtern, unterstützt der Dienst spezielle Methoden, die wichtige Informationen in menschlicher Form aus dem Ausgangstensor extrahieren.
Klassifizierung
Zur Klassifizierung erstellen wir eine separate REST-Methode, die den Ausgangstensor in eine Reihe von Paaren (Klasse, Punktzahl) konvertiert.
def get_class_tensor(data): ret = [] thr = 0.01 while(True): cls = np.argmax(data) if data[cls] < thr: break; logging.debug(("Class", cls, "score", data[cls])) c = {"class" : int(cls), "score" : int(100 * data[cls])} data[cls] = 0 ret.append(c) return ret def classify(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out return True, get_class_tensor(out)
Wie bei der normalen Ausgabe werden zwei Methoden unterstützt - über eine Datei im Speicher und einen Pfad auf der Festplatte.
- POST: / classify / file / $ model
- POST: / classify / path / $ model
Erkennung
Der Detektorausgangstensor enthält eine Menge (Klasse, Wahrscheinlichkeit, normalisierte Koordinaten) und sieht ziemlich umständlich aus.
Wir verwandeln es in eine verständliche Form, während wir unwahrscheinliche Optionen abschneiden:
def get_detect_from_tensor(t, rows, cols): score = int(100 * t[2]) cls = int(t[1]) left = int(t[3] * cols) top = int(t[4] * rows) right = int(t[5] * cols) bottom = int(t[6] * rows) return {"class" : cls, "score" : score, "x" : left, "y" : top, "w" : (right - left), "h" : (bottom - top)} def build_detection(data, thr, rows, cols): T = {} for t in data: score = t[2] if score > thr: cls = int(t[1]) if cls not in T: T[cls] = get_detect_from_tensor(t, rows, cols) else: a = T[cls] if a["score"] < score: T[cls] = get_detect_from_tensor(t, rows, cols) return list(T.values()) def detect(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out rows, cols = img.shape[:2] return True, build_detection(out[0], 0.01, rows, cols)
Wie üblich werden beide Methoden unterstützt:
- POST: / detect / file / $ model
- POST: / detect / path / $ model
Segmentierung
Der Segmentierungstensor enthält Wahrscheinlichkeiten nach Klasse und sogar in der Dimension des neuronalen Netzwerks.
Konvertieren Sie dies einfach in eine Klassenmaske:
def segment(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out out = np.argmax(out, axis=0) out = cv.resize(out, (img.shape[1], img.shape[0]),interpolation=cv.INTER_NEAREST) return True, out
- POST: / segment / file / $ model
- POST: / segment / path / $ model
Fazit
Wie bereits erwähnt, hatte ich ursprünglich vor, in einem der Kapitel des Artikels über den Dienst über den Dienst zu sprechen, aber es stellte sich heraus, dass der Band auf einem separaten Dokument abgerufen wird.
Auch hier verwende ich den Dienst auf dem Raspberry Pi, aber er kann auf jeder Plattform ausgeführt werden, auf der Python und OpenVino mit NCS vorhanden sind.
Referenzen