Computer Vision bleibt eine äußerst überzeugende Anwendung künstlicher Intelligenz. Ob es darum geht, Elemente auf einem Schlachtfeld zu erkennen oder Ernteerträge vorherzusagen, Computer Vision ist wohl einer der kommerziell wertvollsten (und gesellschaftlich wichtigsten) Bereiche der KI.
Ein Hindernis für die Einführung der besten Computer-Vision-Funktionen ist jedoch oft die Komplexität, die mit der Erstellung eines Datensatzes und dem Entwurf eines einfachen End-to-End-Systems verbunden ist, das Ihre Computer-Vision-Aufgabe an einem neuen Bild ausführt.
In diesem Blogbeitrag werden wir uns Schritt für Schritt damit befassen, wie Sie diese Probleme mit erstklassigen Tools wie CVAT und MinIO Bucket Notifications angehen können. Am Ende dieses Beitrags werden Sie in der Lage sein, ein Objekterkennungsmodell anhand eines benutzerdefinierten Datensatzes zu trainieren und damit Vorhersagen zu treffen, wann immer ein neues Bild erscheint.
Nehmen wir an, wir möchten in Satellitenbildern vorhandene Flugzeugtypen erkennen können. Nehmen wir außerdem an, wir fangen bei Null an: keine vorgefertigten Datensätze, keine vorab trainierten Modelle. Hier sind zwei Beispielflugzeuge, die wir in unseren Satellitenbildern erkennen und erkennen möchten:
Die in diesem Beitrag beschriebenen Schritte können auf nahezu jede Domäne übertragen werden. Anstatt Flugzeugtypen zu erkennen, könnten wir die Landnutzung klassifizieren oder eine Regression durchführen, um Ernteerträge vorherzusagen. Über herkömmliche Bilder hinaus könnten wir auch andere Arten mehrdimensionaler Daten wie LiDAR-Punktwolken oder seismische 3D-Bilder trainieren und daraus Rückschlüsse ziehen. Es stellt sich nur noch die Frage, wie die Trainingsdaten aussehen (und möglicherweise ein anderes Deep-Learning-Modell anstelle von YOLO). Wenn Sie weitere Fragen dazu haben, wie dies für einen bestimmten Anwendungsfall aussehen würde, können Sie gerne ein Issue im GitHub- Repo erstellen!
Für dieses Projekt besuchte ich Flugplätze auf Google Earth und machte mehrere Screenshots von Gebieten, in denen einige dieser Flugzeuge sichtbar waren, da ich zum großen Teil keinen On-Demand-Bildsatelliten besaß. Das Zusammenstellen dieses Bildsatzes hat einige Zeit in Anspruch genommen, daher habe ich sie alle in einem Bucket mit dem Namen „Objekterkennung“ auf meinem MinIO-Server gespeichert. In einer Produktionsumgebung werden die Vorteile der Speicherung Ihrer gesammelten Proben auf MinIO noch deutlicher. Aktiv-Aktiv-Replikation, höchste Verschlüsselungsstufen und superschnelle GET/PUTs (um nur einige zu nennen) sorgen dafür, dass Ihre sorgfältig gesammelten Proben hochverfügbar, sicher und geschützt sind.
Um ein Objekterkennungsmodell für Ihren Anwendungsfall zu trainieren, ist ein beschrifteter (oder „annotierter“) Datensatz erforderlich. Ein großartiges Tool hierfür ist CVAT von OpenCV. Eine coole Funktion ist, dass CVAT ein Dienstprogramm bereitstellt, mit dem Sie Ihren MinIO-Bucket als „Cloud-Speicher“ verbinden können, um die Bilder Ihres Buckets direkt an das Datensatz-Annotationstool weiterzuleiten. Stellen Sie dazu sicher, dass der CVAT-Server auf den Host Ihres MinIO-Servers zugreifen kann, insbesondere wenn Sie MinIO-Server vor Ort oder lokal auf Ihrem Laptop ausführen. Bitte beachten Sie, dass es zwei Möglichkeiten gibt, CVAT zu verwenden: (1) die bereitgestellte Web-App unter app.cvat.ai zu verwenden oder (2) sie lokal auszuführen. In beiden Fällen klicken Sie nach dem Öffnen von CVAT in der Menüleiste auf „Cloud-Speicher“. Von dort aus können Sie ein Formular ausfüllen, um Ihren (S3-kompatiblen) MinIO-Bucket anzuhängen:
Erstellen wir nun unter „Aufgaben“ unsere neue Beschriftungsaufgabe:
Sie sollten aufgefordert werden, ein Formular auszufüllen:
Beim Erstellen der Aufgabe ist es wichtig, die Klassenbeschriftungen korrekt zu definieren (ich habe zwei Rechteckbeschriftungen mit den Titeln „SU30“ und „TU95“ definiert, die den beiden Ebenen entsprechen, die ich erkennen wollte):
Der verbleibende Schritt besteht nun darin, unseren zuvor hinzugefügten MinIO-Bucket als Datenquelle anzuhängen. Klicken Sie unter „Dateien auswählen“ auf „Cloud-Speicher“ und geben Sie den Namen ein, den Sie zuvor für diese Quelle angegeben haben. Ich habe oben den Namen „minio-cv-bucket“ verwendet.
Der Hochladevorgang dauert einige Minuten. Sobald der Vorgang abgeschlossen ist, sollten Sie unter „Jobs“ sehen können, dass Ihr Annotationsauftrag verfügbar ist.
Wenn Sie nun auf den Auftrag klicken, können Sie damit beginnen, jedes Ihrer Bilder mit Anmerkungen zu versehen. Achtung: Dies kann ein unverhältnismäßig zeitaufwändiger Prozess sein. Im Allgemeinen kann es in einer Produktionsumgebung mit großem Anmerkungsbedarf am besten sein, diese Aufgabe einem speziellen internen Team oder einem externen Datenkennzeichnungsunternehmen zu übertragen.
Wenn Sie mit dem Kommentieren fertig sind, exportieren Sie den Datensatz im YOLO-Format.
Ihr exportierter Datensatz liegt in Form einer ZIP-Datei vor. Sobald Sie es entpacken, befinden sich die YOLO-formatierten Anmerkungstextdateien in einem geschlossenen Ordner. Schauen Sie sich diese gerne einmal an. Im YOLO-Format befinden sich die Anmerkungen zu jedem Bild in einer Textdatei, in der jede Zeile zwei Ecken eines Begrenzungsrahmens und die Klasse enthält. Die Klassennummer entspricht der Reihenfolge, in der Sie die Beschriftungen beim Erstellen der Aufgabe definiert haben. In diesem Beispiel würde 0 also Su-30 und 1 Tu-95 entsprechen.
Erstellen Sie an dieser Stelle ein neues Arbeitsverzeichnis (oder geben Sie eines ein, das Sie bereits erstellt haben). Erstellen Sie in diesem Verzeichnis ein Unterverzeichnis mit dem Namen „dataset“. Erstellen Sie innerhalb von „dataset“ Verzeichnisse, sodass Ihr Arbeitsverzeichnis wie folgt aussieht:
my_cv_project (WORKING DIRECTORY) |---- dataset |----images |----train |----val |----test |----annotations |----train |----val |----test
Sie müssen nun die Unterverzeichnisse train, val und test für beide Bilder und ihre entsprechenden Anmerkungen (die Textdateien) füllen. Es liegt an Ihnen, wie Sie Ihre Proben abrufen und aufteilen möchten. Eine gute Vorgehensweise besteht darin, die Gesamtmenge an Trainingsbeispielen in 80 % Training, 10 % Validierung und 10 % Test aufzuteilen. Stellen Sie sicher, dass Sie Ihre Bilder zufällig mischen, bevor Sie sie partitionieren.
Persönlich habe ich mc cp des MinIO-Clients in der Befehlszeile verwendet, um schnell alle Bilder aus meinem „Objekterkennungs“-Bucket abzurufen. Alternativ können Sie direkt damit arbeiten, wenn Sie alle Ihre Beispielbilder bereits an einem Ort auf Ihrem lokalen Computer haben. Nachdem ich alle meine Beispiele an einem Ort hatte, verwendete ich ein Python-Skript, um meine Bilder und Anmerkungen zu mischen, aufzuteilen und in die Verzeichnisse train, val und test zu verschieben. Der Einfachheit halber finden Sie hier das bereitgestellte Skript . Wenn Sie Fragen zur Verwendung haben, können Sie gerne ein Problem im Repo stellen!
Stellen Sie abschließend sicher, dass sich für jedes Bild, das Sie entweder in images/train, images/val oder images/test platziert haben, die passende Annotation-.txt-Datei auch im entsprechenden Unterverzeichnis im Verzeichnis annotations/ befindet. Zum Beispiel:
my_cv_project (WORKING DIRECTORY) |---- dataset |----images |----train - 5.png - 3.png - 2.png |----val - 4.png |----test - 1.png |----annotations |----train - 5.txt - 3.txt - 2.txt |----val - 4.txt |----test - 1.txt
Jetzt liegen unsere Daten vor. Es ist Zeit, einen Blick auf unser Objekterkennungsmodell zu werfen und mit dem Training zu beginnen.
Der aktuelle Goldstandard (in Bezug auf Leistung und Benutzerfreundlichkeit) für die Objekterkennung ist die Modellklasse YOLO (You Only Look Once). Zum Zeitpunkt des Schreibens ist YOLOv8 die neueste Version und wird von Ultralytics als Open Source verwaltet. YOLOv8 bietet eine einfache API, die wir nutzen können, um das Modell anhand unserer neu erstellten Annotationen zu trainieren (und schließlich auch Inferenzen auszuführen).
Laden wir YOLOv8 herunter:
$ pip install ultralytics
Wir können jetzt das YOLOv8 CLI-Tool oder das Python SDK zum Trainieren, Validieren und Vorhersagen verwenden. Weitere Informationen finden Sie in den YOLOv8-Dokumenten.
Definieren Sie in Ihrem Arbeitsverzeichnis eine YAML-Datei, die die Speicherorte des Datensatzes und die Details zu den Klassen angibt. Beachten Sie, dass die Pfade mit denen übereinstimmen, die ich zuvor im Arbeitsverzeichnis erstellt habe. Ich habe meine Datei „ objdetect.yaml “ genannt. Beachten Sie außerdem, dass die beiden Flugzeugklassenbezeichnungen in derselben Reihenfolge definiert werden müssen wie in CVAT.
train: ./dataset/images/train/ val: ./dataset/images/val/ test: ./dataset/images/test/ # number of classes nc: 2 # class names names: ["SU-30","TU-95"]
Beginnen Sie mit dem folgenden Befehl (mit dem YOLO CLI-Tool) mit dem Training des YOLOv8-Modells in unserem Datensatz. Weitere Informationen zu den verschiedenen Optionen, die Sie für das Training konfigurieren können, finden Sie in den YOLO-Dokumenten. Hier initiiere ich das Training für 100 Epochen und stelle eine Bildgröße von 640 Pixeln ein (alle unsere Trainingsbilder werden während des Trainings entsprechend skaliert):
$ yolo task=detect \ mode=train \ model=yolov8s.pt \ data=objdetect.yaml \ epochs=100 \ imgsz=640
Das Training wird eine Weile dauern, besonders wenn Sie (wie ich) an einem Laptop arbeiten. Jetzt ist also ein guter Zeitpunkt, eine Pause einzulegen (oder weiterzulesen 😀)!
Am Ende der Trainingsschleife wird Ihr trainiertes Modell zusammen mit anderen interessanten Grafiken und Diagrammen in einem automatisch generierten Verzeichnis namens „runs“ gespeichert. Die Terminalausgabe (wie unten) zeigt den genauen Speicherort der Ergebnisse des letzten Laufs an. Jedes Mal, wenn Sie ein Modell trainieren, wird ein ähnliches Verzeichnis innerhalb von „runs/detect/“ generiert.
Results saved to runs/detect/train
Hinweis: „runs/detect/train/weights/“ enthält die PT-Dateien mit den genauen trainierten Gewichten. Merken Sie sich diesen Ort für später.
Sie können die Validierung mit dem folgenden Befehl ausführen:
$ yolo task=detect \ mode=val \ model=path/to/best.pt \ data=objdetect.yaml
Die Ergebnisse werden automatisch in einem Ordner in Ihrem Arbeitsverzeichnis mit einem Pfad der Form „runs/detect/val“ gespeichert.
Um Rückschlüsse auf den Testsatz zu ziehen, können Sie den folgenden Befehl verwenden:
$ yolo task=detect \ mode=predict \ model=path/to/best.pt \ conf=0.5 \ source=dataset/images/test
Die Ergebnisse werden in „runs/detect/predict“ gespeichert. Hier sind einige Vorhersageergebnisse für den Testsatz:
Nachdem wir nun über ein trainiertes Modell verfügen, das einige in einem Satellitenbild vorhandene Flugzeugtypen erkennen kann, wie können wir es auf einfache Weise für neue Bilder nutzen ?
MinIO Bucket Notifications ist hierfür ein perfektes Tool. Wir können ein System aufbauen, das mithilfe eines Webhooks automatisch eine Objekterkennungsinferenz für ein neues Bild in unserem Bucket durchführen kann.
Auf einem hohen Niveau haben wir 3 Schritte. Zuerst müssen wir einen Endpunkt definieren, der als Webhook dienen kann, um mit unserem trainierten Modell eine Objekterkennung für ein neues Bild durchzuführen. Zweitens müssen wir einige Umgebungsvariablen für unsere MinIO-Server-Bereitstellung konfigurieren, die sie anweisen, bei Eintritt eines Ereignisses unseren Webhook-Endpunkt zu erreichen. Drittens müssen wir konfigurieren, auf welche Arten von Bucket-Ereignissen (z. B. PUT) wir reagieren möchten. Gehen wir es Schritt für Schritt durch.
Hier ist der Code für einen einfachen Flask-basierten Server ( detector_server.py ), der Rückschlüsse auf ein neues Image ausführt, das dem MinIO-Bucket hinzugefügt wurde:
""" This is a simple Flask inference server implementation that serves as a webhook for the event of a new image being added to a MinIO bucket. Object detection using YOLO will be performed on that image and the resulting predictions will be returned. """ from flask import Flask, request, abort, make_response from ultralytics import YOLO import tempfile from minio import Minio # Make sure the following are populated with your MinIO details # (Best practice is to use environment variables!) MINIO_ENDPOINT = '' MINIO_ACCESS_KEY = '' MINIO_SECRET_KEY = '' model = YOLO('/PATH/TO/best.pt') # load a custom model (path to trained weights) client = Minio( MINIO_ENDPOINT, access_key=MINIO_ACCESS_KEY, secret_key=MINIO_SECRET_KEY, ) app = Flask(__name__) @app.route('/', methods=['POST']) async def inference_bucket_webhook(): """ This endpoint will be called when a new object is placed in your inference bucket """ if request.method == 'POST': # Get the request event from the 'POST' call event = request.json bucket = event['Records'][0]['s3']['bucket']['name'] obj_name = event['Records'][0]['s3']['object']['key'] with tempfile.TemporaryDirectory() as temp_dir: temp_file_name = temp_dir+'/'+obj_name client.fget_object(bucket, obj_name, temp_file_name) # See https://docs.ultralytics.com/modes/predict/ for more information about YOLO inference options results = model.predict(source=temp_file_name, conf=0.5, stream=False) # A list of bounding boxes (if any) is returned. # Each bounding box is in the format [x1, y1, x2, y2, probability, class]. result = {"results": results[0].boxes.data.tolist()} print(result) resp = make_response(result, 200) return resp else: abort(400) if __name__ == '__main__': app.run()
Starten wir den Inferenzserver:
$ python detection_server.py * Serving Flask app 'detection_server' * Debug mode: off * Running on http://127.0.0.1:5000 Press CTRL+C to quit
Notieren Sie sich den Hostnamen und den Port, auf dem die Flask-Anwendung ausgeführt wird.
Beginnen wir als Nächstes mit der Konfiguration der Webhooks auf der MinIO-Seite. Legen Sie zunächst die folgenden Umgebungsvariablen fest. Ersetzen Sie <YOURFUNCTIONNAME> durch einen Funktionsnamen Ihrer Wahl. Der Einfachheit halber habe ich mich für „Schlussfolgerung“ entschieden. Stellen Sie außerdem sicher, dass die Endpunktumgebungsvariable auf den richtigen Host und Port für Ihren Inferenzserver eingestellt ist. In diesem Fall ist http://localhost:5000 der Ort, an dem unsere Flask-Anwendung ausgeführt wird.
$ export MINIO_NOTIFY_WEBHOOK_ENABLE_<YOURFUNCTIONNAME>=on $ export MINIO_NOTIFY_WEBHOOK_ENDPOINT_<YOURFUNCTIONNAME>=http://localhost:5000
Starten Sie nun den MinIO-Server mit dem Befehl „mc admin service restart ALIAS“ neu. Wenn Sie den Server zum ersten Mal starten, können Sie auch einfach den Befehl „ minio server“ verwenden. Weitere Informationen zum Neustarten/Neustarten des MinIO-Servers finden Sie in den MinIO-Dokumenten. Hinweis: ALIAS sollte durch den Alias für Ihre MinIO-Serverbereitstellung ersetzt werden. Weitere Informationen zum Festlegen eines Alias oder zum Anzeigen vorhandener Aliase finden Sie in den Dokumenten .
Zum Schluss fügen wir noch den Bucket und das Ereignis hinzu, über das wir benachrichtigt werden möchten. In unserem Fall möchten wir über „ Put“ -Ereignisse (Erstellung neuer Objekte) in unserem Bucket benachrichtigt werden. Zu diesem Zweck habe ich einen brandneuen, leeren Bucket mit dem Titel „detect-inference“ erstellt, also werde ich ihn durch „BUCKET“ ersetzen.
$ mc event add ALIAS/BUCKET arn:minio:sqs::<YOURFUNCTIONNAME>:webhook --event put
Sie können überprüfen, ob Sie den richtigen Ereignistyp für die Bucket-Benachrichtigungen konfiguriert haben, indem Sie überprüfen, ob „ s3:ObjectCreated:* “ ausgegeben wird, wenn Sie diesen Befehl ausführen:
$ mc event ls local/detect-inference arn:minio:sqs::<YOURFUNCTIONNAME>:webhook
Eine ausführlichere Erklärung zum Veröffentlichen von Bucket-Ereignissen in einem Webhook finden Sie in den Dokumenten . Jetzt sind wir bereit, die Objekterkennung an einem brandneuen Bild auszuprobieren!
Hier ist das neue Bild (mit dem Titel „1.png“), auf das ich Rückschlüsse ziehen möchte:
Ich lege das neue Bild in meinen „Detect-Inference“-Bucket:
Fast sofort kann ich die folgenden Ergebnisse auf meinem Flask-Server sehen:
$ python detection_server.py * Serving Flask app 'detection_server' * Debug mode: off * Running on http://127.0.0.1:5000 Press CTRL+C to quit image 1/1 /var/folders/xf/q7x0z8sn5nvckccp1g0m1vpm0000gn/T/tmpo6jx3w8u/1.png: 448x736 2 SU-30s, 101.0ms Speed: 4.1ms preprocess, 101.0ms inference, 5.8ms postprocess per image at shape (1, 3, 448, 736) {'results': [[1927.78369140625, 627.7123413085938, 1995.090576171875, 715.3443603515625, 0.8142037987709045, 0.0], [1735.740234375, 477.2108154296875, 1809.181640625, 555.767578125, 0.7766116261482239, 0.0]]} 127.0.0.1 - - [14/Sep/2023 15:39:21] "POST / HTTP/1.1" 200 -
Beachten Sie, dass jeder erkannte Begrenzungsrahmen in der Ergebnisliste im YOLO-Format [x1, y1, x2, y2, Wahrscheinlichkeit, Klasse] vorliegt. Hier sind die Begrenzungsrahmen und vorhergesagten Klassen, die dem Originalbild überlagert sind:
Hinweis: Für Produktionsumgebungen und/oder große Modelle für maschinelles Lernen empfiehlt es sich, ein etabliertes Modellbereitstellungsframework wie PyTorch Serve oder Triton Server zu verwenden, um die Inferenz robuster und zuverlässiger zu machen. Wenn Sie daran interessiert sind, lesen Sie den vorherigen Beitrag zur Optimierung der KI-Modellbereitstellung mit MinIO und PyTorch Serve .
Wir haben es geschafft! Wir haben erläutert, wie MinIO und CVAT zusammenkamen, um unsere gesammelten Bildproben sicher und verfügbar zu halten, und wie wir unseren benutzerdefinierten Objekterkennungsdatensatz erstellen. Anschließend haben wir unser eigenes benutzerdefiniertes YOLO-Modell für unsere benutzerdefinierte Aufgabe trainiert. Schließlich haben wir mit etwas mehr als 50 Codezeilen einen Inferenzserver mithilfe von MinIO-Bucket-Benachrichtigungen zusammengestellt, der ein neues Bild an unserem individuell trainierten Objekterkennungsmodell vorbeiführen könnte.
Darüber hinaus ist es für die meisten geschäftskritischen Anwendungen von Computer Vision am besten, Inferenzen am Rande durchzuführen. Andernfalls sind diese Anwendungen anfällig für die Latenz, die mit dem Hochladen neuer Daten in die öffentliche Cloud und dem Warten auf die Antwort eines Inferenzservers in der Cloud einhergeht – ganz zu schweigen von den Risiken einer fehlerhaften Netzwerkverbindung. Aus diesem Grund ist eine Computer-Vision-Pipeline, die sich auf MinIO als Datenschicht konzentriert, viel sinnvoller. Stellen Sie sich eine Drohne vor, die über einen Flugplatz fliegt und unser trainiertes Modell mit vollständig integrierter Hardware und Software erfassen, speichern und für neue Bilder verwenden kann. Mit einer lokalen Bereitstellung des MinIO-Servers funktioniert das auf Bucket-Benachrichtigungen basierende Inferenzsystem, das wir am Ende des Beitrags erstellt haben, perfekt für dieses und unzählige andere ähnliche Szenarios.
Wenn Sie Fragen haben, treten Sie unserem Slack-Kanal bei oder schreiben Sie uns eine Nachricht an [email protected] . Wir sind hier, um dir zu helfen.
Auch hier veröffentlicht.