Projektidee
Seitdem ich mich entschieden hatte, mir Programmierkenntnisse anzueignen und immer tiefer in die Materie vorzudringen, habe ich mich – nicht zuletzt im Rahmen meiner Qualifizierung durch Bootcamp-Kurse – immer mehr auch mit dem faszinierenden Feld der Künstlichen Intelligenz, insbesondere mit Deep Learning und andere Formen des Machine Learning auseinandergesetzt. Zuletzt erschlossen habe ich mir dabei über Bücher und Blogs das Thema der Verwendung von Deep Learning für NLP (Natural Language Processing), wobei auch die Verwendung von Transformern angesprochen wurde, wie sie in der technischen Übersetzung Anwendung finden und sich für solche Anwendungen oft besser eigenen als einfache sequenzielle Modelle. Um die Funktionsweise einmal selbst auszuprobieren, wurde Huggingface empfohlen, eine Plattform, auf der KI-Entwickler ihre Modelle für unterschiedlichste Arbeitsgebiete austauschen und zur Verfügung stellen. Dazu gehören neben Übersetzung etwa Texterkennung, Schrifterkennung, Identifikation von Textpassagen, aber auch Objekterkennung. Als Kulturwissenschaftler finde ich dieses Feld, neben allem, was mit Texten zu tun hat, natürlich besonders spannend. Schließlich hat man es in den Geschichts- und Kulturwissenschaften außer mit schriftlichen oft mit Bildquellen zu tun hat, die man nutzt, um sich vergangene Ereignisse und Entwicklungen zu erschließen. Hier leichter Ordnung zu schaffen, indem ein Computer vorab feststellt, was in Bildern zu sehen ist, ist eine verlockende technologische Perspektive.

Huggingface: den richtigen Transformer finden
Wie auf der Webseite von Huggingface erläutert erlauben „Object Detection models […] users to identify objects of certain defined classes. Object detection models receive an image as input and output the images with bounding boxes and labels on detected objects.” Viele werden das vielleicht bereits kennen oder schon gesehen haben: in einem digitalen Bild wird ein bestimmtes Objekt mit einem farbigen Rahmen umgeben und per Namen identifiziert. Prinzipiell erlaubt das Verfahren eine völlig neue Form der Bildsuche: anstatt auf Metadaten zurückgreifen zu müssen, kann der Inhalt von Bildern direkt ausgelesen werden. Man muss die Euphorie hier aber, wie bei allen KI-Anwendungen, ein wenig einschränken: ein intelligenter Algorithmus kann immer nur so gut sein wie die Daten, mit denen er trainiert wurde, was bedeutet, dass er nur das erkennen, womit und wofür er trainiert wurde. Hat man den Algorithmus ausschließlich mit Katzenfotos gefüttert, so wird er auch nur in der Lage sein, Katzen auf Fotos zu identifizieren. Selbst dann kann der Algorithmus bei noch nicht gesehenen Bildern (d.h. bei solchen, mit denen er nicht trainiert wurde), nur eine Wahrscheinlichkeit dafür angeben, dass das Bild zu einer erlernten Kategorie gehört. Absolute Sicherheit (p=100%) ist also selten, und Machine Learning Scientists müssen je nach Aufgabe entscheiden, wie niedrig sie den Schwellenwert ansetzen wollen, ab dem ein Bild einer Kategorie zugeordnet werden soll (bspw. p >= 70%).
Was mein Vorhaben betrifft, so entschied ich mich nach einigem Stöbern für das Modell mit dem Namen „facebook/detr-resnet-101-dc5“. Mit Blick auf Downloadzahlen handelt es sich zwar nicht um das beliebteste Modell, doch hatte ich durch Ausprobieren festgestellt, dass es differenziertere Kategorisierungen vornahm als andere, wie etwa das weitaus beliebtere Modell „facebook/detr-resnet-50“ mit fast 48.000 Downloads (Stand: 07.09.2022). Autor*innen von Des-Resnet 101-dc5 sind Nicolas Carion, Francisco Massa, Gabriel Synnaeve, Nicolas Usunier, Alexander Kirillov und Sergey Zagoruyko. Wie bei anderen Übersetzer-Modellen besteht dieser Transformer im Kern aus einer verschalteten Encoder- und Decoder-Einheit, basierend auf Convolutional Neural Networks (wie sie bei Bildanwendungen standardmäßig verwendet werden), der noch zwei Heads hinzugefügt wurden, welche eine sogenannte Attention-Technologie nutzen. Trainiert wurde der Algorithmus mit der Bilddatenbank COCO (Common Objects in Context). Diese Datenbank umfasst mehr als 330.000 Bilder, von denen mehr als 200.000 mit Labels versehen wurden, mit insgesamt 80 Objektkategorien.
Das Modell nutzt sogenannte Objekt-Abfragevektoren (object queries). Jede dieser Anfragen ist dabei zur Identifikation eines bestimmten Objektstyps im Bild bestimmt. Bei COCO werden jedes Mal 100 Queries generiert. Ohne zu sehr ins Detail gehen zu wollen besteht eine Besonderheit des Modells in der verwendeten Verlustfunktion (bei der man durch Gradientenabstieg versucht Nullstellen zu finden). Genutzt wird eine „bipartite matching loss”:Dabei werden die vorhergesagten Klassen zusammen mit den Objektrahmen der 100 Objektanfragen mit den Wahrheitsannotationen verglichen, die auf die gesamte Anzahl der Queries aufgefüllt werden. Das heißt: werden im Bild nur 4 Objekte identifiziert, erhalten die 96 übrigen Anfragen das Label „no object“ bzw. „no bounding box“ in Bezug auf den Objektrahmen. Das Modell wurde mit 300 Epochen auf 16 V100 CPUs trainiert, was beeindruckende 3 Tage dauerte, wobei 4 Bilder pro CPU parallel verarbeitet werden konnten (dem entspricht eine Batchgröße von 64). Mehr Informationen zu diesem Transformer s. https://arxiv.org/abs/2005.12872.

Die Objektkategorien mit einem Graphen semantisch ordnen lassen
So kompliziert die Architektur eines Transformers auch ist, so lassen sie sich relativ leicht über das Transformers-Modul, das die Huggingface-API nutzt, in Python einbinden. Im Script wird dann zuerst über einen angegebenen Datei-Pfad die entsprechende Bilddatei, in der man Objekte identifizieren möchte, mit Pillow geöffnet. Dann wird über die Pipeline von Huggingface das Modell aufgerufen und seine gewünschten Funktionalitäten aktiviert, in unserem Fall für Objekterkennung (Object Detection) und die Identifikation von Merkmalen (Feature Extraction).
So weit, so gut. Aber was wollen wir mit dem Modell eigentlich genau machen? Als Übung habe ich mir überlegt, dass man das Modell erst einmal dazu nutzen könnte, eine Sammlung von Bildern in Ordner einzusortieren, wobei das Programm die Ordner dafür im Prozess selbst generieren soll. Als weitere Herausforderung soll nicht für jede Kategorie ein eigener Ordner erstellt werden. Denn man kann bei 80 Kategorien schnell den Überblick verlieren und von Ordnern, in denen keins oder nur wenige Fotos liegen, hat man kaum einen Mehrwert. Praktischer wäre, wenn mehrere Kategorien zusammengefasst würden, damit Fotos mit Objekten, die zu dieser Gruppe gehören, gemeinsam in deren Ordner abgelegt würden.
Zu Übungszwecken mache ich mir die Sache noch etwas einfacher und möchte, dass der Computer die Ordnerstruktur auf Grundlage der trainierten Labels des Transformers anlegt, bevor er an die eigentliche Sortierarbeit geht. Die Labels liegen in der Output-Schicht, der letzten Schicht des Modells. Ziel ist ja, dass der Input, in diesem Fall ein Bild, am Ende des Prozesses einem der Labels zugeordnet wird, d.h. der entsprechende Knoten im Netzwerk des Output-Layers aktiviert wird. Das bedeutet, dass es in der Ausgabeschicht so viele Knoten wie Labels geben muss und dass das Modell über die Anzahl der Labels hinaus keine weiteren Bildobjekte erkennen kann. Anders gesagt: die Erkenntnisfähigkeit des Netzwerks wird gerade durch die Anzahl der Knoten im Output-Layer begrenzt, welche zugleich die möglichen Labels bezeichnen.
Also war ein erster Schritt, sich einmal die Labels im Output-Layer anzusehen. Es gibt insgesamt 80, was nochmals verdeutlicht, warum es eine gute Idee ist, Labels zusammenfassen zu wollen, um die Anzahl an Ordnern zu reduzieren. Ansonsten hätte man nach dem Sortierprozess 80 Ordner vor sich. Ich lasse mir also zunächst die Namen der Label ausgeben; die ersten lauten: „person, bicycle, car, motorcycle, airplane, bus, train, truck, boat, traffic light, fire hydrant, stop sign, parking meter, bench, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe…“. Ein Mensch erkennt hier natürlich sofort, welche semantischen Gruppen man bilden könnte, wie z.B. Menschen, Verkehrsmittel, Tiere, wobei man sich hier auch Untergruppen vorstellen könnte, wie etwa Landfahrzeuge, Wasserfahrzeuge, Luftfahrzeuge, Haustiere, Nutztiere, exotische Tiere etc. Oder: Säugetiere, Vögel, motorisierte Fahrzeuge, nicht-motorisierte Fahrzeuge… Der Punkt dieser Überlegung ist, dass es mehr als eine Möglichkeit der Kategorisierung gibt. Das sollte man im Hinterkopf behalten, wenn man darüber nachdenkt, wie ein Computer die Begriffe ordnen sollte.
Mit der Ausgabe der Labels haben wir den ersten Schritt für eine semantische Ordnung getan. Damit der Computer mit den Wörtern arbeiten kann, muss man sie zunächst vektorisieren. Nach ein wenig Ausprobieren entscheide ich mich für das Python-Paket Gensim, weil dieses leichter zu handhaben ist und verschiedene vortrainierte Modelle für die Vektorisierung von Wörtern zur Verfügung stellt. Hier nutze ich das Modell ‚word2vec-google-news-300‘, dessen Geschichte man schon am Namen erkennt. Es bietet Vektoren, die auf einem Teil der Google News Datenbank trainiert wurde, d.h. auf ca. 100 Mrd. (!) Wörter. Die Vektoren haben 300 Dimensionen (d.h. 300 Komponenten bzw. Merkmale) für 3 Millionen Wörter und Phrasen. Mehr zum Word2Vec-Projekt s. https://code.google.com/archive/p/word2vec/
Eine derart große Mengen an Trainingsdaten braucht es auch, um möglichst brauchbare Wordvektoren zu erzeugen, bei denen der Vektorraum sich dem semantischen Raum einer natürlichen Sprache annähert. Repräsentation oder Kongruenz wäre hier zu viel verlangt, da die Komplexität einer natürlichen Sprache die technischen Möglichkeiten (derzeit?) weit übersteigt. Immerhin erlaubt ein solch weiter Vektorraum, die Ähnlichkeit von Wordvektoren miteinander zu vergleichen.
Das macht man allerdings nicht etwa durch die Messung eines linearen Abstandes, sondern mithilfe der sogenannten „Cosinus-Ähnlichkeit“ (Cosine Similarity): Dabei bestimmt man den Winkel zwischen zwei Vektoren mittels Cosinus. Je näher die Vektoren zueinander stehen, desto kleiner ist der Winkel und desto mehr nähert sich der Cosinus 1 an. Bei vollständiger Übereinstimmung hätte man demzufolge einen Winkel von 0° und einen Cosinus von genau 1. In diesem Fall würde man von vollständiger Synonymie ausgehen. Je näher der indes Winkel 90° kommt (einem Cosinus von 0), desto mehr würde man davon ausgehen müssen, dass keine nennenswerte semantische Beziehung zwischen den Wortvektoren besteht. Bei einem Winkel von nahezu 180° bzw. einem Cosinus von -1 hat man es dagegen höchstwahrscheinlich mit einer Antinomie zu tun: die beiden Vektoren stehen hier in Opposition zueinander.
Die Berechnung des Cosinus ist bei mehrdimensionalen Vektoren wie den Wortvektoren von Word2Vec natürlich extrem aufwändig und von einem Menschen kaum noch zu leisten. Dankenswerterweise nimmt einem gensim diese Rechenarbeit ab. Durch den einfachen Befehl model.similarity(‚person‘, ‚dog‘) kann man etwa die semantische Nähe von Hund und Mensch vergleichen. Ein Test, ob die Funktion richtig funktioniert, besteht dagegen im Vergleich eines Wortes mit sich selbst: model.similarity(‚person‘, ‚person‘). Hier sollte immer genau (oder ziemlich genau) 1 herauskommen. Tut es das nicht, sollte man nochmals überprüfen, ob man irgendwo einen Fehler gemacht hat. Hat man, wie in unserem Fall, eine Liste von Wörtern, kann man (mit NumPy) auch eine Matrix herstellen, in der man alle Wörter gleichzeitig miteinander (und sich selbst) vergleicht. Die Diagonale der Matrix dient dann quasi zur Kontrolle: sie sollte ausschließlich aus Einsen bestehen. (Aufgrund interner Rechenvorgänge liegt das Ergebnis allerdings knapp über oder unter 1; hier kann man sich aber durch eine Rundung auf wenige Kommastellen behelfen).


Die Erstellung einer Vergleichsmatrix ist ein wichtiger Zwischenschritt. Denn die Ähnlichkeitswerte in der Matrix können genutzt werden, um daraus ein Netzwerk zu generieren, bei dem Ähnlichkeit als Kanten zwischen den Wörtern (als Knoten) dargestellt werden. Die Höhe der Ähnlichkeit kann dabei durch einen Schwellenwert reguliert werden; denn sonst gäbe es zwischen allen Wörtern Ähnlichkeitsbeziehungen, da die Ähnlichkeit normalerweise nie ganz 0 wird.
Dieses Graphen-Netzwerk nutze ich als eine Art Filtereinrichtung, um Cluster ähnlicher Wörter zu erzeugen. Das könnte man auch durch andere Verfahren, wie z.B. K-Means, doch scheint mir das angesichts der recht kleinen Menge an Wörtern zu aufwändig. Außerdem haben Graphen den Vorteil größerer Anschaulichkeit, weil sich mit Ihnen die Beziehungen zwischen Entitäten abbilden lassen.
Für die Umsetzung muss man die Vergleichsmatrix aber noch ein wenig modifizieren. Die Diagonale der Selbstähnlichkeit benötigt man hierbei ebenso wenig wie die Dopplungen entlang der Diagonalen (man hat es ja mit einer symmetrischen Matrix zu tun). Dazu stelle ich alle Werte unterhalb der Diagonalen und diese selbst auf Null. Zusätzlich setze ich alle Werte unterhalb eines gewünschten Schwellenwertes auf Null. Durch Ausprobieren habe ich festgestellt, dass sich die sinnvollsten Netzwerke bei einem Schwellenwert um 0,48 ergeben. Das heißt, alle Ähnlichkeitswerte, die darunter liegen, werden auf Null gesetzt. Ein Gewicht von 0 bedeutet im Netzwerk, dass keine Verbindung zwischen zwei Kanten bzw., in unserem Fall, zwei Worten besteht. Anders gesagt: Es besteht dann keine Beziehung zwischen beiden Wörtern, und es wird keine Kante erzeugt. Mithilfe des Python-Pakets networkx kann man aus der Matrix (als mehrdimensionales NumPy-Array) mit einem einzigen Befehl ein Netzwerk von Graphen erstellen:

Mithilfe einer Lambda-Funktion relabele ich die Knoten, sodass diese nach den Labels des Output-Layers benannt werden, welche ich zuvor in eine Liste einlese. Das Ergebnis lasse ich mit networkx und Matplotlib visualisieren.


Die einzelnen im Netzwerk isolierten Gruppen speichere ich durch eine List Comprehension in einer Liste ab, damit ich sie bei Bedarf einzeln abfragen und weiterverarbeiten kann.

Zusammenfassung und Ausblick
Mithilfe des os-Moduls von Python lasse ich dann (in einem for-Loop) aus den semantischen Gruppen Ordner erstellen, in welche die Bilder dann eingeordnet werden können. Wie zu sehen, ist das Ergebnis nicht perfekt, aber in einigen Fällen war der Computer durchaus in der Lage, Objekte zu sinnvollen Gruppen zusammenzufassen, besonders bei Lebensmitteln und Tieren.

Zwei wichtige Probleme sind noch ungelöst und werde ich als nächstes angehen: 1) Wie sollen Fotos zugeordnet werden, in denen verschiedene Objekte vorliegen? Der Einfachheit halber lege ich Fotos unter allen Ordnern ab, deren Objekte in ihnen gefunden werden. Aber man könnte auch anders vorgehen, z.B. Fotos unter den Ordnern ablegen, deren Objekte am häufigsten in Fotos gefunden wurden. 2) Englische Worte, die aus zwei oder mehr anderen bestehen, sogenannte compound nouns, stellen ein grundsätzliches Problem von NLP dar, weil der Algorithmus nicht unterscheiden kann, ob er es mit einem zusammengesetzten Wort oder zwei Einzelwörtern zu tun hat. Vorerst hatte ich zusammengesetzte Wörter mit einem Unterstrich als eine zusammengehörende Einheit markiert, aber das verhindert, dass sie semantischen Gruppen richtig zugeordnet werden können. Eine Lösung könnte darin bestehen, den Computer wahlweise das erste oder zweite Wort als lenkendes Wort zu nutzen. Das Wort baseball bat könnte er dann etwa als gleichwertig mit bat betrachten und diesem zuordnen, baseball glove Handschuhen oder wine glass Gläsern. Allerdings wäre das nicht freilich nicht ohne eine gewisse Willkür.
Schreibe einen Kommentar