April 2019

Band 34, Nummer 4

[Künstlich Intelligent]

Wie lernen neuronale Netze?

Von Frank La La | April 2019

Frank La VigneIn meiner vorherigen Kolumne („Ein genauerer Blick auf neuronale Netze“, msdn.com/magazine/mt833269) habe ich die Grundstruktur neuronaler Netze untersucht und mit Python ein solches Netzwerk von Grund auf neu erstellt. Nachdem ich die Grundstrukturen untersucht habe, die allen neuronalen Netzen gemeinsam sind, habe ich ein Beispielframework zur Berechnung der gewichteten Summen und Ausgabewerte erstellt. Neuronen selbst sind einfach und führen grundlegende mathematische Funktionen aus, um ihre Ausgaben zwischen 1 und 0 oder -1 und 1 zu normalisieren. Sie werden jedoch leistungsfähig, wenn sie miteinander verbunden werden. Neuronen sind in einem neuronalen Netz in Schichten angeordnet, und jedes Neuron überträgt Werte an die nächste Schicht. Eingabewerte werden durch das Netzwerk kaskadiert und beeinflussen die Ausgabe in einem Prozess, der als Forward Propagation bezeichnet wird.

Wie jedoch genau lernen neuronale Netze? Wie sieht der Prozess aus und was passiert innerhalb eines neuronalen Netzes, wenn es lernt? In der vorherigen Kolumne lag der Schwerpunkt auf der Forward Propagation von Werten. Für überwachte Lernszenarien können neuronale Netze einen Prozess namens Backpropagation nutzen.

Backpropagation, Verlust und Epochen

Erinnern Sie sich daran, dass jedes Neuron in einem neuronalen Netz Eingabewerte annimmt, die mit einer Gewichtung multipliziert werden, um die Stärke dieser Verbindung darzustellen. Backpropagation ermittelt die richtigen Gewichtungen, die auf Knoten in einem neuronalen Netz angewendet werden sollten, indem die aktuellen Ausgaben des Netzwerks mit den gewünschten oder richtigen Ausgaben verglichen werden. Die Differenz zwischen der gewünschten Ausgabe und der aktuellen Ausgabe wird durch die Verlust- oder Kostenfunktion berechnet. Mit anderen Worten, die Verlustfunktion sagt uns, wie genau unser neuronales Netz Vorhersagen für eine bestimmte Eingabe trifft.

Die Formel zur Berechnung des Verlusts finden Sie in Abbildung 1. Lassen Sie sich nicht von der mathematischen Formel einschüchtern, sie addiert nur die Quadrate aller Differenzen. In der Regel werden Gewichtungen und Bias anfänglich auf Zufallswerte festgelegt, die oft einen hohen Verlustwert ergeben, wenn mit dem Training eines neuronalen Netzes begonnen wird.

Die Kosten- oder Verlustfunktion
Abbildung 1: Die Kosten- oder Verlustfunktion

Der Algorithmus passt dann jede Gewichtung an, um die Differenz zwischen dem berechneten Wert und dem richtigen Wert zu minimieren. Der Begriff „Backpropagation“ kommt daher, dass der Algorithmus zurückkehrt und die Gewichtungen und Bias nach der Berechnung einer Antwort anpasst. Je kleiner der Verlust für ein Netzwerk ist, desto genauer wird es. Der Lernprozess kann somit als Minimierung der Ausgabe der Verlustfunktion quantifiziert werden. Jeder Zyklus der Forward Propagation und Backpropagation-Korrektur zur Senkung des Verlusts wird als Epoche bezeichnet. Einfach ausgedrückt, geht es bei der Backpropagation darum, die besten Eingabegewichtungen und Bias zu ermitteln, um eine genauere Ausgabe zu erhalten oder „den Verlust zu minimieren“. Wenn Sie denken, dass dafür eine hohe Rechenleistung erforderlich ist, haben Sie Recht. Tatsächlich reichte die Rechenleistung bis vor kurzer Zeit nicht aus, um diesen Prozess für einen breiten Einsatz nutzbar zu machen.

Gradientenverfahren, Lernkurve und stochastisches Gradientenverfahren

Wie werden die Gewichtungen in jeder Epoche angepasst? Werden sie zufällig angepasst, oder gibt es einen Prozess? Hier beginnt die Verwirrung bei vielen Anfängern, da zahlreiche unbekannte Begriffe wie „Gradientenverfahren“ und „Lernkurve“ auftauchen. Allerdings ist es wirklich nicht so kompliziert, wenn diese Begriffe richtig erklärt werden. Die Verlustfunktion verringert die gesamte Komplexität eines neuronalen Netzes auf eine einzige Zahl, die angibt, wie weit die Antwort des neuronalen Netzes von der gewünschten Antwort entfernt ist. Wenn wir die Ausgabe des neuronalen Netzes als eine einzige Zahl betrachten, können wir seine Leistung auf einfache Weise verstehen. Das Ziel ist es, die Serie von Gewichtungen zu finden, die den niedrigsten Verlustwert oder das Minimum ergibt.

Die Darstellung in einem Diagramm (wie in Abbildung 2) zeigt, dass die Verlustfunktion eine eigene Kurve und Gradienten aufweist, die als Orientierungshilfe für die Anpassung der Gewichtungen verwendet werden können. Die Steigung der Kurve der Verlustfunktion dient als Orientierung und verweist auf den Minimalwert. Das Ziel ist es, das Minimum über die gesamte Kurve hinweg zu ermitteln, das die Eingaben darstellt, bei denen das neuronale Netz am genauesten ist.

Diagramm der Verlustfunktion mit einer einfachen Kurve
Abbildung 2: Diagramm der Verlustfunktion mit einer einfachen Kurve

In Abbildung 2 führt das Addieren von Werten zu den Gewichtungen zu einem Tiefpunkt, dann beginnt die Kurve erneut anzusteigen. Die Steigung der Kurve gibt die Richtung zu diesem tiefsten Punkt der Kurve an, der den geringsten Verlust darstellt. Wenn die Steigung negativ ist, addieren Sie Werte zu den Gewichtungen. Wenn die Steigung positiv ist, subtrahieren Sie Werte von den Gewichtungen. Der spezifische Wert, der zu den Gewichtungen addiert oder von ihnen subtrahiert wird, wird als Lernkurve bezeichnet. Die Bestimmung einer idealen Lernkurve ist ebenso eine Kunst wie eine Wissenschaft. Ist sie zu groß, kann der Algorithmus das Minimum überschreiten. Ist sie zu klein, dauert das Training zu lange. Dieser Vorgang wird als „Gradientenverfahren“ bezeichnet. Leser, die mit den Feinheiten der Mathematik besser vertraut sind, werden diesen Prozess als das sehen, was er ist: das Bestimmen der Ableitung der Verlustfunktion.

Selten ist die Kurve einer Verlustfunktion jedoch so einfach wie die in Abbildung 2 gezeigte. In der Praxis gibt es viele Höhen und Tiefen. Die Herausforderung besteht dann darin, den niedrigsten der Tiefpunkte (das globale Minimum) zu finden und sich nicht von nahe gelegenen Tiefpunkten (lokale Minima) täuschen zu lassen. Der beste Ansatz in dieser Situation besteht darin, einen Punkt entlang der Kurve zufällig auszuwählen und dann mit dem zuvor beschriebenen Gradientenverfahren fortzufahren, daher der Begriff „stochastisches Gradientenverfahren“. Eine gute Erklärung der mathematischen Konzepte für diesen Prozess finden Sie im YouTube-Video „Gradient Descent, How Neural Networks Learn | Deep Learning, Chapter 2“ unter youtu.be/IHZwWFHWWa-w.

Zum größten Teil wurde diese Ebene der neuronalen Netzwerkarchitektur durch Bibliotheken wie Keras und TensorFlow weitgehend abstrahiert. Wie bei jedem Softwareengineeringvorhaben hilft es immer, die Grundlagen zu kennen, wenn Sie sich den Herausforderungen in diesem Bereich stellen.

Umsetzung der Theorie in die Praxis

In der vorherigen Kolumne hatte ich ein neuronales Netz von Grund auf neu erstellt, um die MNIST-Ziffern zu verarbeiten. Die sich ergebende Codebasis zum Aufzeigen des Problems war großartig, um das Innenleben neuronaler Netzwerkarchitekturen zu veranschaulichen, war aber schwierig in die Praxis umzusetzen. Es gibt inzwischen so viele Frameworks und Bibliotheken, die die gleiche Aufgabe mit weniger Code ausführen.

Öffnen Sie zunächst ein neues Jupyter Notebook, geben Sie Folgendes in eine leere Zelle ein, und führen Sie den Code aus, um alle erforderlichen Bibliotheken zu importieren:

import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical
import matplotlib.pyplot as plt

Beachten Sie, dass die Ausgabe dieser Zelle besagt, dass Keras ein TensorFlow-Back-End verwendet. Da das MNIST-Beispiel für neuronale Netze so gebräuchlich ist, bindet Keras es als Teil seiner API ein und teilt die Daten sogar in ein Trainingsdataset und ein Testdataset auf. Geben Sie den folgenden Code in eine neue Zelle ein, und führen Sie ihn aus, um die Daten herunterzuladen und in die entsprechenden Variablen einzulesen:

# import the data
from keras.datasets import mnist
# read the data
(X_train, y_train), (X_test, y_test) = mnist.load_data()

Sobald die Ausgabe anzeigt, dass die Dateien heruntergeladen wurden, verwenden Sie den folgenden Code, um das Trainings- und Testdataset kurz zu untersuchen:

print(X_train.shape)
print(X_test.shape)

Die Ausgabe sollte angeben, dass das x_train-Dataset 60.000 Elemente und das x_test-Dataset 10.000 Elemente enthält. Beide Datasets bestehen aus einer 28x28-Matrix von Pixeln. Um ein bestimmtes Bild aus den MNIST-Daten anzuzeigen, verwenden Sie MatPlotLib, um ein Bild mit dem folgenden Code zu rendern:

plt.imshow(X_train[10])

Die Ausgabe sollte wie eine handschriftliche Ziffer „3“ aussehen. Um anzuzeigen, was im Testdataset enthalten ist, geben Sie den folgenden Code ein:

plt.imshow(X_test[10])

Die Ausgabe zeigt eine 0. Sie können gerne experimentieren, indem Sie die Indexnummer und das Dataset ändern, um die Bilddatasets zu untersuchen.

Umformen der Daten

Wie bei jedem KI- oder Data Science-Projekt müssen die Eingabedaten umgeformt werden, um die Anforderungen der Algorithmen zu erfüllen. Die Bilddaten müssen in einem eindimensionalen Vektor vereinfacht werden. Da jedes Bild 28x28 Pixel groß ist, ist der eindimensionale Vektor 1 x (28x28) oder 1 x 784. Geben Sie den folgenden Code in eine neue Zelle ein, und führen Sie ihn aus (beachten Sie, dass kein Ausgabetext generiert wird):

num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels).astype('float32')
X_test = X_test.reshape(X_test.shape[0], num_pixels).astype('float32')

Die Pixelwerte reichen von 0 bis 255. Um diese Werte zu verwenden, müssen Sie sie in Werte zwischen 0 und 1 normalisieren. Verwenden Sie dazu den folgenden Code:

X_train = X_train / 255
X_test = X_test / 255

Geben Sie dann den folgenden Code ein, um sich anzuschauen, wie die Daten jetzt aussehen:

X_train[0]

Die Ausgabe zeigt ein Array mit 784 Werten zwischen 0 und 1 an.

Die Aufgabe, verschiedene Bilder von handschriftlichen Ziffern zu erfassen und zu bestimmen, welche Zahl sie darstellen, wird als „Klassifizierung“ bezeichnet. Bevor Sie das Modell erstellen, müssen Sie die Zielvariablen in Kategorien aufteilen. In diesem Fall wissen Sie, dass es 10 sind, aber Sie können die to_categorical-Funktion in Keras verwenden, um diese Anzahl automatisch zu ermitteln. Geben Sie den folgenden Code ein, und führen Sie ihn aus (die Ausgabe sollte 10 anzeigen):

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]
print(num_classes)

Erstellen, Trainieren und Testen des neuronalen Netzes

Nachdem die Daten nun umgeformt und vorbereitet wurden, ist es an der Zeit, die neuronalen Netze mit Keras zu erstellen. Geben Sie den folgenden Code ein, um eine Funktion zu erstellen, die ein sequenzielles neuronales Netzwerk mit drei Schichten mit einer Eingabeschicht aus num_pixels (oder 784) Neuronen erstellt:

def classification_model():
  model = Sequential()
  model.add(Dense(num_pixels, activation='relu', input_shape=(num_pixels,)))
  model.add(Dense(100, activation='relu'))
  model.add(Dense(num_classes, activation='softmax'))
  model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
  return model

Vergleichen Sie diesen Code mit dem Code aus den Methoden für „von Grund auf neue“ Netze meiner letzten Kolumne. Sie werden möglicherweise neue Begriffe wie „relu“ oder 2softmax“ bemerken, die in den Aktivierungsfunktionen erwähnt werden. Bis jetzt habe ich nur die Sigmoid-Aktivierungsfunktion untersucht, aber es gibt mehrere Arten von Aktivierungsfunktionen. Beachten Sie zunächst, dass alle Aktivierungsfunktionen einen Eingabewert komprimieren, indem sie einen Wert zwischen 0 und 1 oder -1 und 1 ausgeben.

Da die gesamte Infrastruktur nun vorhanden ist, ist es an der Zeit, das Modell zu erstellen, zu trainieren und zu bewerten. Geben Sie den folgenden Code in eine leere Zelle ein, und führen Sie ihn aus:

model = classification_model()
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, verbose=2)
scores = model.evaluate(X_test, y_test, verbose=0)

Beachten Sie, dass der Verlustwert mit jeder Iteration sinkt, wenn das neuronale Netz ausgeführt wird. Entsprechend wird auch die Genauigkeit verbessert. Beachten Sie außerdem, wie lange die Ausführung jeder Epoche dauert. Geben Sie nach Abschluss des Vorgangs den folgenden Code ein, um die Genauigkeit und die Fehlerquoten zu erhalten:

print('Model Accuracy: {} \n Error: {}'.format(scores[1], 1 - scores[1]))

Die Ausgabe zeigt eine Genauigkeit von mehr als 98 Prozent und einen Fehler von 1,97 Prozent an.

Speichern des Modells

Nachdem das Modell nun bis zu einem hohen Grad an Genauigkeit trainiert wurde, können Sie es für die spätere Verwendung speichern, um zu vermeiden, dass Sie es erneut trainieren müssen. Glücklicherweise ist dies mit Keras einfach. Geben Sie den folgenden Code in eine neue Zelle ein, und führen Sie ihn aus:

model.save('MNIST_classification_model.h5')

Dadurch wird eine Binärdatei erstellt, die etwa 8 KB groß ist und die optimalen Werte für Gewichtungen und Bias enthält. Das Laden des Modells ist mit Keras ebenfalls einfach:

from keras.models import load_model
pretrained_model = load_model('MNIST_classification_model.h5')

Diese h5-Datei enthält das Modell und kann zusammen mit Code bereitgestellt werden, um die Eingabebilddaten umzuformen und vorzubereiten. Mit anderen Worten, der langwierige Prozess des Trainings eines Modells muss nur ein einziges Mal ausgeführt werden. Das Verweisen auf das vordefinierte Modell erfordert nicht den rechenintensiven Prozess des Trainings, und im endgültigen Produktionssystem kann das neuronale Netz schnell implementiert werden.

Zusammenfassung

Neuronale Netze können Probleme lösen, die traditionellen Algorithmen seit Jahrzehnten Schwierigkeiten bereitet haben. Wie wir gesehen haben, verbirgt ihre einfache Struktur ihre wahre Komplexität. Neuronale Netze arbeiten mithilfe von Forward Propagation für Eingaben, Gewichtungen und Bias. Es ist jedoch der umgekehrte Prozess der Backpropagation, bei dem das Netzwerk tatsächlich lernt, indem es die genauen Änderungen an Gewichtungen und Bias bestimmt, um ein genaues Ergebnis zu erzielen.

Lernen im maschinellen Sinne bedeutet, die Differenz zwischen dem tatsächlichen Ergebnis und dem richtigen Ergebnis zu minimieren. Dieser Prozess ist mühsam und rechenintensiv, wie die Zeit zeigt, die erforderlich ist, um eine Epoche zu durchlaufen. Glücklicherweise muss dieses Training nur ein Mal durchgeführt werden und nicht jedes Mal, wenn das Modell benötigt wird. Außerdem habe ich Keras untersucht, um dieses neuronale Netz zu implementieren. Auch wenn es möglich ist, den Code von Grund auf neu zu schreiben, der zum Erstellen neuronaler Netze benötigt wird, ist es viel einfacher, vorhandene Bibliotheken wie Keras zu verwenden, die sich um die kleinsten Details für Sie kümmern.


Frank La Vigne arbeitet bei Microsoft als AI Technology Solutions-Experte und unterstützt Unternehmen dabei, mehr zu erreichen, indem mit Analysen und KI Daten optimal genutzt werden. Er ist auch Co-Host des DataDriven-Podcasts. Er bloggt regelmäßig auf FranksWorld.com, und Sie können ihm auf seinem YouTube-Kanal „Frank's World TV“ (FranksWorld.TV) folgen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Andy Leonard