Jump to content
Tom Next - Daytrading Community
  • entries
    19
  • comments
    44
  • views
    36,568

Neuronales Netz: Die Implementierung Backprop


Mythos

1,191 views

Kommen wir zum letzten Teil der NN-Lib Implementierung: Der Lernalgorithmus.

 

 

Wie gesagt habe ich mich für den Backprop entschieden, einfach weil ich ihn kenne und verstehe ;)

 

Die implementierung ist derzeit noch nicht performance optimiert, aber das kann man später auch noch machen.

Vom Ablauf her siehts so aus:

  1. Bereche den Output des NN wie übliche und speichere dabei für jede Schicht die Inputliste (also die Outputs der Schicht davor)
     
  2. Berechne die deltas für die Output-schicht und pass die Gewichte an
     
  3. Propagiere zurück durch alle Schichten des NN, berechne die Deltas (mithilfe der Deltas der bereits berechneten Schicht) und pass die Gewichte an.

In code sieht das Ganze so aus:

bullet_go.png

void NNdoBackProp(double input[],double wantedOutput[]) {
if(!initialized)
 return;
int numberLayers= ArraySize(neuronsOnLayer);

if(ArraySize(input) < neuronsOnLayer[0])
 return;
//perform layers, each neuron per layer, calc result and save inputs per layer
double tempresults[];
double tempInputs[];
//saved inputs per layer:
double innerInputs[];
ArrayResize(innerInputs,cumInnerNeuronsTillLayer[numberLayers-1]+neuronsOnLayer[0]);
ArrayResize(tempInputs,ArraySize(input));
ArrayCopy(tempInputs,input,0,0,WHOLE_ARRAY);
ArrayCopy(innerInputs,input,0,0,WHOLE_ARRAY);
int layer,neuron,source_neuron;
//---
// 1.

for(layer= 1;layer < numberLayers;layer++) {
 ArrayResize(tempresults,neuronsOnLayer[layer]);
 for(neuron= 0; neuron < neuronsOnLayer[layer];neuron++) {
   tempresults[neuron]= calculateOutput(layer, neuron,tempInputs);
 }
 //results are inputs of next layer
 ArrayResize(tempInputs,ArraySize(tempresults));
 if(layer != numberLayers - 1)
   ArrayCopy(innerInputs,tempresults,cumInnerNeuronsTillLayer[layer]+neuronsOnLayer[0],0,WHOLE_ARRAY);
 ArrayCopy(tempInputs,tempresults,0,0,WHOLE_ARRAY);
}

//---
// 2.
double newDeltas[];
double deltas[];
double delta_w;

ArrayResize(deltas,neuronsOnLayer[numberLayers-1]);
ArrayResize(tempInputs,neuronsOnLayer[numberLayers-2]);
ArrayCopy(tempInputs,innerInputs,0,cumInnerNeuronsTillLayer[numberLayers-2]+neuronsOnLayer[0],neuronsOnLayer [numberLayers-2]);

// do backprop on final
for(neuron= 0;neuron < neuronsOnLayer[numberLayers-1];neuron++) {
 //tempresults is last output
 deltas[neuron]= calcOutputDash(numberLayers-1,neuron,tempInputs)*(wantedOutput[neuron] - tempresults[neuron]);
 for(source_neuron= 0;source_neuron < neuronsOnLayer[numberLayers-2];source_neuron++) {
   delta_w= randomDeltaW(learningRate*deltas[neuron]*tempInputs[source_neuron]);
 setWeightAt(numberLayers-2,source_neuron,neuron,weightAt(numberLayers-2,source_neuron,neuron)+delta_w);
 }
 delta_w= randomDeltaW(learningRate*deltas[neuron]);
 setBiasAt(numberLayers-2,neuron,biasAt(numberLayers-2,neuron)+delta_w);
}

//----
// 3.
//remaining
double deltaSum= 0;
for(layer= numberLayers-2;layer > 0;layer--) {
 ArrayResize(newDeltas,neuronsOnLayer[layer]);
 ArrayResize(tempInputs,neuronsOnLayer[layer-1]);
 ArrayCopy(tempInputs,innerInputs,0,cumInnerNeuronsTillLayer[layer-1]+neuronsOnLayer[0],neuronsOnLayer[layer-1]);
 //delta= phi-dash*Sum(deltak*weight_jk)
 for(neuron= 0;neuron < neuronsOnLayer[layer];neuron++) {
   deltaSum= 0;
   for(int dest_neuron= 0;dest_neuron < neuronsOnLayer[layer-1];dest_neuron++) {
     deltaSum += deltas[dest_neuron]*weightAt(layer-1,neuron,dest_neuron);
   }
   newDeltas[neuron]= calcOutputDash(layer,neuron,tempInputs)*deltaSum;
   for(source_neuron= 0;source_neuron < neuronsOnLayer[layer-1];source_neuron++) {
     delta_w= randomDeltaW(learningRate*newDeltas[neuron]*tempInputs[source_neuron]);
     setWeightAt(layer-1,source_neuron,neuron,weightAt(layer-1,source_neuron,neuron)+delta_w);
   }

   delta_w= randomDeltaW(learningRate*newDeltas[neuron]);
   setBiasAt(layer-1,neuron,biasAt(layer-1,neuron)+delta_w);
 }
 ArrayResize(deltas,neuronsOnLayer[layer]);
 ArrayCopy(deltas,newDeltas,0,0,WHOLE_ARRAY);
}
}

 

Wie man sieht ist es gar nicht so wenig was berechnet werden muss, trotzdem läuft es zumindest für kleinere Netze (unerwartet) schnell.

 

Ein wichtiger Punkt beim Backprop ist die Ableitung der Aktivierungsfunktion. Für Linear und Sigmoid ist sie wenigstens definiert, die Heavy Side ist leider formal gar nicht differenzierbar, aber die Ableitung gibt in diesem Fall sowieso nur die Richtung der Änderung vor. Daher hab ich sie für die HeavySide auf 1 gesetzt (so wie bei der Linearen). Das ist zwar nicht 100% korrekt, aber die Richtung stimmt.

 

Bei ersten Versuchen hab ich dann auch gemerkt wie wichtig zufällige Startgewichte und eine kleine Lernrate sind. Bei zu hoher Lernrate ( 0.5 zB) sind die Gewichte gegen unendlich explodiert. Bei fixen Startgewichten von 0.1 hat das Netz für einen Input und Output zwar wunderbar gelernt, jedoch unabhängig vom Input. Als ich dann 2 unterschiedliche Inputs mit unterschiedlichen Outputs trainiert habe, war der Output plötzlich immer im Mittelwert zwischen den beiden gewünschten, und der Input wurde brav ignoriert. Mit zufälligen Startgewichten scheint er hier also stärker den Einfluss des Inputs zu merken und entsprechend zu reagieren.

 

Ich werde die Lib jetzt dann auch ins Downloadmodul stellen. Sie ist wie gesagt noch nicht perfekt aber vielleicht hilfts trotzdem dem einen oder anderen. Wer es sich antun will kann auf jeden Fall mal ein NN "von innen" sehen ;)

 

http://www.tom-next....uralnetworkmq4/ declare.gif

2 Comments


Recommended Comments

Hey Mythos,

eine sehr schöne Serie und man kann viel von dir lernen.

Ich habe jedoch ein Problem bei dem Code. Könntest du den code einrücken (Ident) einstellen? Blocks sind ansonsten sehr schwer zu lesen und das Thema ist bereits komplex genug.

Danke dir und super Arbeit.

Marc

Link to comment

Ja würd ich gern/hab ich. Der Quote-tag hat scheinbar die Leerzeichen am Zeilenanfang wieder gelöscht. Ich werds mir nochmal anschauen.

 

Beim 2. Versuch scheints geklappt zu haben, hoffe jetzt is es leichter lesbar. Den gesamten Code gibts eh auch im Downloadbereich. Da is es sicher "richtig" eingerückt.

Link to comment
×
×
  • Create New...