Neuronale Netze werden erfolgreich auf so ziemlich jeden Datentyp angewandt: Bilder, Audio, Texte, Videos, Graphen usw. Nur wenn es um Tabellendaten geht, sind baumbasierte Ensembles wie Random Forests und Gradient Boosted Trees immer noch sehr viel verbreiteter. Wenn man diese erfolgreichen Klassiker durch neuronale Netze ersetzen will, dürfte Ensemble Learning immer noch eine Schlüsselidee sein. Dieser Blogbeitrag erklärt, warum das so ist. Dazu gibt’s ein Notebook mit den praktischen Details.
Tabellendaten: Gradient Boosted Trees ist der Standard
Im Jahr 2017 erfanden Günter Klambauer und seine Koautoren die selbstnormalisierenden neuronalen Netze mit dem erklärten Ziel, neuronale Netze fit zu machen für Tabellendaten. Zu dem Zweck verbesserten sie Dense-Feed-forward-Netze, die Standardarchitektur für Tabellendaten, indem sie eine neue Aktivierungsfunktion benutzten. Mit dieser Modifikation können sehr viel tiefere Architekturen trainiert werden. Die vorherige Grenze einer Tiefe von ungefähr vier Schichten war damit überwunden. Obwohl selbstnormalisierende Netze gut funktionieren und Resultate erzielen, die mit Gradient Boosting und Random Forest vergleichbar sind, konnten sie doch die Dominanz klassischer Ensemblemethoden bis jetzt nicht brechen. Ein Grund dafür könnte sein, dass selbstnormalisierende neuronale Netze keine reiche, vielseitige Theorie dafür liefern, wie eine neuronale Architektur für eine bestimmte Anwendung aussehen sollte. Sie erlauben uns lediglich, deutlich tiefer zu gehen als jemals zuvor (was natürlich bereits ein großer Fortschritt ist).
Ein Schatz aus dem Winter der künstlichen Intelligenz
Letztes Jahr habe ich einen Konferenzvortrag über selbstnormalisierende neuronale Netze gehalten. Ich versuchte, die Idee zu popularisieren, dass jetzt genau der richtige Zeitpunkt ist, um sich intensiv mit neuronalen Netzen zu beschäftigen, wenn man mit Tabellendaten arbeitet. Ich bin immer noch überzeugt, dass das richtig ist. Aber nur einen Tag nach meinem Vortrag hatte ich die Ehre und das Vergnügen, einen kleinen, intensiven Workshop mit Hans Georg Zimmermann zu besuchen. Während dieses Workshops wurde mir klar, dass ich neuronale Netze auf eine ziemlich oberflächliche Weise betrachtet hatte, ohne die mathematische Tiefe dessen, was ich im Workshop gelernt habe. Ich habe außerdem gelernt, dass es sehr viel ältere neuronale Methoden gibt, um mit Tabellendaten zu arbeiten, Methoden, deren Entwicklung schon während des Winters der künstlichen Intelligenz begann. Und in diesem Fall sind Oldies auch Goldies.
Ich habe den Eindruck, dass diese Methoden noch nicht die Aufmerksamkeit gewonnen haben, die sie verdienen. Es ist zweifelhaft, ob ich in der Lage sein werde, das mit einem Blogbeitrag für eine voraussichtlich ziemlich beschränkte Leserschaft zu ändern, aber ich werde mein Bestes geben. Ich werde versuchen, ein paar erste Schritte zu beschreiben; insgesamt ist es deutlich mehr Stoff, als in einen Blogbeitrag passt. Alle guten Ideen, die hier vorgestellt werden, habe ich von Hans Georg Zimmermann; alle Fehler und Verdrehungen in diesem Text sind meine eigenen. Insbesondere der Gebrauch von Embeddings weiter unten ist meine eigene Variante dieser Methoden.
Überparametrisierung – Bug oder Feature?
Es ist immer noch nicht völlig verstanden, warum neuronale Netze so gut funktionieren. Es wird allerdings immer klarer, dass die Überparametrisierung, die sie im Regelfall auszeichnet, dazu beiträgt. Um zu verstehen, warum das so ist, darf man nicht durch die Brille der klassischen Statistik schauen, die uns erklären würde, dass typische neuronale Netze viel zu viele Parameter haben. Das ist tatsächlich ein Problem, aber darum kümmern wir uns später. Im Moment schauen wir uns den Trainingsprozess aus der Perspektive der Optimierungstheorie an. Aus diesem Blickwinkel sieht man, dass die scheinbar überflüssigen Parameter uns (oder vielmehr den Trainingsprozess…) davor bewahren, in einem lokalen Minimum stecken zu bleiben. Ansonsten hätten wir auch ein Problem: In den Optimierungsproblemen, die wir beim Training neuronaler Netze lösen, gilt keine der Konvergenzgarantien der klassischen Optimierungstheorie. Das Steckenbleiben in einem lokalen Minimum wäre also der Normalfall.
Wie gehen wir jetzt mit den problematischen Aspekten der Überparametrisierung um? Sie führt dazu, dass es nicht eine einzige optimale Gewichtskonfiguration für unser neuronales Netz gibt, sondern sehr viele. Wir können nicht wissen, welche die „richtige“ ist. Ensemble Learning bietet einen sehr pragmatischen Ausweg aus diesem Dilemma an: Wir benutzen mehrere Kopien unseres neuronalen Netzes, jedes davon ausgestattet mit einer der optimalen Gewichtskonfigurationen. Anschließend mitteln wir einfach über die Ergebnisse, die sie liefern. Das reduziert die extreme Varianz, mit der wir es aufgrund der Überparametrisierung zu tun haben. Der ganze Vorgang, einschließlich der Mittelwertbildung, kann in einem einzigen neuronalen Netz abgebildet werden: Die identischen Kopien unseres neuronalen Netzes (des schwachen Lerners) werden jeweils mit der Eingangsschicht verbunden und mit einem einzelnen Mittelwertbildungsneuron als Output. Beim Training dieses Gesamtnetzes sorgen die unterschiedlichen Zufallsinitialisierungen der schwachen Lerner dafür, dass jeder von ihnen eine andere optimale Gewichtskonfiguration lernt.
Ein komplettes Rezept für das Sizing der neuronalen Netzwerkarchitektur
Wenn man diesen Argumenten folgt, ist eine Netzwerkarchitektur, die über identische Kopien kleinerer neuronaler Netze (die schwachen Lerner) mittelt, nicht so verrückt, wie sie auf den ersten Blick aussehen mag. Ich nenne diese Art von Architektur gern ein neuronales Averaging Ensemble. Wir haben bis jetzt noch nicht über die schwachen Lerner selbst gesprochen, die Bausteine dieser Architekturen. Was für ein Netzwerk sollten wir als schwachen Lerner benutzen? Egal wofür wir uns entscheiden, wir werden auf jeden Fall diverse Kopien davon haben. Möglicherweise ist es also eine gute Idee, ein kleines, simples Netz auszuwählen. Die einfachste Möglichkeit ist im Universal Approximation Theorem beschrieben: Ein einziger Hidden Layer reicht schon. Natürlich sind oft tiefere Netze in der Praxis überlegen. Aber für den Moment verfolgen wir mal diese einfachste Möglichkeit. Es bleibt dann nur eine einzige Frage zu klären, um unseren schwachen Lerner komplett zu spezifizieren: Wie viele Neuronen brauchen wir?
Die Antwort ist erstaunlich einfach: Wir trainieren einen einzelnen schwachen Lerner auf den Trainingsdaten (und um der Geschwindigkeit willen müssen wir ihn nicht perfekt trainieren, ein paar Epochen reichen aus). Wir fangen mit einer niedrigen Anzahl von Neuronen im Hidden Layer an, zum Beispiel 10. Nachdem wir das Netz trainiert haben, prüfen wir, ob irgendeines der Neuronen im Hidden Layer überflüssig ist, d. h., ob sein Output mit dem eines anderen Neurons hochkorreliert ist. Wenn nicht, probieren wir eine größere Anzahl Neuronen (20 zum Beispiel), bis wir zwei hochkorrelierte Neuronen haben. Auf diese Weise stellen wir sicher, dass der schwache Lerner überparametrisiert ist, aber nicht zu extrem. In der Praxis ist es eine gute Idee, dieses Vorgehen mit verschiedenen Startwerten für den Zufallszahlengenerator zu wiederholen, um eine robuste Lösung zu finden. Im Zweifelsfall wählen wir die Anzahl Neuronen eher zu hoch als zu niedrig.
Jetzt fehlt nur noch eins zu einem kompletten Sizing: die Anzahl Kopien des schwachen Lerners. Menschen mit einem Hang zur Kontrolle können die Anzahl Kopien mit einer ähnlichen Herangehensweise bestimmen, wie wir sie schon für die Größe des schwachen Lerners verwendet haben: Ein Ensemble mit einer gewissen Anzahl Kopien trainieren, dann schrittweise mehr Kopien probieren, bis zwei davon hoch korrelierte Ergebnisse erzeugen. Ich würde mich freuen, wenn das jemand ausprobieren mag, und ich bin neugierig auf die Ergebnisse.
Allerdings hat die Anzahl schwacher Lerner in einem Ensemble drei Eigenschaften, die einen entspannteren Ansatz sinnvoll erscheinen lassen:
- Mehr schwache Lerner ergeben ein besseres Modell (oder zumindest kein schlechteres).
- Die Modellqualität in Abhängigkeit von der Anzahl schwacher Lerner hat ein gutmütiges asymptotisches Verhalten, in dem zusätzliche schwache Lerner desto weniger zusätzlichen Nutzen generieren, je mehr schwache Lerner man bereits hat.
- Der Parameter ist ziemlich robust: Die „falsche“ Anzahl schwacher Lerner zu wählen, ruiniert das Modell nicht gleich.
Wir nehmen daher eine Abkürzung: Wir benutzen eine niedrige Anzahl Kopien (ungefähr 10) für erste Experimente und eine höhere (50 bis 100), wenn wir das Modell finalisieren. Das funktioniert im Regelfall gut. Wenn jemand auf Ausnahmen stößt, bin ich sehr interessiert, davon zu hören.
Kategoriale Komplikationen
Die oben beschriebene Modellierung funktioniert gut, solange wir uns auf Regressionsprobleme mit rein numerischen Eingangsdaten beschränken. Für kategoriale Eingangsvariablen und bei Klassifikationsproblemen (also bei kategorialem Output) müssen wir das Netz modifizieren.
Für Klassifikationsprobleme brauchen wir lediglich die übliche Softmax-Schicht (als letzte Schicht in jedem der schwachen Lerner). Bei binärer Klassifikation können wir an Stelle dessen auch eine Sigmoid-Schicht mit einem einzelnen Neuron verwenden, um die Wahrscheinlichkeit lediglich einer Klasse abzubilden. Das ist der Ansatz, den ich im Notebook verfolgt habe.
Kategoriale Eingangsdaten könnten wir mit Hilfe von Dummyvariablen abbilden. Dieser Ansatz hat bekannte Nachteile, insbesondere treibt er die Dimension der Eingangsdaten in die Höhe. Außerdem führt er oft zu gigantischen schwachen Lernern, wenn man ihn mit der oben beschriebenen Methode des Sizings verwendet. Glücklicherweise gibt es für die Probleme mit Dummyvariablen auch eine ebenso bekannte Lösung, nämlich Embeddings.
Für jede kategoriale Eingangsvariable fügen wir unserem schwachen Lerner eine Embeddingschicht direkt nach dem Input Layer hinzu. Damit das funktioniert, müssen die kategorialen Daten so aufbereitet sein, dass jede Kategorie durch eine ganze Zahl von 0 bis Anzahl der Kategorien minus 1 dargestellt wird. Anstatt jeden schwachen Lerner mit seinen eigenen Embeddings auszustatten, können wir sie auch eine gemeinsame Embeddingschicht teilen lassen. Das Notebook ermöglicht Experimente mit beiden Ansätzen. Mein Eindruck ist, dass eine geteilte Embeddingschicht normalerweise gut funktioniert. Es ist also nicht nötig, das Modell dadurch aufzublasen, dass jeder schwache Lerner seine eigenen Embeddings bekommt.
Embeddings sind ein faszinierender Kniff, aber sie haben einen Preis: zusätzliche Hyperparameter. Für jede kategoriale Variable müssen wir uns entscheiden, wie viele Dimensionen der Output haben soll. Es gibt dafür mehrere Ansätze: Man kann diese Hyperparameter (gemeinsam mit allen anderen) in einer randomisierten Suche bestimmen, man kann die Ausgabedimension von der Anzahl der Kategorien ableiten, oder man kann einfach auf Basis der eigenen Intuition eine Zahl raten. Ich habe mich für die letzte Möglichkeit entschieden. Die Bestimmung der Ausgabedimension ist ein interessantes Detail, und ich freue mich, wenn jemand Lust hat, ihre oder seine Erfahrungen zu teilen.
Ähnlich gut wie Gradient Boosted Trees
Wenn wir die Genauigkeit der beschriebenen Modelle mit Gradient Boosted Trees vergleichen wollen und uns dafür nur einen Absatz lang Zeit nehmen, wird das Ergebnis naturgemäß angreifbar sein. Wir versuchen es trotzdem mal. Um zu zeigen, dass diese Methoden auch dort gut funktionieren, wo Gradient Boosting bekanntermaßen stark ist, benutzen wir einen sehr kleinen Datensatz, der außerdem viele kategoriale Variablen enthält. Letzteres ist wichtig, weil baumbasierte Methoden meistens glänzen, wenn sie auf kategoriale Daten angewandt werden. Glücklicherweise hat Szilard Pafka ziemlich viel Arbeit im Bereich Gradient Boosting Benchmarks geleistet. Diese Arbeit basiert außerdem auf einem Datensatz mit vielen kategorialen Variablen. Szilard hat verschiedene Implementationen von Gradient Boosting getestet, mit verschieden großen Versionen eines einheitlichen Datensatzes. Die kleinste Variante hat nur 10.000 Zeilen. Diese werden wir hier benutzen, nur um den Mythos zu bekämpfen, dass Deep Learning immer riesige Datenmengen braucht. Der beste AUC auf dem Testset in Szilards Benchmark für diesen Datensatz ist 70,3. Erreicht wird er von Xgboost. Szilard hat keine umfangreiche Hyperparametersuche durchgeführt, sondern eine kleinere, eher explorative. Da wir unsere Hyperparameter sogar noch weniger getunt haben, wird der Vergleich mit Szilards Ergebnissen unserem Modell keinen unfairen Vorteil verschaffen. Im Notebook kann man nachvollziehen, dass wir mit dem neuronalen Ansatz einen AUC von 69,5 erzielen, wir sind also grob auf einem vergleichbaren Performancelevel. Diese optimale Performance wird bereits nach nur 4 (!) Trainingsepochen erreicht, so dass das Training schneller ist als mit Gradient Boosting.
Probiert es selbst!
Ich hoffe, dass dieses Beispiel als Appetitanreger taugt. Natürlich ist es nur ein einzelnes Beispiel, mit einem einzigen Datensatz. Wir alle wissen, dass das nicht viel beweist. Darum der Aufruf: Experimentiert mit euren eigenen Daten und lasst mich wissen, wie es läuft!
Wo diese Ideen herkommen, ist noch sehr viel mehr – wir haben nur an der Oberfläche einer tiefen Theorie gekratzt. Diese Techniken können in verschiedene Richtungen ausgebaut werden. Man kann sogar die Analogie zu klassischen Ensembles noch weiter treiben in Richtung von Boostingmodellen, die noch bessere Resultate erzielen. Es gibt auch spezialisierte Techniken für die Modelltransparenz. Ich werde möglicherweise einige dieser Techniken in zukünftigen Blogbeiträgen behandeln. In der Zwischenzeit bin ich gespannt darauf, von euren Erfahrungen zu hören!