 | Code: // Lernfaehiges 2-schichtiges Neuronales Netz // Backpropagation Netz mit 3 Sensor-Eingaengen (Touch an S1, S2, S3) // an 3 verdeckten Neurons // dann 2 Ausgabe-Neurons mit 2 Outputs (Anzeige auf dem Display) // (c) H. W. 2008 // neu: automatisches Training nach Datei string Version="0.466 test";
#define printXY nxtDisplayStringAt #define println nxtDisplayTextLine
//********************************************************************** // Basisdeklarationen fuer Neuronale Netze //**********************************************************************
const int L0 = 3; // max. Neurons in Schicht (Layer) 0 (Schicht fuer Inputs) const int L1 = 2; // max. Neurons in Schicht (Layer) 1 (Ausgabe Schicht bei 2-schichtigen) const int L2 = 1; // max. Neurons in Schicht (Layer) 2 const int L3 = 1; // max. Neurons in Schicht (Layer) 3
const int ni = 3; // max. Dendriten-Eingaenge (Zaehler ab 0) float lf = 0.6; // Lern-Faktor
int key; // gedrueckte NXT-Taste string MenuText=""; // Menue-Steuerung
float sollOut=0;
//********************************************************************** // Neuron-Struktur (vereinfachte Version) //**********************************************************************
typedef struct{ float in[ni]; // Einzel-Inputs (Dendriten) float w[ni]; // Einzel-Wichtungen (jedes Dendriten) float net; // totaler Input float th; // Schwellenwert (threshold) float d; // delta=Fehlersignal float out; // Output (Axon): z.B. 0 oder 1 } tNeuron;
//**********************************************************************
tNeuron Neuron0[L0]; // Neurons-Schicht 0 (Schicht fuer Inputs) tNeuron Neuron1[L1]; // Neurons-Schicht 1 (Ausgabe Schicht bei 2-schichtigen) tNeuron Neuron2[L2]; // Neurons-Schicht 2 tNeuron Neuron3[L3]; // Neurons-Schicht 3
//********************************************************************** // mathematische Hilfsfunktionen //**********************************************************************
float tanh(float x) // Tangens hyperbolicus { float e2x; e2x=exp(2*x); return((e2x-1)/(e2x+1)); }
//********************************************************************** // Ein-/ Ausgabefunktionen (Tatstatur, Display) //**********************************************************************
int buttonPressed(){
TButtons nBtn; nNxtExitClicks=20; // gegen versehentliches Druecken
nBtn = nNxtButtonPressed; // check for button press switch (nBtn) { case kLeftButton: { return 1; break; }
case kEnterButton: { return 2; break; }
case kRightButton: { return 3; break; }
case kExitButton: { return 4; break; }
default: { return 0; break; } } return 0; }
//*****************************************
int getkey() { int k, buf;
k=buttonPressed(); buf=buttonPressed(); while (buf!=0) { buf=buttonPressed(); wait1Msec(20);} return k; }
//**********************************************************************
task DisplayValues(){ int i; // input number = Sensoren an verdeckter Schicht int j; // neuron number = Outputs an Ausgabe-Schicht
while(true) {
printXY( 0, 55, " out(0) | out(1)"); printXY(48, 47, "|"); printXY(48, 39, "|"); printXY(48, 31, "|");
printXY( 0, 63, "IN:"); // OBEN printXY(15, 63, "%2.0f", Neuron0[0].in[0]); // Inputs nebeneinander printXY(30, 63, "%2.0f", Neuron0[0].in[1]); printXY(45, 63, "%2.0f", Neuron0[0].in[2]);
// LINKER BEREICH printXY(00, 47, "%3.1f", Neuron1[0].w[0]); // Wichtungen Ausgabe-Schicht printXY(12, 39, "%3.1f", Neuron1[0].w[1]); // (Neuron 0) printXY(24, 47, "%3.1f", Neuron1[0].w[2]);
// RECHTER BEREICH printXY(00+53, 47, "%3.1f", Neuron1[1].w[0]); // Wichtungen Ausgabe-Schicht printXY(12+53, 39, "%3.1f", Neuron1[1].w[1]); // (Neuron 1) printXY(24+53, 47, "%3.1f", Neuron1[1].w[2]);
// MITTE-UNTEN LINKS printXY(18, 31, "%5.2f", Neuron1[0].th); // Schwellwert Ausgabe-Neuron 0
printXY( 0, 31, "th="); // MITTE-UNTEN RECHTS printXY(18+53, 31, "%5.2f", Neuron1[1].th); // Schwellwert Ausgabe-Neuron 1
printXY( 0, 23, "OUT"); // UNTEN RECHTS (unter Ausgabeschicht) printXY(48, 23, "%3.1f", Neuron1[0].out); // 1. Output printXY(48+25, 23, "%3.1f", Neuron1[1].out); // 2. Output
// GANZ UNTEN println(7, "%s", MenuText); // Menue-Zeile fuer Tastatur-Steuerung
} return; }
//**********************************************************************
//********************************************************************** // File I/O //********************************************************************** const string sFileName = "Memory.dat";
TFileIOResult nIoResult; TFileHandle fHandle;
int nFileSize = (L0 + L1 + L2 + L3 +1)*100;
void SaveMemory() { int i, j;
CloseAllHandles(nIoResult); println(6,"%s","Save Memory..."); wait1Msec(500); PlaySound(soundBeepBeep); wait1Msec(11);
Delete(sFileName, nIoResult);
OpenWrite(fHandle, nIoResult, sFileName, nFileSize); if (nIoResult==0) { eraseDisplay();
for (j=0;j<L0;j++) { for (i=0; i<ni;i++) { WriteFloat (fHandle, nIoResult, Neuron0[j].w[i]); } WriteFloat (fHandle, nIoResult, Neuron0[j].th); }
for (j=0;j<L1;j++) { for (i=0; i<ni;i++) { WriteFloat (fHandle, nIoResult, Neuron1[j].w[i]); } WriteFloat (fHandle, nIoResult, Neuron1[j].th); }
Close(fHandle, nIoResult); if (nIoResult==0) { PlaySound(soundUpwardTones); println(6,"%s","Save Memory: OK"); } else { PlaySound(soundException); println(6,"%s","Save Memory: ERROR"); } } else PlaySound(soundDownwardTones);
}
//*****************************************
void RecallMemory() { int i, j; println(6,"%s","Recall Memory"); CloseAllHandles(nIoResult); wait1Msec(500); PlaySound(soundBeepBeep); wait1Msec(11);
OpenRead(fHandle, nIoResult, sFileName, nFileSize); if (nIoResult==0) {
for (j=0;j<L0;j++) { for (i=0; i<ni;i++) { ReadFloat (fHandle, nIoResult, Neuron0[j].w[i]); } ReadFloat (fHandle, nIoResult, Neuron0[j].th); }
for (j=0;j<L1;j++) { for (i=0; i<ni;i++) { ReadFloat (fHandle, nIoResult, Neuron1[j].w[i]); } ReadFloat (fHandle, nIoResult, Neuron1[j].th); }
Close(fHandle, nIoResult); if (nIoResult==0) PlaySound(soundUpwardTones); else { PlaySound(soundException); println(6,"%s","Recall: ERROR"); } } else PlaySound(soundDownwardTones); eraseDisplay();
}
//********************************************************************** // Funktionen des neuronalen Netzes //**********************************************************************
//********************************************************************** // Inputs //**********************************************************************
task RefreshInputLayer(){ // Inputs aus Sensorwerten int i, j; while(true){ for (j=0; j<L0; j++) { // alle Inputs an alle Eingangs-Neuronen for (i=0; i<ni; i++) { Neuron0[j].in[i]=(float)SensorValue(i); } } } return; }
//*****************************************
void SetInputPattern(int i) // Inputs virtuell generiert { int j, n;
printXY(80, 63, "%d", i); for (j=0; j<L0;j++) {
for (n = 0; n <=ni-1; n++) { Neuron0[j].in[n]= i & 1; i >>= 1; } } }
//********************************************************************** // Propagierungsfunktionen: Eingaenge gewichtet aufsummieren (in -> net) //**********************************************************************
void netPropag(tNeuron &neur){ // Propagierungsfunktion 1 int i=0; // kalkuliert den Gesamt-Input (net) float s=0;
for(i=0;i<ni;i++){ s+= (neur.in[i]*neur.w[i]); // gewichtete Summe } neur.net=s; }
void netPropagThr(tNeuron &neur){ // Propagierungsfunktion 2 int i=0; // kalkuliert den Gesamt-Input (net) float s=0; // und beruecksichtigt Schwellwert
for(i=0;i<ni;i++){ s+= (neur.in[i]*neur.w[i]); // gewichtete Summe } neur.net=s-neur.th; // abzueglich Schwellwert }
//********************************************************************** // Aktivierungsfunktionen inkl. Ausgabe (net -> act -> out) //**********************************************************************
void act_01(tNeuron &neur){ // Aktivierungsfunktion 1 T1: x -> [0; +1] if (neur.net>=0) // 0-1-Schwellwertfunktion {neur.out=1;} // Fkt.-Wert: 0 oder 1 else {neur.out=0;} }
void actIdent(tNeuron &neur){ // Aktivierungsfunktion 2 T2: x -> x neur.out=neur.net; // Identitaets-Funktion } // Fkt-Wert: Identitaet
void actFermi(tNeuron &neur){ // Aktivierungsfunktion 3 T3: x -> [0; +1] float val; // Fermi-Fkt. (Logistisch, differenzierbar) float c=6.0; // c= Steilheit, bei c=1: flach, val= (1/(1+(exp(-c*neur.net)))); // c=10: Sprung zwischen x E [-0.1; +0.1] neur.out=val; }
void actTanH(tNeuron &neur){ // Aktivierungsfunktion 4 T4: x -> [-1; +1] float val; // Tangens Hyperbolicus, differenzierbar float c=2.0; // c= Steilheit, bei c=1: flach val= tanh(c*neur.net); // c=3: Sprung zwischen x E [-0.1; +0.1] neur.out=val; }
//********************************************************************** // Reset / Init //**********************************************************************
void ResetNeuron(tNeuron &neur, int rand){ // alles auf Null bzw. randomisiert int i;
for (i=0; i<ni; i++) { neur.in[i]=0; // Einzel-Input (Dendrit) if (rand==0) {neur.w[i]=0;} // Einzel-Wichtung (Dendrit)=0 else neur.w[i]=-1.0+random(5)*0.4; // Einzel-Wichtung (Dendrit) randomomisiert
} neur.net=0; // totaler Input if (rand==0) {neur.th=0;} // Schwellenwert (threshold)=0 else neur.th=random(5)*0.2; // Schwellenwert (threshold) randomomisiert
neur.out=0; // errechneter Aktivierungswert=Output }
//*****************************************
void InitAllNeurons(){ // alle Netz-Neurons resetten int j; // (0 oder randomisiert)
for (j=0; j<L0; j++) { // Neuron-Schicht 0 ResetNeuron(Neuron0[j],1);}
for (j=0; j<L1; j++) { // Neuron-Schicht 1 ResetNeuron(Neuron1[j],1);}
for (j=0; j<L2; j++) { // Neuron-Schicht 2 ResetNeuron(Neuron2[j],0);}
for (j=0; j<L3; j++) { // Neuron-Schicht 3 ResetNeuron(Neuron3[j],0);} }
//*****************************************
void PrepThisNeuralNet() // for testing { ; // defaults }
//********************************************************************** // einzelne Neurons schichtenweise durchrechnen //**********************************************************************
task RefreshLayers(){ int j, k;
while(true){
for (j=0;j<L0;j++) { netPropagThr(Neuron0[j]); // net-Input Layer 0 actFermi(Neuron0[j]); // Aktivierung T: Fermi-Funktion -> out for (k=0;k<L1;k++) { Neuron1[k].in[j] = Neuron0[j].out; } // Synapse Neuron0->Neuron1 }
for (j=0;j<L1;j++) { netPropagThr(Neuron1[j]); // net-Input Layer 1 actFermi(Neuron1[j]); // Aktivierung T: Fermi-Funktion -> out }
} return; }
//********************************************************************** // Lernverfahren //**********************************************************************
void LearnPerceptronRule() { // Perceptron-Lern-Modus int ErrorCount; int in; // Sensor-Kombinationen int i; // Anzahl Inputs int j; // Anzahl Ausgabe-Neurons
do { ErrorCount=0; PlaySound(soundBeepBeep); MenuText="-- << ok >> ++";
SetInputPattern(in); // virtuelles Muster praesentieren wait1Msec(200);
for (j=0;j<2;j++) // 1 bis Anzahl Ausgabe-Neuron {
sollOut=0; MenuText="-- << ok >> ++"; printXY(0,15, "soll:"); printXY(48+(j*25),15,"%2.0f", sollOut); do // erzeugten Output berichtigen { key=getkey();
if (key==1) { if (sollOut>0) sollOut-=1; } else if (key==3) { if (sollOut< 1) sollOut+=1; } printXY(0,15, "soll:"); printXY(48+(j*25),15,"%2.0f", sollOut); wait1Msec(100); } while ((key!=2)&&(key!=4));
println(5, " ");
//................................................... if (key==4) { // Lern-Modus ENDE PlaySound(soundException); key=0; return; } //....................................................
// Lern-Modus START //.................................................... if (sollOut==Neuron0[j].out ) // teachOut korrekt { PlaySound(soundBlip); PlaySound(soundBlip); wait1Msec(100); } //.................................................... if (sollOut!=Neuron0[j].out) // teachOut falsch { PlaySound(soundException); wait1Msec(100); ErrorCount+=1; //................................................... // LERNEN
for (i=0; i<=L0; i++) // fuer alle i (Inputs) { // Wichtungen anpassen (Delta-Regel) Neuron0[j].w[i] = Neuron0[j].w[i]+ (lf *Neuron0[j].in[i]*(sollOut-Neuron0[j].out)); }
if (sollOut!=Neuron0[j].out) // Schwelle anpassen (Delta-Regel, erweitert) { Neuron0[j].th = Neuron0[j].th + (lf *(sollOut-Neuron0[j].out)); } //................................................... } // if (sollOut!=Neuron0[j].out)
} // for j
} while (ErrorCount>0);
PlaySound(soundUpwardTones); PlaySound(soundUpwardTones); }
//**********************************************************************
int IOpattern[2<<(ni-1)][L1];
void LearnBackpropagation() { // Backpropagation-Lern-Modus // 1 verdeckte/Eingabe-Schicht(L0) + 1 Ausgabe-Schicht(L1) int idummy;
int count; int in; // angelegtes Sensor/Input-Muster(Pattern) int i; // Zaehler Inputs int j, k; // Index fuer Ausgabe-Neurons L1 int m; // Index fuer Eingabe-Neurons L0
float f_sig1; // Fehler-Signal Schicht 1 zum Lernen v. Wichtung und Schwelle float f_sig0; // Fehler-Signal Schicht 0 zum Lernen v. Wichtung und Schwelle
float f_sum=0; // Fehler verdeckte Schicht (Summe (Wichtung*Fehlersignal)) float out; // Neuron-out, Dummy; float fehler=0; // Summe der Fehlersignale float epsilon; // max. zulssiger Netz-Fehler
float delta_w0, delta_w1; // Aenderungswert der Wichtung float delta_th0, delta_th1; // Aenderungswert des Schwellwerts
bool LearnModeAuto=false; // automat. Lernen oder manuell per Taste
count=599; epsilon=(float)(ni*L1)*0.1; // max. 10% Fehler
do { fehler=0;
//MenuText="-- << ok >> ++"; count-=1;
if (!LearnModeAuto) PlaySound(soundBeepBeep); else PlaySound(soundBlip);
for (in=0; in < (2<<(ni-1)); in++) // in = 1. bis letztes Eingabe-Neuron { SetInputPattern(in); // Eingabe-Muster anlegen
wait1Msec(200); // durch alle Schichten durchrechnen
for (j=0;j<L1;j++) // j = 1. bis letztes Ausgabe-Neuron { //===================================================================================== if (!LearnModeAuto) // einlesen per Tastatur {
sollOut=0; MenuText="-- << ok >> ++"; printXY(0,15, "soll:"); printXY(48+(j*25),15,"%2.0f", sollOut); do { key=getkey(); if (key==4) { // Lern-Schritt ueberspringen
IOpattern[in][j]=-99; key=0; goto NEXT_INPUT; } // if key
if (key==1) { if (sollOut==1) sollOut=0; } else if (key==3) { if (sollOut==0) sollOut=1; }
IOpattern[in][j]=sollOut; // IO-Muster in IO-array schreiben
printXY(0,15, "soll:"); printXY(48+(j*25),15,"%2.0f", sollOut); wait1Msec(100); } while ((key!=2)&&(key!=4)); } //===================================================================================== else // autom. einlesen per IO-array { PlaySound(soundBlip); if (IOpattern[in][j]!=-99) { sollOut=IOpattern[in][j];} else goto NEXT_INPUT;
printXY(48+(j*25),15,"%2.0f", sollOut); } //=====================================================================================
wait1Msec(200); // time to calculate net, println(6, " ");
if (!LearnModeAuto) { // Lern-Modus START //.................................................... if (sollOut==Neuron1[j].out ) // teachOut korrekt { PlaySound(soundBlip); PlaySound(soundBlip); wait1Msec(100); goto NEXT_INPUT; } // //.................................................... else // // teachOut falsch { PlaySound(soundException); wait1Msec(100); } }
// 1. Schritt: Fehlersignal Ausgabeschicht bestimmen // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out=Neuron1[j].out;
f_sig1=out*(1-out)*(sollOut-out); // Fehler-Signal (j) fuer Ausgabeschicht Neuron1[j].d=f_sig1; // im Neuron1[j] speichern
fehler=fehler + abs(sollOut-out); // Gesamtfehler aller Ausgabeneuronen
} // for j= 1. bis letztes Ausgabe-Neuron
// 2. Schritt: Fehlersignale verdeckte/Eingabe-Schicht bestimmen // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (k=0;k<L1;k++) // k = 1. bis letztes Neuron in der Eingabe-Schicht
{ f_sig1=Neuron1[k].d; // Fehlersignal des Nachfolger-(Ausgabe)-Neurons (k)
f_sum=0; for (m=0; m<L0; m++) { f_sum=f_sum + (Neuron1[k].w[m] * f_sig1); } // Summe ueber alle (Wichtungen(L1)*FSignale(L1)
out=Neuron1[k].out; f_sig0 = out * (1-out) * f_sum; // Fehlersignal Eingabe/verdeckte Schicht L0
for (m=0; m<L0; m++) { Neuron0[m].d = f_sig0; // Fehlersignal im Neuron0 speichern }
// 3. Schritt: neue Wichtungen und Schwellenwerte fuer Ausgabeschicht L1 berechnen // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (m=0; m<L0; m++) { out=Neuron0[m].out; // Vorgaenger-out delta_w1 = lf * out * f_sig1; // Aenderungswert fuer Wichtungen Ausgabe-Schicht
Neuron1[k].w[m] = Neuron1[k].w[m] + delta_w1; // neue Wichtungen fuer Ausgabe-Schicht L1 }
delta_th1 = lf * f_sig1; // Aenderungswert delta_th Neuron1[k].th = Neuron1[k].th - delta_th1; // neue Schwellenw. fuer Ausgabe-Schicht L1
// 4. Schritt: neue Wichtungen und Schwellenwerte fuer Eingabeschicht L0 berechnen // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (m=0; m<L0; m++) { f_sig0 = Neuron0[m].d;
for (i=0; i<ni; i++) { out=Neuron0[m].in[i]; // Vorgaunger-out = Sensor-out = eigener Input delta_w0 = lf * out * f_sig0; // Aenderungswert fuer Wichtungen Eingabe-Schicht
Neuron0[m].w[i] = Neuron0[m].w[i] + delta_w0; // neue Wichtungen fuer Eingabe-Schicht L0 }
delta_th0 = lf * f_sig0; // Aenderungswert fuer Schellenw. Eingabe-Schicht Neuron0[m].th = Neuron0[m].th - delta_th0; // neue Schwellenw. fuer Eingabe-Schicht L0 } NEXT_INPUT: ;
} // for k = 1. bis letztes Neuron in der Ausgabe-Schicht
} // for in=1... (Eingabe-Muster)
if (!LearnModeAuto) { MenuText="Menu manual. auto";
do { key=getkey(); if (key==1) { return; } if (key==2) { LearnModeAuto=false; } if (key==3) { LearnModeAuto=true; } if (key==4) { return; } } while (key==0); key=0; } else PlaySound(soundBlip);
if (fehler>4) lf=0.8; else lf=fehler/5; idummy=(int)(lf*10); eraseDisplay(); MenuText=(string)count+" "+(string)idummy; MenuText=MenuText+" "+" f="+(string)fehler;
} while ((fehler>epsilon)&&(count>=0)); PlaySound(soundUpwardTones); PlaySound(soundUpwardTones);
key=getkey(); MenuText= " "; }
//********************************************************************** // Programmablauf-Steuerung, Menues //**********************************************************************
int Menu_Recall() { eraseDisplay(); MenuText="<Recall Clear>"; println(7, "%s", MenuText); println(0, "%s", " Hal "+Version); println(1, "%s", "----------------"); println(2, "%s", "Reload my brain -"); println(4, "%s", " Total Recall ?"); do { key=getkey(); if (key==1) { return 1; } if (key==2) { PlaySound(soundException); } if (key==3) { return 3; } if (key==4) { PlaySound(soundException); }
wait1Msec(100); } while ((key==0)||(key==2)||(key==4)); }
int Menu_LearnSaveRun() { eraseDisplay(); MenuText="<Learn Sav Run>"; do { key=getkey(); if (key==1) { return 1; } if (key==2) { SaveMemory(); } if (key==3) { return 3; } if (key==4) { PlaySound(soundException); }
wait1Msec(100); } while ((key==0)||(key==2)||(key==4)); }
//********************************************************************** // Hauptprogramm //********************************************************************** int choice;
task main(){ SensorType(S1)=sensorTouch; SensorType(S2)=sensorTouch; SensorType(S3)=sensorTouch;
nVolume=2; InitAllNeurons(); PrepThisNeuralNet();
choice=Menu_Recall(); if (choice==1) { RecallMemory(); } // altes Gedaechtnis laden
StartTask (DisplayValues); StartTask (RefreshLayers);
while(true) { choice=Menu_LearnSaveRun(); if (choice==1) { StopTask(RefreshInputLayer); LearnBackpropagation(); // Lern-Modus } MenuText="Menue: [ESC]"; PlaySound(soundFastUpwardTones); StartTask (RefreshInputLayer); // Run-Modus do { key=getkey(); wait1Msec(100); } while (key!=4); }
}
|  |