Lernen kann das System durch Verändern der Wichtungen, wobei die Änderungen über entsprechende Funktionen gesteuert werden. Im hier vorgestellten Beispiel wird zu einem Muster von Eingangszahlen ein Ausgabemuster vorgegeben, das dann mit dem vom Netz berechneten Muster verglichen wird. Die Abweichung
beider Muster wird durch eine Zahl ausgedrückt, die die Weite der Wichtungsänderungen eingrenzt (überwachtes Lernen).
Es gibt auch die Möglichkeit, das Netz zu optimieren, ohne ein Ausgabemuster vorzugeben (unüberwachtes Lernen).
Das System soll ein binäres Eingangsmuster, bestehend aus den Zahlen 0 und 1, mit einem Ausgabemuster verbinden. Das Ausgabemuster wird aus den Zahlen 0 und 1 vorgegeben. Die Ausgabe zeigt dann das berechnete Muster und, wie viele Schritte das System zum Lernen benötigte und wie groß die Abweichung von berechnetem und vorgegebenem Ausgabemuster ist. Ein- und Ausgaben erfolgen der Einfachheit halber im Textmodus ohne Nutzung der grafischen Oberfläche.
Berechnung des Ausgangswertes eines Neurons: Ein Neuron bekommt über das Array ein[] Eingangswerte geliefert. ein[] enthält alle vorgegebenen und alle bisher berechneten Eingangswerte. Das Neuron darf aber nur die Ergebnisse der Vorgängerebene auswerten. Diese beginnen bei dem Index anf, während inz deren
Anzahl angibt. anf und inz sind Variablen, die bei der Initialisierung des Neurons mit new neuron(..) konkrete Werte erhalten haben (vgl. Gesamtlisting).
Die Eingangswerte werden mit Hilfe entsprechender Faktoren aus dem Array fak[] gewichtet und addiert. Der Ausgangswert könnte über eine Abfrage festgelegt werden, wird aber hier über die Sigmoid-Funktion s(x)=1/(exp(-x)+1) berechnet, um näher am Verhalten natürlicher Systeme zu sein. Dies erfordert, dass x
nach unten beschränkt wird, da sonst für zu kleine x exp(-x) nicht mehr berechnet werden kann.
|
public void wert(double ein[]) { double s=0; for(int i=0;i ein[pos]=1/(Math.exp(-s)+1); }; |
Methodenkopf, Übergabe des Arrays ein[] mit den Eingangswerten
Berechnung der gewichteten Summe s |
|
public void lernen(double kickN) { if(kickN<kickO) / for(int i=0;i<inz;i++) fakO[i]=fak[i]; kickO=kickN; } for(int i=0;i<inz;i++) fak[i]=fakO[i]+lrn*(Math.random()-0.5)*kickN; }; |
Methodenkopf, Übergabe der Abweichung der Ausgangswerte wenn die aktuelle Abweichung kleiner als die bisher kleinste, dann alte Faktoren fakO[i] ersetzen durch die aktuellen Faktoren fak[i] Mindestabweichung kickO ersetzen durch aktuelle Abweichung kick
für alle Eingänge Neuberechnung der Faktoren fak[i] durch: |
|
for(lsz=0;(lsz<10000)&&(kick>0.02);lsz++) { for(int i=0;i<gez;i++) al[i].wert(ein);
kick=0;
for(int i=0;i<gez;i++) al[i].lernen(kick); |
Schleife, Abbruch, wenn lsz 10000 ist oder kick 0.02 unterschreitet Berechung der Ausgangswerte aller Neuronen Berechnung der Abweichung: fez Anfangsindex der letzten Ebene in all[], ein[fez+i] berechneter Ausgang, aus[i] zugehörige Vorgabe Lernprozess für alle Neuronen |
|
Oben findet man die Eingaben zur Organisation des Netzes mit 3 Ebenen, 4 Eingängen, den Neuronenzahlen pro Ebene und 2 Ausgängen. Es folgen die Vorgaben für das Eingabe- und das Ausgabemuster.
Angezeigt werden dann Werte der einzelnen Lernschritte: Zum Schluss wird die Zahl der Lernschritte und die letzte Abweichung von vorgegeber und berechneter Ausgabe angezeigt.
|
Der Lernprozess muss nicht über Zufallszahlen ablaufen: Wenn man die Abweichungen der einzelnen Ausgänge nicht in einer Summe zusammenfasst und auch nicht den Betrag bildet, kann man die Abweichungen direkt zur Korrektur der Wichtungen nutzen. Bei mehreren Ebenen wären dann die Abweichungen mittels der Eingangswichtungen zu neuen Werten umzurechnen, bevor man sie als Abweichungen in darunter liegenden Neuronenebenen nutzt (Delta-Regel, Backpropagation).
Ein umfassendes Programm, das vielseitige Möglichkeiten zur Netzorganisation bietet und das in der Lage ist, einfache grafische Muster zu erkennen, findet man in dem Paket neuronnetz.zip. Das Paket enthält ein konfigurierbares Java-Programm (.jar), Beispielmuster und die zugehörige Beschreibung.