Overview and Setting

Python and R have become the most important languages in analytics and data science. Usually a data scientist can at least navigate one language with relative ease and at STATWORX we luckily have both expertises available. While, with enough will and effort, any coding project can be completed in either language, perhaps they differ in some perfomance aspects. To assess this question, we chose a task no data scientist can escape – the inevitable data preparation (80% data preparation, 20% analysis as the proverb goes).

While R has a native object type for tabular data – it was made by statisticians after all – Python lacks this feature in the standard library. Relational data can be crunched with the pandas library, which has become the de-facto standard package for tabular data manipulation in Python. It is based on the even more popular framework of numPy, a linear algebra and matrix operation framework. Even though R uses the so called dataframe by default, there has been put some effort into upgrading and enhancing the built-in type. One of the most famous and widely used packages in this regard is data.table. The class data.table excels at combining operations in a performant way, since most of its codebase relies on C functions (for other ways to speed up R code with C, check out out the blog post of our colleague Andre Bleier about Rcpp – German only!).

We will compare the performance of pandas with that of data.table in a structured and systematic experiment split up to four different cases:

  • Data retrieval with a select-like operation
  • Data filtering with a conditional select operation
  • Data sort operations
  • Data aggregation operations

For each of these tasks we will use the same simulated dataset which contains the following data types:

  • Integer, in three different distributions (Normal, zero inflated Poisson, Uniform)
  • Double, in the same distributions
  • String, random lengths within parameters
  • Bool, Bernoulli distributed

The computations were performed on a machine with an Intel i7 2.2GHz with 4 physical cores, 16GB RAM and a SSD harddrive. Software Versions were OS X 10.13.3, Python 3.6.4 and R 3.4.2. The respective library versions used were 0.22 for pandas and 1.10.4-3 for data.table.

Select

In the first step, we checked the speed of accessing several columns in a dataset. We have timed this operation for a single column as well as a group of three columns. The setup is further divided by the position of the columns. For both, the single and multiple column scenarios, we used columns from the beginning, middle, end and for the multiple column case additionally a random location in the dataset.

comparison-select

The left side shows the results of the single table queries, while the right grid presents the three column case. The measured value is the median execution time of pandas relative to the median execution time of data.table. The results are mixed. In the majority of the tested scenarios pandas selection takes roughly 1.5 to 2 times longer than the equivalent data.table query in R. There are also a lot of nearly equal execution speeds across all settings, especially in the scenarios with 50k/100k observations. In absolute terms execution speed for both data.table and pandas was nearly instantanous. The maximum median value across all queries lies around 2ms for both packages. See the Github project repo of this simulation for additional plots and the source code.

Filter

filter-experiment-setup

We have tested seven different scenarios using filter operations. Scenario 1 through 4 perform a filter operation on a single column of varying data type (with the aforementioned order – int, double, string, bool). Scenarios 5 – 7 are combinations of the first four with scenario 5 consisting of 1 and 2 and scenario 6 of 1,2 and 3. Scenario 7 includes all single scenarios together in a combined filter query.

comparison-filter

For the given settings pandas yields lower medians for most of the executed scenarios. These relative timings range from nearly identical median timings down to a tenth of the data.table results. Scenario 3 is an exception to this observation. The biggest computation gap between pandas and data.table in absolute terms is the median timing scenario 2 with 100k rows and 1200 columns. The pandas library completed the query in roughly 1.4 seconds, whereas data.table took around 12 seconds. It is unclear where this spike in execution time stems from, as this run seems to be a clear outlier. Other execution times range from 80-600 ms (Again, if you are interested in the detailed numbers check the project repo).

Conclusion, Critique and Outlook

We have seen roughly equal performance for select operations with data.table performing slightly faster in relative terms with very low absolute execution speeds. Pandas, however, copes better with filter operations, since the computation time is overall lower as opposed to the R counterpart.

This experiment does not claim absolute authority but wants to provide a glimpse at general performance of tabular data processing in both languages. There are of course modifications to this experimental setting which would improve the general design. The most important point would possibly be to alternate between different queries per scenario instead of running the same job 100 times all over. If you have any ideas to improve this study, please share your thoughts in an email.

We have learned some interesting facts – both languages, R and Python possess competitive tabular data libraries, which have coped fairly even amongst our two tests. Another observation we were able to make is the reduced query time for more complex filter operations in both packages. This indicates that there seems to be some parallel search in place across all filter conditions which reduces the search-space for additional conditions.

Stay tuned for the second part of this series, where we will be examining the more computational intense group-by and arrange operations.

References

  • Dowle, Matt, et al. data. table: Extension of data. frame. (2014).
  • McKinney, Wes, Data Structures for Statistical Computing in Python, Proceedings of the 9th Python in Science Conference, 51-56. (2010).

„Bilder lernen“ mit Neuronalen Netzwerken

Convolutional Neural Networks (CNN) sind ein beliebter Architekturtyp neuronaler Netzwerke, die hauptsächlich zur Klassifikation von Bildern und Videos eingesetzt werden. Der Aufbau von Convolutional Networks unterscheidet sich deutlich von dem des Multilayer Perceptron (MLP), das bereits in vorherigen Posts zur Einführung und Programmierung neuronaler Netze besprochen wurde.

Convolutional und Pooling Layers

CNNs verwenden eine spezielle Architektur, die sogenannten Convolutional und Pooling Layers. Der Zweck dieser Schicht ist die Betrachtung eines Inputs aus verschiedenen Perspektiven, bei dem die räumliche Anordnung der Features von Bedeutung ist. Jedes Neuron im Convolutional Layer überprüft einen bestimmten Bereich des Input Feldes mithilfe eines sogenannten Filters. Einen Filter untersucht das Bild auf eine bestimmte Eigenschaft, wie z.B. Farbzusammensetzung oder Kanten und ist prinzipiell nichts Anderes als eine eigens für diese Aufgabe trainierte und zuständige Schicht.

Animation eines Filters

Das Ergebnis eines Filters ist der gewichtete Input für einen Bereich und wird im Convolutional Layer gespeichert. Die Anzahl der Convolutional Layers ist gleich die Anzahl der verwendeten Filter, da jeweils das gesamte Input Bild von jedem Filter geprüft wird. Diese Information wird zwischen den einzelnen Convolutional Layern mit sogenannten Pooling Layern komprimiert. Die Pooling Layer laufen die durch die Filter erstellte Feature Map ab und komprimieren diese. Komprimieren bedeutet in diesem Fall die Reduktion der Pixelanzahl nach einer gegebenen Logik. Diese kann beispielsweise eine Summe oder der Maximalwert eines kleinen Bereichs der Feature Map sein. Dies ähnelt den Convolutional Layern, mit dem Unterschied, dass die Pooling Schichten nicht trainiert werden und ihre Logik somit bestehen bleibt. Danach können noch weitere Convolutional Layer und/oder Pooling Schichten folgen. Anschließend werden die abstrahierten Features in ein voll vernetztes MLP übergeben (siehe Abb). Eine höhere Anzahl von Schichten führt meist zu einem höheren Grad der Abstraktion des Ursprungsbildes. In diesem MLP wird z.B. bei der Bildklassifikation eine abschließende Multiclass Klassifikation durchgeführt und die wahrscheinlichste Klasse zurückgegeben. Multiclass bezeichnet dabei ein Problem bei dem die Zielvariable zu einer von mehreren Klassen gehören kann. Das Gegenstück hierzu ist die binäre Klassifikation, bei der die Zielvariable nur zwei Zustände annehmen kann.

Schema von Convolutional Layern

Benchmarking und Evaluation

Bildklassifikation ist einer der Benchmark Cases für die Evaluation aktueller Deep Learning Forschung. Bekannte Datensätze die hierfür verwendet werden sind bspw. MNIST oder CIFAR10 bzw. CIFAR100. Ersterer enthält die Bilder handgeschriebener Ziffern auf Postumschlägen des US Postal Service und ist somit ein Multi-Class Klassifikationsproblem mit 10 Ausprägungen (Ziffern 0-9). Die CIFAR Datensätze beinhalten Bilder von Tieren und Fahrzeugen –in der Version mit 10 Ausprägungen– oder auch von Pflanzen, Menschen oder sogar Elektro-Geräten in dem Datensatz mit 100 Klassen. 10 und 100 stehen dabei für die Anzahl an unterschiedlichen Klassen, die durch Bilder im Datensatz repräsentiert sind. Eine höhere Anzahl an Klassen, stellt in der Regel ein schwierigeres Problem für den Algorithmus dar. Aktuelle state-of-the-art Modelle zur Bildklassifikation erreichen eine Genauigkeit von bis zu 99.8% für MNIST, 96.5% für CIFAR-10 bzw. 75.7% für den CIFAR-100 Datensatz2.

Natürlich bieten die Deep Learning Frameworks Tensorflow und Keras eine Reihe von vorkonfigurierten Bausteinen zur Erstellung von Convolutional Neural Networks. Im kommenden Blogbeitrag veranschaulichen wir die Erstellung eines CNN, mit dem wir versuchen werden, die Kategorien des CIFAR-10 Datensatzes zu erlernen.

Referenzen

  1. Bildnachweis CNN: Beitrag auf Statsexchange
  2. Sammlung von CNN-Benchmark Ergebnissen

Wie bereits im ersten Teil unserer Einführungsreihe zu Deep Learning erwähnt, sind neuronale Netze und Deep Learning aktuell ein aktiver Bereich der Machine Learning Forschung. Während die zugrundeliegenden Idee und Konzepte bereits mehrere Jahrzehnte alt sind, ist die Komplexität der Modelle und Architekturen in den letzten Jahren stetig angewachsen. In diesem Blogbeitrag gehen wir der Frage nach, ob durch die Hinzunahme weiterer Hidden Layer im Kontext von Feedforward Netzen, die Modellgüte zwangsläufig verbessert wird oder ob die erhöhte Komplexität des Modells möglicherweise nicht immer von Vorteil ist. Das Praxisbeispiel der S&P 500 Prognose entspricht dem bereits aus Teil 2 der Einführungsreihe bekannten Setting.

Deep Learning – die Bedeutung der Anzahl der Schichten und Neuronen

Das Team um Geoffrey Hinton zeigte 2006 erstmals, dass das Training eines mehrschichtigen neuronalen Netzes möglich ist. Seitdem ist die sogenannte Tiefe eines neuronalen Netzes, d.h. die Anzahl der verwendeten Schichten, ein möglicher Hyperparameter des Modells. Dies bedeutet, dass während der Entwicklung des neuronalen Netzes die prädiktive Güte des Modells auch über unterschiedlich tiefe Netzarchitekturen hin getestet werden sollte. Aus theoretischer Perspektive führt eine Erhöhung der Schichtenzahl zu einer besseren bzw. komplexeren Abstraktionsfähigkeit des Modells, da die Anzahl der berechneten Interaktionen zwischen den Inputs ansteigt. Die strikte Erhöhung der Anzahl von Neuronenschichten kann jedoch auch zu Problemen führen. Einerseits riskiert man mit tiefen Netzarchitekturen ein schnelles Overfitting auf die Trainingsdaten. Dies bedeutet, dass das Modell durch seine erhöhte Kapazität zu schnell spezifische Eigenschaften der Trainingsdaten lernt, die keine Relevanz für eine Generalisierung auf künftige Testdaten haben. Weiterhin führt eine gesteigerte Modellkomplexität auch zu einem komplexeren Szenario im Rahmen der Parameterschätzung des Modells. Alle neuronalen Netze und Deep Learning Modelle werden mittels numerischen Optimierungsmethoden trainiert. Neue Neuronenschichten bedingen neue zu schätzende Parameter, die das Optimierungsproblem herausfordernder gestalten. Somit kann in sehr komplexen Modellarchitekturen möglicherweise keine ausreichend gute Lösung des zugrunde liegenden Optimierungsproblems gefunden werden. Dies wiederum wirkt sich negativ auf die Qualität des Modells aus.

Die Anzahl der Schichten im neuronalen Netz ist nur einer von vielen Hyperparametern, die bei der Gestaltung der Modellarchitektur berücksichtigt werden müssen. Neben der Anzahl der Schichten ist ein weiterer bedeutsamer Aspekt die Anzahl der Neuronen pro Schicht. Trotz intensiver Bemühungen und Forschung in diesem Bereich ist es bis dato nicht gelungen ein allgemeingültiges Konzept bezüglich der Architektur Neuronaler Netze zu finden. Jeff Heaton, ein bekannter Autor mehrerer Einstiegswerke zum Thema Deep Learning gibt in seinen Büchern(1) die folgenden Faustregeln zur Hand:

  • Die Anzahl der Neuronen sollte zwischen der Größe der Input und Output Schicht liegen
  • Die Anzahl der Neuronen in der ersten Schicht sollte etwa 2/3 der Größe der Input Schicht betragen
  • Die Anzahl der Neuronen sollte die doppelte Größe der Input Schicht nicht überschreiten

Diese Ratschläge sind für Deep Learning Einsteiger hilfreich, besitzen jedoch kaum einen Anspruch auf Allgemeingültigkeit. Die Architektur des Netzes muss i.d.R. von Problem zu Problem neu gestaltet werden.

Empirisches Beispiel

Die unten stehenden Abbildung zeigt die Prognosen zweier neuronaler Netze (orange, blau) sowie der Originalwerte (grau). Das blaue Modell verfügt über eine Neuronenschicht, das orangene Modell über vier Schichten. Beide Modelle wurden für 10 Epochen trainiert. Es lässt sich leicht erkennen, dass durch das Hinzufügen weiterer Schichten ein Zuwachs der Modellgüte beobachtet werden kann.

SP500 comparison

Aufgrund dieses Beispiels sollte jedoch nicht darauf geschlossen werden, dass das Hinzufügen weiterer Schichten immer eine Verbesserung bedingt. Neben dem bereits erwähnten Overfitting-Problem ist es häufig schlichtweg nicht nötig für einfache Probleme ein tief gestaffeltes Netz zu entwerfen. So lässt sich bspw. mathematisch zeigen, dass ein neuronales Netz mit nur einer Schicht und einer endlichen Anzahl an Neuronen jede kontinuierliche Funktion – unter milden Annahmen an die verwendete Aktivierungsfunktion der Neuronen – approximieren kann. Dies nennt man universal approximation theorem.

Im zweiten Experiment untersuchen wir das Zusammenspiel aus Netztiefe und Anzahl der Neuronen. In der unten stehenden Abbildung wurden verschiedene Neuronenkonfigurationen der einzelnen Schichten zusammen mit einer variierenden Netztiefe trainiert. Die Anzahl der Neuronen in der ersten Schicht ist dabei immer abhängig von der Größe der Input Schicht – d.h. der Anzahl der verwendeten Inputs. So weist die Konfiguration „Hälfte des Inputs“ für die hier verwendeten 500 Input Faktoren (alle Titel des S&P 500) bspw. die folgenden Neuronenzahl pro Schicht auf – 250, 125, 63, 32, 16. Jede weitere Schicht halbiert in diesem Experiment die Anzahl der Neuronen der vorhergehenden Schicht, um die extrahierten Informationen immer weiter zu verdichten.

SP500 full comparison

Der Effekt eines tieferen Netzes ist also nicht immer positiv. Besonders deutlich wird dies in der nächsten Grafik. Hier ist der durchschnittliche Vorhersagefehler in Abhängigkeit der gleichen Neuronenkonfigurationen und variierender Netztiefe – von einer bis zu fünf Schichten – abgetragen. Es ist dabei kein generell niedrigerer Fehler bei tieferen Netzen erkennbar.

SP500 RMSE

Ergebnis und Ausblick

Wie lassen sich die Ergebnisse dieses Experiments zusammenfassen? Der wichtigste Punkt ist, dass es selbst für einfache neuronale Netze kein „Standardrezept“ gibt. Im Machine Learning Kontext ist dies auch als „no free lunch theorem“ bekannt. Weiterhin kann festgehalten werden, dass mehr Schichten nicht zwangsläufig besser sind. Selbst bei einfachen Architekturen sollte man die Anzahl der Schichten sowie die Neuronenkonfiguration als variablen Hyperparameter behandeln und mittels Kreuzvalidierung für die vorliegende Aufgabe optimieren. Hierfür gibt es verschiedene Ansätze, wie z.B. den stückweisen Aufbau eines Netzes bis eine Zielperformance erreicht wird oder das Trimmen großer Netze, bei denen unwichtige Neuronen (solche die nie aktiviert werden) schrittweise entfernt werden. Die Faustregeln von Heaton und Anderen können hierfür als gute Einstiegspunkte dienen, ersetzen aber nicht ein ausgiebiges Modelltuning.

Referenzen

  1. Heaton, Jeff (2008): Introduction to Neural Networks for Java

Aufbauend auf der theoretischen Einführung in neuronale Netze und Deep Learning im Rahmen des letzten Blogbeitrags, soll in Teil 2 der Reihe „Deep Learning“ die Implementierung eines einfachen neuronalen Netzes (Feedforward Netz) in Python anschaulich dargestellt werden. Hierzu stehen dem Anwender viele verschiedene Frameworks zur Verfügung. In diesem Beitrag verwenden wir Keras, eine der wichtigsten Python Libraries, zur Programmierung von neuronalen Netzen und Deep Learning Modellen.

Übersicht Deep Learning Frameworks

In den letzten Jahren gab es viele Neuzugänge im Deep Learning Software-Ökosystem. Zahlreiche Frameworks, die bereits vordefinierte Bausteine zur Konstruktion von Deep Learning Netzen bieten, wurden in die Open Source Community eingeführt. Darunter sind beispielsweise das von Facebook genutzte Torch, dessen High Level Interface die Skriptsprache Lua nutzt, Caffee, das im akademischen Umfeld große Popularität genießt, Deeplearning4j, das eine Java basierte Deep Learning Umgebung bereitstellt, oder Theano, mit einem Fokus auf mathematisch effiziente Berechnungen. Neben den zuvor genannten Frameworks existieren noch viele weitere Bibliotheken, die dem Anwender die Programmierung von einfach und komplexen Deep Learning Modellen erlauben. Hierzu zählen insbesondere noch Apache MxNet oder IntelNervana NEON. Das größte und ressourcenreichste Deep Learning Frameworks ist aktuell jedoch Tensorflow, das ursprünglich vom Google Brain Team entwickelt und mittlerweile als Open Source Software veröffentlicht wurde. TensorFlow ist in C++ und Python implementiert, lässt sich jedoch auch mit kleinen oder großen Umwegen in weiteren Sprachen wie R, Julia oder Go integrieren und nutzen. Zuletzt hat sich Python vor allem aufgrund von TensorFlow sowie der darauf aufbauenden Library Keras zur Lingua Franca der Programmierung von Deep Learning Modellen etabliert.

Google TensorFlow

TensorFlow ist eine Softwarebibliothek für Python, die es erlaubt, mathematische Operationen in Form eines Graphen zu modellieren. Der Graph bildet ein Gerüst, in dessen Verlauf die Daten in jedem Knoten mathematisch transformiert an den darauf folgenden Konten weitergereicht werden. Dabei werden die Daten in sog. Tensoren gespeichert und verarbeitet. Ein Tensor ist, vereinfacht ausgedrückt, ein Container, der die im Graphen berechneten Werte speichert. Die folgende Abbildung sowie die unten stehende Python-Codebox sollen dies an einem einfachen Beispiel illustrieren:

tensorflow graph

Der Graph definiert eine einfache mathematische Operation, hier eine Addition. Die Werte (Tensoren) a und b werden am quadratischen Konten des Graphen addiert und bilden den Wert c. In TensorFlow sieht das ganze dann so aus:

# TensorFlow laden
import tensorflow as tf
# a und b als Konstanten definieren
a = tf.constant(5)
b = tf.constant(4)
# Die Addition definieren
c = tf.add(a, b)
# Den Graphen initialisieren
graph = tf.Session()
# Den Graphen an der Stelle c ausführen
graph.run(c)

Das Ergebnis der Berechnung ist 9. Man sieht in diesem einfachen Beispiel schnell die grundlegende Logik hinter TensorFlow. Im ersten Schritt wird ein abstraktes Konzept des zu berechnenden Modells angefertigt (der Graph), das im folgenden Schritt mit Tensoren befüllt und an einer bestimmten Stelle ausgeführt und evaluiert wird. Natürlich sind Graphen für Deep Learning Modelle vielfach komplexer als im Minimalbeispiel oben. TensorFlow bietet dem Anwender mit der Funktion TensorBoard eine Möglichkeit, den programmierten Graphen bis ins letzte Detai zu visualisieren. Ein Beispiel für einen komplexeren Graphen sehen Sie in der unten stehen Abbildung.

Beispiel eines Tensorboards

Da in TensorFlow beliebige mathematischen Operationen definiert werden können, handelt es sich streng genommen um kein reines Deep Learning Framework. Auch andere Machine Learning Modelle lassen sich als Graph repräsentieren und mit TensorFlow berechnen. Jedoch wurde TensorFlow vor allem mit Fokus auf Deep Learning entwickelt und mit zahlreichen vordefinierten Bausteinen zur Implementierung von neuronalen Netze ausgeliefert (z.B. vorgefertigte Layer für MLPs oder CNNs). Nachdem 2016 Version 1.0 veröffentlicht wurde und die Arbeit an der Tensorflow API einen wichtigen Zwischenstand erreicht hat, ist in den kommenden Jahren eine gewisse Stabilität und Konsistenz des Codes zu erwarten. Dies wird der weiteren Verbreitung von TensorFlow und dem Einsatz in Produktivsystemen weiter zugutekommen. Neben der Python Library existiert auch ein TensorFlow Server, der für den Einsatz in Unternehmen konzipiert wurde und fertige TensorFlow Modelle als Service bereitstellt.

Einführung in Keras

TensorFlow ist trotz der bereits angesprochenen, vorgefertigten Blöcke immer noch ein Expertensystem und erfordert für den Anwender eine hohe Einarbeitungszeit. Die relative Entwicklungszeit von der ersten Idee bis hin zum fertigen Deep Learning Modell ist in TensorFlow extrem lang – jedoch auch maximal flexibel.

Die langwierige Explorationszeit in den meisten Deep Learning Frameworks soll durch Keras, einem einfach zu bedienenden Interface zur Erstellung von Deep Learning Modellen, abgemildert werden. Keras vereinfacht mithilfe einer simpleren Syntax die Konzeption, das Training und die Evaluierung von Deep Learning Modellen. Keras arbeitet grundsätzlich auf der Abstraktionsebene der einzelnen Schichten des neuronalen Netzes und verbindet diese in der Regel bereits automatisch. Detailfragen zur Netzarchitektur bzw. zur Implementierung selbiger entfallen größtenteils. So können einfache Standardmodelle wie MLPs, CNNs und RNNs schnell und effektiv prototypisiert werden. Das Besondere an Keras ist, dass es kein eigenes Backend für Berechnungen bereitstellt, sondern lediglich die vereinfachte Nutzung darunterliegender Bibliotheken wie TensorFlow, Theano oder CNTK ermöglicht. Ein Vorteil hiervon ist, dass der Code zur Spezifikation der

Netzarchitekturen dabei für alle Backends gleich ist und von Keras automatisch „übersetzt“ wird. Dies erlaubt eine extrem schnelle Entwicklung in verschiedenen Frameworks ohne sich in die komplexe Syntax der Backends einarbeiten zu müssen. Dementsprechend ist der Code sowohl deutlich kürzer als auch besser lesbar im Vergleich zu dem entsprechenden Pendant in der Original-Syntax des verwendeten Frameworks.

Beispiel: Implementierung eines neuronalen Netzes

Im Rahmen der folgenden Codebeispiele zur Implementierung von Deep Learning Modellen wird die Python API von Keras verwendet. Das folgende Beispiel befasst sich mit der Vorhersage des S&P500 Kurses für die jeweils nächste Handelsminute auf Basis der Kurse der enthaltenen Aktientitlel im Index. Das Beispiel dient lediglich zur Veranschaulichung der Implementierung eines neuronalen Netzes und ist nicht auf Performance optimiert (es sei an dieser Stelle angemerkt, dass die Prognose von Aktien- und Indexkursen nach wie vor extrem schwierig ist, insbesondere je kleinteiliger die Zeitintervalle werden. Einen interessanten Approach zur Ensemble-Prognose von Aktien verfolgt der AI-Fonds NUMERAI). Die Datenbasis für das Modelltraining besteht aus den Kursen der Underlyings und des Index in Handelsminuten im Zeitraum April – Juli 2017. Jede Zeile des Trainingsdatensatzes beinhaltet somit die Kurse aller Indextitel als Features für die Vorhersage sowie dem Indexkurs der nächsten Minute als Target. Der Testdatensatz besteht aus den gleichen Daten für den Monat August 2017.

Im folgenden Abschnitt wird die Python Modellspezifikation in Keras erläutert. Dabei wird angenommen, dass die DataFrames, die die Trainings- und Testdaten beinhalten bereits vorliegen:

# Layer aus der Keras Bibliothek laden
from keras.layers import Dense
from keras.models import Sequential

Zuerst werden die notwendigen Komponenten aus Keras importiert. Hier sind dies die Klassen für einen voll verbundenen Layer (Dense) und der Typ des Modells (Sequential), sowie die Hauptbibliothek für die Berechnungen.

# Initialisierung eines leeren Netzes
model = Sequential()
# Hinzufügen von 2 Feedforward Layern
model.add(Dense(512, activation="relu", input_shape=(ncols,)))
model.add(Dense(256, activation="relu"))
# Output Layer
model.add(Dense(1))

Zunächst wird das Gerüst des Modells in einem Objekt definiert (model). Nachdem der Container für das Modell instanziert wurde, werden diesem zwei Hidden Layer mit 512 bzw. 256 Neuronen und der ReLu Aktivierungsfunktion hinzugefügt. Die letzte Schicht im Modell, der Output Layer, summiert die zuvor berechneten Outputs der vorangehenden Knoten auf und gewichtet diese entsprechend.

# Modell kompilieren
model.compile(optimizer="adam", loss="mean_squared_error")
# Modell trainieren mit den Trainingsdaten
model.fit(x=stockdata_train_scaled,
          y=stockdata_train_target,
          epochs=100, batch_size=128)
# Geschätztes Modell auf den Testdaten evaluieren
results = model.evaluate(x=stockdata_test_scaled, y=stockdata_test_target)

Nach der Definition der Netzarchitektur wird das Netz zusammen mit den Trainingsparametern kompiliert. Da es sich bei diesem Beispiel um ein Regressionsproblem handelt, wird hier der Mean Squared Error (MSE) als Kostenfunktion verwendet. Der MSE berechnet in jeder Iteration die mittlere quadratische Abweichung zwischen den tatsächlich beobachteten und durch das Netz vorhergesagten Werten. Während des Trainings wird der MSE iterativ durch ein adaptives Gradientenverfahren (ADAM) minimiert. Mittels „Backpropagation“ werden die Gewichtungen zwischen den Neuronen so angepasst, dass in jeder Iteration der MSE niedriger wird (bzw. werden sollte). Im vorliegenden Beispiel wurden 100 Epochen als Trainingszeit gewählt, jedoch sollte in einem realen Anwendungsfall auch dieser Parameter durch ausgiebige Tests optimiert werden. Eine Epoche entspricht einem kompletten Durchlauf der Daten, d.h. das Netz hat jeden Datenpunkt der Trainingsdaten einmal „gesehen“.

Ergebnis und Ausblick

Wie die folgende Abbildung zeigt, ist das Ergebnis nicht ideal. Die blaue Linie zeigt die tatsächlichen S&P500 Indexwerte, die Schätzung durch das Modell ist in orange dargestellt.

Interessant ist, dass bereits das nicht optimierte Netz die Struktur des Verlaufs gelernt hat, auch wenn die Ausschläge und Effekte noch massiv überschätzt werden.

In nächsten Beitrag aus der Reihe „Deep Learning“ setzen wir an dieser Stelle an und werden die Performance unseres Modells durch die Anwendung verschiedener Tuningansätze weiter verbessern.

Aufbauend auf der theoretischen Einführung in neuronale Netze und Deep Learning im Rahmen des letzten Blogbeitrags, soll in Teil 2 der Reihe „Deep Learning“ die Implementierung eines einfachen neuronalen Netzes (Feedforward Netz) in Python anschaulich dargestellt werden. Hierzu stehen dem Anwender viele verschiedene Frameworks zur Verfügung. In diesem Beitrag verwenden wir Keras, eine der wichtigsten Python Libraries, zur Programmierung von neuronalen Netzen und Deep Learning Modellen.

Übersicht Deep Learning Frameworks

In den letzten Jahren gab es viele Neuzugänge im Deep Learning Software-Ökosystem. Zahlreiche Frameworks, die bereits vordefinierte Bausteine zur Konstruktion von Deep Learning Netzen bieten, wurden in die Open Source Community eingeführt. Darunter sind beispielsweise das von Facebook genutzte Torch, dessen High Level Interface die Skriptsprache Lua nutzt, Caffee, das im akademischen Umfeld große Popularität genießt, Deeplearning4j, das eine Java basierte Deep Learning Umgebung bereitstellt, oder Theano, mit einem Fokus auf mathematisch effiziente Berechnungen. Neben den zuvor genannten Frameworks existieren noch viele weitere Bibliotheken, die dem Anwender die Programmierung von einfach und komplexen Deep Learning Modellen erlauben. Hierzu zählen insbesondere noch Apache MxNet oder IntelNervana NEON. Das größte und ressourcenreichste Deep Learning Frameworks ist aktuell jedoch Tensorflow, das ursprünglich vom Google Brain Team entwickelt und mittlerweile als Open Source Software veröffentlicht wurde. TensorFlow ist in C++ und Python implementiert, lässt sich jedoch auch mit kleinen oder großen Umwegen in weiteren Sprachen wie R, Julia oder Go integrieren und nutzen. Zuletzt hat sich Python vor allem aufgrund von TensorFlow sowie der darauf aufbauenden Library Keras zur Lingua Franca der Programmierung von Deep Learning Modellen etabliert.

Google TensorFlow

TensorFlow ist eine Softwarebibliothek für Python, die es erlaubt, mathematische Operationen in Form eines Graphen zu modellieren. Der Graph bildet ein Gerüst, in dessen Verlauf die Daten in jedem Knoten mathematisch transformiert an den darauf folgenden Konten weitergereicht werden. Dabei werden die Daten in sog. Tensoren gespeichert und verarbeitet. Ein Tensor ist, vereinfacht ausgedrückt, ein Container, der die im Graphen berechneten Werte speichert. Die folgende Abbildung sowie die unten stehende Python-Codebox sollen dies an einem einfachen Beispiel illustrieren:

tensorflow graph

Der Graph definiert eine einfache mathematische Operation, hier eine Addition. Die Werte (Tensoren) a und b werden am quadratischen Konten des Graphen addiert und bilden den Wert c. In TensorFlow sieht das ganze dann so aus:

# TensorFlow laden
import tensorflow as tf
# a und b als Konstanten definieren
a = tf.constant(5)
b = tf.constant(4)
# Die Addition definieren
c = tf.add(a, b)
# Den Graphen initialisieren
graph = tf.Session()
# Den Graphen an der Stelle c ausführen
graph.run(c)

Das Ergebnis der Berechnung ist 9. Man sieht in diesem einfachen Beispiel schnell die grundlegende Logik hinter TensorFlow. Im ersten Schritt wird ein abstraktes Konzept des zu berechnenden Modells angefertigt (der Graph), das im folgenden Schritt mit Tensoren befüllt und an einer bestimmten Stelle ausgeführt und evaluiert wird. Natürlich sind Graphen für Deep Learning Modelle vielfach komplexer als im Minimalbeispiel oben. TensorFlow bietet dem Anwender mit der Funktion TensorBoard eine Möglichkeit, den programmierten Graphen bis ins letzte Detai zu visualisieren. Ein Beispiel für einen komplexeren Graphen sehen Sie in der unten stehen Abbildung.

Beispiel eines Tensorboards

Da in TensorFlow beliebige mathematischen Operationen definiert werden können, handelt es sich streng genommen um kein reines Deep Learning Framework. Auch andere Machine Learning Modelle lassen sich als Graph repräsentieren und mit TensorFlow berechnen. Jedoch wurde TensorFlow vor allem mit Fokus auf Deep Learning entwickelt und mit zahlreichen vordefinierten Bausteinen zur Implementierung von neuronalen Netze ausgeliefert (z.B. vorgefertigte Layer für MLPs oder CNNs). Nachdem 2016 Version 1.0 veröffentlicht wurde und die Arbeit an der Tensorflow API einen wichtigen Zwischenstand erreicht hat, ist in den kommenden Jahren eine gewisse Stabilität und Konsistenz des Codes zu erwarten. Dies wird der weiteren Verbreitung von TensorFlow und dem Einsatz in Produktivsystemen weiter zugutekommen. Neben der Python Library existiert auch ein TensorFlow Server, der für den Einsatz in Unternehmen konzipiert wurde und fertige TensorFlow Modelle als Service bereitstellt.

Einführung in Keras

TensorFlow ist trotz der bereits angesprochenen, vorgefertigten Blöcke immer noch ein Expertensystem und erfordert für den Anwender eine hohe Einarbeitungszeit. Die relative Entwicklungszeit von der ersten Idee bis hin zum fertigen Deep Learning Modell ist in TensorFlow extrem lang – jedoch auch maximal flexibel.

Die langwierige Explorationszeit in den meisten Deep Learning Frameworks soll durch Keras, einem einfach zu bedienenden Interface zur Erstellung von Deep Learning Modellen, abgemildert werden. Keras vereinfacht mithilfe einer simpleren Syntax die Konzeption, das Training und die Evaluierung von Deep Learning Modellen. Keras arbeitet grundsätzlich auf der Abstraktionsebene der einzelnen Schichten des neuronalen Netzes und verbindet diese in der Regel bereits automatisch. Detailfragen zur Netzarchitektur bzw. zur Implementierung selbiger entfallen größtenteils. So können einfache Standardmodelle wie MLPs, CNNs und RNNs schnell und effektiv prototypisiert werden. Das Besondere an Keras ist, dass es kein eigenes Backend für Berechnungen bereitstellt, sondern lediglich die vereinfachte Nutzung darunterliegender Bibliotheken wie TensorFlow, Theano oder CNTK ermöglicht. Ein Vorteil hiervon ist, dass der Code zur Spezifikation der

Netzarchitekturen dabei für alle Backends gleich ist und von Keras automatisch „übersetzt“ wird. Dies erlaubt eine extrem schnelle Entwicklung in verschiedenen Frameworks ohne sich in die komplexe Syntax der Backends einarbeiten zu müssen. Dementsprechend ist der Code sowohl deutlich kürzer als auch besser lesbar im Vergleich zu dem entsprechenden Pendant in der Original-Syntax des verwendeten Frameworks.

Beispiel: Implementierung eines neuronalen Netzes

Im Rahmen der folgenden Codebeispiele zur Implementierung von Deep Learning Modellen wird die Python API von Keras verwendet. Das folgende Beispiel befasst sich mit der Vorhersage des S&P500 Kurses für die jeweils nächste Handelsminute auf Basis der Kurse der enthaltenen Aktientitlel im Index. Das Beispiel dient lediglich zur Veranschaulichung der Implementierung eines neuronalen Netzes und ist nicht auf Performance optimiert (es sei an dieser Stelle angemerkt, dass die Prognose von Aktien- und Indexkursen nach wie vor extrem schwierig ist, insbesondere je kleinteiliger die Zeitintervalle werden. Einen interessanten Approach zur Ensemble-Prognose von Aktien verfolgt der AI-Fonds NUMERAI). Die Datenbasis für das Modelltraining besteht aus den Kursen der Underlyings und des Index in Handelsminuten im Zeitraum April – Juli 2017. Jede Zeile des Trainingsdatensatzes beinhaltet somit die Kurse aller Indextitel als Features für die Vorhersage sowie dem Indexkurs der nächsten Minute als Target. Der Testdatensatz besteht aus den gleichen Daten für den Monat August 2017.

Im folgenden Abschnitt wird die Python Modellspezifikation in Keras erläutert. Dabei wird angenommen, dass die DataFrames, die die Trainings- und Testdaten beinhalten bereits vorliegen:

# Layer aus der Keras Bibliothek laden
from keras.layers import Dense
from keras.models import Sequential

Zuerst werden die notwendigen Komponenten aus Keras importiert. Hier sind dies die Klassen für einen voll verbundenen Layer (Dense) und der Typ des Modells (Sequential), sowie die Hauptbibliothek für die Berechnungen.

# Initialisierung eines leeren Netzes
model = Sequential()
# Hinzufügen von 2 Feedforward Layern
model.add(Dense(512, activation="relu", input_shape=(ncols,)))
model.add(Dense(256, activation="relu"))
# Output Layer
model.add(Dense(1))

Zunächst wird das Gerüst des Modells in einem Objekt definiert (model). Nachdem der Container für das Modell instanziert wurde, werden diesem zwei Hidden Layer mit 512 bzw. 256 Neuronen und der ReLu Aktivierungsfunktion hinzugefügt. Die letzte Schicht im Modell, der Output Layer, summiert die zuvor berechneten Outputs der vorangehenden Knoten auf und gewichtet diese entsprechend.

# Modell kompilieren
model.compile(optimizer="adam", loss="mean_squared_error")
# Modell trainieren mit den Trainingsdaten
model.fit(x=stockdata_train_scaled,
          y=stockdata_train_target,
          epochs=100, batch_size=128)
# Geschätztes Modell auf den Testdaten evaluieren
results = model.evaluate(x=stockdata_test_scaled, y=stockdata_test_target)

Nach der Definition der Netzarchitektur wird das Netz zusammen mit den Trainingsparametern kompiliert. Da es sich bei diesem Beispiel um ein Regressionsproblem handelt, wird hier der Mean Squared Error (MSE) als Kostenfunktion verwendet. Der MSE berechnet in jeder Iteration die mittlere quadratische Abweichung zwischen den tatsächlich beobachteten und durch das Netz vorhergesagten Werten. Während des Trainings wird der MSE iterativ durch ein adaptives Gradientenverfahren (ADAM) minimiert. Mittels „Backpropagation“ werden die Gewichtungen zwischen den Neuronen so angepasst, dass in jeder Iteration der MSE niedriger wird (bzw. werden sollte). Im vorliegenden Beispiel wurden 100 Epochen als Trainingszeit gewählt, jedoch sollte in einem realen Anwendungsfall auch dieser Parameter durch ausgiebige Tests optimiert werden. Eine Epoche entspricht einem kompletten Durchlauf der Daten, d.h. das Netz hat jeden Datenpunkt der Trainingsdaten einmal „gesehen“.

Ergebnis und Ausblick

Wie die folgende Abbildung zeigt, ist das Ergebnis nicht ideal. Die blaue Linie zeigt die tatsächlichen S&P500 Indexwerte, die Schätzung durch das Modell ist in orange dargestellt.

Interessant ist, dass bereits das nicht optimierte Netz die Struktur des Verlaufs gelernt hat, auch wenn die Ausschläge und Effekte noch massiv überschätzt werden.

In nächsten Beitrag aus der Reihe „Deep Learning“ setzen wir an dieser Stelle an und werden die Performance unseres Modells durch die Anwendung verschiedener Tuningansätze weiter verbessern.