Deep-Learning-Frameworks wie TensorFlow oder PyTorch verstecken eine komplexe Maschinerie, die dafür sorgt, dass das Training eines neuronalen Netzes funktioniert: Insbesondere läuft im Hintergrund gar nicht ein Python-Programm, welches das Netz beschreibt, sondern ein Graph. Warum ist das eigentlich so?
Dieser Artikel wirft einen Blick in den Maschinenraum von Deep Learning und ist für alle relevant, die Interesse an den Grundlagen haben. Wir zeigen, dass Deep Learning auch anders (und deutlich einfacher) geht, nämlich mit etwas höherer (aber nicht komplizierter) Mathematik und funktionaler Programmierung.
Ausgangspunkt für diesen Artikel ist die Entwicklung eines Produkts zur Erkennung von Anomalien in den Sensordaten industrieller Produktionsanlagen. Solche Anomalien können Probleme ankündigen, die anderweitig länger unerkannt bleiben würden. Ein sehr einfaches Beispiel ist ein Kühlhaus für Lebensmittel: Steigt die Temperatur, obwohl Strom in das Kühlaggregat fließt, stimmt etwas nicht – rechtzeitig erkannt können die Lebensmittel aber noch gerettet werden.
Anders als im Beispiel ist in vielen Anlagen der Zusammenhang zwischen den Sensorwerten zu komplex, als dass vorher genau bekannt wäre, wie eine Anomalie eigentlich aussieht. Hier kommen neuronale Netze ins Spiel: Sie werden auf „normalen“ Sensordaten trainiert und melden so, wenn es Abweichungen beim Verhältnis der Sensordaten untereinander oder im zeitlichen Verlauf gibt.
Als Kundenvorgabe müssen die neuronalen Netze dieser Applikation auf Maschinen laufen, deren Rechenleistung und Speicher beschränkt sind und für die gewichtige Frameworks wie TensorFlow zu groß und zu speicherhungrig sind.
In Listing 1 sehen Sie ein einfaches neuronales Netz in Python, einen sogenannten Autoencoder, mit dem Framework TensorFlow implementiert. Wer sonst in Python Analytikcode schreibt, wundert sich vielleicht, dass TensorFlow eigene Operationen für das Rechnen mit Matrizen mitbringt, anstatt etablierte Libraries wie Pandas zu verwenden. Das liegt daran, dass tf.matmul überhaupt nicht rechnet, sondern stattdessen einen Knoten in einem Graphen konstruiert, der die Berechnungsfunktion des neuronalen Netzes beschreibt. Dieser Graph wird dann in einem separaten Schritt in effizienten Code übersetzt – zum Beispiel auf einer GPU.
Listing 1
def __call__(self, xs):
inputs = tf.convert_to_tensor([xs], dtype=tf.float32)
out = tf.matmul(self.weights[0], inputs, transpose_b=True) + self.biases[0]
out = tf.tanh(out)
out = tf.matmul(self.weights[1], out) + self.biases[1]
out = tf.nn.relu(out)
out = tf.matmul(self.weights[2], out) + self.biases[2]
out = tf.tanh(out)
return tf.matmul(self.weights[3], out) + self.biases[3]
Aber der Graph hat noch einen weiteren Grund: Für das Training des neuronalen Netzes wird gar nicht die Funktion benötigt, die das neuronale Netz beschreibt, sondern deren Ableitung. Diese wird von einem Algorithmus berechnet, der ebenfalls den Graphen benötigt. In jedem Fall ist diese Graphenmaschinerie aufwendig; insbesondere gehört dazu ein komplettes Python-Frontend, welches das Python-Programm in ihren Graphen übersetzt (früher mussten Deep-Learning-Entwickler:innen das sogar von Hand machen).
Neuronale Netze (NNs) sind Funktionen mit sehr vielen Parametern. Damit ein NN so wundersame Dinge vollbringen kann, wie natürliche Sprache zu generieren, passende Musik vorzuschlagen oder Anomalien in Produktionsprozessen zu finden, müssen diese Parameter entsprechend eingestellt werden. In der Regel wird dazu ein NN auf einem gegebenen Trainingsdatensatz ausgewertet und auf den Ausgaben eine Zahl berechnet, die ausdrückt, wie gut diese sind. Die Ableitung der Komposition aus NN und Bewertungsfunktion nach den Parametern des NNs gibt Auskunft darüber, wie man die Parameter anpassen kann, damit die Ausgaben besser werden.
Das kann man sich so vorstellen, als würde man in dichtem Gestrüpp auf einer Hügellandschaft stehen und wollte einen Weg ins Tal finden. Die Koordinaten, an denen man sich befindet, stehen hierbei für die aktuellen Parameterwerte und die Höhe der Hügellandschaft für die Ausgabe der Bewertungsfunktion (hohe Werte stehen für eine große Abweichung vom gewünschten Ergebnis). Man sieht nur die lokale Umgebung und weiß nicht, in welcher Richtung das Tal liegt. Die Ableitung zeigt aber in Richtung des steilsten Anstiegs – bewegt man sich ein kleines Stück in genau entgegengesetzter Richtung und wiederholt dieses Vorgehen von dort aus, stehen die Chancen gut, dass man irgendwann im Tal ankommt. Die Hauptaufgabe von Deep-Learning-Frameworks (DLFs) in diesem iterativen Prozess (der sich übrigens Gradient Descent nennt), ist es nun, immer und immer wieder Ableitungen...