Abläufe von Modellen zu erklären, die mit Machine Learning-Algorithmen trainiert wurden, wie z. B. Object Detection-Modelle, wird immer wichtiger. Das liegt zum Einen an rechtlichen Standards, die Einblicke in diese Modelle verlangen. Zum Anderen liegt es an der zunehmenden Komplexität von State-of-the-Art-Modellen. Wir haben in unserem Blog bereits über erklärbare KI (XAI) geschrieben und einen Vortrag darüber gehalten.
In diesem Artikel untersuchen wir eine AI-Anwendung, bei der die Erklärung des Modell-Outputs besonders interessant und herausfordernd ist: Das Erkennen von Objekten in Bildern. Object Detection ist ein Teilgebiet der Computer Vision, das intensiv weiterentwickelt und verbessert wird. Praktische Anwendungen reichen vom Zählen von Tierbeständen bis zur Erstellung besserer Thumbnails für soziale Medien. Hier gibt es ein Beispiel, was man mit Object Detection-Modellen alles machen kann - und warum es wichtig ist, ein Modell über Bewertungsmetriken hinaus zu verstehen. Explainable AI ist kein neues Thema in der Computer Vision. Für Object Detection-Modelle jedoch gibt es bisher keine Explainable Object Detection Tools, die das Modell für einen bestimmten Input analysieren. Daher zeigen wir nun an einem Beispiel einen Ansatz für eine detaillierte Erklärung von Modellergebnissen.
Wir wollen menschlich interpretierbare Gründe für eine bestimmte Modellentscheidung erhalten. Dazu nutzen wir die SHAP-Bibliothek um shapley-Werte zu berechnen. Shapley-Werte bestimmen den Beitrag eines Features zum Ergebnis. Sie berücksichtigen dabei eine Hintergrundverteilung der anderen Merkmale. Dazu gibt es hier eine detaillierte Erklärung. In diesem Artikel erstellen wir ein Modell, bei dem wir selektiv einen Teil des Inputs entfernen können, d. h. wir blenden einzelne Stellen im Bild aus. Diese Stellen dienen als Ersatz-Features, für die wir shapley-Werte berechnen und visualisieren.
XAI-Bibliotheken wie SHAP und LIME unterstützen eine Reihe von Standardmodellen und -eingaben. Dazu gehören (tiefe) neuronale Netze und Bildeingaben. Object Detection unterscheidet sich von den unterstützten Modellen: Am ähnlichsten wäre die Bildklassifikation. Bei der Bildklassifikation ist die Eingabe zwar die gleiche und auch die verwendeten Modelle sind typischerweise tiefe neuronale Netze. Allerdings geht die Ausgabe der Object Detection über nur einen Wert für eine Klasse oder einen Bereich von Klassenwahrscheinlichkeiten hinaus. Bei der Object Detection kann ein Bild mehrere Objekte in unterschiedlichen Größen zeigen. Moderne Modelle zur Object Detection beinhalten auch einen sogenannten Non-Maximum-Suppression-Schritt (NMS). Dieser filtert die Ausgabe der letzten Schicht des neuronale Netzes bis hin zu den endgültigen Vorhersagen.
Non-maximum-suppression (NMS)
Der Non-maximum-suppression step (NMS) entfernt schrittweise Boxen mit niedrigerem Score. Dafür muss die Intersection over Union mit einer anderen (besser bewerteten) Box über einem bestimmten Schwellenwert liegen. Die Wahl des Schwellenwertes ist dem Benutzer überlassen und hängt z. B. von den erwarteten Situationen ab: Sind Objekte häufig nahe beieinander gruppiert oder weiter voneinander entfernt?
Diesen Schritt muss man nicht trainieren und er ist nicht differenzierbar. Das bedeutet, dass es über den Gradienten des neuronalen Netzwerks keine direkte Verbindung von der Eingabe zur endgültigen Ausgabe gibt. Hinzu kommt das Problem der mehrfachen Ausgabe des Modells, welches die Verwendung von Tools wie DeepLift oder dem DeepExplainer in der SHAP-Bibliothek erschwert. Allerdings bietet SHAP ein Tool, das wir für generische Black-Box-Modelle verwenden können: Den KernelExplainer. Die Herausforderung bei unserem Beispiel der Explainable Object Detection liegt in zwei Aspekten. Erstens müssen wir die Aufgabe der Object Detection an das Schema des KernelExplainers anpassen. Und zweitens müssen wir Modell und XAI-Framework auf der technischen Ebene verbinden.
Den Code zu diesem Artikel gibt es hier in Colab. Im Folgenden erklären und besprechen wir die wichtigsten Schritte.
Für diesen Artikel verwenden wir den Object Detection-Algorithmus YOLOv5. Er läuft auf PyTorch, das in Colab unterstützt wird. Außerdem liefert er mit die besten Leistungen. Darüber hinaus erlauben die Modelle ein einfaches Fine-Tuning für neue Objekttypen.
Von den verfügbaren vortrainierten Modellen verwenden wir YOLOv5s. Es ist das kleinste Modell und ist auf dem COCO-Datensatz und seinen 80 Klassen vortrainiert.
PyTorch ist bereits in der Colab-Umgebung verfügbar. Allerdings müssen wir noch die SHAP-Bibliothek und YOLOv5 installieren und die vortrainierten Modellgewichte herunterladen. Aus dem YOLOv5-Code benötigen wir Hilfsfunktionen für NMS und für die Überprüfung der Überlappung von Boxen (die Intersection over Union oder IoU).
Mit der OpenCV-Bibliothek lesen wir ein Bild ein. Wir betten es in ein Quadrat ein und ändern die Größe auf 160×160 Pixel. Stattdessen können wir auch kleinere oder größere Eingabegrößen verwenden. Allerdings muss die Breite und Höhe der Quadrate ein Vielfaches von 32 sein, um als Eingabe in das vortrainierte Modell zu passen. Object Detection mit riefen neuronalen Netzen und XAI-Methoden ist ziemlich ressourcen- und zeitaufwändig. Daher verkleinern wir die Eingabebilder, um die Object Detection-Zeit zu beschleunigen. Wer jedoch an genaueren Detections und Erklärungen interessiert ist, kann hier in weiteren Berechnungen gerne verschiedene Bildgrößen ausprobieren.
Nun muss unsere Eingabe mit dem erwarteten Format (RGB-Farbformat) übereinstimmen. Dafür ordnen wir die Bilddimensionen neu an (OpenCV liest Bilder im BGR-Farbformat). Außerdem wandeln wir das Bild in einen PyTorch-Tensor um. Anschließend fahren wir mit dem Modell fort. Wir laden einfach die Modellgewichte und sind bereit, die Bilder zu analysieren.
Das Modell gibt uns folgende Ausgaben: Die Koordinaten - x und y des Mittelpunkts des identifizierten Objekts sowie Breite und Höhe. Wir erhalten auch die Wahrscheinlichkeit für ein Objekt an diesen Koordinaten, plus die Wahrscheinlichkeiten für jede der 80 Klassen. Dieser Vektor listet die Detections für alle möglichen Ankerpunkte auf. Von diesen werden die meisten sehr niedrige Wahrscheinlichkeiten haben:
Nun fassen wir die Vorhersagen mit NMS zusammen und filtern sie. Dadurch bleiben zwei erkannte Objekte mit hoher Wahrscheinlichkeit übrig. Wie erwartet sind das die beiden Personen auf dem Bild.
Für jede Detection erhalten wir die Koordinaten, jetzt im Format x1, y1, x2, y2. Außerdem erhalten wir das Produkt aus Objekt-Score und der Klasse mit dem höchsten Score. Der dritte Wert ist der Index der wahrscheinlichsten Klasse: Der COCO-Klassenindex 0 entspricht der Objektkategorie 'Person'. Nun wollen wir sehen, wie sicher wir die Person auf der rechten Seite gefunden haben. Dafür schauen wir uns die kombinierten Scores der zweiten Detection an: Das Modell erreichte einen Score von 68,6 %.
Es gibt einige Ansätze, um einen höheren Konfidenzwert zu erzielen. Man kann z.B. größere Modellgewichte verwenden oder die Bildauflösung erhöhen.
Die gesamte Modellausgabe in Bezug auf das komplette Eingabebild zu erklären, ist schwierig. Das liegt einfach daran, dass es nicht ein einzelnes, klar umrissenes Ergebnis gibt. Wir können uns aber auf eine Vorhersage pro Bild konzentrieren. Dann ist die Frage aus der XAI-Perspektive viel enger gefasst und besser definiert: Welcher Teil des Bildes trägt zu dieser bestimmten Detection bei und wie viel?
Um dieses Modell mit dem KernelExplainer von SHAP zu verwenden, müssen wir die obigen Schritte in ein PyTorch-Modell einpassen:
1. Konvertierung des Bildes in einen PyTorch-Tensor
2. Anwenden des Kernmodells
3. Anwenden von NMS
4. Berechnen des Scores der relevanten Erkennung.
Wir implementieren diese Schritte als einzelne Ebenen in PyTorch. Schritt 1 führen wir mit der Klasse Numpy2TorchCaster aus. Schritt 2 ist das Modell, das wir oben verwendet haben. Die Schritte 3 und 4 führen wir mit der Klasse OD2Score aus.
Wir extrahieren den Score für unsere Detection. Die erkannte Box kann dabei von unserem festgelegten Objekt abweichen. Wenn das passiert, multiplizieren wir die Punktzahl mit der Schnittmenge zwischen der richtigen Ziel-Box und der erkannten Box. Der endgültige Score hängt also sowohl davon ab, wie sicher das Modell die Person vorhersagen kann, als auch davon, wie gut die Box positioniert ist.
Man beachte auch, dass wir nur herausfinden wollen, wie das Modell zu diesem bestimmten Ergebnis kommt. Dabei spielt es keine Rolle, ob die Detection von einem Menschen als korrekt eingestuft wird oder einem anderen Goldstandard entspricht.
Wir können diese Aufgaben mit PyTorch nacheinander verknüpfen, wie wir hier zeigen:
Das scoring_model nimmt ein Bild als Eingabe und gibt zwei Dinge zurück: Wie zuversichtlich es ist, eine Person im richtigen Bereich zu erkennen, und wie gut die Box positioniert ist. Wir werden jedoch noch eine weitere Anpassung für die Eingabe vornehmen.
Jetzt könnten wir damit beginnen, das Eingangsbild zu verändern um zu sehen, wie sich die Ausgabe entsprechend ändert. Leider hat das Bild 160×160 = 25.600 Pixelwerte - 76.800, wenn man die drei Farbkanäle einzeln zählt. Die Berechnung des Einflusses jedes einzelnen Pixels erfordert enorme Rechenressourcen. Außerdem trägt jedes einzelne Pixel wahrscheinlich nur sehr wenig zur Erkennung bei. Daher fassen wir die Pixel stattdessen zu Superpixeln zusammen, d. h. zu zusammenhängenden Bereichen von Pixeln. Wir können ein Bild auf verschiedene Weise in Superpixel aufteilen. Wir halten es einfach und verwenden ein rechteckiges Gitter mit fester Breite und Höhe.
Für die Aufteilung erstellen wir eine neue Ebene, den SuperPixler. Diese Schicht nimmt eine Liste mit den Superpixeln entgegen, die für ein Bild verfügbar/nicht verfügbar sein sollen. Sie erstellt auch das passende Bild als Eingabe für die nächste Schicht. Das Feld eines inaktiven Superpixels wird durch die Durchschnittsfarbe des Bildes ersetzt.
Das super_pixel_model nimmt als Eingabe die Information, welche Superpixel aktiv sind und welche ausgegraut sind. Es wandelt dies intern in das Bild um, das unser Objekt enthält. Dann gibt es den Score zurück, wie gut wir unser Objekt erkannt haben. Wir haben nun ein Superpixel von 8×8 und ein Bild von 160×160 Pixel. Der Eingaberaum ist also nur ein Vektor aus 400 Binärwerten. Die sind den 400 Superpixeln zugewiesen, die das gesamte Bild abdecken.
Das super_pixel_model hat nun einen überschaubaren Eingaberaum. So kann der KernelExplainer es jetzt interpretieren. Nun speisen wir das Modell in den Explainer ein. Damit lassen wir ihn den Beitrag jedes Superpixels zur Ausgabe des Detektors bestimmen.
Wir bilden die Superpixel wieder auf die einzelnen Pixel ab (die Zuordnung ergibt sich sehr einfach aus dem verwendeten Gitter) und skalieren die Werte von 0 bis 1. In der von uns verwendeten Farbzuordnung bedeutet Rot einen positiven Beitrag des Pixels, Blau einen negativen Beitrag.
Wir sehen, dass sich die Bereiche mit hohem Beitrag tatsächlich innerhalb der Box unseres Objekts befinden. Interessanterweise scheint der höchste Beitrag von Kopf und Schultern zu kommen. Der Innenraum und der Unterkörper spielen eine geringere Rolle. Scheinbar wird für das Modell eine Person am stärksten mit dem Gesicht und den Konturen des Oberkörpers assoziiert. Das ergibt Sinn und korreliert wahrscheinlich auch mit der Art und Weise, wie Personen in den ursprünglichen COCO-Trainingsdaten erscheinen: Wir neigen dazu, uns beim Fotografieren auf den Kopf und den Oberkörper zu konzentrieren.
Wir sehen nur wenige Bereiche, die die Erkennung erschweren. Das ist auch logisch, denn nichts im Bild verdeckt das Motiv und macht es weniger wahrscheinlich, eine Person zu sehen. Der eine blaue Fleck hier könnte einem ausgestreckten Arm entsprechen. Das Fehlen dieses Flecks könnte also den Gesamteindruck einer Person für das Modell schwächen.
Es gibt alle möglichen Verbesserungen, die wir hier vornehmen könnten.
Z.B. können wir die Superpixel in einem ausgefeilteren Verfahren auswählen. Ein besserer Weg könnte ein Clustering von Bildelementen sein, bei dem der Ersatzwert die durchschnittliche Farbe ihrer benachbarten Superpixel ist.
Wir können auch mehr als einen Ersatzwert für jedes Superpixel definieren. Dies gibt eine bessere Charakterisierung, wie das "Fehlen" eines Pixels aussehen würde.
Auch unterschiedliche Skalen für das Bild und die Superpixel spielen eine Rolle. Mit diesen Werten kann man im Rahmen der verfügbaren Rechenleistung experimentieren.
Wir haben nur einen Fall betrachtet, in dem das Modell sowohl die Position als auch auf den Typ des Objekts richtig erkennt. Fälle, in denen das Modell einen oder beide Fehler macht, können uns möglicherweise mehr über dessen "Annahmen" und " Vorurteile" verraten. Das Beispielbild hier zeigt eine falsche Klassifizierung als Giraffe. Die korrekte Vorhersage liegt in der Tat außerhalb der Reichweite des Modells. Allerdings entsprechen die Bereiche, die zum Erkennen beitragen, dem langgezogenen Kopf und Hals des Kängurus.
Eine weitere Einschränkung ist das Erklären von falsch-negativen Ergebnissen: Wir könnten das Scoring-Verfahren so modifizieren, dass es nach einem Objekt sucht, das nur sehr schwach vorhergesagt ist. Aber um zu bestimmen, welche Eingabe das Modell für einen Treffer sehen müsste, müssten wir dem Bild Informationen hinzufügen. Angenommen, eine Person wird nicht erkannt, weil der Kopf verdeckt ist. Um zu überprüfen, ob die Verdeckung das Problem ist, müssten wir einen Kopf in der erwarteten Position in das Bild einfügen. Dies ist nicht nur schwieriger umzusetzen, der Raum der möglichen Imputationen ist für eine gegebene Fehlerkennung auch viel größer.
XAI-Tools unterstützen derzeit nicht von Haus aus die Objekterkennung. Daher mussten wir die Eingabe und Ausgabe eines Erkennungsmodells anpassen, um einen generischen Erklärer zu verwenden. Nachdem wir alle Komponenten erfolgreich miteinander verbunden hatten, waren wir in der Lage, ein einzelnes Ziel zu untersuchen. Wir konnten auch bestimmen, welcher Teil des Bildes zur Erkennung beiträgt. Das Modell scheint sich korrekt auf Bereiche des Bildes zu konzentrieren, die einen Bezug zum Ziel haben. Allerdings sehen wir in der Klasse "Personen" eine Neigung zum Oberkörper und Kopf. Das bedeutet, dass die Erkennung von Personen aus bestimmten Winkeln oder von teilweise sichtbaren Personen für das Modell schwieriger sein könnte. Von Fall zu Fall haben wir durch Explainable Object Detection einige wertvolle Einblicke in den Entscheidungsprozess des Modells gewonnen. Und dadurch vielleicht auch etwas mehr Vertrauen in seine Detections.