17
.
April
2023
·
8
Minuten Lesezeit

Ist es ein Vogel? Ist es ein Flugzeug? Computer Vision mit Java

Die Klassifizierung von Objekten stellt Menschen nicht nur in Comic-Filmen wie „Superman“ vor Probleme. Nur logisch also, dass wir uns bei derartigen Aufgaben gerne von Algorithmen aus dem Bereich Computer Vision (CV) unterstützen lassen. Üblicherweise ist Python hierfür die Programmiersprache der Wahl, doch auch Java bietet vergleichbare Mittel, die der Community den wichtigsten CV-Baustein, Deep Learning, näherbringen können.

Nicht nur die Identifizierung übermenschlicher Wesen am Horizont, sondern auch reale Anwendungsfälle wie das autonome Fahren, die Aufdeckung schwerwiegender Krankheiten oder die Optimierung landwirtschaftlicher Prozesse greifen auf CV-Algorithmen zurück. So unterschiedlich diese Beispiele sein mögen, alle nutzen bewegtes oder unbewegtes Bildmaterial, um Fragen zu beantworten wie „Wird der Fußgänger vor mir die Fahrbahn kreuzen?“, „Ist auf diesem Ultraschallbild ein Tumor zu erkennen?“, oder „Benötigen die Ackerpflanzen Dünger?“. In vielen Fällen ist das Herzstück des CVAlgorithmus ein vortrainiertes Convolutional Neural Network (CNN). Was das genau ist, und wie man selbst ein CNN erstellen und trainieren kann, darum soll es sich in diesem Artikel drehen. Die hier erläuterten Konzepte sind technologieunabhängig, jedoch sind Kenntnisse über die Grundlagen neuronaler Netze sowie der Programmiersprache Java von Vorteil.

Die Anwendungsfälle von CV sind vielfältig. Zum einen gibt es simple Klassifikationsprobleme, die ein Bild einer Klasse zuordnen. Ein Beispiel hierfür ist der zuvor genannte Anwendungsfall in der Landwirtschaft. Dem CV-Algorithmus wird die Fotografie einer Ackerpflanze übergeben, woraufhin er Wahrscheinlichkeiten für die Klassen „benötigt Dünger“ und „benötigt kein Dünger“ berechnet. Auch die Erkennung von Emotionen oder die Klassifikation handgeschriebener Buchstaben fällt in diese Kategorie. Ein anderes realitätsnahes Beispiel ist die Momentaufnahme aus dem Straßenverkehr. Der CV-Algorithmus muss hier zunächst Regionen bestimmen, die potenziell Fußgänger enthalten und anschließend die entsprechende Region klassifizieren. Aber nicht nur Fußgänger, sondern auch Verkehrsschilder, andere Fahrzeuge und Ampeln sind von Interesse. Dementsprechend sollte der CV-Algorithmus dazu in der Lage sein, neben Fußgängern auch andere Objekte zu identifizieren.

Häufig nutzen solche CV-Algorithmen eine besondere Art von neuronalen Netzen, Convolutional Neural Networks (CNNs). In seiner einfachsten Form ist ein solches Netz auf Single-Label/Multi-Class-Bildklassifikationen spezialisiert, die wie beim Beispiel aus der Emotionserkennung dem gesamten Bild nur eine Klasse, wie z. B. „fröhlich“ oder „traurig“, zuordnen. Jedoch stellen CNNs auch die Grundlage für die Bewältigung komplexerer Anwendungsfälle dar, wie in dem letzten Beispiel aus dem Straßenverkehr beschrieben. Diese Probleme werden auch als Multi-Label/Multi-Class- Probleme bezeichnet (Abb. 1) und können u. a. mit einer Erweiterung des klassischen CNNs, dem sogenannten Region-based Convolutional Neural Network (R-CNN) gelöst werden. Auch noch komplexere Beispiele wie die Nachverfolgung von Objekten in einem Videostream können von speziellen CNNs, sogenannten Deep-Learning-based Trackern bewältigt werden.

Multi-Label/Multi-Class-Detektion mit Hilfe der R-CNN-Alternative YOLO
Abb. 1: Multi-Label/Multi-Class-Detektion mit Hilfe der R-CNN-Alternative YOLO

CNNs stellen also einen wichtigen Teil von Computer Vision dar. Aufgrund dessen soll dieser Artikel die wichtigsten Konzepte erklären und zudem einen Einblick in die praktische Nutzung dieser Netze geben. Für den praktischen Anteil wird die Open-Source-Bibliothek deeplearning4j (dl4j) verwendet. Neben Keras oder Pytorch zählt auch dl4j zu den in der Praxis am häufigsten verwendeten Deep-Learning-Frameworks und wird insbesondere dann genutzt, wenn nicht Python, sondern Java die Programmiersprache der Wahl ist. Die hier erläuterten Konzepte finden sich jedoch auch in anderen Frameworks wieder und sollten auch Python-Fans nicht davon abhalten, weiterzulesen.

Convolutional Neural Networks

Der grundlegende Aufbau eines CNN folgt einem charakteristischen Muster (Abb. 2). Auf einen Input-Layer, der die Dimensionen des Inputbilds besitzt, folgt eine beliebige Anzahl von Convolutional Layers, die jeweils mit einem Pooling Layer verknüpft sind. Diese Layer-Typen stellen eine Besonderheit von CNNs dar und werden später näher erläutert. Den Abschluss stellen ein oder mehrere Dense Layers, auch Fully Connected Layers genannt, dar, die vollständig mit dem vorherigen Layer verknüpft sind und mit Hilfe einer Softmax-Aktivierungsfunktion Wahrscheinlichkeiten für jede der möglichen Klassen annehmen.

In Listing 1 ist die dl4j-Konfiguration einer simplen CNN-Architektur aufgeführt, die ein Bild mit den Dimensionen (IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_CHANNELS) verarbeiten kann. IMAGE_CHANNELS steht hier für die Anzahl an Farbkanälen. So besitzt ein Farbbild meistens drei Kanäle (jeweils einen für Rot-, Grün- und Blauwerte) und ein Graustufenbild einen Kanal, der die Intensität eines Pixels beschreibt. Unabhängig davon, ob es sich bei dem Bild um ein Farb- oder Graustufenbild handelt, sind die einzelnen Pixel in einem Raster angeordnet. Die Dimensionen dieses Rasters entsprechen der Breite und Höhe des Bilds. Im Beispiel aus Listing 1 sind diese Werte durch die Variablen IMAGE_WIDTH und IMAGE_ HEIGHT gegeben. Während die Kostenfunktion (hier CATEGORICAL_CROSSENTROPY) und der Optimierungsalgorithmus (hier STOCHASTIC_GRADIENT) nicht zwingend CNN-spezifisch sind, stellen die hier verwendeten Layer eine Besonderheit von CNNs dar und werden deshalb nachfolgend näher betrachtet.

Abb. 2 Charakteristischer Aufbau eines CNN
Abb. 2: Charakteristischer Aufbau eines CNN

Listing 1
Listing 1

Convolutional Layer

Herzstück eines Convolutional Layer ist der Kernel. Dabei handelt es sich um einen zweidimensionalen Filter, der schrittweise über das Bild wandert und dabei das Skalarprodukt aus Kernel und dem jeweiligen Bildausschnitt bildet (Abb. 3). Die Größe dieses Filters (kernel- Size) und die Schrittweite, mit der der Filter über das Bild bewegt wird (stride), sind konfigurierbar.

Abb. 3: Bildung des Skalarproduktes aus Bildausschnitt (rot umrandet) und 3x3 Kernel
Abb. 3: Bildung des Skalarproduktes aus Bildausschnitt (rot umrandet) und 3x3 Kernel

Die Werte des Kernels (hier w1 bis w9) bilden die Gewichte dieses Layers und werden während des Trainings gelernt. Dementsprechend müssen in diesem Beispiel nur neun Gewichte gelernt werden, obwohl das Inputbild 208 Werte (13x16 Pixel) besitzt. Üblicherweise verwendet ein Convolutional Layer jedoch mehr als einen Kernel, wobei sich (optimalerweise) jeder im Trainingsprozess auf ein Feature im Bild spezialisiert. Features können in diesem Fall horizontale oder vertikale Kanten sein. In Listing 1 besitzt der erste Convolutional Layer beispielsweise 16 (nOut) Kernel, die jeweils eine Größe von 5x5 haben.

Pooling Layer

Auch der Pooling Layer besitzt einen Kernel. Im Unterschied zum Convolutional Layer hat dieser Kernel jedoch keine Parameter, die während des Trainings gelernt werden. Anstelle eines Skalarprodukts bestimmt eine vordefinierte Funktion, wie z. B. max() oder mean(), den resultierenden Wert. Ziel ist es, das Netz robuster gegenüber der exakten Position des eingehenden Features zu machen und eine Verkleinerung des Bilds und damit auch des Rechenaufwands hervorzurufen. CNNs nutzen häufig Pooling Layer, die den Maximalwert innerhalb des Kernels suchen. Wie in Listing 1 zu erkennen ist, folgen diese Layer meistens auf einen Convolutional Layer.

Kompatibilität der Layer

Grundsätzlich können die einzelnen Layer beliebig miteinander verknüpft werden. Dennoch gibt es einige Einschränkungen, die es zu beachten gilt. Zum einen muss der Input-Layer zwingend die Dimensionen des Bilds besitzen, das später klassifiziert werden soll. Des Weiteren muss der Output-Layer für jede mögliche Klasse genau ein Neuron besitzen. Zusätzlich sollte der Output-Layer die Aktivierungsfunktion Softmax nutzen, da hierdurch die Summe aller Ausgabewerte 1 ist und demnach eine Wahrscheinlichkeit für jede einzelne Klasse ausgegeben wird.

Auch beim Zusammensetzen der restlichen Layer muss insbesondere ein Aspekt beachtet werden: Das aus dem vorherigen Layer resultierende Bild muss groß genug sein, um den Kernel des aktuellen Layers nutzen zu können. Ist die Größe des Bilds aufgrund vorangegangenem Pooling auf 3x3 Pixel reduziert, kann kein Kernel der Größe 5x5 angewendet werden. Was sind aber die Faktoren, die Einfluss auf die Größe des Bilds haben?

Größe des Kernels

Der Kernel iteriert von links nach rechts sowie von oben nach unten über das Bild. Voraussetzung für die erfolgreiche Anwendung des Kernels ist, dass für jedes Gewicht auch ein Wert im jeweils betrachteten Bildausschnitt existiert. Zur besseren Veranschaulichung betrachten wir nur die x-Dimension des Bilds. Hat das Bild eine Weite von 5 Pixeln, so kann ein Kernel der Größe 3 nur dreimal angewendet werden. Im ersten Schritt würde der Kernel die Pixel 1 bis 3 nutzen, im zweiten Schritt dann die Pixel 2 bis 4 und im letzten Schritt die Pixel 3 bis 5. Das resultierende Bild hätte demnach eine Weite von 3 Pixeln. Die ursprüngliche Breite des Bilds hat sich demnach von 5 auf 3 Pixel reduziert. Voraussetzung für dieses Beispiel ist ein Padding von 0 sowie ein Stride von 1.

Stride bezeichnet die Schrittweite, mit der der Kernel über das Inputbild iteriert. stride=1 bedeutet, dass nach Anwendung des Kernels auf einen Bildausschnitt der Kernel um genau einen Pixel weiter verschoben wird. Je größer der Stride, desto weniger Anwendungen fi ndet der Kernel in der entsprechenden Richtung. Für die Größe des resultierenden Bilds bedeutet das also, dass diese abnimmt, je größer der Stride ist. Während die Kernelgröße und der Stride die Bildgröße verringern, kann das Padding dazu genutzt werden, dem entgegenzuwirken. Dabei handelt es sich um einen Rahmen, bestehend aus Nullen, welcher um das Inputbild gelegt wird, bevor die Convolution angewendet wird. Das Bild wird demnach künstlich vergrößert, um den Größenverlust durch Kernelgröße und Stride auszugleichen. Zusätzlich dient das Padding dazu, die Pixel am Rand eines Bilds in eine größere Anzahl an Convolutions einzubeziehen.

Zusammenfassend bewirken sowohl kernelSize als auch stride mit zunehmenden Werten eine Reduzierung der resultierenden Bildgröße, wohingegen padding in einem positiven Zusammenhang dazu steht.

Training eines eigenen CNN mit dem CIFARDatensatz

Die wichtigsten Bestandteile und Stolpersteine eines CNN sind nun bekannt. Im nächsten Schritt soll eine vorliegende Modellkonfi guration (Listing 1) genutzt werden, um ein Modell zu initialisieren, trainieren und anschließend zu evaluieren. Hierbei sei erwähnt, dass der Fokus nicht darauf liegt, einen besonders guten Klassifi kator zu erzeugen, sondern einen Einblick in die generelle Herangehensweise zu geben. Dafür dient uns im Folgenden der CIFAR-10-Datensatz des Canadian Institute for Advanced Research als Input. Bestehend aus 60 000 Bildern mit einer Aufl ösung von 32x32 Pixeln bietet dieser Datensatz genug Material, um einen Klassifi kator zu trainieren. Die geringe Auflösung ist in einer notwendigen Anpassung der Bildgröße begründet, die in der Praxis einen Trade-off zwischen der Erhöhung des Rechenaufwands und dem Verlust der in dem Bild enthaltenen visuellen Informationen darstellt.

Die Bilder sind aufgeteilt in 50 000 Trainings- und 10 000 Testdaten, wobei jede der zehn vorliegenden Klassen in genau 6 000 Bildern vertreten ist. Die zehn gleichverteilten Klassen sind: Flugzeug, Auto, Vogel, Katze, Hirsch, Hund, Frosch, Pferd, Schiff und Truck. Diese Gleichverteilung ist wichtig, um gute Ergebnisse während des Trainings zu erzielen. Je nach Fragestellung kann das in der Praxis nur schwer erreicht werden, deshalb werden derartige Datensätze häufi g zum Vortrainieren eines neuen Klassifikators genutzt. Anschließend wird ein auf den Anwendungsfall zugeschnittener Datensatz genutzt, um den Klassifikator auf die eigentliche Fragestellung zu spezialisieren (Transfer Learning). Heutzutage wird CIFAR-10 jedoch zum Großteil als Benchmark genutzt, um das Potenzial eines neuen Netzes zu testen und mit anderen Klassifikatoren zu vergleichen.

Listing 2
Listing 2

Listing 3
Listing 3

Listing 4
Listing 4

Aufgrund seiner Popularität ist dieser Datensatz häufig Bestandteil von Deep-Learning-Bibliotheken. Das ist auch bei dl4j der Fall, weshalb der Datensatz mit Hilfe des Cifar10DataSetIterator direkt geladen werden kann (Listing 2). Ein DataSetIterator dient in dl4j als Container für die Daten und organisiert sie in Batches einer zuvor definierten Größe.

Vorverarbeitung

Um sicherzustellen, dass das erstellte Netzwerk mit den zur Verfügung gestellten Bildern umgehen kann, müssen diese entsprechend aufbereitet werden. Die Bildgröße sowie die Anzahl der Farbkanäle müssen im gesamten Datensatz einheitlich sein. Optional können weitere Anpassungen vorgenommen werden, wie z. B. die Normalisierung der Pixelwerte auf den Bereich [0,1] und die Umwandlung von Farben in Graustufen. Zu den fortgeschrittenen Vorverarbeitungstechniken zählen zudem die Histogrammangleichung oder die Anwendung von Filtern, um bestimmte Merkmale des Bilds hervorzuheben. Speziell die Durchführung letzterer Techniken können durch Bibliotheken wie OpenCV erleichtert werden. dl4j bietet für die Vorverarbeitung verschiedene Präprozessoren an, die das Interface DataSetPreProcessor implementieren. Mit Hilfe des CompositeDataSetPre- Processor können sogar mehrere Präprozessoren hintereinander ausgeführt werden, um einen mehrstufigen Vorverarbeitungsprozess zu realisieren.

Neben der Registrierung der Präprozessoren in den Data-Iteratoren zeigt Listing 3 die Verwendung eines eigenen Präprozessors, der durch Implementierung des DataSetPreProcessor-Interface realisiert wurde (Listing 4). Dieser Präprozessor dient dazu, dem DataSet eine weitere Dimension der Größe 1 zu verleihen, die durch die Umwandlung von RGB zu Graustufe verloren geht, aber vom neuronalen Netz erwartet wird.

Auch die Verwendung von OpenCV-Funktionalitäten zur weiteren Aufbereitung der Bilder kann in einem derartigen Präprozessor stattfinden.

Listing 5
Listing 5

Training und Evaluation

Nach erfolgreicher Registrierung der Vorverarbeitung kann das Modell mit den zur Verfügung stehenden Daten trainiert werden (Listing 5). Bevor das jedoch geschehen kann, muss das Modell initialisiert werden. In diesem Schritt wird das neuronale Netz gemäß der zuvor definierten Architektur erstellt und die Gewichte entsprechend der Konfiguration initial gesetzt. Um während des Trainings einen Einblick in den Fortschritt zu haben, können sogenannte Listener registriert werden. Je nach Art des Listener werden entsprechende Information im Trainingsverlauf in der Konsole oder sogar einem Web-UI geloggt.

Die hier definierten Listener loggen den momentanen Score (Wert der Loss-Funktion) und führen eine Evaluation mit den Daten des testIterators aus. Die Evaluation bietet einen detaillierten Einblick in den Trainingsfortschritt, indem typische Metriken wie Accuracy, Precision, Recall, F1 Score sowie eine Konfusionsmatrix dargestellt werden. Nach Registrierung der Listener wird das Training mit Hilfe der fit-Funktion für eine beliebige Anzahl Epochen gestartet. Anschließend sollten möglichst unbekannte Bilder dafür genutzt werden, erneut eine Evaluation durchzuführen, um die wichtigsten Metriken zu identifizieren. Mindestens ebenso wichtig ist es jedoch, einen genauen Einblick in die Klassifikationsergebnisse zu wagen (Abb. 4). Insbesondere die falschen Klassifikationen sind dabei von Interesse.

Abb. 4: Fehlerhafte Klassifikationen eines CNN mit einer Accuracy von ca. 68 Prozent
Abb. 4: Fehlerhafte Klassifikationen eines CNN mit einer Accuracyvon ca. 68 Prozent

Erst beim Blick auf die fehlerhaften Klassifikationen können die zuvor ermittelten Metriken wie Accuracy eingeordnet werden. Ein Beispiel dafür stellt das vierte Bild in der ersten Spalte dar. Hintergrund und Farbgebung des Objekts im Zentrum könnten bei dieser Auflösung auch von einem Menschen zu einer Klassifikation von „Hirsch“ führen. Zudem wird hier, wie in einer entsprechenden Konfusionsmatrix, sichtbar, dass Autos häufig mit Trucks und Tiere häufig mit anderen Tieren verwechselt werden. Selbst wenn 68 Prozent Accuracy für das ursprüngliche Klassifikationsproblem also als zu wenig erscheint, könnte dieser Klassifikator definitiv dafür genutzt werden, Tiere von Fahrzeugen zu unterscheiden. Metriken wie Accuracy oder Recall bestimmen demnach nicht allein die Qualität des Klassifikators, sondern auch die fachliche Fragestellung.

Fazit

Dieser Artikel hat gezeigt, dass CNNs die Grundlage vieler CV-Algorithmen darstellen. Zudem zeigte der Praxisteil, dass auch die JVM genug Mittel bietet, um ein auf das eigene Problem zugeschnittenes CNN trainieren zu können. Auch wenn die Community um dl4j mit anderen Frameworks verglichen noch nicht so groß ist, sind die Konzepte die gleichen und durch den offenen Quellcode können viele Probleme eigenständig verstanden und bewältigt werden. Mindestens zwei Themenblöcke wurden in diesem Artikel jedoch verschwiegen, sollten aber bei der Lösung eigener Klassifikationsprobleme genutzt werden. Zum einen handelt es sich dabei um Data Augmentation, also die Vervielfältigung sowie Veränderung der Inputbilder, um das Netz robuster gegenüber ungesehenen Bildern zu machen. Zum anderen sollte Transfer Learning nicht außer Acht gelassen werden. Hierbei wird eine bestehende etablierte Modellarchitektur auf ein neues Problem angewendet und zu diesem Zweck leicht verändert.

Ob dieses neue Problem nun die Identifizierung von Superman oder von Fußgängern im Straßenverkehr ist, die bestehende Modellarchitektur nutzt in den meisten Fällen CNNs, deren Grundlagen nun bekannt sind.

Links & Literatur

[1] https://www.cs.toronto.edu/~kriz/cifar.html

[2] https://deeplearning4j.konduit.ai/

[3] Milicevic, Mario; Zubrinic, Krunoslav; Grbavac, Ivan; Obradovic, Ines: „Application of Deep Learning Architectures for Accurate Detection of Olive Tree Flowering Phenophase“; in: Remote Sens. 12.2020: https://doi.org/10.3390/ rs12132120

Zum Originalartikel „Ist es ein Vogel? Ist es ein Flugzeug? Computer Vision mit Java" gelangst du hier.

Entwickler Magazin Spezial Vol. 35, S. 60

No items found.

Nicht nur die Identifizierung übermenschlicher Wesen am Horizont, sondern auch reale Anwendungsfälle wie das autonome Fahren, die Aufdeckung schwerwiegender Krankheiten oder die Optimierung landwirtschaftlicher Prozesse greifen auf CV-Algorithmen zurück. So unterschiedlich diese Beispiele sein mögen, alle nutzen bewegtes oder unbewegtes Bildmaterial, um Fragen zu beantworten wie „Wird der Fußgänger vor mir die Fahrbahn kreuzen?“, „Ist auf diesem Ultraschallbild ein Tumor zu erkennen?“, oder „Benötigen die Ackerpflanzen Dünger?“. In vielen Fällen ist das Herzstück des CVAlgorithmus ein vortrainiertes Convolutional Neural Network (CNN). Was das genau ist, und wie man selbst ein CNN erstellen und trainieren kann, darum soll es sich in diesem Artikel drehen. Die hier erläuterten Konzepte sind technologieunabhängig, jedoch sind Kenntnisse über die Grundlagen neuronaler Netze sowie der Programmiersprache Java von Vorteil.

Die Anwendungsfälle von CV sind vielfältig. Zum einen gibt es simple Klassifikationsprobleme, die ein Bild einer Klasse zuordnen. Ein Beispiel hierfür ist der zuvor genannte Anwendungsfall in der Landwirtschaft. Dem CV-Algorithmus wird die Fotografie einer Ackerpflanze übergeben, woraufhin er Wahrscheinlichkeiten für die Klassen „benötigt Dünger“ und „benötigt kein Dünger“ berechnet. Auch die Erkennung von Emotionen oder die Klassifikation handgeschriebener Buchstaben fällt in diese Kategorie. Ein anderes realitätsnahes Beispiel ist die Momentaufnahme aus dem Straßenverkehr. Der CV-Algorithmus muss hier zunächst Regionen bestimmen, die potenziell Fußgänger enthalten und anschließend die entsprechende Region klassifizieren. Aber nicht nur Fußgänger, sondern auch Verkehrsschilder, andere Fahrzeuge und Ampeln sind von Interesse. Dementsprechend sollte der CV-Algorithmus dazu in der Lage sein, neben Fußgängern auch andere Objekte zu identifizieren.

Häufig nutzen solche CV-Algorithmen eine besondere Art von neuronalen Netzen, Convolutional Neural Networks (CNNs). In seiner einfachsten Form ist ein solches Netz auf Single-Label/Multi-Class-Bildklassifikationen spezialisiert, die wie beim Beispiel aus der Emotionserkennung dem gesamten Bild nur eine Klasse, wie z. B. „fröhlich“ oder „traurig“, zuordnen. Jedoch stellen CNNs auch die Grundlage für die Bewältigung komplexerer Anwendungsfälle dar, wie in dem letzten Beispiel aus dem Straßenverkehr beschrieben. Diese Probleme werden auch als Multi-Label/Multi-Class- Probleme bezeichnet (Abb. 1) und können u. a. mit einer Erweiterung des klassischen CNNs, dem sogenannten Region-based Convolutional Neural Network (R-CNN) gelöst werden. Auch noch komplexere Beispiele wie die Nachverfolgung von Objekten in einem Videostream können von speziellen CNNs, sogenannten Deep-Learning-based Trackern bewältigt werden.

Multi-Label/Multi-Class-Detektion mit Hilfe der R-CNN-Alternative YOLO
Abb. 1: Multi-Label/Multi-Class-Detektion mit Hilfe der R-CNN-Alternative YOLO

CNNs stellen also einen wichtigen Teil von Computer Vision dar. Aufgrund dessen soll dieser Artikel die wichtigsten Konzepte erklären und zudem einen Einblick in die praktische Nutzung dieser Netze geben. Für den praktischen Anteil wird die Open-Source-Bibliothek deeplearning4j (dl4j) verwendet. Neben Keras oder Pytorch zählt auch dl4j zu den in der Praxis am häufigsten verwendeten Deep-Learning-Frameworks und wird insbesondere dann genutzt, wenn nicht Python, sondern Java die Programmiersprache der Wahl ist. Die hier erläuterten Konzepte finden sich jedoch auch in anderen Frameworks wieder und sollten auch Python-Fans nicht davon abhalten, weiterzulesen.

Convolutional Neural Networks

Der grundlegende Aufbau eines CNN folgt einem charakteristischen Muster (Abb. 2). Auf einen Input-Layer, der die Dimensionen des Inputbilds besitzt, folgt eine beliebige Anzahl von Convolutional Layers, die jeweils mit einem Pooling Layer verknüpft sind. Diese Layer-Typen stellen eine Besonderheit von CNNs dar und werden später näher erläutert. Den Abschluss stellen ein oder mehrere Dense Layers, auch Fully Connected Layers genannt, dar, die vollständig mit dem vorherigen Layer verknüpft sind und mit Hilfe einer Softmax-Aktivierungsfunktion Wahrscheinlichkeiten für jede der möglichen Klassen annehmen.

In Listing 1 ist die dl4j-Konfiguration einer simplen CNN-Architektur aufgeführt, die ein Bild mit den Dimensionen (IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_CHANNELS) verarbeiten kann. IMAGE_CHANNELS steht hier für die Anzahl an Farbkanälen. So besitzt ein Farbbild meistens drei Kanäle (jeweils einen für Rot-, Grün- und Blauwerte) und ein Graustufenbild einen Kanal, der die Intensität eines Pixels beschreibt. Unabhängig davon, ob es sich bei dem Bild um ein Farb- oder Graustufenbild handelt, sind die einzelnen Pixel in einem Raster angeordnet. Die Dimensionen dieses Rasters entsprechen der Breite und Höhe des Bilds. Im Beispiel aus Listing 1 sind diese Werte durch die Variablen IMAGE_WIDTH und IMAGE_ HEIGHT gegeben. Während die Kostenfunktion (hier CATEGORICAL_CROSSENTROPY) und der Optimierungsalgorithmus (hier STOCHASTIC_GRADIENT) nicht zwingend CNN-spezifisch sind, stellen die hier verwendeten Layer eine Besonderheit von CNNs dar und werden deshalb nachfolgend näher betrachtet.

Abb. 2 Charakteristischer Aufbau eines CNN
Abb. 2: Charakteristischer Aufbau eines CNN

Listing 1
Listing 1

Convolutional Layer

Herzstück eines Convolutional Layer ist der Kernel. Dabei handelt es sich um einen zweidimensionalen Filter, der schrittweise über das Bild wandert und dabei das Skalarprodukt aus Kernel und dem jeweiligen Bildausschnitt bildet (Abb. 3). Die Größe dieses Filters (kernel- Size) und die Schrittweite, mit der der Filter über das Bild bewegt wird (stride), sind konfigurierbar.

Abb. 3: Bildung des Skalarproduktes aus Bildausschnitt (rot umrandet) und 3x3 Kernel
Abb. 3: Bildung des Skalarproduktes aus Bildausschnitt (rot umrandet) und 3x3 Kernel

Die Werte des Kernels (hier w1 bis w9) bilden die Gewichte dieses Layers und werden während des Trainings gelernt. Dementsprechend müssen in diesem Beispiel nur neun Gewichte gelernt werden, obwohl das Inputbild 208 Werte (13x16 Pixel) besitzt. Üblicherweise verwendet ein Convolutional Layer jedoch mehr als einen Kernel, wobei sich (optimalerweise) jeder im Trainingsprozess auf ein Feature im Bild spezialisiert. Features können in diesem Fall horizontale oder vertikale Kanten sein. In Listing 1 besitzt der erste Convolutional Layer beispielsweise 16 (nOut) Kernel, die jeweils eine Größe von 5x5 haben.

Pooling Layer

Auch der Pooling Layer besitzt einen Kernel. Im Unterschied zum Convolutional Layer hat dieser Kernel jedoch keine Parameter, die während des Trainings gelernt werden. Anstelle eines Skalarprodukts bestimmt eine vordefinierte Funktion, wie z. B. max() oder mean(), den resultierenden Wert. Ziel ist es, das Netz robuster gegenüber der exakten Position des eingehenden Features zu machen und eine Verkleinerung des Bilds und damit auch des Rechenaufwands hervorzurufen. CNNs nutzen häufig Pooling Layer, die den Maximalwert innerhalb des Kernels suchen. Wie in Listing 1 zu erkennen ist, folgen diese Layer meistens auf einen Convolutional Layer.

Kompatibilität der Layer

Grundsätzlich können die einzelnen Layer beliebig miteinander verknüpft werden. Dennoch gibt es einige Einschränkungen, die es zu beachten gilt. Zum einen muss der Input-Layer zwingend die Dimensionen des Bilds besitzen, das später klassifiziert werden soll. Des Weiteren muss der Output-Layer für jede mögliche Klasse genau ein Neuron besitzen. Zusätzlich sollte der Output-Layer die Aktivierungsfunktion Softmax nutzen, da hierdurch die Summe aller Ausgabewerte 1 ist und demnach eine Wahrscheinlichkeit für jede einzelne Klasse ausgegeben wird.

Auch beim Zusammensetzen der restlichen Layer muss insbesondere ein Aspekt beachtet werden: Das aus dem vorherigen Layer resultierende Bild muss groß genug sein, um den Kernel des aktuellen Layers nutzen zu können. Ist die Größe des Bilds aufgrund vorangegangenem Pooling auf 3x3 Pixel reduziert, kann kein Kernel der Größe 5x5 angewendet werden. Was sind aber die Faktoren, die Einfluss auf die Größe des Bilds haben?

Größe des Kernels

Der Kernel iteriert von links nach rechts sowie von oben nach unten über das Bild. Voraussetzung für die erfolgreiche Anwendung des Kernels ist, dass für jedes Gewicht auch ein Wert im jeweils betrachteten Bildausschnitt existiert. Zur besseren Veranschaulichung betrachten wir nur die x-Dimension des Bilds. Hat das Bild eine Weite von 5 Pixeln, so kann ein Kernel der Größe 3 nur dreimal angewendet werden. Im ersten Schritt würde der Kernel die Pixel 1 bis 3 nutzen, im zweiten Schritt dann die Pixel 2 bis 4 und im letzten Schritt die Pixel 3 bis 5. Das resultierende Bild hätte demnach eine Weite von 3 Pixeln. Die ursprüngliche Breite des Bilds hat sich demnach von 5 auf 3 Pixel reduziert. Voraussetzung für dieses Beispiel ist ein Padding von 0 sowie ein Stride von 1.

Stride bezeichnet die Schrittweite, mit der der Kernel über das Inputbild iteriert. stride=1 bedeutet, dass nach Anwendung des Kernels auf einen Bildausschnitt der Kernel um genau einen Pixel weiter verschoben wird. Je größer der Stride, desto weniger Anwendungen fi ndet der Kernel in der entsprechenden Richtung. Für die Größe des resultierenden Bilds bedeutet das also, dass diese abnimmt, je größer der Stride ist. Während die Kernelgröße und der Stride die Bildgröße verringern, kann das Padding dazu genutzt werden, dem entgegenzuwirken. Dabei handelt es sich um einen Rahmen, bestehend aus Nullen, welcher um das Inputbild gelegt wird, bevor die Convolution angewendet wird. Das Bild wird demnach künstlich vergrößert, um den Größenverlust durch Kernelgröße und Stride auszugleichen. Zusätzlich dient das Padding dazu, die Pixel am Rand eines Bilds in eine größere Anzahl an Convolutions einzubeziehen.

Zusammenfassend bewirken sowohl kernelSize als auch stride mit zunehmenden Werten eine Reduzierung der resultierenden Bildgröße, wohingegen padding in einem positiven Zusammenhang dazu steht.

Training eines eigenen CNN mit dem CIFARDatensatz

Die wichtigsten Bestandteile und Stolpersteine eines CNN sind nun bekannt. Im nächsten Schritt soll eine vorliegende Modellkonfi guration (Listing 1) genutzt werden, um ein Modell zu initialisieren, trainieren und anschließend zu evaluieren. Hierbei sei erwähnt, dass der Fokus nicht darauf liegt, einen besonders guten Klassifi kator zu erzeugen, sondern einen Einblick in die generelle Herangehensweise zu geben. Dafür dient uns im Folgenden der CIFAR-10-Datensatz des Canadian Institute for Advanced Research als Input. Bestehend aus 60 000 Bildern mit einer Aufl ösung von 32x32 Pixeln bietet dieser Datensatz genug Material, um einen Klassifi kator zu trainieren. Die geringe Auflösung ist in einer notwendigen Anpassung der Bildgröße begründet, die in der Praxis einen Trade-off zwischen der Erhöhung des Rechenaufwands und dem Verlust der in dem Bild enthaltenen visuellen Informationen darstellt.

Die Bilder sind aufgeteilt in 50 000 Trainings- und 10 000 Testdaten, wobei jede der zehn vorliegenden Klassen in genau 6 000 Bildern vertreten ist. Die zehn gleichverteilten Klassen sind: Flugzeug, Auto, Vogel, Katze, Hirsch, Hund, Frosch, Pferd, Schiff und Truck. Diese Gleichverteilung ist wichtig, um gute Ergebnisse während des Trainings zu erzielen. Je nach Fragestellung kann das in der Praxis nur schwer erreicht werden, deshalb werden derartige Datensätze häufi g zum Vortrainieren eines neuen Klassifikators genutzt. Anschließend wird ein auf den Anwendungsfall zugeschnittener Datensatz genutzt, um den Klassifikator auf die eigentliche Fragestellung zu spezialisieren (Transfer Learning). Heutzutage wird CIFAR-10 jedoch zum Großteil als Benchmark genutzt, um das Potenzial eines neuen Netzes zu testen und mit anderen Klassifikatoren zu vergleichen.

Listing 2
Listing 2

Listing 3
Listing 3

Listing 4
Listing 4

Aufgrund seiner Popularität ist dieser Datensatz häufig Bestandteil von Deep-Learning-Bibliotheken. Das ist auch bei dl4j der Fall, weshalb der Datensatz mit Hilfe des Cifar10DataSetIterator direkt geladen werden kann (Listing 2). Ein DataSetIterator dient in dl4j als Container für die Daten und organisiert sie in Batches einer zuvor definierten Größe.

Vorverarbeitung

Um sicherzustellen, dass das erstellte Netzwerk mit den zur Verfügung gestellten Bildern umgehen kann, müssen diese entsprechend aufbereitet werden. Die Bildgröße sowie die Anzahl der Farbkanäle müssen im gesamten Datensatz einheitlich sein. Optional können weitere Anpassungen vorgenommen werden, wie z. B. die Normalisierung der Pixelwerte auf den Bereich [0,1] und die Umwandlung von Farben in Graustufen. Zu den fortgeschrittenen Vorverarbeitungstechniken zählen zudem die Histogrammangleichung oder die Anwendung von Filtern, um bestimmte Merkmale des Bilds hervorzuheben. Speziell die Durchführung letzterer Techniken können durch Bibliotheken wie OpenCV erleichtert werden. dl4j bietet für die Vorverarbeitung verschiedene Präprozessoren an, die das Interface DataSetPreProcessor implementieren. Mit Hilfe des CompositeDataSetPre- Processor können sogar mehrere Präprozessoren hintereinander ausgeführt werden, um einen mehrstufigen Vorverarbeitungsprozess zu realisieren.

Neben der Registrierung der Präprozessoren in den Data-Iteratoren zeigt Listing 3 die Verwendung eines eigenen Präprozessors, der durch Implementierung des DataSetPreProcessor-Interface realisiert wurde (Listing 4). Dieser Präprozessor dient dazu, dem DataSet eine weitere Dimension der Größe 1 zu verleihen, die durch die Umwandlung von RGB zu Graustufe verloren geht, aber vom neuronalen Netz erwartet wird.

Auch die Verwendung von OpenCV-Funktionalitäten zur weiteren Aufbereitung der Bilder kann in einem derartigen Präprozessor stattfinden.

Listing 5
Listing 5

Training und Evaluation

Nach erfolgreicher Registrierung der Vorverarbeitung kann das Modell mit den zur Verfügung stehenden Daten trainiert werden (Listing 5). Bevor das jedoch geschehen kann, muss das Modell initialisiert werden. In diesem Schritt wird das neuronale Netz gemäß der zuvor definierten Architektur erstellt und die Gewichte entsprechend der Konfiguration initial gesetzt. Um während des Trainings einen Einblick in den Fortschritt zu haben, können sogenannte Listener registriert werden. Je nach Art des Listener werden entsprechende Information im Trainingsverlauf in der Konsole oder sogar einem Web-UI geloggt.

Die hier definierten Listener loggen den momentanen Score (Wert der Loss-Funktion) und führen eine Evaluation mit den Daten des testIterators aus. Die Evaluation bietet einen detaillierten Einblick in den Trainingsfortschritt, indem typische Metriken wie Accuracy, Precision, Recall, F1 Score sowie eine Konfusionsmatrix dargestellt werden. Nach Registrierung der Listener wird das Training mit Hilfe der fit-Funktion für eine beliebige Anzahl Epochen gestartet. Anschließend sollten möglichst unbekannte Bilder dafür genutzt werden, erneut eine Evaluation durchzuführen, um die wichtigsten Metriken zu identifizieren. Mindestens ebenso wichtig ist es jedoch, einen genauen Einblick in die Klassifikationsergebnisse zu wagen (Abb. 4). Insbesondere die falschen Klassifikationen sind dabei von Interesse.

Abb. 4: Fehlerhafte Klassifikationen eines CNN mit einer Accuracy von ca. 68 Prozent
Abb. 4: Fehlerhafte Klassifikationen eines CNN mit einer Accuracyvon ca. 68 Prozent

Erst beim Blick auf die fehlerhaften Klassifikationen können die zuvor ermittelten Metriken wie Accuracy eingeordnet werden. Ein Beispiel dafür stellt das vierte Bild in der ersten Spalte dar. Hintergrund und Farbgebung des Objekts im Zentrum könnten bei dieser Auflösung auch von einem Menschen zu einer Klassifikation von „Hirsch“ führen. Zudem wird hier, wie in einer entsprechenden Konfusionsmatrix, sichtbar, dass Autos häufig mit Trucks und Tiere häufig mit anderen Tieren verwechselt werden. Selbst wenn 68 Prozent Accuracy für das ursprüngliche Klassifikationsproblem also als zu wenig erscheint, könnte dieser Klassifikator definitiv dafür genutzt werden, Tiere von Fahrzeugen zu unterscheiden. Metriken wie Accuracy oder Recall bestimmen demnach nicht allein die Qualität des Klassifikators, sondern auch die fachliche Fragestellung.

Fazit

Dieser Artikel hat gezeigt, dass CNNs die Grundlage vieler CV-Algorithmen darstellen. Zudem zeigte der Praxisteil, dass auch die JVM genug Mittel bietet, um ein auf das eigene Problem zugeschnittenes CNN trainieren zu können. Auch wenn die Community um dl4j mit anderen Frameworks verglichen noch nicht so groß ist, sind die Konzepte die gleichen und durch den offenen Quellcode können viele Probleme eigenständig verstanden und bewältigt werden. Mindestens zwei Themenblöcke wurden in diesem Artikel jedoch verschwiegen, sollten aber bei der Lösung eigener Klassifikationsprobleme genutzt werden. Zum einen handelt es sich dabei um Data Augmentation, also die Vervielfältigung sowie Veränderung der Inputbilder, um das Netz robuster gegenüber ungesehenen Bildern zu machen. Zum anderen sollte Transfer Learning nicht außer Acht gelassen werden. Hierbei wird eine bestehende etablierte Modellarchitektur auf ein neues Problem angewendet und zu diesem Zweck leicht verändert.

Ob dieses neue Problem nun die Identifizierung von Superman oder von Fußgängern im Straßenverkehr ist, die bestehende Modellarchitektur nutzt in den meisten Fällen CNNs, deren Grundlagen nun bekannt sind.

Links & Literatur

[1] https://www.cs.toronto.edu/~kriz/cifar.html

[2] https://deeplearning4j.konduit.ai/

[3] Milicevic, Mario; Zubrinic, Krunoslav; Grbavac, Ivan; Obradovic, Ines: „Application of Deep Learning Architectures for Accurate Detection of Olive Tree Flowering Phenophase“; in: Remote Sens. 12.2020: https://doi.org/10.3390/ rs12132120

Zum Originalartikel „Ist es ein Vogel? Ist es ein Flugzeug? Computer Vision mit Java" gelangst du hier.

Entwickler Magazin Spezial Vol. 35, S. 60

No items found.

Weitere Artikel aus unserem Blog