Bilderkennung in Python mit TensorFlow und Keras

Einführung

Eine der häufigsten Verwendungen von TensorFlow und Keras ist die Erkennung/Klassifizierung von Bildern. Wenn Sie lernen möchten, wie Sie Keras zur Klassifizierung oder Erkennung von Bildern verwenden können, erfahren Sie in diesem Artikel, wie das geht.

Definitionen

Wenn Sie sich nicht über die grundlegenden Konzepte der Bilderkennung im Klaren sind, wird es schwierig sein, den Rest dieses Artikels vollständig zu verstehen. Bevor wir also weitermachen, sollten wir uns einen Moment Zeit nehmen, um einige Begriffe zu definieren.

TensorFlow/Keras


Credit: commons.wikimedia.org

TensorFlow ist eine Open-Source-Bibliothek, die vom Google Brain Team für Python entwickelt wurde. TensorFlow fasst viele verschiedene Algorithmen und Modelle zusammen und ermöglicht es dem Benutzer, tiefe neuronale Netze für Aufgaben wie Bilderkennung/Klassifizierung und natürliche Sprachverarbeitung zu implementieren. TensorFlow ist ein leistungsfähiges Framework, das durch die Implementierung einer Reihe von Verarbeitungsknoten funktioniert, wobei jeder Knoten eine mathematische Operation darstellt, wobei die gesamte Reihe von Knoten als „Graph“ bezeichnet wird.

In Bezug auf Keras handelt es sich um eine High-Level-API (Anwendungsprogrammierschnittstelle), die die darunter liegenden Funktionen von TensorFlow (sowie andere ML-Bibliotheken wie Theano) nutzen kann. Keras wurde mit Benutzerfreundlichkeit und Modularität als Leitprinzipien entwickelt. In der Praxis macht Keras die Implementierung der vielen leistungsstarken, aber oft komplexen Funktionen von TensorFlow so einfach wie möglich, und es ist so konfiguriert, dass es mit Python ohne größere Änderungen oder Konfigurationen funktioniert.

Bilderkennung (Klassifizierung)

Bilderkennung bezieht sich auf die Aufgabe, ein Bild in ein neuronales Netzwerk einzugeben und es eine Art Etikett für dieses Bild ausgeben zu lassen. Die Kennzeichnung, die das Netz ausgibt, entspricht einer vordefinierten Klasse. Es kann mehrere Klassen geben, denen das Bild zugeordnet werden kann, oder nur eine. Wenn es sich um eine einzige Klasse handelt, wird häufig der Begriff „Erkennung“ verwendet, wohingegen eine Erkennungsaufgabe mit mehreren Klassen oft als „Klassifizierung“ bezeichnet wird.

Eine Teilmenge der Bildklassifizierung ist die Objekterkennung, bei der bestimmte Instanzen von Objekten als zu einer bestimmten Klasse gehörend identifiziert werden, wie z. B. Tiere, Autos oder Menschen.

Merkmalsextraktion

Um eine Bilderkennung/Klassifizierung durchführen zu können, muss das neuronale Netz eine Merkmalsextraktion durchführen. Merkmale sind die Elemente der Daten, für die man sich interessiert und die durch das Netz geleitet werden. Im speziellen Fall der Bilderkennung sind die Merkmale die Gruppen von Pixeln, wie Kanten und Punkte, eines Objekts, die das Netz nach Mustern analysiert.

Merkmalerkennung (oder Merkmalsextraktion) ist der Prozess des Herausziehens der relevanten Merkmale aus einem Eingabebild, damit diese Merkmale analysiert werden können. Viele Bilder enthalten Anmerkungen oder Metadaten über das Bild, die dem Netz helfen, die relevanten Merkmale zu finden.

Wie neuronale Netze lernen, Bilder zu erkennen

Ein Gefühl dafür zu bekommen, wie ein neuronales Netz Bilder erkennt, wird Ihnen helfen, wenn Sie ein neuronales Netzmodell implementieren, also lassen Sie uns in den nächsten Abschnitten kurz den Bilderkennungsprozess untersuchen.

Merkmalsextraktion mit Filtern


Credit: commons.wikimedia.org

Die erste Schicht eines neuronalen Netzes nimmt alle Pixel eines Bildes auf. Nachdem alle Daten in das Netz eingespeist wurden, werden verschiedene Filter auf das Bild angewendet, die Repräsentationen verschiedener Teile des Bildes bilden. Dies ist die Merkmalsextraktion, und es werden „Merkmalskarten“ erstellt.

Dieser Prozess der Merkmalsextraktion aus einem Bild wird mit einer „Faltungsschicht“ durchgeführt, und die Faltung ist einfach die Bildung einer Darstellung eines Teils eines Bildes. Von diesem Faltungskonzept leitet sich der Begriff „Convolutional Neural Network“ (CNN) ab, die Art von neuronalem Netzwerk, die am häufigsten bei der Klassifizierung/Erkennung von Bildern verwendet wird.

Wenn Sie sich vorstellen wollen, wie die Erstellung von Merkmalskarten funktioniert, denken Sie an eine Taschenlampe, die in einem dunklen Raum auf ein Bild gerichtet ist. Wenn Sie den Strahl über das Bild gleiten lassen, lernen Sie etwas über die Merkmale des Bildes. In dieser Metapher ist das Licht der Taschenlampe der Filter.

Die Breite des Strahls Ihrer Taschenlampe bestimmt, wie viel des Bildes Sie auf einmal untersuchen, und neuronale Netze haben einen ähnlichen Parameter, die Filtergröße. Die Filtergröße bestimmt, wie viel des Bildes, wie viele Pixel, auf einmal untersucht werden. Eine übliche Filtergröße, die in CNNs verwendet wird, ist 3, und dies deckt sowohl die Höhe als auch die Breite ab, so dass der Filter einen Bereich von 3 x 3 Pixeln untersucht.


Credit: commons.wikimedia.org

Während die Filtergröße die Höhe und Breite des Filters abdeckt, muss auch die Tiefe des Filters angegeben werden.

Wie kann ein 2D-Bild Tiefe haben?

Digitale Bilder werden als Höhe, Breite und einige RGB-Werte gerendert, die die Farben des Pixels definieren, so dass die „Tiefe“, die verfolgt wird, die Anzahl der Farbkanäle ist, die das Bild hat. Graustufenbilder (ohne Farbe) haben nur einen Farbkanal, während Farbbilder drei Tiefenkanäle haben.

Das bedeutet, dass ein Filter der Größe 3, der auf ein Vollfarbbild angewendet wird, 3 x 3 x 3 groß ist. Für jedes Pixel, das von diesem Filter abgedeckt wird, multipliziert das Netzwerk die Filterwerte mit den Werten in den Pixeln selbst, um eine numerische Darstellung dieses Pixels zu erhalten. Dieser Vorgang wird dann für das gesamte Bild durchgeführt, um eine vollständige Darstellung zu erhalten. Der Filter wird über den Rest des Bildes entsprechend einem Parameter namens „stride“ verschoben, der festlegt, um wie viele Pixel der Filter verschoben werden soll, nachdem er den Wert an seiner aktuellen Position berechnet hat. Eine übliche Stride-Größe für ein CNN ist 2.

Das Endergebnis all dieser Berechnungen ist eine Merkmalskarte. Dieser Prozess wird in der Regel mit mehr als einem Filter durchgeführt, was dazu beiträgt, die Komplexität des Bildes zu erhalten.

Aktivierungsfunktionen

Nachdem die Merkmalskarte des Bildes erstellt wurde, werden die Werte, die das Bild darstellen, durch eine Aktivierungsfunktion oder Aktivierungsschicht geleitet. Die Aktivierungsfunktion nimmt die Werte, die das Bild repräsentieren, die dank der Faltungsschicht in einer linearen Form (d.h. nur eine Liste von Zahlen) vorliegen, und erhöht ihre Nichtlinearität, da Bilder selbst nichtlinear sind.

Die typische Aktivierungsfunktion, die hierfür verwendet wird, ist eine Rectified Linear Unit (ReLU), obwohl gelegentlich auch andere Aktivierungsfunktionen verwendet werden (über diese können Sie hier lesen).

Pooling-Schichten

Nachdem die Daten aktiviert wurden, werden sie durch eine Pooling-Schicht geschickt. Pooling „verkleinert“ ein Bild, d.h. es nimmt die Informationen, die das Bild darstellen, und komprimiert sie, wodurch sie kleiner werden. Der Pooling-Prozess macht das Netzwerk flexibler und geschickter bei der Erkennung von Objekten/Bildern auf der Grundlage der relevanten Merkmale.

Wenn wir ein Bild betrachten, sind wir normalerweise nicht mit allen Informationen im Hintergrund des Bildes beschäftigt, sondern nur mit den Merkmalen, die uns interessieren, wie Menschen oder Tiere.

In ähnlicher Weise abstrahiert eine Pooling-Schicht in einem CNN die unnötigen Teile des Bildes und behält nur die Teile des Bildes, die es für relevant hält, was durch die festgelegte Größe der Pooling-Schicht gesteuert wird.

Da es Entscheidungen über die relevantesten Teile des Bildes treffen muss, hofft man, dass das Netzwerk nur die Teile des Bildes lernt, die das betreffende Objekt wirklich darstellen. Dies hilft, eine Überanpassung zu verhindern, bei der das Netz Aspekte des Trainingsfalls zu gut lernt und es versäumt, auf neue Daten zu verallgemeinern.


Credit: commons.wikimedia.org

Es gibt verschiedene Möglichkeiten, Werte zu poolen, aber Max-Pooling wird am häufigsten verwendet. Beim Max-Pooling wird der maximale Wert der Pixel innerhalb eines einzelnen Filters (innerhalb eines einzelnen Punktes im Bild) ermittelt. Dadurch gehen 3/4 der Informationen verloren, wenn man davon ausgeht, dass 2 x 2 Filter verwendet werden.

Die Maximalwerte der Pixel werden verwendet, um mögliche Bildverzerrungen zu berücksichtigen, und die Parameter/Größe des Bildes werden reduziert, um eine Überanpassung zu kontrollieren. Es gibt auch andere Pooling-Typen wie Durchschnitts-Pooling oder Summen-Pooling, die aber nicht so häufig verwendet werden, weil das Max-Pooling tendenziell eine bessere Genauigkeit liefert.

Flattening

Die letzten Schichten unseres CNN, die dicht verknüpften Schichten, erfordern, dass die zu verarbeitenden Daten in Form eines Vektors vorliegen. Aus diesem Grund müssen die Daten „geglättet“ werden. Die Werte werden zu einem langen Vektor oder einer Spalte mit fortlaufend angeordneten Zahlen komprimiert.

Vollständig verbundene Schichten

Die letzten Schichten des CNN sind dicht verbundene Schichten oder ein künstliches neuronales Netz (ANN). Die Hauptfunktion des ANN besteht darin, die Eingangsmerkmale zu analysieren und sie zu verschiedenen Attributen zu kombinieren, die bei der Klassifizierung helfen. Diese Schichten bilden im Wesentlichen Sammlungen von Neuronen, die verschiedene Teile des fraglichen Objekts repräsentieren, und eine Sammlung von Neuronen kann die Schlappohren eines Hundes oder die Röte eines Apfels darstellen. Wenn genügend dieser Neuronen als Reaktion auf ein Eingangsbild aktiviert werden, wird das Bild als Objekt klassifiziert.


Credit: commons.wikimedia.org

Der Fehler oder die Differenz zwischen den berechneten Werten und dem erwarteten Wert im Trainingssatz wird vom ANN berechnet. Das Netzwerk durchläuft dann ein Backpropagation-Verfahren, bei dem der Einfluss eines bestimmten Neurons auf ein Neuron in der nächsten Schicht berechnet und sein Einfluss angepasst wird. Dies geschieht, um die Leistung des Modells zu optimieren. Dieser Prozess wird dann immer und immer wieder wiederholt. Auf diese Weise trainiert das Netz Daten und lernt Assoziationen zwischen Eingabemerkmalen und Ausgabeklassen.

Die Neuronen in den mittleren voll verknüpften Schichten geben binäre Werte aus, die sich auf die möglichen Klassen beziehen. Wenn Sie vier verschiedene Klassen haben (sagen wir einen Hund, ein Auto, ein Haus und eine Person), wird das Neuron einen „1“-Wert für die Klasse haben, von der es glaubt, dass das Bild sie repräsentiert, und einen „0“-Wert für die anderen Klassen.

Die letzte vollständig verknüpfte Schicht empfängt die Ausgabe der Schicht vor ihr und liefert eine Wahrscheinlichkeit für jede der Klassen, die sich zu eins summiert. Ein Wert von 0,75 in der Kategorie „Hund“ bedeutet, dass das Bild mit 75-prozentiger Sicherheit ein Hund ist.

Der Bildklassifikator wurde nun trainiert, und die Bilder können an das CNN übergeben werden, das nun eine Vermutung über den Inhalt des Bildes ausgibt.

Der Arbeitsablauf des maschinellen Lernens

Bevor wir uns mit einem Beispiel für das Training eines Bildklassifikators befassen, sollten wir uns einen Moment Zeit nehmen, um den Arbeitsablauf des maschinellen Lernens oder die Pipeline zu verstehen. Der Prozess für das Training eines neuronalen Netzwerkmodells ist ziemlich standardisiert und kann in vier verschiedene Phasen unterteilt werden.

Datenvorbereitung

Zunächst müssen Sie Ihre Daten sammeln und sie in eine Form bringen, mit der das Netzwerk trainieren kann. Dazu gehört das Sammeln von Bildern und deren Beschriftung. Selbst wenn Sie einen Datensatz heruntergeladen haben, den jemand anderes vorbereitet hat, müssen Sie ihn wahrscheinlich vorbereiten, bevor Sie ihn für das Training verwenden können. Die Datenvorbereitung ist eine Kunst für sich, die den Umgang mit fehlenden Werten, beschädigten Daten, Daten im falschen Format, falschen Beschriftungen usw. beinhaltet.

In diesem Artikel werden wir einen vorverarbeiteten Datensatz verwenden.

Erstellung des Modells

Die Erstellung des neuronalen Netzwerkmodells beinhaltet Entscheidungen über verschiedene Parameter und Hyperparameter. Sie müssen entscheiden, wie viele Schichten Sie in Ihrem Modell verwenden, wie groß die Eingabe- und Ausgabegrößen der Schichten sein werden, welche Art von Aktivierungsfunktionen Sie verwenden werden, ob Sie Dropout verwenden werden oder nicht usw.

Um zu lernen, welche Parameter und Hyperparameter zu verwenden sind, braucht man Zeit (und eine Menge Lernaufwand), aber es gibt einige Heuristiken, die man verwenden kann, um loszulegen, und wir werden einige davon während des Implementierungsbeispiels behandeln.

Training des Modells

Nachdem Sie Ihr Modell erstellt haben, erstellen Sie einfach eine Instanz des Modells und passen es mit Ihren Trainingsdaten an. Das wichtigste Kriterium beim Trainieren eines Modells ist die Zeit, die das Modell zum Trainieren benötigt. Sie können die Dauer des Trainings für ein Netzwerk festlegen, indem Sie die Anzahl der Epochen angeben, über die trainiert werden soll. Je länger Sie ein Modell trainieren, desto besser wird seine Leistung, aber bei zu vielen Trainingsepochen besteht die Gefahr einer Überanpassung.

Die Wahl der Anzahl der Epochen, für die trainiert werden soll, ist etwas, für das Sie ein Gefühl bekommen werden, und es ist üblich, die Gewichte eines Netzes zwischen den Trainingssitzungen zu speichern, so dass Sie nicht wieder von vorne anfangen müssen, sobald Sie beim Training des Netzes einige Fortschritte gemacht haben.

Modellauswertung

Es gibt mehrere Schritte zur Auswertung des Modells. Der erste Schritt bei der Bewertung des Modells besteht darin, die Leistung des Modells mit einem Validierungsdatensatz zu vergleichen, d. h. mit einem Datensatz, auf dem das Modell nicht trainiert wurde. Sie vergleichen die Leistung des Modells mit diesem Validierungsdatensatz und analysieren seine Leistung anhand verschiedener Metriken.

Es gibt verschiedene Metriken zur Bestimmung der Leistung eines neuronalen Netzwerkmodells, aber die gängigste Metrik ist die „Genauigkeit“, d. h. die Anzahl der korrekt klassifizierten Bilder geteilt durch die Gesamtzahl der Bilder in Ihrem Datensatz.

Nachdem Sie die Genauigkeit der Leistung des Modells auf einem Validierungsdatensatz gesehen haben, gehen Sie normalerweise zurück und trainieren das Netzwerk erneut mit leicht veränderten Parametern, denn es ist unwahrscheinlich, dass Sie mit der Leistung Ihres Netzwerks beim ersten Training zufrieden sind. Sie werden die Parameter Ihres Netzes immer wieder ändern, es neu trainieren und seine Leistung messen, bis Sie mit der Genauigkeit des Netzes zufrieden sind.

Schließlich testen Sie die Leistung des Netzes an einem Testsatz. Dieser Testsatz ist ein weiterer Datensatz, den Ihr Modell noch nie zuvor gesehen hat.

Vielleicht fragen Sie sich:

Warum die Mühe mit dem Testsatz? Wenn Sie sich ein Bild von der Genauigkeit Ihres Modells machen wollen, ist das nicht der Zweck des Validierungssatzes?

Es ist eine gute Idee, einen Stapel von Daten, die das Netz noch nie gesehen hat, zum Testen aufzubewahren, denn die ganze Optimierung der Parameter, die Sie vornehmen, könnte in Verbindung mit dem erneuten Testen des Validierungssatzes bedeuten, dass Ihr Netz einige Eigenheiten des Validierungssatzes gelernt hat, die sich nicht auf Daten außerhalb der Stichprobe verallgemeinern lassen.

Der Zweck des Testsatzes besteht also darin, auf Probleme wie Überanpassung zu prüfen und sicherer zu sein, dass Ihr Modell wirklich geeignet ist, um in der realen Welt zu funktionieren.

Bilderkennung mit einem CNN

Wir haben bisher eine Menge behandelt, und wenn all diese Informationen ein wenig überwältigend waren, sollten diese Konzepte in einem Beispiel-Klassifikator, der mit einem Datensatz trainiert wurde, zusammenkommen, um diese Konzepte konkreter zu machen. Schauen wir uns also ein vollständiges Beispiel für Bilderkennung mit Keras an, vom Laden der Daten bis zur Auswertung.


Credit: www.cs.toronto.edu

Zunächst benötigen wir einen Datensatz zum Trainieren. In diesem Beispiel werden wir den berühmten CIFAR-10-Datensatz verwenden. CIFAR-10 ist ein großer Bilddatensatz mit über 60.000 Bildern, die 10 verschiedene Klassen von Objekten wie Katzen, Flugzeuge und Autos darstellen.

Die Bilder sind vollfarbige RGB-Bilder, aber sie sind ziemlich klein, nur 32 x 32. Ein großer Vorteil des CIFAR-10-Datensatzes ist, dass er bereits mit Keras geliefert wird, so dass es sehr einfach ist, den Datensatz zu laden, und die Bilder müssen nur wenig vorverarbeitet werden.

Als erstes sollten wir die notwendigen Bibliotheken importieren. Ich werde zeigen, wie diese Importe verwendet werden, wenn wir weitermachen, aber im Moment wissen wir, dass wir Numpy und verschiedene Module, die mit Keras verbunden sind, verwenden werden:

import numpyfrom keras.models import Sequentialfrom keras.layers import Dense, Dropout, Flatten, BatchNormalization, Activationfrom keras.layers.convolutional import Conv2D, MaxPooling2Dfrom keras.constraints import maxnormfrom keras.utils import np_utils

Wir werden hier einen zufälligen Seed verwenden, damit die in diesem Artikel erzielten Ergebnisse von Ihnen repliziert werden können, weshalb wir numpy benötigen:

# Set random seed for purposes of reproducibilityseed = 21

Vorbereitung der Daten

Wir benötigen einen weiteren Import: den Datensatz.

from keras.datasets import cifar10

Laden wir nun das Dataset ein. Dazu geben wir einfach an, in welche Variablen wir die Daten laden wollen, und verwenden dann die Funktion load_data():

# loading in the data (X_train, y_train), (X_test, y_test) = cifar10.load_data()

In den meisten Fällen müssen Sie Ihre Daten vorverarbeiten, um sie für die Verwendung vorzubereiten, aber da wir einen vorgefertigten Datensatz verwenden, muss nur sehr wenig vorverarbeitet werden. Eine Sache, die wir tun wollen, ist die Normalisierung der Eingabedaten.

Wenn die Werte der Eingabedaten in einem zu großen Bereich liegen, kann dies die Leistung des Netzwerks negativ beeinflussen. In diesem Fall sind die Eingabedaten die Pixel im Bild, die einen Wert zwischen 0 und 255 haben.

Um die Daten zu normalisieren, können wir also einfach die Bildwerte durch 255 teilen. Um dies zu tun, müssen wir zunächst die Daten in einen Float-Typ umwandeln, da sie derzeit Ganzzahlen sind. Dazu verwenden wir den astype() Numpy-Befehl und geben dann den gewünschten Datentyp an:

# normalize the inputs from 0-255 to between 0 and 1 by dividing by 255 X_train = X_train.astype('float32')X_test = X_test.astype('float32')X_train = X_train / 255.0X_test = X_test / 255.0

Um die Daten für das Netzwerk vorzubereiten, müssen wir außerdem die Werte mit einem Punkt kodieren. Ich werde hier nicht auf die Besonderheiten der One-Hot-Kodierung eingehen, aber Sie sollten wissen, dass die Bilder so, wie sie sind, nicht vom Netzwerk verwendet werden können. Sie müssen zuerst kodiert werden, und One-Hot-Kodierung wird am besten verwendet, wenn man eine binäre Klassifizierung durchführt.

Wir führen hier tatsächlich eine binäre Klassifizierung durch, weil ein Bild entweder zu einer Klasse gehört oder nicht, es kann nicht irgendwo dazwischen liegen. Der Numpy-Befehl to_categorical() wird zur One-Hot-Codierung verwendet. Deshalb haben wir die Funktion np_utils aus Keras importiert, da sie to_categorical() enthält.

Wir müssen auch die Anzahl der Klassen im Datensatz angeben, damit wir wissen, auf wie viele Neuronen wir die letzte Schicht komprimieren müssen:

# one hot encode outputsy_train = np_utils.to_categorical(y_train)y_test = np_utils.to_categorical(y_test)class_num = y_test.shape

Entwerfen des Modells

Wir haben die Phase erreicht, in der wir das CNN-Modell entwerfen. Das erste, was wir tun müssen, ist das Format zu definieren, das wir für das Modell verwenden möchten. Keras hat mehrere verschiedene Formate oder Blaupausen, um Modelle zu erstellen, aber Sequential ist das am häufigsten verwendete, und aus diesem Grund haben wir es aus Keras importiert.

Erstellen Sie das Modell

model = Sequential()

Die erste Schicht unseres Modells ist eine Faltungsschicht. Sie nimmt die Eingaben auf und führt Faltungsfilter auf ihnen aus.

Wenn wir diese in Keras implementieren, müssen wir die Anzahl der Kanäle/Filter angeben, die wir wollen (das sind die 32 unten), die Größe des Filters, die wir wollen (3 x 3 in diesem Fall), die Eingabeform (beim Erstellen der ersten Schicht) und die Aktivierung und das Padding, das wir brauchen.

Wie bereits erwähnt, ist relu die gebräuchlichste Aktivierung, und padding='same' bedeutet einfach, dass wir die Größe des Bildes überhaupt nicht verändern:

model.add(Conv2D(32, (3, 3), input_shape=X_train.shape, padding='same'))model.add(Activation('relu'))

Anmerkung: Sie können die Aktivierungen und Auffüllungen auch aneinanderreihen, etwa so:

model.add(Conv2D(32, (3, 3), input_shape=(3, 32, 32), activation='relu', padding='same'))

Nun werden wir eine Dropout-Schicht erstellen, um eine Überanpassung zu verhindern, die funktioniert, indem sie zufällig einige der Verbindungen zwischen den Schichten eliminiert (0.2 bedeutet, dass 20 % der bestehenden Verbindungen entfernt werden):

model.add(Dropout(0.2))

Möglicherweise möchten wir hier auch eine Stapelnormalisierung durchführen. Die Stapelnormalisierung normalisiert die Eingaben, die in die nächste Schicht einfließen, und stellt sicher, dass das Netzwerk immer Aktivierungen mit der gleichen Verteilung erzeugt, die wir uns wünschen:

model.add(BatchNormalization())

Jetzt kommt eine weitere Faltungsschicht, aber die Filtergröße nimmt zu, damit das Netzwerk komplexere Darstellungen lernen kann:

model.add(Conv2D(64, (3, 3), padding='same'))model.add(Activation('relu'))

Hier ist die Pooling-Schicht, die, wie bereits besprochen, dazu beiträgt, den Bildklassifikator robuster zu machen, damit er relevante Muster lernen kann. Außerdem gibt es das Dropout und die Stapelnormalisierung:

model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.2))model.add(BatchNormalization())

Das ist der grundlegende Ablauf für die erste Hälfte einer CNN-Implementierung: Convolutional, Aktivierung, Dropout, Pooling. Sie können jetzt sehen, warum wir Dropout, BatchNormalization, Activation, Conv2d und MaxPooling2d importiert haben.

Sie können die genaue Anzahl der Faltungsschichten nach Ihrem Geschmack variieren, obwohl jede weitere Schicht mehr Rechenaufwand bedeutet. Beachten Sie, dass Sie beim Hinzufügen von Faltungsschichten in der Regel deren Anzahl an Filtern erhöhen, damit das Modell komplexere Darstellungen lernen kann. Wenn Ihnen die Anzahl der Schichten etwas willkürlich erscheint, sollten Sie wissen, dass Sie im Allgemeinen die Anzahl der Filter nach und nach erhöhen und dass es ratsam ist, sie zu Potenzen von 2 zu machen, was beim Training auf einem Grafikprozessor einen leichten Vorteil bringen kann.

Es ist wichtig, nicht zu viele Pooling-Schichten zu haben, da bei jedem Pooling einige Daten verworfen werden. Zu häufiges Pooling führt dazu, dass die dicht verknüpften Schichten fast nichts mehr lernen können, wenn die Daten sie erreichen.

Die genaue Anzahl der Pooling-Schichten, die Sie verwenden sollten, hängt von der Aufgabe ab, die Sie bearbeiten, und es ist etwas, für das Sie mit der Zeit ein Gefühl bekommen werden. Da die Bilder hier bereits so klein sind, werden wir nicht mehr als zweimal poolen.

Sie können nun diese Schichten wiederholen, um Ihrem Netzwerk mehr Repräsentationen zur Verfügung zu stellen, mit denen es arbeiten kann:

model.add(Conv2D(64, (3, 3), padding='same'))model.add(Activation('relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.2))model.add(BatchNormalization()) model.add(Conv2D(128, (3, 3), padding='same'))model.add(Activation('relu'))model.add(Dropout(0.2))model.add(BatchNormalization())

Nachdem wir mit den Faltungsschichten fertig sind, müssen wir Flatten die Daten verarbeiten, weshalb wir die obige Funktion importiert haben. Wir fügen auch wieder eine Dropout-Schicht hinzu:

model.add(Flatten())model.add(Dropout(0.2))

Nun nutzen wir den Dense Import und erstellen die erste dicht verknüpfte Schicht. Wir müssen die Anzahl der Neuronen in der dichten Schicht angeben. Beachten Sie, dass die Anzahl der Neuronen in den nachfolgenden Schichten abnimmt und sich schließlich der Anzahl der Neuronen nähert, die der Anzahl der Klassen im Datensatz entspricht (in diesem Fall 10). Die Kernel-Beschränkung kann die Daten während des Lernens regulieren, was ebenfalls dazu beiträgt, eine Überanpassung zu verhindern. Aus diesem Grund haben wir zuvor maxnorm importiert.

model.add(Dense(256, kernel_constraint=maxnorm(3)))model.add(Activation('relu'))model.add(Dropout(0.2))model.add(BatchNormalization()) model.add(Dense(128, kernel_constraint=maxnorm(3)))model.add(Activation('relu'))model.add(Dropout(0.2))model.add(BatchNormalization())

In dieser letzten Schicht geben wir die Anzahl der Klassen für die Anzahl der Neuronen ein. Jedes Neuron repräsentiert eine Klasse, und die Ausgabe dieser Schicht wird ein Vektor mit 10 Neuronen sein, wobei jedes Neuron eine gewisse Wahrscheinlichkeit speichert, dass das betreffende Bild zu der Klasse gehört, die es repräsentiert.

Schließlich wählt die Aktivierungsfunktion softmax das Neuron mit der höchsten Wahrscheinlichkeit als Ausgabe aus und stimmt damit ab, dass das Bild zu dieser Klasse gehört:

model.add(Dense(class_num))model.add(Activation('softmax'))

Nun, da wir das Modell entworfen haben, das wir verwenden wollen, müssen wir es nur noch kompilieren. Geben wir die Anzahl der Epochen an, für die wir trainieren wollen, sowie den Optimierer, den wir verwenden wollen:

Der Optimierer wird die Gewichte in Ihrem Netzwerk so abstimmen, dass sie sich dem Punkt mit dem geringsten Verlust nähern. Der Adam-Algorithmus ist einer der am häufigsten verwendeten Optimierer, da er bei den meisten Problemen eine gute Leistung erbringt:

epochs = 25optimizer = 'adam'

Wir kompilieren nun das Modell mit den von uns gewählten Parametern. Außerdem geben wir eine zu verwendende Metrik an:

model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=)

Wir können die Modellzusammenfassung ausdrucken, um zu sehen, wie das gesamte Modell aussieht:

print(model.summary())

Das Ausdrucken der Zusammenfassung liefert uns eine ganze Menge Informationen:

Results: Layer (type) Output Shape Param #=================================================================conv2d_1 (Conv2D) (None, 32, 32, 32) 896_________________________________________________________________activation_1 (Activation) (None, 32, 32, 32) 0_________________________________________________________________dropout_1 (Dropout) (None, 32, 32, 32) 0_________________________________________________________________batch_normalization_1 (Batch (None, 32, 32, 32) 128_________________________________________________________________conv2d_2 (Conv2D) (None, 32, 32, 64) 18496_________________________________________________________________activation_2 (Activation) (None, 32, 32, 64) 0_________________________________________________________________max_pooling2d_1 (MaxPooling2 (None, 16, 16, 64) 0_________________________________________________________________dropout_2 (Dropout) (None, 16, 16, 64) 0_________________________________________________________________batch_normalization_2 (Batch (None, 16, 16, 64) 256_________________________________________________________________conv2d_3 (Conv2D) (None, 16, 16, 64) 36928_________________________________________________________________activation_3 (Activation) (None, 16, 16, 64) 0_________________________________________________________________max_pooling2d_2 (MaxPooling2 (None, 8, 8, 64) 0_________________________________________________________________dropout_3 (Dropout) (None, 8, 8, 64) 0_________________________________________________________________batch_normalization_3 (Batch (None, 8, 8, 64) 256_________________________________________________________________conv2d_4 (Conv2D) (None, 8, 8, 128) 73856_________________________________________________________________activation_4 (Activation) (None, 8, 8, 128) 0_________________________________________________________________dropout_4 (Dropout) (None, 8, 8, 128) 0_________________________________________________________________batch_normalization_4 (Batch (None, 8, 8, 128) 512_________________________________________________________________flatten_1 (Flatten) (None, 8192) 0_________________________________________________________________dropout_5 (Dropout) (None, 8192) 0_________________________________________________________________dense_1 (Dense) (None, 256) 2097408_________________________________________________________________activation_5 (Activation) (None, 256) 0_________________________________________________________________dropout_6 (Dropout) (None, 256) 0_________________________________________________________________batch_normalization_5 (Batch (None, 256) 1024_________________________________________________________________dense_2 (Dense) (None, 128) 32896_________________________________________________________________activation_6 (Activation) (None, 128) 0_________________________________________________________________dropout_7 (Dropout) (None, 128) 0_________________________________________________________________batch_normalization_6 (Batch (None, 128) 512_________________________________________________________________dense_3 (Dense) (None, 10) 1290_________________________________________________________________activation_7 (Activation) (None, 10) 0=================================================================Total params: 2,264,458Trainable params: 2,263,114Non-trainable params: 1,344

Jetzt können wir das Modell trainieren. Dazu müssen wir nur die Funktion fit() für das Modell aufrufen und die gewählten Parameter übergeben.

Hier verwende ich den von mir gewählten Seed, um die Reproduzierbarkeit zu gewährleisten.

numpy.random.seed(seed)model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=epochs, batch_size=64)

Wir werden mit 50000 Stichproben trainieren und mit 10000 Stichproben validieren.

Wenn Sie diesen Code ausführen, erhalten Sie folgendes Ergebnis:

Epoch 1/2564/50000 - ETA: 16:57 - loss: 3.1479 - acc: 0.0938128/50000 - ETA: 10:12 - loss: 3.0212 - acc: 0.0938192/50000 - ETA: 7:57 - loss: 2.9781 - acc: 0.1250256/50000 - ETA: 6:48 - loss: 2.8830 - acc: 0.1484320/50000 - ETA: 6:07 - loss: 2.8878 - acc: 0.1469384/50000 - ETA: 5:40 - loss: 2.8732 - acc: 0.1458448/50000 - ETA: 5:20 - loss: 2.8842 - acc: 0.1406.........49664/50000 - ETA: 1s - loss: 1.5160 - acc: 0.461149728/50000 - ETA: 1s - loss: 1.5157 - acc: 0.461249792/50000 - ETA: 1s - loss: 1.5153 - acc: 0.461449856/50000 - ETA: 0s - loss: 1.5147 - acc: 0.461549920/50000 - ETA: 0s - loss: 1.5144 - acc: 0.461749984/50000 - ETA: 0s - loss: 1.5141 - acc: 0.461750000/50000 - 262s 5ms/step - loss: 1.5140 - acc: 0.4618 - val_loss: 1.0715 - val_acc: 0.6195End of Epoch 1

Beachten Sie, dass Sie in den meisten Fällen einen Validierungssatz haben möchten, der sich vom Testsatz unterscheidet, und dass Sie daher einen Prozentsatz der Trainingsdaten angeben, der als Validierungssatz verwendet wird. In diesem Fall geben wir nur die Testdaten ein, um sicherzustellen, dass die Testdaten beiseite gelegt und nicht darauf trainiert werden. In diesem Beispiel haben wir nur Testdaten, um die Dinge einfach zu halten.

Jetzt können wir das Modell auswerten und sehen, wie es funktioniert. Rufen Sie einfach model.evaluate():

# Model evaluationscores = model.evaluate(X_test, y_test, verbose=0)print("Accuracy: %.2f%%" % (scores*100))

Auf und wir erhalten das Ergebnis:

Accuracy: 83.01%

Und das war’s! Wir haben jetzt ein trainiertes CNN zur Bilderkennung. Nicht schlecht für den ersten Durchlauf, aber Sie werden wahrscheinlich mit der Modellstruktur und den Parametern herumspielen wollen, um zu sehen, ob Sie nicht eine bessere Leistung erzielen können.

Fazit

Nachdem Sie nun Ihr erstes Bilderkennungsnetzwerk in Keras implementiert haben, wäre es eine gute Idee, mit dem Modell herumzuspielen und zu sehen, wie sich das Ändern der Parameter auf die Leistung auswirkt.

Das wird Ihnen eine gewisse Intuition für die beste Wahl der verschiedenen Modellparameter geben. Sie sollten sich dabei auch über die verschiedenen Parameter und Hyperparameter informieren. Wenn Sie sich mit diesen vertraut gemacht haben, können Sie versuchen, Ihren eigenen Bildklassifikator auf einem anderen Datensatz zu implementieren.

Wenn Sie mit dem Code herumspielen oder ihn einfach nur ein wenig genauer studieren möchten, ist das Projekt auf GitHub hochgeladen!