GMU:Neuronale Netze - eine Einführung für Künstler/Präsentation/Neuronale Netze in Processing: Difference between revisions
mNo edit summary |
|||
(19 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
== Processing == | == Processing == | ||
[[Image: | [[Image:processing env example.png|thumb|200px|Processing IDE]] | ||
[[Processing]] ist eine Programmiersprache und Entwicklungsumgebung, die 2001 am Massachusetts Institute of Technology von den Erfindern [http://benfry.com/ Ben Fry] und [http://reas.com/ Casey Reas] als quelloffenes Projekt ins Leben gerufen wurde. Die Sprache wurde speziell für die Bereiche Simulation und Animation entworfen und richtet sich daher vorwiegend an Personen aus der Gestaltung und Kunst. | [[Processing]] ist eine Programmiersprache und Entwicklungsumgebung, die 2001 am Massachusetts Institute of Technology von den Erfindern [http://benfry.com/ Ben Fry] und [http://reas.com/ Casey Reas] als quelloffenes Projekt ins Leben gerufen wurde. Die Sprache wurde speziell für die Bereiche Simulation und Animation entworfen und richtet sich daher vorwiegend an Personen aus der Gestaltung und Kunst. | ||
Line 6: | Line 6: | ||
Weitere Informationen bzgl. Processing und dessen Verwendung lassen sich auf der [http://processing.org/learning/ Hauptseite] und im Wiki-Eintrag zu [[Processing]] finden. | Weitere Informationen bzgl. Processing und dessen Verwendung lassen sich auf der [http://processing.org/learning/ Hauptseite] und im Wiki-Eintrag zu [[Processing]] finden. | ||
<br clear="all" /> | |||
== Neuronale Netze == | == Neuronale Netze == | ||
Da man in Processing direkt in Java programmieren kann, sind einem bzgl. der Komplexität der Programme keine Grenzen gesetzt. Für die einfache Erstellung neuronaler Netze kann man zusätzliche [http://processing.org/reference/libraries/ Libraries] verwenden, die man dem Sketchbook beifügt. Als Beispiel wird hier die [http://www.shiffman.net/teaching/nature/nn/ nn-library] von Daniel Shiffman verwendet. | Da man in Processing direkt in Java programmieren kann, sind einem bzgl. der Komplexität der Programme keine Grenzen gesetzt. Für die einfache Erstellung neuronaler Netze kann man zusätzliche [http://processing.org/reference/libraries/ Libraries] verwenden, die man dem Sketchbook beifügt. Als Beispiel wird hier die [http://www.shiffman.net/teaching/nature/nn/ nn-library] von Daniel Shiffman verwendet. | ||
{| class="wikitable" style="text-align: center;border: 1px solid darkgray;float: | {| class="wikitable" style="text-align: center;border: 1px solid darkgray;float: left;margin: 0.2em 1em 0.2em 0;" | ||
|- | |- | ||
! i(1) !! i(2) !! ⊕ | ! i(1) !! i(2) !! ⊕ | ||
Line 24: | Line 24: | ||
|} | |} | ||
Bei dem | Bei dem unten aufgeführtem Beispiel handelt es sich um eine leicht modifizierte Version eines neuronalen Netzes von Daniel Shiffman <ref>Daniel Shiffman: [http://www.shiffman.net/teaching/nature/nn/ The Nature of Code - Neural Networks]</ref>. Mittels eines [[wikipedia:Multilayer perceptron]]s wird hier versucht das nichtlinear trennbare [http://en.wikipedia.org/wiki/Xor XOR Problem] zu lösen. Jede Ecke des Würfels entspricht einer 0 oder 1, wobei sich gleiche Werte je Fläche diagonal spiegeln, d.h. benachbarte Eckpunkte sind niemals beide gleichzeitig 0 oder 1, sondern immer im Wechsel. Zwischen 2 Eckpunkten wird entlang der Flächen interpoliert, sodass man für Werte zwischen 0 und 1 eine Annährung erhält. Zur Verdeutlichung wurde in den folgenden Beispielbildern als mögliches Antwortpaar für das Training via [[wikipedia:Backpropagation]] [0.2, 0.8] gewählt (siehe angepasste Wahrheitstabelle links). Damit wird dem Netz ein schnelleres Lernen ermöglicht, da die Ableitungen der [[wikipedia:Sigmoid function]] an diesen Stellen nicht allzu niedrig sind. Je weiter gen 0 und 1, desto niedriger werden die abgeleiten Werte (Anstieg der Tangenten an den jeweiligen Punkten) und desto weniger wird der Lernprozess beeinflusst, bzw. die Gewichte verändert. | ||
[[Image: | [[Image:processing neural network xor 1.png|thumb|left|100px|XOR possible state (1)]] | ||
[[Image: | [[Image:processing neural network xor 2.png|thumb|left|100px|XOR possible state (2)]] | ||
[[Image: | [[Image:processing neural network xor 3.png|thumb|left|100px|XOR possible state (3)]] | ||
[[Image: | [[Image:processing neural network xor 4.png|thumb|left|100px|XOR possible state (4)]] | ||
[[Image: | [[Image:processing neural network xor 5.png|thumb|left|100px|XOR possible state (5)]] | ||
<br clear="all" /> | <br clear="all" /> | ||
=== Anlegen eines neuronalen Netzes === | === Anlegen eines neuronalen Netzes === | ||
[[Image:neural network multilayer perceptron.png|thumb|200px|Multilayer Perceptron]] | |||
[[Image: | Zunächst erzeugt man ein neues Netzwerk, für welches man vorgibt, wieviele input und hidden Neuronen enthalten sein sollen. Zusätzlich wird je Ebene intern ein BIAS Neuron angelegt (siehe Abbildung rechts). Nach Modifikation der Library wäre es ebenfalls möglich die Anzahl der Output-Neuronen zu bestimmen. Davon wurde hierbei jedoch abgesehen. | ||
Zunächst erzeugt man ein neues Netzwerk, für welches man vorgibt, wieviele input und hidden Neuronen enthalten sein sollen. Zusätzlich wird je Ebene intern ein BIAS Neuron angelegt (siehe Abbildung rechts). Nach Modifikation der Library wäre es ebenfalls möglich die Anzahl der Output-Neuronen zu bestimmen. Davon wurde hierbei jedoch abgesehen | |||
<source lang="java"> | <source lang="java"> | ||
Network net = new Network(2,3); | Network net = new Network(2,3); | ||
</source> | </source> | ||
<br clear="all" /> | |||
In der setup() Methode initialisiert man die Eingabewerte: | === Trainieren des neuronalen Netzes === | ||
In der setup() Methode initialisiert man einmalig die Eingabewerte (hier mit den ursprünglichen Werten 0 und 1 für das XOR Problem), welche im globalen [[wikipedia:Variable (programming)#Scope and extent|Scope]], d.h. dem programmweiten Namensraum, zur Verfügung stehen. Dies bedeutet, dass jede erstellte Methode und instanziierte Klasse auf diese Variablen Zugriff hat. | |||
<source lang="java"> | <source lang="java"> | ||
ArrayList inputs = new ArrayList(); | |||
In dem einmaligen Setup werden die Eingabewerte, welche für das Training benötigt werden, initialisiert. | |||
void setup() { | void setup() { | ||
size(400, 400, OPENGL); | size(400, 400, OPENGL); | ||
inputs.add(new float[] { 0 | inputs.add(new float[] { 0, 0 }); | ||
inputs.add(new float[] { | inputs.add(new float[] { 1, 0 }); | ||
inputs.add(new float[] { 0 | inputs.add(new float[] { 0, 1 }); | ||
inputs.add(new float[] { 0. | inputs.add(new float[] { 1, 1 }); | ||
} | |||
</source> | |||
Das Training wird randomisiert jeden Frame 5 mal wiederholt. Die draw()-Methode wird fortlaufend nach setup() ausgeführt und bei Programmabbruch terminiert. Sie sollte daher niemals explizit aufgerufen werden. | |||
<source lang="java"> | |||
void draw() { | |||
// One epoch. | |||
int rate = 5; | |||
for (int i = 0; i < rate; i++) { | |||
// Randomly select from training set. | |||
int pick = int(random(inputs.size())); | |||
float[] inp = (float[]) inputs.get(pick); | |||
// Choose the appropriate answer for the training's input. | |||
float known = 1.0; | |||
if ((inp[0] > 0.5 && inp[1] > 0.5) || (inp[0] < 0.5 && inp[1] < 0.5)) known = 0.0; | |||
float result = net.train(inp, known); | |||
} | |||
} | |||
</source> | |||
Um nach jedem Durchlauf den mittleren quadratischen Fehler zu erhalten, berechnet man zunächst die Ausgabe des gesamten Netzes und anschließend den Fehler wie folgt: | |||
<source lang="java"> | |||
float calcError() { | |||
float mse = 0.0; | |||
for (int i = 0; i < inputs.size(); ++i) { | |||
// Retrieve single test input pair. | |||
float[] inp = (float[]) inputs.get(i); | |||
// Calculate appropriate answer. | |||
float known = 1.0; | |||
if ((inp[0] > 0.5 && inp[1] > 0.5) || (inp[0] < 0.5 && inp[1] < 0.5)) known = 0.0; | |||
// Feed forward each test input. | |||
float result = nn.feedForward(inp); | |||
// Add up mean square error from output. | |||
mse += (result - known) * (result - known); | |||
} | |||
// Return root mean squared error. | |||
return sqrt(mse / inputs.size()); | |||
} | } | ||
</source> | </source> | ||
== Erläuterung der Library == | |||
Ein Neuron hat grundlegend folgende Eigenschaften: | |||
* Ausgabewert | |||
* Liste der Verbindungen | |||
* BIAS-Status (0 oder 1) | |||
Für alle nicht-BIAS Neuronen ist der BIAS-Status 0. Ein Neuron ist dann ein BIAS Neuron, wenn es bei der Initialisierung einen beliebigen Integer erhält. Die Ausgabe eines Neurons wird für jedes einzeln berechnet. Dafür stellt die Basisklasse folgende Methode bereit: | |||
<source lang="java"> | |||
public void calcOutput() { | |||
if (!bias) { | |||
float sum = 0; | |||
for (int i = 0; i < connections.size(); ++i) { | |||
Connection c = (Connection) connections.get(i); | |||
Neuron from = c.getFrom(); | |||
Neuron to = c.getTo(); | |||
// Only calculate the incoming connection's weight (input) only. | |||
if (to == this) { sum += from.getOutput() * c.getWeight(); } | |||
} | |||
output = f(sum); | |||
} | |||
} | |||
</source> | |||
Die Ausgabe ist das Resultat der [http://en.wikipedia.org/wiki/Artificial_neuron#Types_of_transfer_functions Aktiviersungsfunktion] (transfer function), hier eine Sigmoid Funktion. | |||
<source lang="java"> | |||
public static float f(float x) { | |||
return 1.0f / (1.0f + (float) Math.exp(-x)); | |||
} | |||
</source> | |||
Bei der initialen Erstellung des Netzes werden die einzelnen Ebenen miteinander verbunden. Eine Verbindung hält die Referenz von einem Neuron zu einem anderen, sowie das dazugehörige Gewicht, welches zunächst zufällig gewählt wird. Für den Konstruktor und die Gewichtsanpassung ergibt sich: | |||
<source lang="java"> | |||
public Connection(Neuron a_, Neuron b_) { | |||
from = a_; | |||
to = b_; | |||
// A random value between -1 and 1. | |||
weight = (float) Math.random() * 2 - 1; | |||
} | |||
public void adjustWeight(float delta) { | |||
weight += delta; | |||
} | |||
</source> | |||
=== Der Lernprozess - Backpropagation === | |||
Als Eingabe wird ein zufällig gewähltes Paar an Werten aus dem Trainingsset und der entsprechende Erwartungswert übergeben. | |||
<source lang="java"> | |||
public float train(float[] inputs, float answer) { | |||
... | |||
} | |||
</source> | |||
Zunächst wird die derzeitige Netzausgabe via eines [http://en.wikipedia.org/wiki/Feed-forward#Feed-forward_systems_in_computing Feed-forward] Systems berechnet - Ebene für Ebene werden die Ausgabewerte einzelner Neuronen akkumuliert, um am Ende eine Netzausgabe zu erhalten. | |||
<source lang="java"> | |||
float result = feedForward(inputs); | |||
public float feedForward(float[] inputVals) { | |||
// Feed input. | |||
for (int i = 0; i < inputVals.length; i++) { | |||
input[i].input(inputVals[i]); | |||
} | |||
// Calculate hidden layer output. | |||
for (int i = 0; i < hidden.length - 1; i++) { | |||
hidden[i].calcOutput(); | |||
} | |||
// Calculate net output. | |||
output.calcOutput(); | |||
return output.getOutput(); | |||
} | |||
</source> | |||
Daraufhin werden zuerst die äußeren Gewichte, vom Output zum Hidden Layer, angepasst. Die hier verwendete Lernrate (allgemein mit dem griechischen Buchstaben Eta bezeichnet) bestimmt zusätzlich den Lernfortschritt. Je größer dieser Wert, desto schneller lernt das Netz (mit größeren Sprüngen). Dabei kann es jedoch passieren, dass die Anpassungen zu schnell zu ungenau werden, oder man sogar zwischen Ergebnissen hin und her springt. Ein weiteres, schwerwiegendes Problem ist der Lernstillstand, der durch [http://reference.kfupm.edu.sa/content/o/n/on_the_problem_of_local_minima_in_backpr_67452.pdf lokale Minima] verursacht werden kann. | |||
<source lang="java"> | |||
ArrayList connections = output.getConnections(); | |||
float deltaOutput = result * (1 - result) * (answer - result); | |||
for (int i = 0; i < connections.size(); i++) { | |||
Connection c = (Connection) connections.get(i); | |||
// The weight is adjusted depending on the incoming input | |||
// and a calculated difference between the expected answer | |||
// and overall net result, as well as a predefined defined ETA | |||
c.adjustWeight(deltaOutput * c.getFrom().getOutput() * ETA); | |||
} | |||
</source> | |||
Danach erst erfolgt die Anpassung der inneren Gewichte - vom Hidden Layer zum Input Layer. | |||
<source lang="java"> | |||
// For all hidden neurons... | |||
for (int i = 0; i < hidden.length; i++) { | |||
connections = hidden[i].getConnections(); | |||
float sum = 0; | |||
for (int j = 0; j < connections.size(); j++) { | |||
Connection c = (Connection) connections.get(j); | |||
// Sum up the output of all outgoing connections. | |||
if (c.getFrom() == hidden[i]) { sum += c.getWeight() * deltaOutput; } | |||
} | |||
for (int j = 0; j < connections.size(); j++) { | |||
// Adjust the weight of all incoming connections accordingly. | |||
Connection c = (Connection) connections.get(j); | |||
if (c.getTo() == hidden[i]) { | |||
float output = hidden[i].getOutput(); | |||
float deltaHidden = output * (1 - output) * sum; | |||
c.adjustWeight(deltaHidden * c.getFrom().getOutput() * ETA); | |||
} | |||
} | |||
} | |||
return result; | |||
</source> | |||
Dies sei nur ein Beispiel bzgl. neuronaler Netze in Processing. Desweiterin existiert noch die Möglichkeit [http://leenissen.dk/fann/ FANN] Java Bindings zu benutzen. Dabei handelt es sich um eine Open Source (Fast Artificial) Neural Network Library, deren Multilayer Netzwerke in C implementiert sind und damit einen klaren Geschwindigkeitsvorteil bieten. | |||
Unter näherer Betrachtung der hier vorgestellten Library scheint eine solche mit wenig Aufwand umsetzbar. Zumindest Konzepte wie simple Multilayer Perceptrons mit jeweils nur einer Lage Input, Hidden und Output Neuronen stellen keine Herausforderung dar. | |||
== Weitere Beispiele == | |||
[http://www.openprocessing.org/visuals/?visualID=4255 Genetic Algorithm] - Das Ziel dieser Simulation ist das Lernen über Generationen. Hinter jeder Ameise steckt ein neuronales Netz, das ihre Bewegungen beeinflusst. Dessen Aufgabe ist es, die roten Punkte (Nahrung) einzusammeln. Nach einem bestimmten Zeitraum wird eine neue, 'bessere' Generation an Ameisen geschaffen. | |||
[http://www.openprocessing.org/visuals/?visualID=8360 Neural Network Visualization] - Hierbei handelt es sich um eine simple Visualisierung eines neuronalen Netzes, bei dem sich eine Fläche an eine Vorgabe anpasst. Die grüne Fläche stellt die Zielausrichtung und die blauen Flächen die verschiedenen Hidden Neuronenschichten dar. | |||
== Referenzen == | == Referenzen == | ||
<references/> | <references/> | ||
[[Category:Neuronales Netz]] | |||
[[Category:Processing]] | |||
[[Category:Neuronales Netz]] | [[Category:Neuronales Netz]] | ||
[[Category:Processing]] | [[Category:Processing]] |
Latest revision as of 19:15, 5 October 2010
Processing
Processing ist eine Programmiersprache und Entwicklungsumgebung, die 2001 am Massachusetts Institute of Technology von den Erfindern Ben Fry und Casey Reas als quelloffenes Projekt ins Leben gerufen wurde. Die Sprache wurde speziell für die Bereiche Simulation und Animation entworfen und richtet sich daher vorwiegend an Personen aus der Gestaltung und Kunst.
Das sogenannte Sketchbook verkörpert das Prinzip einer stark vereinfachten IDE (Integrated Development Environment). Dem Nutzer steht eine simple Toolbar, ein Editor und Textfeld zur Verfügung. Über das Hauptprogramm hinaus erzeugte Klassen werden in eigenen Tabs untergebracht. Jede dieser Sketches ist tatsächlich nur eine Unterklasse der PApplet Java-Klasse, welche fast alle Features der Sprache implementiert. Alle zusätzlich erstellten Klassen werden beim Kompilieren als innere Klassen behandelt. Wenn man in Java programmieren will, muss dies explizit geschehen.
Weitere Informationen bzgl. Processing und dessen Verwendung lassen sich auf der Hauptseite und im Wiki-Eintrag zu Processing finden.
Neuronale Netze
Da man in Processing direkt in Java programmieren kann, sind einem bzgl. der Komplexität der Programme keine Grenzen gesetzt. Für die einfache Erstellung neuronaler Netze kann man zusätzliche Libraries verwenden, die man dem Sketchbook beifügt. Als Beispiel wird hier die nn-library von Daniel Shiffman verwendet.
i(1) | i(2) | ⊕ |
---|---|---|
0.2 | 0.2 | 0.2 |
0.2 | 0.2 | 0.8 |
0.8 | 0.2 | 0.8 |
0.8 | 0.8 | 0.2 |
Bei dem unten aufgeführtem Beispiel handelt es sich um eine leicht modifizierte Version eines neuronalen Netzes von Daniel Shiffman [1]. Mittels eines wikipedia:Multilayer perceptrons wird hier versucht das nichtlinear trennbare XOR Problem zu lösen. Jede Ecke des Würfels entspricht einer 0 oder 1, wobei sich gleiche Werte je Fläche diagonal spiegeln, d.h. benachbarte Eckpunkte sind niemals beide gleichzeitig 0 oder 1, sondern immer im Wechsel. Zwischen 2 Eckpunkten wird entlang der Flächen interpoliert, sodass man für Werte zwischen 0 und 1 eine Annährung erhält. Zur Verdeutlichung wurde in den folgenden Beispielbildern als mögliches Antwortpaar für das Training via wikipedia:Backpropagation [0.2, 0.8] gewählt (siehe angepasste Wahrheitstabelle links). Damit wird dem Netz ein schnelleres Lernen ermöglicht, da die Ableitungen der wikipedia:Sigmoid function an diesen Stellen nicht allzu niedrig sind. Je weiter gen 0 und 1, desto niedriger werden die abgeleiten Werte (Anstieg der Tangenten an den jeweiligen Punkten) und desto weniger wird der Lernprozess beeinflusst, bzw. die Gewichte verändert.
Anlegen eines neuronalen Netzes
Zunächst erzeugt man ein neues Netzwerk, für welches man vorgibt, wieviele input und hidden Neuronen enthalten sein sollen. Zusätzlich wird je Ebene intern ein BIAS Neuron angelegt (siehe Abbildung rechts). Nach Modifikation der Library wäre es ebenfalls möglich die Anzahl der Output-Neuronen zu bestimmen. Davon wurde hierbei jedoch abgesehen.
Network net = new Network(2,3);
Trainieren des neuronalen Netzes
In der setup() Methode initialisiert man einmalig die Eingabewerte (hier mit den ursprünglichen Werten 0 und 1 für das XOR Problem), welche im globalen Scope, d.h. dem programmweiten Namensraum, zur Verfügung stehen. Dies bedeutet, dass jede erstellte Methode und instanziierte Klasse auf diese Variablen Zugriff hat.
ArrayList inputs = new ArrayList();
In dem einmaligen Setup werden die Eingabewerte, welche für das Training benötigt werden, initialisiert.
void setup() {
size(400, 400, OPENGL);
inputs.add(new float[] { 0, 0 });
inputs.add(new float[] { 1, 0 });
inputs.add(new float[] { 0, 1 });
inputs.add(new float[] { 1, 1 });
}
Das Training wird randomisiert jeden Frame 5 mal wiederholt. Die draw()-Methode wird fortlaufend nach setup() ausgeführt und bei Programmabbruch terminiert. Sie sollte daher niemals explizit aufgerufen werden.
void draw() {
// One epoch.
int rate = 5;
for (int i = 0; i < rate; i++) {
// Randomly select from training set.
int pick = int(random(inputs.size()));
float[] inp = (float[]) inputs.get(pick);
// Choose the appropriate answer for the training's input.
float known = 1.0;
if ((inp[0] > 0.5 && inp[1] > 0.5) || (inp[0] < 0.5 && inp[1] < 0.5)) known = 0.0;
float result = net.train(inp, known);
}
}
Um nach jedem Durchlauf den mittleren quadratischen Fehler zu erhalten, berechnet man zunächst die Ausgabe des gesamten Netzes und anschließend den Fehler wie folgt:
float calcError() {
float mse = 0.0;
for (int i = 0; i < inputs.size(); ++i) {
// Retrieve single test input pair.
float[] inp = (float[]) inputs.get(i);
// Calculate appropriate answer.
float known = 1.0;
if ((inp[0] > 0.5 && inp[1] > 0.5) || (inp[0] < 0.5 && inp[1] < 0.5)) known = 0.0;
// Feed forward each test input.
float result = nn.feedForward(inp);
// Add up mean square error from output.
mse += (result - known) * (result - known);
}
// Return root mean squared error.
return sqrt(mse / inputs.size());
}
Erläuterung der Library
Ein Neuron hat grundlegend folgende Eigenschaften:
- Ausgabewert
- Liste der Verbindungen
- BIAS-Status (0 oder 1)
Für alle nicht-BIAS Neuronen ist der BIAS-Status 0. Ein Neuron ist dann ein BIAS Neuron, wenn es bei der Initialisierung einen beliebigen Integer erhält. Die Ausgabe eines Neurons wird für jedes einzeln berechnet. Dafür stellt die Basisklasse folgende Methode bereit:
public void calcOutput() {
if (!bias) {
float sum = 0;
for (int i = 0; i < connections.size(); ++i) {
Connection c = (Connection) connections.get(i);
Neuron from = c.getFrom();
Neuron to = c.getTo();
// Only calculate the incoming connection's weight (input) only.
if (to == this) { sum += from.getOutput() * c.getWeight(); }
}
output = f(sum);
}
}
Die Ausgabe ist das Resultat der Aktiviersungsfunktion (transfer function), hier eine Sigmoid Funktion.
public static float f(float x) {
return 1.0f / (1.0f + (float) Math.exp(-x));
}
Bei der initialen Erstellung des Netzes werden die einzelnen Ebenen miteinander verbunden. Eine Verbindung hält die Referenz von einem Neuron zu einem anderen, sowie das dazugehörige Gewicht, welches zunächst zufällig gewählt wird. Für den Konstruktor und die Gewichtsanpassung ergibt sich:
public Connection(Neuron a_, Neuron b_) {
from = a_;
to = b_;
// A random value between -1 and 1.
weight = (float) Math.random() * 2 - 1;
}
public void adjustWeight(float delta) {
weight += delta;
}
Der Lernprozess - Backpropagation
Als Eingabe wird ein zufällig gewähltes Paar an Werten aus dem Trainingsset und der entsprechende Erwartungswert übergeben.
public float train(float[] inputs, float answer) {
...
}
Zunächst wird die derzeitige Netzausgabe via eines Feed-forward Systems berechnet - Ebene für Ebene werden die Ausgabewerte einzelner Neuronen akkumuliert, um am Ende eine Netzausgabe zu erhalten.
float result = feedForward(inputs);
public float feedForward(float[] inputVals) {
// Feed input.
for (int i = 0; i < inputVals.length; i++) {
input[i].input(inputVals[i]);
}
// Calculate hidden layer output.
for (int i = 0; i < hidden.length - 1; i++) {
hidden[i].calcOutput();
}
// Calculate net output.
output.calcOutput();
return output.getOutput();
}
Daraufhin werden zuerst die äußeren Gewichte, vom Output zum Hidden Layer, angepasst. Die hier verwendete Lernrate (allgemein mit dem griechischen Buchstaben Eta bezeichnet) bestimmt zusätzlich den Lernfortschritt. Je größer dieser Wert, desto schneller lernt das Netz (mit größeren Sprüngen). Dabei kann es jedoch passieren, dass die Anpassungen zu schnell zu ungenau werden, oder man sogar zwischen Ergebnissen hin und her springt. Ein weiteres, schwerwiegendes Problem ist der Lernstillstand, der durch lokale Minima verursacht werden kann.
ArrayList connections = output.getConnections();
float deltaOutput = result * (1 - result) * (answer - result);
for (int i = 0; i < connections.size(); i++) {
Connection c = (Connection) connections.get(i);
// The weight is adjusted depending on the incoming input
// and a calculated difference between the expected answer
// and overall net result, as well as a predefined defined ETA
c.adjustWeight(deltaOutput * c.getFrom().getOutput() * ETA);
}
Danach erst erfolgt die Anpassung der inneren Gewichte - vom Hidden Layer zum Input Layer.
// For all hidden neurons...
for (int i = 0; i < hidden.length; i++) {
connections = hidden[i].getConnections();
float sum = 0;
for (int j = 0; j < connections.size(); j++) {
Connection c = (Connection) connections.get(j);
// Sum up the output of all outgoing connections.
if (c.getFrom() == hidden[i]) { sum += c.getWeight() * deltaOutput; }
}
for (int j = 0; j < connections.size(); j++) {
// Adjust the weight of all incoming connections accordingly.
Connection c = (Connection) connections.get(j);
if (c.getTo() == hidden[i]) {
float output = hidden[i].getOutput();
float deltaHidden = output * (1 - output) * sum;
c.adjustWeight(deltaHidden * c.getFrom().getOutput() * ETA);
}
}
}
return result;
Dies sei nur ein Beispiel bzgl. neuronaler Netze in Processing. Desweiterin existiert noch die Möglichkeit FANN Java Bindings zu benutzen. Dabei handelt es sich um eine Open Source (Fast Artificial) Neural Network Library, deren Multilayer Netzwerke in C implementiert sind und damit einen klaren Geschwindigkeitsvorteil bieten.
Unter näherer Betrachtung der hier vorgestellten Library scheint eine solche mit wenig Aufwand umsetzbar. Zumindest Konzepte wie simple Multilayer Perceptrons mit jeweils nur einer Lage Input, Hidden und Output Neuronen stellen keine Herausforderung dar.
Weitere Beispiele
Genetic Algorithm - Das Ziel dieser Simulation ist das Lernen über Generationen. Hinter jeder Ameise steckt ein neuronales Netz, das ihre Bewegungen beeinflusst. Dessen Aufgabe ist es, die roten Punkte (Nahrung) einzusammeln. Nach einem bestimmten Zeitraum wird eine neue, 'bessere' Generation an Ameisen geschaffen.
Neural Network Visualization - Hierbei handelt es sich um eine simple Visualisierung eines neuronalen Netzes, bei dem sich eine Fläche an eine Vorgabe anpasst. Die grüne Fläche stellt die Zielausrichtung und die blauen Flächen die verschiedenen Hidden Neuronenschichten dar.
Referenzen
- ↑ Daniel Shiffman: The Nature of Code - Neural Networks