Neuronale Netze in Java mit NEUROPH

Zoran Sevarac, Valentin Steinhauer

NEUROPH ist nicht nur eine gute Wahl für jeden, der den Umgang mit Neuronalen Netzen erlernen oder damit experimentieren will. Auch für die schnelle Integration in das eigene Projekt ist es sehr gut geeignet. Das Paket ist klein, gut dokumentiert, leicht zu benutzen und sehr flexibel.

Inspiriert durch die Funktionsweise des menschlichen Gehirns können Neuronale Netze gut Probleme verallgemeinern. Klassische Einsatzgebiete von neuronalen Netzen sind Klassifizierung, Vorhersage,  Mustererkennung und Approximation. Sie sind in Industrie, diagnostischen Medizin, militärischen Bewertungen (z.B. Radar-Bild-Erkennung), in der Finanzwissenschaft, Robotik und anderen wissenschaftlichen Bereichen präsent. Neuerdings sind sie aber auch verstärkt in der Spielindustrie populär geworden.

Aber nicht nur für komplexe Aufgaben sind neuronale Netze hilfreich. Auch in alltäglichen Anwendungen können sie effektiv eingesetzt werden. Besonders jetzt, im Multicore Jahrhundert werden diese Netze eine wichtige Rolle spielen.

Wer den Einstieg in die Welt der neuronalen Netze sucht, sieht sich schnell einer großen Vielfalt an Themen und Frameworks gegenüber. Neuroph ist kompakt und gut dokumentiert. Es ist daher ein idealer Einstieg in diese faszinierende Welt. Dieser Artikel wird anhand der einfachsten Netze – so genannte Perzeptronen [1] – zeigen, wie Sie dieses Paket effektiv nutzen können. Und vielleicht macht es ja auch neugierig auf mehr [2].

Um mit neuen unbekannten oder unvollständigen Daten umzugehen bzw. um Vorhersagen oder Entscheidungen treffen zu können, werden zuerst Trainingsdaten benötigt. Diese Daten dienen dem neuronalen Netz als Grundlage für die spätere Auswertung. Jedes neuronale Netz besteht aus mehreren, miteinander verbundenen Basiseinheiten die Neuronen genannt werden. Während der Trainingsphase werden, ähnlich dem menschlichen Gehirn, die Verbindungen zwischen Neuronen ständig modifiziert. Nämlich während des Lernverfahrens werden die Verbindungen zwischen den Neuronen verschieden gewichtet. Diese Gewichtung wird so lange angepasst bis das erwünschte Verhalten erreicht wird.

Alle dazu benötigten Lernregeln und Komponenten sind in Neuroph bereits integriert. So stellt das Paket z.B. eine GUI zum Experimentieren mit Lernparametern zur Verfügung, weil sie oft an das zu lösende Problem individuell eingestellt werden müssen. Außerdem wird Ihnen durch das GUI erlaubt, verschiedene Netzarchitekturen auszuprobieren und kennenzulernen. In der weiteren Entwicklung von Neuroph ist geplant, die Einstellung solcher Lernparameter und die Verwendung von unterschiedlichen Netzarchitekturen mit Ausgabe der optimalen Ergebnisse zu automatisieren. Damit werden Java-Entwickler noch mehr von der Verwendung des Neuroph-Frameworks profitieren können, besonders mit dem zukünftigen Neuroph NetBeans Plug-in.

Getting Started

Neuroph ist frei und unterliegt der LGPL. Die jeweils aktuelle Version kann also frei unter [3] heruntergeladen werden. Momentan ist Version 2.4 (u1) aktuell. Das Downloadpaket enthält neben dem vollständigen Quellcode, Binaries, Javadoc, die Klassendiagramme und einem Getting Started Tutorial auch einige Beispiele im Hilfssystem, um neuronale Netze besser zu verstehen. Fundiertes Java-Wissen ist allerdings Voraussetzung.

In den folgenden Beispielen werden wir zeugen, wie neuronale Netze in der Software-Entwicklung und im Betrieb von komplexen Softwaresystemen verwenden werden können.

Automatisierte Prozessteuerung eines Order Management Systems mit einfachem Perzeptron

Im produktiven Betrieb eines Order Management Systems wird ständig analysiert, ob Störungen bei der Bearbeitung der Transaktionen entstehen und wie sie schnell beseitigen werden können. Die Bearbeitung der einzelnen Transaktionen in solchen Systemen besteht normalerweise aus mehreren Schritten. Nehmen wir als Beispiel folgende Bearbeitungsschritte (States) an:

  1. State 0 – die Requests befinden sich im Input-Filestore
  2. State 1 – Annahme der Requests im System (NEW REQUEST)
  3. State 2 – Konvertierung der ankommenden Input-Formate in das interne Format (CONVERTED-REQUEST)
  4. State 3 – Mapping der Request-Datenmodelle (MAPPED REQUEST)
  5. State 4 – Versenden der Requests an das Backend-System (SEND REQUEST)
  6. State 5 – Empfangen der Resultate (RECIEVED RESPONSE)
  7. State 6 – Mapping der Resultat-Datenmodelle (MAPPED RESPONSE)
  8. State 7 – Konvertierung des internen Formats in das entsprechende Output-Format (CONVERT RESPONSE – z.B. EDIFACT, XML)
  9. State 8 – Versenden der Resultate (SEND RESULT)

Jede Transaktion, die für die Bearbeitung eines Requests im System erzeugt wird, läuft sequenziell diese States durch. Zusätzlich ist die Bearbeitung der Transaktionen von bestimmten Systemparametern abhängig, wie z.B. dem verfügbaren Plattenplatz (Files-PP), dem freien Tablespace in der Datenbank (DB-TS) oder der CPU-Auslastung (CPU-A). Nehmen wir an, dass die Bearbeitung der Transaktionen an jedem oben genannten Schritt mit einem eigenen Modul (z.B. einem Adapter)  durchgeführt wird. Als Outputwerte bzw. Entscheidungen können in einer kritischen Situation folgende Aktionen dienen:

  1. Input – Filestore schließen (Rechte für das Einstellen der Aufträge entziehen)
  2. E-Mail an die System-Verantwortlichen schicken
  3. Alle Adapter herunterfahren
  4. Nachstarten einzelner Adapter
  5. Nachstarten aller Adapter

Diese Liste umfasst natürlich nicht alle möglichen Situationen, erlaubt es aber zu zeigen, wie neuronale Netze für die automatische Prozesssteuerung benutzt werden können und wie diese Aufgabe mit Neuroph umgesetzt werden kann.

Also haben  wir in diesem Beispiel als Input 12 Neuronen (9 States und  drei Systemparameter) und als Output 5 Neuronen (siehe obige Liste).

Die Input-Neuronen werden nur die Werte 0 oder 1 annehmen. Bei den States bedeutet der Wert 0, dass kein Stau von Transaktionen vor dem Adapter zur Bearbeitung gebildet worden ist. Eine Stau von Transaktionen wird dann festgestellt bzw. der Wert auf 1 gesetzt, wenn die Anzahl der zur Bearbeitung anstehenden Transaktionen einen konfigurierten Wert (z.B. 10000) erreicht hat. Bei den Systemparametern sind die Werte 0 und 1 auch entsprechend zu interpretieren: 1 wenn ein konfigurierter Schwellenwert erreicht und 0 wenn er nicht erreicht wurde.

Jetzt kann dieses Modell als ein neuronales Netz dargestellt werden, nämlich als ein einfaches Perzeptron:

Abb. 1:  Prozesssteuerung-Perzeptron

 Die möglichen Zustände der Bearbeitung der Transaktionen im System und die entsprechenden Entscheidungen sind in Vektorform in der Tabelle 1 präsentiert. Wie sind diese Daten zu interpretieren? Nehmen wir Zeile 10: Plattenplatz steht ausreichend zur Verfügung, die Größe des verfügbaren Tablespace in der Datenbank ist auch ausreichend, die CPU-Auslastung der Maschine ist kritisch, weil der konfigurierter Schwellenwert erreicht ist – in State 3 ( Mapping der Request – Datenmodellen – MAPPED REQUEST) befinden sich zu viel Transaktionen. Also  hat sich an dieser Stelle ein Stau gebildet, wobei alle anderen States vom Problem nicht betroffen sind. Da ein Nachstart ( ein zusätzliches Thread ) des entsprechenden Adapters wegen hoher CPU-Auslastung nicht möglich ist, wird als Entscheidung bzw. als resultierende Aktion eine Email mit diesen Informationen an die System-Verantwortlichen geschickt.

Tabelle 1: Beispiel der Entscheidungsmatrix für die Prozesssteuerung eines Order Management Systems

Die oben beschriebenen Situationen sind aus der Praxis genommen und decken natürlich nicht alle Fälle ab. Sie sind aber hilfreich, um den Einsatz eines Perzeptron für Prozesssteuerung zu verdeutlichen. Mit den Daten dieser Tabelle wird das Prozesssteuerungsperzeptron trainiert. Bei Bedarf können diese Daten erweitert werden, ohne dass die Software angepasst werden muss.

Als nächstes werden die Schritte in Neuroph gezeigt, mit denen der Prozesssteuerungsperzeptron erzeugt, trainiert und getestet werden kann. Nach dem Start von easyNeuroph (start.bat ausführen oder java -jar easyNeurons.jar eingeben) muss die Topologie (Perzeptron, 12 Input Neuronen, 5 Output Neuronen) des Netzes definiert und die Trainingsdaten eingelesen werden. Dann wird das Netz trainiert und getestet. Alle Schritte sind auf den folgenden Abbildungen dargestellt:

Tabelle 2 Bearbeitungsschritte mit easyNeuroph

Mit letztem Schritt wurde das Netz gespeichert und jetzt ist es möglich mit ganz einfachem Code das trainierte Netz weiter zu verwenden:

//Lesen des Netzes
NeuralNetwork neuralNet = NeuralNetwork.load("OrderManagement.nnet");
//Öffnen des controlSet 
TrainingSet controlSet = new TrainingSet();
//Tragen Kontrolle Vektor ein
controlSet.addElement(new TrainingElement(new double[]{0.0,0.0,1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0}));
//Gehen durch controlSet um output zu bekommen : networkOutput 
for (TrainingElement testElement : controlSet.trainingElements()) {
neuralNet.setInput(testElement.getInput());
neuralNet.calculate();
Vector<Double> networkOutput = neuralNet.getOutput();
System.out.println(networkOutput);
}

Der komplette Code als NetBeans-Projekt  und die Trainingsdatei kann unter [4] angefragt werden.

Diese einfache Perzeptronanwendung kann operative Prozesssteuerung in der Produktion nicht nur effektiv, sondern auch in angemessener Zeit gestalten. Was passiert, wenn eine unerwartete Situation eintritt? Zum Beispiel, dass alles blockiert ist und damit alle Werte 1 sind. Das Perzeptron gibt aus: 1 1 1 0 0. Das bedeutet es müssen die Input-Filestores geschlossen,  eine E-Mail an die System-Verantwortlichen geschickt und alle Adapter heruntergefahren werden.

Wie wir gesehen haben, ist das Perzeptron geeignet, diese lineare Aufgabe zu lösen. In einigen anderen Fällen stellt das Problem aber keine lineare Aufgabe dar. Zur Lösung solcher Probleme kann die sogenannte Multi Layer Perzeptron (MPL) eingesetzt werden. Wie mit einem MLP in Neuroph Framework umzugehen ist,  wird im nächsten Abschnitt besprochen.

Neuroph als Framework in Time Series Prediction

Eine Prognose aufgrund der in der Vergangenheit anfallenden Werte spielt insbesondere im wirtschaftlichen Bereich eine große Rolle. Sowohl bei Aktien- und Börsenkursen als auch beim Verbrauch von Strom und Wärme versuchen Analytiker, aus dem Verlauf der regelmäßig anfallenden Daten die zukünftige Entwicklung vorherzusagen, um fundierte Entscheidungen treffen zu können. Hierzu werden neuerdings zunehmend auch neuronale Netze eingesetzt. Unser kleines Beispiel unten gehört zu den so genannten „Time Series Prediction“-Anwendungen. Die theoretischen Grundlagen können dafür in Neuroph unter [5] entnommen werden.

Um zu zeigen, wie eine solche Anwendung auf Basis des Neuroph Frameworks funktionieren kann, wurde die DAX–Entwicklung  für den  Monat August 2010 (02.-26.) genommen und ein neuronales Netz mit diesen Daten trainiert. Dieses trainierte Netz wurde für die Vorhersage des Wertes am 27. August 2010 verwendet. An dieser Stelle muss betont werden, dass dieses Beispiel als Neuroph-Demo dient und nicht zur Analyse und Vorhersage der produktiven Börsendaten verwendet werden kann. Eine solche Analyse und Vorhersage ist viel komplizierter als das Beispiel unten. Wir arbeiten aber daran und befinden uns noch in der ersten Phase [6]. Die Daten des DAX sind zum Testzweck aber täglich durch die Abfrage unter [7] abrufbar. Für das Training des Netzes wurden folgende Daten genommen:

double[] data = {6292.13, 6307.91, 6331.33, 6333.58, 6259.63,6351.6, 
6286.25, 6154.07, 6135.17, 6110.41, 6110.57, 6206.4, 6186.31, 6075.13, 
6005.16, 6010.91, 5935.44, 5899.5, 5912.58};

Bevor das Netz trainiert wurde, wurden die Daten auf den Bereich (0-1) normiert. Die Formel für diese Normierung sieht wie folgt aus:

dax[i] = ((data[i]-datamin) / datamax) , 

wobei datamax  (multipliziert mit 1.2) der maximale Wert  und datamin (multipliziert mit 0.8) der minimale Wert ist. Die Multiplikation ist nötig um die Grenzwerte 1.0 und 0.0 auszuschließen. Dann wurde der Typ des Netzes – das sogenannte Multi Layer Perzeptron – festgelegt. Beim Multi Layer Perzeptron sind die Neuronen so strukturiert, dass jedes Neuron eines Layers mit allen Neuronen der beiden benachbarten Layer verbunden ist. Hierbei gilt, dass der Input-Layer keine Vorgänger und der Output-Layer keine Nachfolger haben. Die Schichten zwischen Input- und Output-Layer werden Hidden-Layer (verdeckte Schichten) genannt. Weiter wurde die Topologie Parameter des Netzes definiert, z.B. wie viele Schichten und wie viele Neuronen pro Schicht verwendet werden sollen. Es gibt momentan keine festen Regeln dafür. Als Hinweis gilt: experimentieren! Eine Empfehlung sei jedoch genannt:  (2n+1) Neuronen für den Hidden-Layer, wobei n die Zahl der Input-Neuronen ist. In unserem Beispiel wurden gute Resultate mit folgender Topologie und Parametrisierung erreicht:

NeuralNetwork neuralNet = new MultiLayerPerceptron(TransferFunctionType.GAUSSIAN, 4, 9, 1);
((LMS)neuralNet.getLearningRule()).setMaxError(0.01);//0-1
((LMS) neuralNet.getLearningRule()).setLearningRate(0.5);//0-1
((LMS) neuralNet.getLearningRule()).setMaxIterations(100000); 

Die Neuron-Aktivierungsfunktion ist bei Approximationen oft eine Gauß-Funktion. Deswegen wurde TransferFunctionType als GAUSSIAN definiert. Das Netz wurde folgendermaßen trainiert: der Eingangsvektor enthält immer 4 DAX-Werte an vier aufeinanderfolgenden Tagen und die Hidden-Schicht hat 9 Neuronen. Der DAX-Wert am fünften Tag wird bei der Ausgabe erwartet, wie es in der Abbildung 2 gezeigt ist.

Abbildung 2 Time Series Prediction Perzeptron (MLP)

Die Parameter MaxIterations, LearningRate (der Lerngeschwindigkeitskoeffizient) und MaxError gehören zu den so genannten Lernregeln. Wenn der Klassifikationsfehler ( MaxError ) nicht erreicht ist, wird die Trainingsaktion immer weiter wiederholt. Falls der Lernprozess nicht konvergiert, bricht er nach einer bestimmten Zahl von Iterationen ab (MaxIterations). Je gezielter das Netz trainiert wird und je mehr Datensätze genommen werden, desto mehr Rechnerkapazitäten werden benötigt. Die Ursache liegt in der großen Menge von Vektor- und Matrixoperationen, die bei neuronalen Netzen verwendet werden.

Nach Definition der Lernregeln und der Topologie des Netzes können die Trainingssätze normiert eingelesen werden (siehe Tabelle 3).

Tabelle 3: Training Datensätze für MLP

Code zum Training des MLP:

TrainingSet trainingSet = new TrainingSet();
for (int i = 0; i <= data.length - 5; i++) {
 trainingSet.addElement(new SupervisedTrainingElement(new double[]{
(data[i] - datamin) / datamax, 
(data[i + 1] - datamin) / datamax, 
(data[i + 2] - datamin) / datamax, 
(data[i + 3] - datamin) / datamax}, 
new double[]{(data[i + 4] - datamin) / datamax}));
 }
neuralNet.learnInSameThread(trainingSet);

Wie wird erkannt, ob das MLP am Ende gut trainiert wurde? Dazu kann ein sogenannter RFactor beitragen. Zur Prüfung des trainierten Netzes werden wieder die Trainingsdaten genommen. Für jeden Input-Vektor wird jetzt die Prognose ausgegeben, die mit dem bekannten Zielwert verglichen wird. Die relative, mittlere Abweichung wird RFactor genannt. Eine Abweichung mit mehr als 5% ist nicht akzeptabel.

Hier ist ein Codebeispiel zur Prüfung eines Netzes:

TrainingSet testSet = trainingSet; 
testSet.addElement(new TrainingElement(new double[]{(6010.91D - datamin) / datamax, (5935.44D - datamin) / datamax, (5899.5D - datamin) / datamax, (5912.58D - datamin) / datamax})); 
int counter = -1; 
double summa = 0.0; 
for (TrainingElement testElement : testSet.trainingElements()) { 
neuralNet.setInput(testElement.getInput()); 
neuralNet.calculate(); 
Vector<Double> networkOutput = neuralNet.getOutput(); 
double pred = (networkOutput.elementAt(0)) * datamax + datamin;  
++counter; 
if (counter <= data.length - 5) {
double error =Math.abs((goals[counter] - pred) / goals[counter]) * 100.0D; 
summa += error; 
System.out.printf("goal =%6.2f redicted=%6.2f error=%4.2f pern", goals[counter], pred, error); 
} else { 
double RFactor = summa / (data.length - 5); 
System.out.printf(" predicted: redicted=%6.2f RFactor=%4.2fn", pred, RFactor); 
} 
 }


Jetzt prognostizieren wir den DAX-Wert am 27.08.2010. Zu diesem Zweck geben wir in das Netz die DAX-Werte der vier vorigen Tagen ein: am 23., 24., 25. und 26. August 2010. Das Netz gibt uns den vorhergesagten Wert (die Prognose) zurück – 5930.61. Da der tatsächliche DAX-Wert am 27.08.2010 5951.17 war, ist der RFactor (die Abweichung) für diese Vorhersage 0.35%. Für eine präzisere Prognose bräuchte man das Stock-Prozess-Modell, eine bessere Vorbereitung der Trainingsdaten und vieles mehr. Mit diesem Beispiel möchten wir aber zeigen, dass auch bei komplizierten Aufgaben das MLP und  das Neuroph Framework behilflich sein können.

Zusammenfassung

Heutzutage sind neuronale Netze bei B2B- oder B2C-Systemen noch nicht so weit verbreitet. Nicht zuletzt spielt an dieser Stelle die Komplexität der existierenden Frameworks eine Rolle. Neuroph versucht, dem Benutzer viele Hilfsmittel zur Verfügung zu stellen und die internen Prozesse möglichst weit zu automatisieren. Damit schließt es eine Lücke zwischen wissenschaftlichen und produktiven Anwendung von neuronalen Netzen.

Wir hoffen, Ihr Interesse an neuronalen Netzen und Neuroph geweckt zu haben.

Zoran Sevarac ist ein wissenschaftliche Assistent an der Belgrad-Universität und arbeitet im Labor der künstlichen Intelligenz. In Rahmen seiner Forschung hat er das Neuroph Paket entwickelt.

Dr.Valentin Steinhauer ist bei der Devoteam GmbH in Weiterstadt. Er verfügt über mehrjährige Erfahrung aus B2B, B2C Softwareprojekten als Coach, Trainer, Architekt, Teamleiter und Entwickler

Geschrieben von
Zoran Sevarac, Valentin Steinhauer
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.