PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Inkas Anfrage bezüglich Code, im Thema Linienfolger



Moppi
15.12.2019, 15:05
Hallo!

Leider habe ich den Code nicht mehr, den ich verwendet habe, um die untenstehenden Ausgaben zu erzeugen.
Aber trotzdem will ich mal einen Anfang machen.


Hier noch mal die Idee zum Linienfolger, wobei der ganz genaue Antrieb (mit Problemen etc.) jezt mal nicht so wichtig ist.
Es geht hier nur um eine gedankliche Skizze, um die KNN-Funktion anhand eines Beispiels zu veranschaulichen.




Ich bin mit dem Thema noch nicht durch, aber ich habe zumindest einmal den Linienfolgermechanismus mit einer Wahrheitstabelle getestet.
Als Quellcode habe ich den genommen, wo MXT den Link auf die Beispielseite gesetzt hat.


Es sind etwa 2400 bis 2800 Trainingszyklen notwendig, bis die vorgegebene Genauigkeit erreicht wurde. Die Werte, welche diese bestimmen, wurden aus dem Originalquelltext beibehalten.
Das Training dauert, auf einem nodeMCU ESP-12E, ca. 5 Sekunden.


Da es sich um analoge Berechnungen handelt, sind die Werte der Ausgabeneurone Fließkomma-Näherungswerte. Eine Berechnung im KNN stellt immer eine Annäherung dar.
Würden die Ausgaben, ohne Nachkommastellen, gerundet, erhielte man das gewünschte Ergebnis, als "0" oder "1".


Die Trainingszyklen variieren bei jedem Neustart, weil in den Berechnungen Zufallswerte mitspielen. So werden u.a. die Neuronengewichte Anfangs mit Zufallswerten und nicht mit immer gleichen festen Werten initialisiert.
Die Fließkommaergebnisse variieren genau so, weil es Näherungswerte sind und eben in den Berechnungen Zufallswerte mitspielen.


Hier ein Schema, für das Netz aus 2 Input, 2 Hidden und 2 Output Neurons (ich hatte auf die Schnelle keine runden Symbole):

34563



Im Input haben wir 2 Eingänge. Der Erste, für den linken Sensor. Der Zweite für den rechten Sensor. In der Mitte der Sensoren soll die schwarze Linie verlaufen.


Im Output haben wir 2 Ausgänge.
Der Erste, für den linken Motor, der langsam vorwärts fährt oder steht (0); oder schneller vorwärts fährt (1).
Der Zweite, für den rechten Motor, der langsam vorwärts fährt oder steht (0); oder schneller vorwärts fährt (1).




1. Wenn sich der linke Sensor neben der schwarzen Linie befindet (0), kann der linke Motor laufen (1).
2. Wenn sich der rechte Sensor neben der schwarzen Linie befindet (0), kann der rechte Motor laufen (1).
3. Wenn sich der linke Sensor auf der schwarzen Linie befindet (1), kann der linke Motor stehen oder langsam drehen (0).
4. Wenn sich der rechte Sensor auf der schwarzen Linie befindet (1), kann der rechte Motor stehen oder langsam drehen (0).


Das Ergebnis des neuronalen Netzes sieht dazu so aus; zum Vergleich wird Target (das Muster, auf das zuvor trainiert wurde) mit ausgegeben; Target ist eine Vorgabe und wird nicht durch das neuronale Netz erzeugt:


Input 0 0 Target 1 1 Output 0.98662 0.98579
Input 1 0 Target 0 1 Output 0.01587 0.99852
Input 0 1 Target 1 0 Output 0.99999 0.00660
Input 1 1 Target 1 0 Output 0.99995 0.01100


Input sind die Werte des linken und rechten Lichtsensors, auf die das KNN eine Ausgabe (Output) für den linken und rechten Motor erzeugen würde, um das Fahrzeug zu steuern.


Das stellt für mich jetzt den einfachsten Fall einer solchen Steuerung dar, den man mit einem KNN umsetzen kann. Sozusagen das Notwendigste. Das Trainingsset ist als Wahrheitstabelle fest vorgegeben und wird durch die CPU so lange abgearbeitet, bis die richtigen Reaktionen der Ausgänge, auf die Eingangssignale, stattfinden.
Verwendet habe ich 2 Eingangsneuronen, 2 versteckte Neuronen und 2 Ausgangsneuronen.


Mit einem versteckten Neuron funktioniert es auch. Die Ergebnisse sind dann etwas genauer, die Trainingsphase dauert aber ca. 9 Sekunden, bei ca. 9000 Zyklen:


Input 0 0 Target 1 1 Output 0.98612 0.98504
Input 1 0 Target 0 1 Output 0.01456 0.99999
Input 0 1 Target 1 0 Output 1.00000 0.00847
Input 1 1 Target 1 0 Output 1.00000 0.00998


Mit 10 versteckten Neuronen, ca. 900 Trainingszyklen, bei 4 Sekunden:


Input 0 0 Target 1 1 Output 0.98621 0.98668
Input 1 0 Target 0 1 Output 0.01535 0.99720
Input 0 1 Target 1 0 Output 0.99992 0.00877
Input 1 1 Target 1 0 Output 0.99469 0.00908


Mit 20 versteckten Neuronen, ca. 800 Trainingszyklen, bei 4 Sekunden:


Input 0 0 Target 1 1 Output 0.98555 0.98742
Input 1 0 Target 0 1 Output 0.01570 0.99923
Input 0 1 Target 1 0 Output 0.99999 0.00424
Input 1 1 Target 1 0 Output 0.99645 0.01238


Mit 50 versteckten Neuronen dauern die Berechnungen ca. 12 Sekunden, bei ca. 650 Trainingszyklen:


Input 0 0 Target 1 1 Output 0.98657 0.98793
Input 1 0 Target 0 1 Output 0.01528 0.99561
Input 0 1 Target 1 0 Output 0.99999 0.00667
Input 1 1 Target 1 0 Output 0.99218 0.01062






Der Code müsste dafür so modifiziert werden, wenn der auf einem nodeMCU ausgeführt wird.
Nur die Ausgabe entspricht nicht der, die ich hatte, weil ich dafür den Code
noch weiter geändert habe, bloß diesen geänderten Code habe ich nicht mehr.




/************************************************** ****************
ArduinoANN - An artificial neural network for the Arduino
All basic settings can be controlled via the Network Configuration
section.
See robotics.hobbizine.com/arduinoann.html for details.
************************************************** ****************/


#include <math.h>


/************************************************** ****************
Network Configuration - customized per network
************************************************** ****************/


const float LearningRate = 0.3;
const float Momentum = 0.9;
const float InitialWeightMax = 0.5;
const float Success = 0.0004;


const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4; //zu trainierende Muster

const byte Input[PatternCount][InputNodes] =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};



const byte Target[PatternCount][OutputNodes] =
{
{ 1, 1 },
{ 0, 1 },
{ 1, 0 },
{ 1, 0 }
};


/************************************************** ****************
End Network Configuration
************************************************** ****************/




int i, j, p, q, r;
int ReportEvery1000;
int RandomizedIndex[PatternCount];
long TrainingCycle;
float Rando;
float Error;
float Accum;




float Hidden[HiddenNodes];
float Output[OutputNodes];
float HiddenWeights[InputNodes + 1][HiddenNodes];
float OutputWeights[HiddenNodes + 1][OutputNodes];
float HiddenDelta[HiddenNodes];
float OutputDelta[OutputNodes];
float ChangeHiddenWeights[InputNodes + 1][HiddenNodes];
float ChangeOutputWeights[HiddenNodes + 1][OutputNodes];


void setup() {
Serial.begin(9600);
randomSeed(analogRead(3));
ReportEvery1000 = 1;
for ( p = 0 ; p < PatternCount ; p++ )
{
RandomizedIndex[p] = p ;
}
}


void loop ()
{




/************************************************** ****************
Initialize HiddenWeights and ChangeHiddenWeights
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
for ( j = 0 ; j <= InputNodes ; j++ )


{
ChangeHiddenWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
/************************************************** ****************
Initialize OutputWeights and ChangeOutputWeights
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i ++ )
{
for ( j = 0 ; j <= HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
Serial.println("Initial/Untrained Outputs: ");
toTerminal();
/************************************************** ****************
Begin training
************************************************** ****************/


for ( TrainingCycle = 1 ; TrainingCycle < 2147483647 ; TrainingCycle++)
{
yield();


/************************************************** ****************
Randomize order of training patterns
************************************************** ****************/


for ( p = 0 ; p < PatternCount ; p++)
{
q = random(PatternCount);
r = RandomizedIndex[p] ;
RandomizedIndex[p] = RandomizedIndex[q] ;
RandomizedIndex[q] = r ;
}
Error = 0.0 ;
/************************************************** ****************
Cycle through each training pattern in the randomized order
************************************************** ****************/
for ( q = 0 ; q < PatternCount ; q++ )
{
p = RandomizedIndex[q];


/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}


/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;
Error += 0.5 * (Target[p][i] - Output[i]) * (Target[p][i] - Output[i]) ;
}


/************************************************** ****************
Backpropagate errors to hidden layer
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = 0.0 ;
for ( j = 0 ; j < OutputNodes ; j++ ) {
Accum += OutputWeights[i][j] * OutputDelta[j] ;
}
HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;
}




/************************************************** ****************
Update Inner-->Hidden Weights
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
ChangeHiddenWeights[InputNodes][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[InputNodes][i] ;
HiddenWeights[InputNodes][i] += ChangeHiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;
}
}


/************************************************** ****************
Update Hidden-->Output Weights
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i ++ )
{
ChangeOutputWeights[HiddenNodes][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[HiddenNodes][i] ;
OutputWeights[HiddenNodes][i] += ChangeOutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
OutputWeights[j][i] += ChangeOutputWeights[j][i] ;
}
}
}


/************************************************** ****************
Every 1000 cycles send data to terminal for display
************************************************** ****************/
ReportEvery1000 = ReportEvery1000 - 1;
if (ReportEvery1000 == 0)
{
Serial.println();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);


toTerminal();


if (TrainingCycle == 1)
{
ReportEvery1000 = 999;
}
else
{
ReportEvery1000 = 1000;
}
}




/************************************************** ****************
If error rate is less than pre-determined threshold then end
************************************************** ****************/


if ( Error < Success ) break ;
}
Serial.println ();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);


toTerminal();


Serial.println ();
Serial.println ();
Serial.println ("Training Set Solved! ");
Serial.println ("--------");
Serial.println ();
Serial.println ();
ReportEvery1000 = 1;
while(1){yield();}
}


void toTerminal()
{


for ( p = 0 ; p < PatternCount ; p++ )
{
Serial.println();
Serial.print (" Training Pattern: ");
Serial.println (p);
Serial.print (" Input ");
for ( i = 0 ; i < InputNodes ; i++ )
{
Serial.print (Input[p][i], DEC);
Serial.print (" ");
}
Serial.print (" Target ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Target[p][i], DEC);
Serial.print (" ");
}
/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}


/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
}
Serial.print (" Output ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Output[i], 5);
Serial.print (" ");
}
}




}



MfG
:Weihnacht

- - - Aktualisiert - - -

Zur Frage, was muss wo gestellt werden oder eingetragen werden, ist ein Grundverständnis notwendig.

in Input[PatternCount][InputNodes] werden die möglichen Eingangszustände vorgegeben, bei 2 Eingängen sind es eben 4 Stück (PatternCount = 4).
in Target[PatternCount][OutputNodes] werden zu den Eingangszuständen, die Ausgangszustände vorgegeben.

Das Netz wird trainiert, indem die Eingangszustände "eingeübt" werden. Dafür werden bei jedem Durchlauf immer die Ergebnisse an den Ausgabeneuronen mit den Werten in Target[x][y] verglichen und dann Korrekturen durchgeführt. Wenn eine vorgegebene Genauigkeit erreicht ist, ist das Training beendet. Ab dem Zeitpunkt kann dann jeder der möglichen Eingangszustände auf den trainierten Ausgangszustand "übersetzt" werden. Und zwar ohne, dass die Trainingsdaten für die Ausgänge hinzugezogen werden.
So wie es vorliegt, mit einer verdeckten Schicht, ähnelt das Ganze mehr einem Logikgatter mit beliebig vielen Eingängen und beliebig vielen Ausgängen. Die Logik ist aber nicht fest verdrahtet, sondern frei wählbar. Das Programm rechnet dann so lange, bis es alle Eingangszustände auf die gewünschten Ausgangszustände abbilden kann.

inka
17.12.2019, 11:55
ich glaube, man muss zunächst einmal die KI bzw. NN "freaks" in zwei kategorien aufteilen. 1) anwender und 2) entwickler. Zu einem entwickler eigne ich mich nicht. Bin also "nur" anwender...
Das ist ok, ich benutze - wie milionen andere - ein pkw, ohne im detail zu wissen wie jede einzelner part da funktioniert. Vereinfacht ausgedrückt reicht es zu wissen, wenn ich die kupplung trete, kann ich schalten, ohne dass es knirscht. So möchte ich auch KI bzw. NN nutzen, ich stelle mir das folgendermassen vor:

Den code, den Moppi (danke!) hier gepostet habe, habe ich erstmal etwas aufgeräumt (sorry!) indem ich alles, was in der loop war in eine funktion "calculation()" quasi ausgelagert habe, um die loop übersichtlich zu halten. Ob man diese funktion noch einmal sinnvollerweise in mehr einzelteile, die man sich - nur bei bedarf - anschauen kann, aufteilen sollte, lasse ich erstmal dahingestellt, der code funktioniert zunächst einmal so wie ich ihn aufgeteilt habe.


/************************************************** ****************
ArduinoANN - An artificial neural network for the Arduino
All basic settings can be controlled via the Network Configuration
section.
See robotics.hobbizine.com/arduinoann.html for details.
************************************************** ****************/


#include <math.h>


/************************************************** ****************
Network Configuration - customized per network
************************************************** ****************/


const float LearningRate = 0.3;
const float Momentum = 0.9;
const float InitialWeightMax = 0.5;
const float Success = 0.0004;


const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4; //zu trainierende Muster

const byte Input[PatternCount][InputNodes] =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};
//};


const byte Target[PatternCount][OutputNodes] =
{
{ 1, 1 },
{ 0, 1 },
{ 1, 0 },
{ 1, 0 }
};


/************************************************** ****************
End Network Configuration
************************************************** ****************/




int i, j, p, q, r;
int ReportEvery1000;
int RandomizedIndex[PatternCount];
long TrainingCycle;
float Rando;
float Error;
float Accum;




float Hidden[HiddenNodes];
float Output[OutputNodes];
float HiddenWeights[InputNodes + 1][HiddenNodes];
float OutputWeights[HiddenNodes + 1][OutputNodes];
float HiddenDelta[HiddenNodes];
float OutputDelta[OutputNodes];
float ChangeHiddenWeights[InputNodes + 1][HiddenNodes];
float ChangeOutputWeights[HiddenNodes + 1][OutputNodes];


void setup() {
Serial.begin(9600);
randomSeed(analogRead(3));
ReportEvery1000 = 1;
for ( p = 0 ; p < PatternCount ; p++ )
{
RandomizedIndex[p] = p ;
}
}


void loop ()
{

//input_NN();

calculation();
}


/************************************************** ****************
Input
************************************************** ****************/

int intInput()
{
char str[10];
int charcount = 0;
memset(str, 0, sizeof(str)); // String-Puffer löschen
while (!Serial.available()); // Warten bis 1. Zeichen im Eingangspuffer
delay(100); // Warten auf weitere Zeichen im Eigangspuffer
while (Serial.available() && charcount < 9)
{
str[charcount] = Serial.read(); // Zeichen aus Eingangspuffer lesen
charcount++;
}
return atoi(str); // String in Integer-Rückgabewert wandeln
}

void input_NN (void)
{
/*
Serial.print("Geben Sie > LearningRate < (standard = 0.3)ein: ");
LearningRate = intInput();
Serial.println(LearningRate);

Serial.print("Geben Sie > Momentum < (standard = 0.9)ein: ");
Momentum = intInput();
Serial.println(Momentum);

Serial.print("Geben Sie > InitialWeightMax < (standard = 0.5)ein: ");
InitialWeightMax = intInput();
Serial.println(InitialWeightMax);

Serial.print("Geben Sie > Success < (standard = 0.0004)ein: ");
Success = intInput();
Serial.println(Success);

Serial.print("Geben Sie > InputNodes < (standard = 2)ein: ");
InputNodes = intInput();
Serial.println(InputNodes);

Serial.print("Geben Sie > HiddenNodes < (standard = 2)ein: ");
HiddenNodes = intInput();
Serial.println(HiddenNodes);

Serial.print("Geben Sie > OutputNodes < (standard = 2)ein: ");
OutputNodes = intInput();
Serial.println(OutputNodes);

Serial.print("Geben Sie > PatternCount < (standard = 4)ein: ");
PatternCount = intInput();
Serial.println(PatternCount);
*/
/*
Serial.print("Das Ergebnis Wert1 * Wert2 ist = ");
long wert3 = (long)wert1 * wert2;
Serial.println(wert3);
*/
Serial.println();
}


void calculation()
{
/************************************************** ****************
Initialize HiddenWeights and ChangeHiddenWeights
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
for ( j = 0 ; j <= InputNodes ; j++ )


{
ChangeHiddenWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
/************************************************** ****************
Initialize OutputWeights and ChangeOutputWeights
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i ++ )
{
for ( j = 0 ; j <= HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
Serial.println("Initial/Untrained Outputs: ");
toTerminal();
/************************************************** ****************
Begin training
************************************************** ****************/


for ( TrainingCycle = 1 ; TrainingCycle < 2147483647 ; TrainingCycle++)
{
yield();


/************************************************** ****************
Randomize order of training patterns
************************************************** ****************/


for ( p = 0 ; p < PatternCount ; p++)
{
q = random(PatternCount);
r = RandomizedIndex[p] ;
RandomizedIndex[p] = RandomizedIndex[q] ;
RandomizedIndex[q] = r ;
}
Error = 0.0 ;
/************************************************** ****************
Cycle through each training pattern in the randomized order
************************************************** ****************/
for ( q = 0 ; q < PatternCount ; q++ )
{
p = RandomizedIndex[q];


/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}


/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;
Error += 0.5 * (Target[p][i] - Output[i]) * (Target[p][i] - Output[i]) ;
}


/************************************************** ****************
Backpropagate errors to hidden layer
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = 0.0 ;
for ( j = 0 ; j < OutputNodes ; j++ ) {
Accum += OutputWeights[i][j] * OutputDelta[j] ;
}
HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;
}




/************************************************** ****************
Update Inner-->Hidden Weights
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
ChangeHiddenWeights[InputNodes][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[InputNodes][i] ;
HiddenWeights[InputNodes][i] += ChangeHiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;
}
}


/************************************************** ****************
Update Hidden-->Output Weights
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i ++ )
{
ChangeOutputWeights[HiddenNodes][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[HiddenNodes][i] ;
OutputWeights[HiddenNodes][i] += ChangeOutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
OutputWeights[j][i] += ChangeOutputWeights[j][i] ;
}
}
}


/************************************************** ****************
Every 1000 cycles send data to terminal for display
************************************************** ****************/
ReportEvery1000 = ReportEvery1000 - 1;
if (ReportEvery1000 == 0)
{
Serial.println();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);


toTerminal();


if (TrainingCycle == 1)
{
ReportEvery1000 = 999;
}
else
{
ReportEvery1000 = 1000;
}
}




/************************************************** ****************
If error rate is less than pre-determined threshold then end
************************************************** ****************/


if ( Error < Success ) break ;
}
Serial.println ();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);


toTerminal();


Serial.println ();
Serial.println ();
Serial.println ("Training Set Solved! ");
Serial.println ("--------");
Serial.println ();
Serial.println ();
ReportEvery1000 = 1;
while (1) {
yield();
}
}


void toTerminal()
{


for ( p = 0 ; p < PatternCount ; p++ )
{
Serial.println();
Serial.print (" Training Pattern: ");
Serial.println (p);
Serial.print (" Input ");
for ( i = 0 ; i < InputNodes ; i++ )
{
Serial.print (Input[p][i], DEC);
Serial.print (" ");
}
Serial.print (" Target ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Target[p][i], DEC);
Serial.print (" ");
}
/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/


for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}


/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/


for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
}
Serial.print (" Output ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Output[i], 5);
Serial.print (" ");
}
}




}

Andeutungsweise habe ich in der loop mit // erstmal deaktiviert die funktion "input_NN()" eingefügt, der inhalt findet sich auch weiter unten im code. Ich möchte nämlich auch und gerade als "nur" user die möglichkeit haben, die parameter für mein NN vorab einzugeben um die einzelnen parameter auch nebeneinander stellen zu können um sie zu vergleichen. Wie ein vergleich zwischen dem ersten- und rückwärts- gang. Diesen "input code" habe ich (weil die datentypenumwandlung nicht funktioniert hat) nur für die "int" dAtentypen freigeschaltet:



#include <math.h>


/************************************************** ****************
Network Configuration - customized per network
************************************************** ****************/


float LearningRate = 0.3;
int Momentum = 0.9;
int InitialWeightMax = 0.5;
int Success = 0.0004;


int InputNodes; // = 2;
int HiddenNodes; // = 2;
int OutputNodes; // = 2;
int PatternCount; // = 4; //zu trainierende Muster



void setup()
{

Serial.begin(9600);
}

void loop()
{

input_NN();

}
int intInput()
{
char str[10];
int charcount = 0;
memset(str, 0, sizeof(str)); // String-Puffer löschen
while (!Serial.available()); // Warten bis 1. Zeichen im Eingangspuffer
delay(100); // Warten auf weitere Zeichen im Eigangspuffer
while (Serial.available() && charcount < 9)
{
str[charcount] = Serial.read(); // Zeichen aus Eingangspuffer lesen
charcount++;
}
return atoi(str); // String in Integer-Rückgabewert wandeln
}

void input_NN (void)
{
/*
Serial.print("Geben Sie > LearningRate < (standard = 0.3)ein: ");
LearningRate = intInput();
Serial.println(LearningRate);

Serial.print("Geben Sie > Momentum < (standard = 0.9)ein: ");
Momentum = intInput();
Serial.println(Momentum);

Serial.print("Geben Sie > InitialWeightMax < (standard = 0.5)ein: ");
InitialWeightMax = intInput();
Serial.println(InitialWeightMax);

Serial.print("Geben Sie > Success < (standard = 0.0004)ein: ");
Success = intInput();
Serial.println(Success);
*/
Serial.print("Geben Sie > InputNodes < (standard = 2)ein: ");
InputNodes = intInput();
Serial.println(InputNodes);

Serial.print("Geben Sie > HiddenNodes < (standard = 2)ein: ");
HiddenNodes = intInput();
Serial.println(HiddenNodes);

Serial.print("Geben Sie > OutputNodes < (standard = 2)ein: ");
OutputNodes = intInput();
Serial.println(OutputNodes);

Serial.print("Geben Sie > PatternCount < (standard = 4)ein: ");
PatternCount = intInput();
Serial.println(PatternCount);

Serial.println();
}


ich weiss es nicht wo es für einen "nur" user sinn macht welche daten und parameter als freie (mit einer empfehlung versehen vielleicht) eingabe zu schalten und welche nicht. So eine möglichkeit wäre eine super hilfe für das verständnis der KI und NN. Auch ohne ein detailiertes wissen um die innereien. Wie beim auto eben...

Moppi
17.12.2019, 12:35
Hallo inka,

hier ein Auszug aus Deinem Code, um etwas zu erklären.


/************************************************** ****************
Network Configuration - customized per network
************************************************** ****************/


const float LearningRate = 0.3;
const float Momentum = 0.9;
const float InitialWeightMax = 0.5;
const float Success = 0.0004;


const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4; //zu trainierende Muster


Die Variablen sind als Konstanten definiert und damit nicht beschreibbar.
Wenn Du InputNodes oder PatternCount ändern/beschreiben willst, musst Du das "const" entfernen.


:Weihnacht

inka
17.12.2019, 13:48
ich versuchs mal mit einer reaktion auf Moppis ausführungen...


Zur Frage, was muss wo gestellt werden oder eingetragen werden, ist ein Grundverständnis notwendig.
hier wäre meine antwort für einen user ein "nein".
Ich komme wieder zum beispiel auto zurück. Die zeiten, wo im theorieunterricht einem noch beigebracht wurde wie man die zündkerzen wechselt sind definitiv überholt...
Frage ist was will man hier: wie es einer hier schon mal gesagt hat "intelektuelle inzucht" oder ein plausibel verständlich machen von abläufen, die man für die anwendung selbst nicht verstanden haben muss...



Das Netz wird trainiert, indem die Eingangszustände "eingeübt" werden. Dafür werden bei jedem Durchlauf immer die Ergebnisse an den Ausgabeneuronen mit den Werten in Target[x][y] verglichen und dann Korrekturen durchgeführt. Wenn eine vorgegebene Genauigkeit erreicht ist, ist das Training beendet. Ab dem Zeitpunkt kann dann jeder der möglichen Eingangszustände auf den trainierten Ausgangszustand "übersetzt" werden. Und zwar ohne, dass die Trainingsdaten für die Ausgänge hinzugezogen werden.
So wie es vorliegt, mit einer verdeckten Schicht, ähnelt das Ganze mehr einem Logikgatter mit beliebig vielen Eingängen und beliebig vielen Ausgängen. Die Logik ist aber nicht fest verdrahtet, sondern frei wählbar. Das Programm rechnet dann so lange, bis es alle Eingangszustände auf die gewünschten Ausgangszustände abbilden kann.
das klingt als hintergrundwissen einleuchtend, liesse sich aber mit meiner idee von praktischen durchläufen mit zu verädernden parametern wesentlich besser veranschaulichen...

Moppi
17.12.2019, 14:39
Das Problem an dem Beispiel, das von MXT ursprünglich verlinkt hatte, ist einfach, dass es nicht ganz für Einsteiger ist. Steht in dem Text auf der Seite.
Dennoch ist so einfach, dass man damit zurechtkommen kann.

Um es mal mit dem Auto zu vergleichen, so sind in dem Beispielcode Einstellungen enthalten, welche die Motorsteuerung betreffen, dafür benötigt man etwas mehr Verständnis des Motors. Um das Auto zu fahren, brauchst Du aber nur ein paar Parameter verändern, Lenkung, Gas ...

Ich hatte das versucht etwas abzusetzen, um auf die wichtigen Werte aufmerksam zu machen, nämlich diese:



const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4; //zu trainierende Muster

const byte Input[PatternCount][InputNodes] =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};



const byte Target[PatternCount][OutputNodes] =
{
{ 1, 1 },
{ 0, 1 },
{ 1, 0 },
{ 1, 0 } };

Auf diese Werte bin ich beispielhaft eingegangen.
Und wie gesagt, wenn Du Werte zur Laufzeit ändern willst, die mit "const" bezeichnet sind, wird das nicht funktionieren, weil die eben so geschützt sind vor Änderung.
Dann muss das "const" weg.



MfG
:Weihnacht

- - - Aktualisiert - - -

Inka, ich verstehe was Du willst.
Ich weiß aber nicht, in wie weit Du bis jetzt etwas von der Materie verstanden hast.

Weil ich gerne helfen will, versuche ich das noch mal von vorne, immer bezogen auf das Beispiel, wie oben.
Ich hatte das schon mal am Anfang des Themas erklärt, aber ich mach das gerne noch mal. Ist kein Problem.

1. Logikgatter sind bekannt?

Ganz einfach ausgedrückt, hast Du hier ein Gatter, von dem Du die Eingänge und Ausgänge in der Anzahl und in der Logik selber bestimmen kannst.

Die Zahl der Eingänge und die Logik legst Du hier fest:


const byte Input[PatternCount][InputNodes] =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};


Die Zahl der Ausgänge und deren Logik legst Du hier fest:




const byte Target[PatternCount][OutputNodes] =
{
{ 1, 1 },
{ 0, 1 },
{ 1, 0 },
{ 1, 0 }
};



Im Code musst Du außerdem berücksichtigen:



int InputNodes = 2; //Die Zahl der Eingänge
int HiddenNodes = 2; //Die Zahl der verdeckten Neurone (Knoten)
int OutputNodes = 2; //Die Zahl der Ausgänge
int PatternCount = 4; //zu trainierende Eingangsmuster




Diese Werte: Zahl der Eingänge, Zahl der Ausgänge und die Zahl der Muster muss angegeben werden und mit den Daten in Input[PatternCount][InputNodes] und Target[PatternCount][OutputNodes] übereinstimmen.
Am Anfang bin ich beispielhaft auf die Änderung dieser Werte und Arrays eingegangen.

Damit dies Auto - jetzt ein Prototyp - für Dich einfach fahrbar wird, kann man eine Karosserie drum herum bauen, Knöpfe für Einstellungen zugänglich machen/lassen und andere verdecken (im Motorraum) und Türen als Zugang einbauen. Außerdem musst Du noch wissen, wo der Zündschlüssel rein gehört und Du bräuchtest einen. Zurzeit sind da, im übertragenen Sinn, überwiegend nur farbige Drähte.




MfG

inka
17.12.2019, 15:23
Lieber Moppi,

ich glaube nicht, dass Du wirklich verstehst worum es mir geht. Ich versuche - wie Du auch schon oft - (oder war's doch der Rumgucker?) hier einfach einen anderen zugang zu der materie zu finden. Und nicht nur für mich...

btw: Gatter kenne ich nur vom namen her und ehrlich gesagt, interessieren sie mich nur am rande - ich will autofahren...

Als ich 1971 meinen führerschein gemacht habe, hat mir mein fahrlehrer den zündschlüssel in die hand gedrückt und gesagt - fahr mal...

Ich fand das loch, wo der schlüssel reinpasste, habe gedreht, das auto sprang an, aber weil ich zu lange draufgehalten habe, hat's gekratzt und vor lauter schreck habe ich den motor wieder abgestellt. Dass es das anlassergetriebe war, erfuhr ich erst viel später. Aber beim nächsten anlassen passierte es nicht mehr!

Das gleiche beim anfahren: 2x abgewürgt, dann mit heuledem motor und schleifender kupplung endlich mal im 2.ten gang angefahren. Auch das ist aber nur einmal passiert, beim nächsten mal nahm ich die gände der reihe nach. Und wie gings weiter? Naja, als ich im 2ten gang dann 80 fuhr und es wurde laut, hat er gefragt wann ich denn höher schalten wollte. Tja, beim käfer ging das alles noch :-)

Moppi,

was ich, und sicher einige mehr, haben möchte sind keine theoretischen erklärungen über gatter, bunte drähte und ähnliches. Und ob das KI-auto binär, oktal oder hexa fährt ist auch egal...

Wenn's geht, und nicht zu viel verlangt ist, möchte ich ein fahrbereites KI-auto, einen zündschlüssel und eine startseite, auf der ich nach belieben (wenn ich auch gerne vorschläge entgegen nehme) in den parametern schalten und walten kann. Was kann schon passieren?

Moppi
17.12.2019, 17:16
Hallo inka,

Copy & Paste, um irgendwas zum funktionieren zu bekommen,
bläht aber den Code womöglich um unnötige Zeilen auf.
Kann man zur Not machen.

Einfach mal ein Menü einbauen, wo Du ganze Datensätze eingeben kannst,
ist so schnell auch nicht gemacht. Zumal die Datensätze (Arrays) im Quelltext
vom Compiler übersetzt werden. Das sind feste Größen, die sich später nicht
einfach per Menü ändern lassen. :(

Die Vorgehensweise bei dem Quelltext würde ich so sehen:

1. Überlegen, was ich damit machen will.
2. Die Eingangszustände definieren.
3. Die Ausgangszustände definieren.
4. Den Quelltext für Deine Zwecke anpassen.

Dann wäre Dein Auto fertig, mit den Eigenschaften, die Du benötigst.
Ein Universalauto, das alles kann und nur eingestellt werden müsste,
ist es leider nicht. Bis dahin ist es ein weiter Weg.



MfG
:Weihnacht

inka
17.12.2019, 18:11
hallo Moppi,
zunächst, nichts liegt mir ferner als hier irgendwelche forderungen zu stellen, ich versuche nur zu erreichen, dass wir die KI endlich von dem podest - ich bin nur was für absolute korypheen - runterstossen :-) - wir müssen reden...

ich habs mal ein bischen aufgedröselt:

Copy & Paste, um irgendwas zum funktionieren zu bekommen, bläht aber den Code womöglich um unnötige Zeilen auf. Kann man zur Not machen. In der reinen lehre hast du natürlich recht, früher hatten die autos noch eine vernünftige karroserie, unter 1mm dicke war da nix. Heute spart man. Und optimiert. Kostet zeit, nerven, ressourcen, klima...


Einfach mal ein Menü einbauen, wo Du ganze Datensätze eingeben kannst, ist so schnell auch nicht gemacht. Zumal die Datensätze (Arrays) im Quelltext vom Compiler übersetzt werden. Das sind feste Größen, die sich später nicht einfach per Menü ändern lassen. :( Ich wollte nur einzelne werte eingeben. Da wo es nicht geht gibt es standardeinstellungen - zunächst, bis man die ersten probefahrten hinter sich hat...



1. Überlegen, was ich damit machen will. ich denke damit kann man alles machen? :-) Jetzt aber im ernst: ich wollte nur testen, was z.b. eine änderung an einem wert für konsequenzen für das ergebnis und die laufzteit hat...

2. Die Eingangszustände definieren. sind die nicht zufällig?

3. Die Ausgangszustände definieren. das geht, denke ich.

4. Den Quelltext für Deine Zwecke anpassen. und da sind wir beim problem: für das testen möchte ich eigentlich am quelltext nix anfassen - wenn Du mit quelltext das allerheiligste des Ki-systems meinst...


Dann wäre Dein Auto fertig, mit den Eigenschaften, die Du benötigst.
Ein Universalauto, das alles kann und nur eingestellt werden müsste, ist es leider nicht. Bis dahin ist es ein weiter Weg. Ich kenne ja die eigenschaften nicht. siehe punkt 1. Es soll im prinzip nur ein simples "spielzeug" sein mit dem ich verständnis für abhängigkeiten von bestimmten eingans- und ausgangs- werten, von laufzeitabhängigkeit usw. bekomme...

ich sehe schon, das wird nicht einfach sein...

Moppi
17.12.2019, 20:01
ich versuche nur zu erreichen, dass wir die KI endlich von dem podest - ich bin nur was für absolute korypheen - runterstossen

Da bin ich ja bei Dir, deshalb erkläre ich das schon so, dass es verständlich ist.
Ich denke, "Künstliche Intelligenz" muss man zuerst mal durch so was wie "einstellbares Logikgatter" ersetzen. Um der Sache und der Grundfunktion dieses Quelltextes irgendwie gerecht zu werden.


Jetzt aber im ernst: ich wollte nur testen, was z.b. eine änderung an einem wert für konsequenzen für das ergebnis und die laufzteit hat...
Das hatte ich so auch etwa verstanden.


sind die nicht zufällig?
Die Eingangszustände nein. Die Ausgangszustände: nein.
Diese Zustände sind vorgegeben.



Für die Eingabe kannst Du digitale Signale verwenden "0" oder "1".

Eingang#1: Umgebungslichtsensor (Licht da: ja / nein)
Eingang#2: Endschalter an der Fahrzeugfront (an Hindernis gestoßen: ja / nein)

Für die Ausgabe kommt im Grunde auch "0" oder "1" raus:

Ausgang#1: Fahren oder Anhalten (Fahren = 1, Anhalten = 0)
Ausgang#2: Scheinwerfer (Licht an = 1, Licht aus = 0)

Mit einer Wahrheitstabelle kann man nun die Regeln aufstellen, wie zum Beispiel:

Wenn kein Umgebungslicht da ist und das Fahrzeug angestoßen ist, dann mache am Scheinwerfer das Licht aus und halte das Fahrzeug an.
Tabelle dafür:

Eingang#1,Eingang#2,Ausgang#1,Ausgang#2
----0------,----1------,------0------,----0------

Das entspräche einem Trainingsmuster (Pattern). Davon kannst Du so viele erstellen, wie der Speicher am Ende her gibt.
Das Programm lernt dann diese Regeln auswendig.
Später reagiert es auf die Eingangszustände mit den eingeübten Ausgangszuständen.

Falls Eingangszustände nicht definiert wurden, sie aber dennoch auftreten können, kann das KNN auch darauf reagieren, womöglich sogar "richtig", in unserm Verständnis, obwohl es diese Zustände nie trainiert hat. Erst hier zeigt sich dann die wirkliche Intelligenz, bis dahin kann man davon eigentlich noch nicht so recht sprechen (finde ich). Um das letztere Verhalten auszunutzen und das Netz möglichst intelligent zu machen, benötigt man mehr Schichten, u.U. einige Schichten mehr. Hier gibt es nur eine Zwischenschicht. Ohne diese eine Zwischenschicht würde das noch trauriger aussehen, mit der Intelligenz. Dann könnte das Netz sich noch nicht einmal eine XOR-Wahrheitstabelle "merken".



MfG
:Weihnacht

- - - Aktualisiert - - -

Mal kurz geschaut:

const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4:


Können zur Laufzeit nicht geändert werden, da der Compiler Speicherplatz für Arrays, aufgrund dieser Werte, organisiert.
Diese Werte sollten mit der Wahrheitstabelle übereinstimmen und dann muss neu kompiliert werden.






Gruß

- - - Aktualisiert - - -

den Quelltext mit den Eingabemöglichkeiten habe ich einmal angepasst, damit er wenigstens ein wenig funktioniert:




/************************************************** ****************
ArduinoANN - An artificial neural network for the Arduino
All basic settings can be controlled via the Network Configuration
section.
See robotics.hobbizine.com/arduinoann.html for details.
************************************************** ****************/




#include <math.h>




/************************************************** ****************
Network Configuration - customized per network
************************************************** ****************/




float LearningRate = 0.3;
float Momentum = 0.9;
float InitialWeightMax = 0.5;
float Success = 0.0004;




const int InputNodes = 2;
const int HiddenNodes = 2;
const int OutputNodes = 2;
const int PatternCount = 4; //zu trainierende Muster


const byte Input[PatternCount][InputNodes] =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};
//};




const byte Target[PatternCount][OutputNodes] =
{
{ 1, 1 },
{ 0, 1 },
{ 1, 0 },
{ 1, 0 }
};




/************************************************** ****************
End Network Configuration
************************************************** ****************/








int i, j, p, q, r;
int ReportEvery1000;
int RandomizedIndex[PatternCount];
long TrainingCycle;
float Rando;
float Error;
float Accum;








float Hidden[HiddenNodes];
float Output[OutputNodes];
float HiddenWeights[InputNodes + 1][HiddenNodes];
float OutputWeights[HiddenNodes + 1][OutputNodes];
float HiddenDelta[HiddenNodes];
float OutputDelta[OutputNodes];
float ChangeHiddenWeights[InputNodes + 1][HiddenNodes];
float ChangeOutputWeights[HiddenNodes + 1][OutputNodes];




void setup() {
Serial.begin(9600);
randomSeed(analogRead(3));
ReportEvery1000 = 1;
for ( p = 0 ; p < PatternCount ; p++ )
{
RandomizedIndex[p] = p ;
}
}




void loop ()
{


input_NN();


calculation();
}




/************************************************** ****************
Input
************************************************** ****************/
char str[10];
int charcount;
float intInput()
{
for(int i=0;i<10;i++)str[i]=0; // String-Puffer löschen
charcount = 0;
//memset(str, 0, sizeof(str)); // String-Puffer löschen
while (!Serial.available()); // Warten bis 1. Zeichen im Eingangspuffer
delay(100); // Warten auf weitere Zeichen im Eigangspuffer
while (Serial.available() && charcount < 10)
{
str[charcount] = Serial.read(); // Zeichen aus Eingangspuffer lesen
charcount++;
}
return atof(str); // String in Integer-Rückgabewert wandeln
}


void input_NN (void)
{
Serial.println();
Serial.println();
Serial.print("Geben Sie > LearningRate < (standard = 0.3)ein: ");
LearningRate = intInput();
Serial.println(LearningRate, 5);


Serial.print("Geben Sie > Momentum < (standard = 0.9)ein: ");
Momentum = intInput();
Serial.println(Momentum, 5);


Serial.print("Geben Sie > InitialWeightMax < (standard = 0.5)ein: ");
InitialWeightMax = intInput();
Serial.println(InitialWeightMax, 5);


Serial.print("Geben Sie > Success < (standard = 0.0004)ein: ");
Success = intInput();
Serial.println(Success, 5);
/*
Serial.print("Geben Sie > InputNodes < (standard = 2)ein: ");
InputNodes = intInput();
Serial.println(InputNodes);


Serial.print("Geben Sie > HiddenNodes < (standard = 2)ein: ");
HiddenNodes = intInput();
Serial.println(HiddenNodes);


Serial.print("Geben Sie > OutputNodes < (standard = 2)ein: ");
OutputNodes = intInput();
Serial.println(OutputNodes);


Serial.print("Geben Sie > PatternCount < (standard = 4)ein: ");
PatternCount = intInput();
Serial.println(PatternCount);
*/
/*
Serial.print("Das Ergebnis Wert1 * Wert2 ist = ");
long wert3 = (long)wert1 * wert2;
Serial.println(wert3);
*/
Serial.println();
}




void calculation()
{
/************************************************** ****************
Initialize HiddenWeights and ChangeHiddenWeights
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
for ( j = 0 ; j <= InputNodes ; j++ )




{
ChangeHiddenWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
/************************************************** ****************
Initialize OutputWeights and ChangeOutputWeights
************************************************** ****************/




for ( i = 0 ; i < OutputNodes ; i ++ )
{
for ( j = 0 ; j <= HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = 0.0 ;
Rando = float(random(100)) / 100;
OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
}
}
Serial.println("Initial/Untrained Outputs: ");
toTerminal();
/************************************************** ****************
Begin training
************************************************** ****************/




for ( TrainingCycle = 1 ; TrainingCycle < 2147483647 ; TrainingCycle++)
{
yield();




/************************************************** ****************
Randomize order of training patterns
************************************************** ****************/




for ( p = 0 ; p < PatternCount ; p++)
{
q = random(PatternCount);
r = RandomizedIndex[p] ;
RandomizedIndex[p] = RandomizedIndex[q] ;
RandomizedIndex[q] = r ;
}
Error = 0.0 ;
/************************************************** ****************
Cycle through each training pattern in the randomized order
************************************************** ****************/
for ( q = 0 ; q < PatternCount ; q++ )
{
p = RandomizedIndex[q];




/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}




/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/




for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;
Error += 0.5 * (Target[p][i] - Output[i]) * (Target[p][i] - Output[i]) ;
}




/************************************************** ****************
Backpropagate errors to hidden layer
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = 0.0 ;
for ( j = 0 ; j < OutputNodes ; j++ ) {
Accum += OutputWeights[i][j] * OutputDelta[j] ;
}
HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;
}








/************************************************** ****************
Update Inner-->Hidden Weights
************************************************** ****************/








for ( i = 0 ; i < HiddenNodes ; i++ )
{
ChangeHiddenWeights[InputNodes][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[InputNodes][i] ;
HiddenWeights[InputNodes][i] += ChangeHiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;
}
}




/************************************************** ****************
Update Hidden-->Output Weights
************************************************** ****************/




for ( i = 0 ; i < OutputNodes ; i ++ )
{
ChangeOutputWeights[HiddenNodes][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[HiddenNodes][i] ;
OutputWeights[HiddenNodes][i] += ChangeOutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
OutputWeights[j][i] += ChangeOutputWeights[j][i] ;
}
}
}




/************************************************** ****************
Every 1000 cycles send data to terminal for display
************************************************** ****************/
ReportEvery1000 = ReportEvery1000 - 1;
if (ReportEvery1000 == 0)
{
Serial.println();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);




toTerminal();




if (TrainingCycle == 1)
{
ReportEvery1000 = 999;
}
else
{
ReportEvery1000 = 1000;
}
}








/************************************************** ****************
If error rate is less than pre-determined threshold then end
************************************************** ****************/




if ( Error < Success ) break ;
}
Serial.println ();
Serial.println();
Serial.print ("TrainingCycle: ");
Serial.print (TrainingCycle);
Serial.print (" Error = ");
Serial.println (Error, 5);




toTerminal();




Serial.println ();
Serial.println ();
Serial.println ("Training Set Solved! ");
Serial.println ("--------");
Serial.println ();
Serial.println ();
ReportEvery1000 = 1;


}




void toTerminal()
{




for ( p = 0 ; p < PatternCount ; p++ )
{
Serial.println();
Serial.print (" Training Pattern: ");
Serial.println (p);
Serial.print (" Input ");
for ( i = 0 ; i < InputNodes ; i++ )
{
Serial.print (Input[p][i], DEC);
Serial.print (" ");
}
Serial.print (" Target ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Target[p][i], DEC);
Serial.print (" ");
}
/************************************************** ****************
Compute hidden layer activations
************************************************** ****************/




for ( i = 0 ; i < HiddenNodes ; i++ )
{
Accum = HiddenWeights[InputNodes][i] ;
for ( j = 0 ; j < InputNodes ; j++ )
{
Accum += Input[p][j] * HiddenWeights[j][i] ;
}
Hidden[i] = 1.0 / (1.0 + exp(-Accum)) ;
}




/************************************************** ****************
Compute output layer activations and calculate errors
************************************************** ****************/




for ( i = 0 ; i < OutputNodes ; i++ )
{
Accum = OutputWeights[HiddenNodes][i] ;
for ( j = 0 ; j < HiddenNodes ; j++ )
{
Accum += Hidden[j] * OutputWeights[j][i] ;
}
Output[i] = 1.0 / (1.0 + exp(-Accum)) ;
}
Serial.print (" Output ");
for ( i = 0 ; i < OutputNodes ; i++ )
{
Serial.print (Output[i], 5);
Serial.print (" ");
}
}








}



MfG

inka
19.12.2019, 11:29
ich habe den geänderten code ein paarmal durchlaufen lassen, also das handling ist wesentlich angenehmer :-) - über ergebnisse zu sprechen wäre viel zu früh, ich bin aber der meinung, dass der geänderte - praktisch orientierte - zugang zu dem KI-code sich auf das interesse im forum an der KI und NN positiv auswirken wird. Sollte bei weiteren änderungen am code vielleicht nicht in vergessenheit geraten....