Benutzer-Werkzeuge

Webseiten-Werkzeuge


ein_neuronales_netz_fuer_den_nxt_roboter

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
ein_neuronales_netz_fuer_den_nxt_roboter [2024/01/26 10:29] – [Neural Networks] torsten.roehlein_neuronales_netz_fuer_den_nxt_roboter [2024/01/26 11:20] (aktuell) – [Quellcode] torsten.roehl
Zeile 19: Zeile 19:
 |Abb. 2: Das Perzeptron mit einfacher Aktivierungsfunktion| |Abb. 2: Das Perzeptron mit einfacher Aktivierungsfunktion|
  
-Die Inputwerte ($x_1$, $x_2$, $x_3$, ..., $x_n$) und die Verbindungsgewichte ($w_1$,$w_2$,$w_3$, ...,$w_n$) in der Grafik sind normalerweise reelle Werte. \\ BeispielWenn $x_i$ das //Perzeptron// zum feuern anregt (Aktivierungsfunktion gibt den Wert 1 aus), muss das Gewicht $w_i$ folglich einen positiven Wert gehabt haben. Ist das Gewicht $w_i$ allerdings negativ, so wird das Neuron hingegen nicht feuern (Aktivierungsfunktion nimmt den Wert 0 an). \\ Wie //Elaine Rich// und //Kevon Knight// in ihrem Buch //Artificial Intelligence (McGraw-Hill, 1990)// schreiben: \\+Die Inputwerte ($x_1$, $x_2$, $x_3$, ..., $x_n$) und die Verbindungsgewichte ($w_1$,$w_2$,$w_3$, ...,$w_n$) in der Grafik sind normalerweise reelle Werte.  
 +=== Beispiel === 
 +Wenn $x_i$ das //Perzeptron// zum feuern anregt (Aktivierungsfunktion gibt den Wert 1 aus), muss das Gewicht $w_i$ folglich einen positiven Wert gehabt haben. Ist das Gewicht $w_i$ allerdings negativ, so wird das Neuron hingegen nicht feuern (Aktivierungsfunktion nimmt den Wert 0 an). \\ Wie //Elaine Rich// und //Kevon Knight// in ihrem Buch //Artificial Intelligence (McGraw-Hill, 1990)// schreiben: \\
 <color #00a2e8>"Das Perzeptron selbst besteht aus Gewichten, einem Summierungsprozessor und dem feszulegenden Grenzwert. Das Lernen ist der Prozess des Modifizierens der Gewichtswerte und des Grenzwertes"</color> <color #00a2e8>"Das Perzeptron selbst besteht aus Gewichten, einem Summierungsprozessor und dem feszulegenden Grenzwert. Das Lernen ist der Prozess des Modifizierens der Gewichtswerte und des Grenzwertes"</color>
 ==== Backpropagation-Netzwerke ==== ==== Backpropagation-Netzwerke ====
 +Ein Backpropagation-Netzerk (**Backpropagation** engl. für Fehlerrückführung) ist ein neuronales Netz, in dem die Neuronen in Ebenen angeordnet sind, wobei jedes Neuron eines Layers (einer Ebene) mit jedem Neuron des darüber liegenden Layers verbunden ist.\\ Des weiteren feuern die Neuronen nur in eine Richtung, auf die nächsthöhere Ebene. In diesem Fall fließt die Aktivität vom //Input-Layer// über den //Hidden-Layer// zum //Output-Layer//. \\ Die Gewichte zwischen den Einheiten stellen den Wissensstand des Netzwerkes kodiert dar.
 +
 +|{{ :inf:msr:nn_3.png? |}}|
 +|Abb. 3: Ein typisches neuronales Netzwerk mit 3 Layers (Schichten)|
 +
 +
 +Den Gewichten in einem //Backpropagation-Netzwerk// werden normalerweise zu Beginn Zufallswerte zugeordnet. Jedes Mal, wenn ein Paar von Input- und Outputvektoren generiert wird, justiert das Netzwerk die Gewichte nach festgelegten Lernregeln nach. Jedes Vektorenpaar durchläuft dabei zwei Aktivierungsschritte, einen Vorwärtsschritt (**forward pass**) und einen Rückwärtsschritt (**backward pass**).\\ Beim Vorwärtsschritt durchläuft ein Eingabemuster das Netzwerk (Input-Layer →  Output-Layer), welches daraus einen Output berechnet. \\ Beim Rückwärtsschritt wird der berechnete Output (aus dem Vorwärtsschritt) mit dem vorhergesagten Ziel-Output verglichen und Fehlerwerte für die Output-Neuronen werden berechnet.
 +
 +Diese werden genutzt, um die Gewichte, die mit dem Output-Layer verbunden sind, so neu zu berechnen, dass die Fehler reduziert werden. Aus den Fehlerwerten der Output-Neuronen werden nun Fehlerabschätzungen der Hidden-Neuronen berechnet. Aus diesen Fehlerwerten werden die Gewichte zwischen Input- und Hidden-Layer neu justiert.
 +
 +Nach jeder Runde "lernt" das System Taktweise von anhand der Input-Output-Daten und reduziert dabei die Fehlerdifferenz zwischen dem berechneten Output und dem vorhergesagten Ziel-Output. Nach ausgiebigem Training hat das Netzwerk die Gewichte so justiert, dass kein Fehler mehr auftritt.
 ====  Der Backpropagation-Algorithmus==== ====  Der Backpropagation-Algorithmus====
 +Ausgehend von einem Satz von Inputvektoren und den zugehörigen Oututvektoren (Vektorenpaar), kann man die Gewichte des neuronalen Netzwerks so justieren, dass die Inputs auf die vorhergesagten Ziel-Outputs abgebildet werden.
 +
 +Sei A die Anzahl der Neuronen auf dem Input-Layer, die Größe von A hängt dabei von der Größe des für das Training verwendeten Input-Vektors ab. Sei C die Anzahl der Neuronen auf dem Output-Layer. Nun ist B, die Anzahl der Neuronen auf den Hidden-Layer festzulegen. Wie in Abbildung 4 dargestellt, beinhalten der Hidden- und der Input-Layer ein zusätzliches Neuron als festgelegten Grenzwert der Aktivierungsfunktion. Deshalb werden die Neuronen auf diesen Ebenen häufig von 0 bis A indiziert, anstatt von 1 bis A. Wir bezeichnen  die Neuronen auf dem Input-Layer mit $x_j$ , auf dem Hidden-Layer mit $h_j$ , und auf dem Output-Layer mit $o_j$ . Gewichte, die den Input- und Hidden-Layer verbinden, heißen $w1_{ij}$ , wobei **i** die Input-Neuronen bezeichnet und **j** die Hidden-Neuronen. Gewichte, die den Hidden- und den Output-Layer verbinden, heißen $w2_{ij}$ , **i** bezeichnet analog die Hidden-Neuronen und **j** die Output-Neuronen.
 +
 +
 +^Step^Beschreibung^
 +|1.|<WRAP>Initialisieren der Gewichte. Dabei wird jedem Gewicht ein Zufallswert zwischen -0,1 und 0,1 zugeordnet.\\
 +  * $w1_{ij}$=''random(-0.1, 0.1)'' für alle i=0, ..., A, j=1, ..., B
 +  * $w2_{ij}$=''random(-0.1, 0.1)'' für alle i=0, ..., B, j=1, ..., C
 +</WRAP>|
 +|2.|<WRAP>Initialisieren der Grenzwert-Neuronen. Für jeden Layer wird der Grenzwert auf 1 gesetzt und sollte nie verändert werden.
 +  * $x_0=1.0$
 +  * $h_0=1.0$
 +</WRAP>|
 +|3.|<WRAP>Auswählen eines Input-Output-Paars. Sei $x_i$ ein Input-Vektor und $y_i$ der vorausgesagte Ziel-Vektor. Den Input-Neuronen werden Aktivierungslevel zugeordnet. 
 +</WRAP>|
 +|4.|<WRAP>Durchlaufen der Aktivierungen der Input-Neuronen zu den Hidden-Neuronen mit der Aktivierungsfunktion
 +\begin{equation}  h_j= \frac{1}{1 + e^{- \sum_{i=0}^{A} w1_{ij}\;x_i} } \end{equation}
 +</WRAP>|
 +|5.|<WRAP>Durchlaufen der Aktivierungen der Hidden-Neuronen zu den Output-Neuronen:
 +\begin{equation}  o_j= \frac{1}{1 + e^{- \sum_{i=0}^{B} w2_{ij}\;h_i} } \end{equation}
 +</WRAP>|
 +|6.|<WRAP>Berechnen der Fehlerwerte der Neuronen auf dem Output-Layer, bezeichnet mit δ$2_j$. Die Fehlerwerte basieren auf dem berechneten Output ($o_j$) und dem Ziel-Output $y_j$:
 +\begin{equation} \delta 2_j = o_j(1-o_j)(y_j-o_j) \end{equation}
 +
 +</WRAP>|
 +|7.|<WRAP>Berechnen der Fehlerwerte Der Hidden-Neuronen, bezeichnet als δ1:
 +
 +\begin{equation} \delta 1_j = h_j(1-h_j) \sum_{i_1}^C \delta2_i\;w2_{ji} \end{equation}
 +</WRAP>|
 +|8.|<WRAP>Justieren der Gewichte zwischen Hidden- und Output-Layer. Der Lernfaktor sei η; seine Funktion ist die gleiche wie beim Lernen des Perzeptrons. Ein angemessener Wert für η ist z.B. 0.35:
 +\begin{equation} \Delta w2_{ij} = \eta \delta 2_jh_i \end{equation}
 +    * für alle i=0, ..., B, j=1, ..., C
 +</WRAP>|
 +|9.|<WRAP>Justieren der Gewichte zwischen Input- und Hidden-Layer:
 +\begin{equation} \Delta w1_{ij} = \eta \delta 1_jh_i \end{equation}
 +  * für alle i=0, ..., A, j=1, ..., B
 +</WRAP>|
 +|10.|<WRAP>Ab Schritt 4 alles wiederholen, bis alle Input-Output-Paare dem Netzwerk "präsentiert" worden sind. Dann ist eine //"Epoche"// vervollständigt.  Die Schritte 4-10 für eine gewünschte Zahl an Epochen wiederholen.
 +</WRAP>|
 +
 +
 +
 +Die Aktivierungsfunktion ist s-förmig. Damit die berechneten //Outputs// werte von 0.0 oder 1.0 annehmen, müssten die Gewichte unendlich groß sein. Deshalb werden die Ziel-Outputs (Oben in Schritt 4 und 7 als $y_j$ dargestellt) auf 0.1 und 0.9 gesetzt. Die S-Förmigkeit der Aktivierungsfunktion ist deshalb wichtig, weil sie differenzierbar sein muss. Ansonsten könnten die Gewichtsupdates im //Backpropagationprozess// nicht berechnet werden.
 +
 +
 ==== Das Backpropagation-Netzwerk für den NXT ==== ==== Das Backpropagation-Netzwerk für den NXT ====
 +Das Netzwerk kann von der Theorie nicht eins zu eins auf den NXT-Roboter bertragen werden, sondern es muss an dessen Hardware angepasst werden. Der Roboter hat drei Inputs (zwei Touch-Sensoren und ein Licht-Sensor) und zwei Outputs (Motor A und Motor C). Es kann also ein 3-Layer Netzwerk verwendet werden (Wie in Grafik 5), in welchem die Indizes der Neuronen mit 0 beginnen, damit Java-Arrays verwendet werden können.
 +
 +|{{ :inf:msr:nn_7.png? |}}|
 +|Abb. 4 Der Aufbau des Lego NXT Roboters|
 +
 +|{{ :inf:msr:nn_6.png? |}}|
 +|Abb. 5:Das Backpropagation-Netzwerk |
 +
 +{{:inf:hinweis.gif?|}}Dass hier ein 3-Layer-Netzwerk mit 3 Neuronen im Hidden-Layer verwendet wird, ist im Grunde eine willkürliche Entscheidung. Allerdings ist ein einfaches Netzwerk für den Lerneffekt sinnvoll. 
 +
 +Die Input-Output-Vektor-Paare (Ziel-Outputs) können nicht willkürlich festgelegt werden, da sie von den Input- und Output-Möglichkeiten des Roboters abhängen. 
 +
 +
 +Grundlegende Bewegungsregeln:
 +  - **Vorwärtsbewegung**: Sensor 1 aus, Sensor 2 auf weißer Oberflächte, Sensor 3 aus → Motoren A und C laufen vorwärts
 +  - **Rechtskurve**: Sensor 3 an → Motor A läuft vorwärts, Motor B läuft rückwärts
 +  - **Linkskurve**: Sensor 1 an → Motor A läuft rückwärts, Motor B läuft vorwärts
 +  - **Rückwärtsbewegung**: Sensor 2 auf schwarzer Oberfläche→ Motoren A und B laufen rückwärts
 +Diese Regeln werden in Trainingsmuster übersetzt. 
 +
 +|{{ :inf:msr:nn_4.png? |}}|
 +|Abb. 6 Lernregeln|
 +
 +|{{ :inf:msr:nn_5.png? |}}|
 +|Abb. 7 Input-Output-Vektoren|
 +
 +Die Input-Output-Vektorpaare sind diese Trainigsmuster, anhand derer das Netzwerk lernt. Abhängig von den Zuständen seiner Input-Sensoren wird der Roboter also lernen, sich vorwärts zu bewegen, eine Rechtskurve zu machen, usw. Doch was würde zum Beispiel passieren, wenn beide Touch-Sensoren aktiviert sind? Für diesen Fall hat der Roboter kein Lernmuster, doch würde das Netzwerk ein bestimmtes Verhalten berechnen.
 +
 +
 +
 ====  Quellcode==== ====  Quellcode====
 +Abschließend noch der vollständige Quellcode um mit dem Experimentieren anzufangen zu können. Das Programm ist in zwei Java-Klassen aufgeteilt, eine Klasse für die Berechnungen (//Methods//) und eine für die Ausführung (Main).
 === Quellen === === Quellen ===
  
  
 +<Code Java linenums:1 | Listing 1: Die Main-Klasse>
 +import lejos.nxt.*;
 +import lejos.nxt.LCD;
 + 
 +public class Main {
 +   public static Methods bpn = new Methods();
 + 
 +   public static void main(String args[]) throws InterruptedException {
 + 
 +   //Initialisieren der Sensoren
 +  
 +   TouchSensor s1 = new TouchSensor(SensorPort.S1);
 +   LightSensor s2 = new LightSensor(SensorPort.S2);
 +   TouchSensor s3 = new TouchSensor(SensorPort.S3);
 + 
 +   //Initialisieren der Zählvariable i, des Weißstandarts
 +   // und des Input- und Output-Vektors
 +  
 +   int i, white;
 +   int inp[] = { 0, 0, 0 };
 +   int out[] = { 0, 0 };
 + 
 +   Sound.beep();
 +   System.out.println("Train");
 + 
 +   // Trainieren des bpn (Backpropagation-Netzwerks) in 500 Epochen
 +   for (i = 0; i < 500; i++) {
 +      bpn.train(1);
 +      System.out.println(bpn.trainedEpochs);
 +   }
 + 
 +   Sound.twoBeeps();
 +  
 +   //Definieren des Weißstandards und der Motorgeschwindigkeiten
 +  
 +   white = s2.readValue();
 + 
 +   Motor.A.setSpeed(300);
 +   Motor.C.setSpeed(300); 
 +   Sound.twoBeeps();
 + 
 +   while (!Button.ENTER.isPressed()) {
 + 
 +       System.out.println(s2.readValue());
 + 
 +       if (s1.isPressed()==true)
 +          inp[0] = 1; // Sensor 1 an
 +       else
 +          inp[0] = 0; // Sensor 1 aus
 + 
 +       if (s2.readValue() > white + 15)
 +           inp[1] = 1; // Sensor 2 schwarz
 +       else
 +           inp[1] = 0; // Sensor 2 weiß
 + 
 +       if (s3.isPressed()==true)
 +           inp[2] = 1; // Sensor 3 an
 +       else
 +           inp[2] = 0; // Sensor 3 aus
 + 
 +       bpn.test(inp, out);
 + 
 +       if (out[0] == 1)
 +           Motor.A.forward();
 +       else
 +          Motor.A.backward();
 + 
 +      if (out[1] == 1)
 +          Motor.C.forward();
 +      else
 +          Motor.C.backward();
 + 
 +      Thread.sleep(500);
 + 
 +   
 +  
 + 
 + 
 +   Motor.A.stop();
 +   Motor.C.stop();
 +   Sound.beep();
 +  
 +   Button.ESCAPE.addButtonListener(new ButtonListener() {
 +      public void buttonPressed(Button b) {
 +           LCD.drawString("Program stop", 0, 3);
 +      }
 +      
 +      public void buttonReleased(Button b) {
 +         System.exit(0);
 +      }
 +   });
 + 
 + 
 + 
 +}
 +
 +
 +</Code>
 +
 +<Code Java linenums:1 | Listing 1: Die Methods-Klasse>
 +class Methods {
 + public static int data1[][] = {{0,0,0}, {1,1}};
 + public static int data2[][] = {{1,0,0}, {1,0}};
 + public static int data3[][] = {{0,0,1}, {0,1}};
 + public static int data4[][] = {{0,1,0}, {0,0}};
 +  
 + public static double input[] = {0,0,0,1};
 + public static double w1[][] = {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}};
 + public static double hidden[] = {0,0,1};
 + public static double w2[][] = {{0,0}, {0,0}, {0,0}};
 + public static double output[] = {0,0};
 + public static double delta2[] = {0,0};
 + public static double delta1[] = {0,0,0};
 + 
 + public static int trainedEpochs = 0;
 +   
 + public Methods() {
 + byte i, j;
 + // Initialisieren der zufälligen Gewichte zwischen 0.1 und 0.9
 + for(i=0; i
 + 
 + for(i=0;
 + }
 + 
 + 
 + public static void train(int e) {
 +    for(int i=0; i
 +      learn( data1[0], data1[1] );
 +      learn( data2[0], data2[1] );
 +      learn( data3[0], data3[1] );
 +      learn( data4[0], data4[1] );
 +      trainedEpochs++;
 +      }
 + }
 + 
 + 
 + public static void learn( int inp[], int out[] ) {
 + int i, j;
 + double sum, out_j; 
 + 
 + // Initialisieren der Input-Vekoren
 + for(i=0; i
 + 
 + // Berechnen der Werte im Hidden-Layer
 + for(j=0; j for(i=0; i
 + 
 + 
 + hidden[j] = 1 / ( 1 + Math.exp(-sum));
 + 
 + 
 + // Berechnen der Output-Werte
 + for(j=0; j for(i=0; i
 + 
 + 
 + output[j] = 1 / (1 + Math.exp(-sum));
 + }
 + 
 + 
 + // Berechnen der delta2 Fehler
 + for(j=0; j
 + out_j = 0.1;
 + else if( out[j] == 1 )
 + out_j = 0.9;
 + else
 + out_j = out[j];
 + delta2[j] = output[j]*(1-output[j])*(out_j-output[j]);
 + }
 + 
 + 
 + // Berechnen der delta1 Fehler
 + for(j=0; j for(i=0; i
 + 
 + 
 + delta1[j] = hidden[j]*(1-hidden[j])*sum;
 + }
 + 
 + 
 + // Anpassen der Gewichte w2
 + for(i=0; i
 + 
 + 
 + // Anpassen der Gewichte w1
 + for(i=0; i
 + 
 + 
 + public static void test(int inp[], int out[]) {
 + int i, j;
 + double sum;
 + 
 + 
 + // Initialisieren der Input-Werte
 + for(i=0; i
 + 
 + // Berechnen der Wete im Hidden-Layer
 + for(j=0; j for(i=0; i
 + 
 + 
 + hidden[j] = 1 / ( 1 + Math.exp(-sum));
 + }
 + 
 + 
 + // Berechnen der Output-Werte
 + for(j=0; j
 + sum = 0;
 + for(i=0; i
 + 
 + output[j] = 1 / (1 + Math.exp(-sum));
 + }
 + 
 + 
 + // Übertragen des Outputs auf das Array out[]
 +     for(i=0; i= 0.5 )
 +         out[i] = 1;
 +     else
 +         out[i] = 0;
 + }
 +    
 + 
 +}
 +
 +
 +
 +</Code>
 +
 +<WRAP center round info 60%>
 +FIXME Der Quellcode enthält noch Fehler 
 +</WRAP>
  
  
 +==== Quellen ====
 +  * Download des orginales Artikels erschienen in JavaWorld 2005
 +          * [[https://www.informatics4kids.de/addons-i4k/doc/article/neural_network_lego.pdf|A neural network for Java Lego robots]]
  
ein_neuronales_netz_fuer_den_nxt_roboter.1706264974.txt.gz · Zuletzt geändert: 2024/01/26 10:29 von torsten.roehl