Alle Inhalte von Mythos
-
Neuronales Netz: Die Implementierung Backprop
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: Bereche den Output des NN wie übliche und speichere dabei für jede Schicht die Inputliste (also die Outputs der Schicht davor) Berechne die deltas für die Output-schicht und pass die Gewichte an 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: 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/
-
Gabriel (SPD) befürwortet Vergemeinschaftung der Schulden
lol, nice find
-
Neuronales Netz: Lernen
Es ist wiedermal ein bissl Zeit für Theorie. Ich habs in Neuronale Netze: Hintergrund bereits kurz angerissen, aber da es jetzt zur konkreten Implementierung geht werd ich es doch nochmal detailierter angehen. Also: wie lernt ein Neuronales Netz? Etwas präziser gehts um den sogenannten Backpropagation Lernalgorithmus. Ich erspar euch die mathematischen Details und beschränk mich auf verständliches Kauderwelsch ;) Der backprop-algo ist ein häufig verwendeter Algorithmus zum trainieren von feed-forward Netzen. Hierfür eine kurze Wiederholung der Funktionsweise eines feedforward Netzes: Es besteht aus mehreren Schichten von Neuronen. Für eine Inputliste, wird für jedes Neuron der ersten Schicht der resultierende Output berechnet, aus diesen Outputs ergibt sich eine neue Inputliste, diese wird nun auf die 2. Schicht angewandt und so weiter bis zur letzten Schicht. Die Outputs der letzten Schicht sind dann die Outputs des Neuronalen Netzes. Es wird also der Input von vorne nach hinten "durch propagiert". Wenn man das Netz jetzt trainieren will, nimmt man eine Inputliste wo man die gewünschte Output liste kennt und propagiert die Inputliste durch das Netz. Nun hat man den tatsächlichen Output und den gewünschten Output. Die Differenz dieser beiden wird als Error bezeichnet. Auf der letzten Schicht kann man nun sehr einfach an der Behebung dieses Erros arbeiten: Da man sowohl die inputs dieser Schicht als auch die Aktivierungsfunktionen kennt, muss man lediglich die Gewichte so anpassen, das der Fehler "verschwindet". Aber man hat ja mehrere Schichten und will nicht sofort auswendig lernen. Also bewegt man die Gewichte nur einen kleinen Schritt (die sogenannte Lernrate) in die entsprechende Richtung und "propagiert" den Fehler weiter zur vorigen Schicht. Man "propagiert" den Fehler also zurück zum Anfang, deswegen Back-Propagation. Bei der Outputschicht ist die Bestimmung des Fehlers ja noch recht einfach. Bei den tieferen Schichten wirds dann schon schwieriger. Zunächst berechnet man wie beschrieben für die Outputneuronen, in welche Richtung sich der gesamte Input des Neurons, wie stark bewegen müsste damit der Output passt. Das ist für jedes Neuron auf dem Layer eine Zahl, wir nennen es Delta. Dann schiebt man die Gewichte für jedes Neuron einen kleinen Schritt in diese Richtung. Die größe dieses Schrittes hängt ab von der vorgegebenen Lernrate und dem Inputwert. Ist der Inputwert für dieses Gewicht negativ gewesen, muss man natürlich in die andere Richtung gehen, war der Wert klein, geht man einen kleinen Schritt, war er groß, einen großen Schritt. Für ein Neuron auf der nächsten Schicht, multipliziert man die Deltas der verbundenen Neuronen auf der nächsten Schicht, multipliziert sie mit den Gewichten und verwendet diesen Wert als "Error" für dieses Neuron. Mit diesem Error berechnet man das Delta für dieses Neuron und so weiter... Im Inneren des Netzes kann man also nicht direkt den Fehler und damit die notwendige Änderungen bestimmen, sondern muss aufgrund der "durchgereichten" Fehler der späteren Schichten Rückschlüsse auf den Fehler dieser Schicht ziehen. In der Praxis funktioniert das normal recht gut. Die Lernrate Im Backprop wird an sich der Outputfehler so durchgereicht als würde nur eine Schicht verändert werden. Es darf also auf keinen Fall jede Schicht einen vollen Schritt in diese Richtung gehen. Dadurch würde man übers Ziel hinausschießen und einen großen Schritt zu weit gehen. Bei der nächsten Iteration geht man dann einen noch größeren Schritt zurück etc. Die Lernrate sollte also einerseits klein sein und zur Anzahl der Schichten passen. Je mehr Schichten desto geringer sollte die Lernrate sein. Ich hab in ersten Test gute Ergebnisse mit 1% Lernrate gemacht. Man kann das ganze natürlich verbessern indem man die Lernrate variabel gestaltet je nach derzeitigem Error (bei großem Fehler, hohe Lernrate..) oder mit Momentum (die Gewichtsveränderung ist ein Mittelwert zwischen letzter Iteration und neuem Wert). Initiale Gewichte Es hilft bei der Lerngeschwindigkeit (und teils überhaupt beim Lernen), wenn die Gewichte zu Beginn nicht alle gleich sind, sondern Zufallswerte sind. Dadurch "merkt" das Netz leichter die Unterschiede bei den Inputs... Zufällige Änderungen Ich weiß jetzt nicht ob das irgendwo wissenschaftlich untersucht ist, aber ich werde beim Lernen auch noch leichte Zufällige "Verzerrungen" einbauen. Das hilft (hoffentlich) einerseits gegen zu schlimmes auswendig lernen, andererseits bekommt der Lernalgo eine nichtdeterministische Komponente was ihm die Möglichkeit gibt aus lokalen Mini auszubrechen und ggf. bessere Minima zu finden. soda, ich hoff das war noch halbwegs verständlich ;)
-
Neuronales Netz: Die Implementierung Teil2
Es hat (leider) etwas länger gedauert als erwartet, aber jetzt steht mal die erste Version. Ausgeführt wie gesagt als eine Library. Folgende Probleme (und Lösungen) sind während der Implementierung aufgetreten: Transferfunktionen Ich kann leider nicht beliebige Transferfunktionen zulassen, Grund ist genaugenommen nur die "Beschränktheit" von MQL. Mit OO könnte man eine Abstrakte "Transferfunction" Klasse machen und dem NN pro neuron ein solches Objekt übergeben. Aber is halt nicht. Also gibt es derzeit 3 mögliche Funktionen: Linear, sigmoid und heavy side. Welches Neuron welche Funktion verwendet wird bei der Initialisierung durch ein Array von IDs festgelegt (Linear hat ID 0, Sigmoid 1, HeavySide 2). Damit keine verwechslungen aufkommen, hab ich die Definition dieser IDs über Prekompilerkonstanten in ein eigenes mqh gelöst. Je nachdem welche TransferId das Neuron hat, verhält sich nun also die Funktion zur Berechnung des Neuron-Outputs entsprechend anders. MT und die Arrays In MQL gibt es ja bekanntlich Arrays, und man kann mit ArrayResize auch zur Laufzeit beliebig die Größe des Arrays ändern. Das gilt in der Form aber leider nur für eindimensionale Arrays. Für ein NN benötigt man aber auch 2 und 3 dimensionale Arrays. Und da ich nicht ein konkretes NN mit fixer Anzahl an Layern und Neuronen bauen will, muss auch die Größe der verschiedenen Dimensionen dynamisch sein. Also musste ein workaround her. Sieht im konkreten Fall so aus, das ich alles eindimensionale Arrays habe und die mehrdimensionalität mit der Indizierung "simuliere". realesArray[0] entspricht theoretischesArray[0][0][0] realesArray[1] entspricht theoretischesArray[0][0][1] Schwierig wirds jetzt zusätzlich weil die Dimensionen nicht nur variabel in der Größe sind, sondern natürlich komplett unterschiedlich sind. Nehmen wir als Beispiel das "zweidimensionale" Array welches die Transferfunction-Id pro neuron speichert. Theoretisch ist das Array so aufgebaut: transferId[layer][neuron] gibt es jetzt 5 Neuronen am Eingangslayer, 3 am layer1 und 2 am Layer2, dann ist transferId[0][1]= realesArray[1] transferId[1][1]= realesArray[5+1]; transferId[1][2]= realesArray[5+2]; transferId[2][0]= realesArray[5+3+0]; Ich muss mir hier also merken wieviele Neuronen es bis zum LayerX gibt, um dann für LayerX den richtigen Offset im Array zu wissen. Diese Info hab ich wiederum in ein Array gespeichert (simple, eindimensional ;) das für jeden Layer genau diesen Offset speichert. Noch eine Ebene komplizierter wirds dann bei den Gewichten, die eigentlich 3-dimensional sind. Hier arbeite ich wieder mit solchen gespeicherten Offsets. wobei ich mir speichere wieviele Gewichtswerte es bis zu LayerX gibt (wieder in einem Array) und dann im LayerX einfach nach dem fixen 2D prinzip gehe. Hier funktioniert es, da innerhalb eines Layers alle 2. Dimensionen gleiche Länge haben, da ja jeder Konten auf dem Layer gleichviele Outputknoten hat (Anzahl Knoten vom nächsten Layer). Biases Jedes Neuron in einem NN hat ja einerseits die inputs, aber zusätzlich noch einen Schwellwert oder Bias der zur gewichteten Summe dazugezählt wird. Ich habs einfach so implementiert das es ein weiteres Array gibt das sämtliche Biases beinhaltet, analog zum Array mit den Ids der Transferfunktionen. Speichern und Laden Die nächste Herausforderung war die Speicherung eines NN (also die Anzahl der Layer, Neuronen pro Layer, TransferId und Bias pro Neuron sowie alle Gewichte zwischen den Neuronen). Da ich die Gewichte grundsätzlich auch händisch/extern anaylisieren will, hab ich mich für csv entschieden, obwohl binär natürlich schneller und effizienter wäre. MQL hat ja an sich recht nette Filezugriffsmöglichkeiten, aber der Schreibbefehl für csv nimmt zwar eine beliebig lange liste an werten, aber macht immer eine newline. wenn man also zur compilezeit noch nicht weiß wieviele werte das werden und über eine Schleife schreiben will, kann man nur entweder die gesamte line selber als string bauen oder jeden wert in eine eigene Zeile schreiben. Ich hab mich für zweiteres entschieden. Dadurch is das file zwar recht lang, aber mei, die Anzahl der Zeichen ist gleich ;) Weitere Herausforderungen gabs dann noch beim Einbau des Lernalgorithmus, aber dazu mehr nachdem ich den in einem anderen Eintrag erklärt hab ;)
-
Stoppen Sie das scharfe Vorgehen gegen Wikileaks!
Also das er damals in die Botschaft geflüchtet ist, war schon in den Medien, halt nit so die Topschlagzeile. Seit gestern hat er halt auch noch Asyl. Ansonsten bin ich voll bei dir.
-
Einstieg zu MT4 Programmierung
Nein, nur StringKonstanten sind auf 255 Zeichen begrenzt. Bsp: string strtest = "11111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666612345"; //260 Bytes string strtest2 = "ABCDE1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444"; string strtest3= strtest + strtest2; hier wird strtest auf 255 abgeschnitten, aber strtest3 enthält die gesamte Länge. Ich bekomme übrigens schon ein Warning beim compilieren, is aber recht nichtssagend weil es nur den zu langen String enthält. Mein Fehler, hab auf die Schnelle die User verwechselt, es war der Wolf: also einfach den überlangen String (wenn er schon über eine Konstante geschrieben werden soll) in mehrere Zuweisungen aufteilen. Erhöht auch sicher die Lesbarkeit enorm.
-
Einstieg zu MT4 Programmierung
d.h. du schreibst die Werte in den Sourcecode als Konstanten? Frage 1: Wieso die Werte in eine Konstante? Frage 2: ggf. Wieso nicht so wie KB Wolf vorschlägt?
-
Einstieg zu MT4 Programmierung
ah ok, interessant das String Konstanten auf 255 beschränkt sind, aber wieso willst du Stringkonstanten mit mehr als 255 Zeichen machen?
-
Einstieg zu MT4 Programmierung
Was meinst du mit "senden"? per DLL Aufruf? - wusste nicht das es da ein Beschränkung auf 256 Bytes gibt.
-
Einstieg zu MT4 Programmierung
In MT kann man leider nur die 1. dimension eines Arrays resizen, der Rest ist ab Initialisierung fix. Du kannst lediglich selbst über Zugriffsfunktionen mit einem "normalen" Array ein mehrdimensionales simulieren: int valueAt(int row, int col) { return my_array[row*COLS_PER_ROW+col]; } void initArray(int numRows) { ArrayResize(my_array,numRows*COLS_PER_ROW); } zu den 60 zeilen: wie sieht denn der Code dazu aus?
-
Neuronales Netz: Die Implementierung Teil1
habs auch nicht wirklich als Kritik aufgenommen. Freu mich immer über Input. Fürs Daten analysieren ist RapidMiner dann sicher besser weil mehr Möglichkeiten. Ich will aber auch kein statisches NN machen, sondern es im weiteren Verlauf automatisch "nachlernen" lassen etc. Könnte man in der Praxis dann zwar händisch einmal wöchentlich über RapidMiner spielen, aber ich wills vollautomatisch backtesten auch ;)
-
Neuronales Netz: Die Implementierung Teil1
Hauptgrund: weil ichs nicht kannte. Jetzt wo ichs kenne, neuer Hauptgrund: Ich will ja was lernen ;) Im Ernst, einerseits bin ich mir jetzt nicht sicher ob man RapidMiner ohne Probleme vollautomatisieren und an MT anbinden kann, andererseits will ich ein NN mal komplett selber bauen. Fertige Blackboxen sind eben immer noch Blackboxen auch wenn sie viel können. Falls das eigene NN scheitert, kann leicht sein das ich mir den RapidMiner anschaue, aber erstmal bau ichs selber.
- Patrick Hahn - EA Vertriebsportfolio
-
Patrick Hahn - EA Vertriebsportfolio
Ich geh mal davon aus das in den konkreten Konditionen die "garantierten" 9% doch nicht mehr so garantiert sind. Einerseits wären 9% garantierter Ertrag pro Jahr gar nicht schlecht/unrealistisch, andererseits würde sonst das "maximaler Verlust..." ein bissl umsonst. Bei garantierten 9% Gewinn gibts es per Definition keinen Verlust. Ich hoffe nur es fallen nicht zuviele auf die 9% herein. Ala "auf der Webseite stehts, die genauen Konditionen werden dann nicht mehr gelesen"... Kann man ihn da nicht wegen vorsätzlicher Täuschung drankriegen oder so? EDIT: Also jetzt bin i fast vom Stuhl gefallen: Auf der Seite "wirbt" er bei den LIVE Accounts doch tatsächlich mit seinen myfxbook accounts die teils 35% im Minus sind. Es gibt sogar "LIVE Performance bilder" wo die Performance kurve gerade bei -40% steht... Ich glaub er hat die Seite/Accounts schon länger nimmer angeschaut. "Garantierte 9%" und wirbt mit Performancekurven von -40%... dreister gehts nimmer
-
Internet Map
d.h. tom-next geht 2016 mit 100Mrd an die Börse? SCNR @map: coole Idee, sieht nett aus ;)
- Neuronales Netz: Das Design
-
Neuronales Netz: Die Implementierung Teil1
Kommen wir zum schwierigen Teil: Die Implementierung. Für das ganze restliche rundherum hab ich das EA Kitchen Framework genommen. Den aktuellen Source gibts dann immer im dazugehörigen Thread. Die konkrete Implementierung der NN Logik wird in eine Lib ausgelagert. Ich hatte zwar schon mal so eine geschrieben, aber keine Ahnung mehr wo die is. Also nochmal. Das ganze wird jetzt sicher ein bissl hardcore programmiertechnisch, also wems nur ums prinzip geht: besser wegschaun ;) Softwarestruktur Die Lib wird so aufgebaut das sie genau 1 NN repräsentiert. Sprich es gibt globale variablen in der Lib die Status und Konfiguration speichern, und dann Funktionen für die entsprechenden Zugriffe. Gespeichert werden muss: Anzahl der Schichten -> integer bzw. direkt über größe des layers array Anzahl der Neuronen pro Schicht -> int-array: layers Definition der Übergangsfunktion pro Neuron -> zweidimensionales array (anzahl schichten x knoten pro schicht) Gewichte pro Verbindung -> wird wohl ein dreidimensionales double array. Ebene 1: layer, ebene 2: neuron am layer Ebene 3: neuron auf nächstem Layer Nötige Zugriffsfunktionen: Erzeuge NN. Parameter: ,, , Berechne NN. , Lade Parameter von File: Speichere in File: Autosave (nimmt filename aus dem geladen wurde Trainiere 1 Schritt: , Als Lernalgorithmus werd ich den backpropagation verwenden, einfach weil ich ihn kenne ;) Sprich die Lib wird ein komplett allgemeines NN, das man ggf. auch für ganz andere Varianten verwenden kann. Alle Projektspezifischen Einstellungen werd ich in eine mqh packen die die NN-Lib dann verwendet. Dann geh ich mal an die Implementation der Lib. Mal sehen wie lang es dauert ;)
-
EA Kitchen: KI vs. Evolution
Der eine oder andere hat vielleicht schon gesehen das ich wieder mal an einem EA-Projekt arbeite. Für die Umsetzung hab ich einfach mal das Kitchen-Framework hergenommen. Details zur Idee und Hintergründe gibts im Blog. Hier mal die Version 0.x, ich hab eigentlich nur mal das Framework für den konkreten EA angepasst und Stops/TP etc. implementiert (was in dem Fall äußerst minimalistisch ist.) Interessant wird dann die calcEntry Funktion. Ich werde im weiteren Verlauf 2 include dateien machen. Eine für die KI-Variante, eine für die Evo-Variante. Aber das dürfte noch ein bissl dauern ;) Die Kitchen soll ja nicht nur aus Indikatoren bestehen, sondern darf auch ein bissl "fancy-math-stuff" kriegen ;) eKIvsEvo.mq4
-
Er ist wieder da!
nicht nur live sondern LIVE ;)
-
Er ist wieder da!
Ich steh auf den Schokohasen im Bild. Ist der auch Trader? ;)
-
Zeit für ein neues Projekt
Natürlich: Er liefert dauerhaft Gewinne bei minimalem Drawdown. Und das ohne das nachjustierungen nötig wären. Sprich du überweist Geld auf ein Konto, startest den Gral und wenn du dann einmal im Monat auf das Konto schaust, wirds einfach ständig viel viel mehr ;)
-
Neuronales Netz: Das Design
Kommen wir zum ersten spannenden Teil: Das Design des NN. Ob das NN die gewünschte Funktion richtig erlernt, hängt einerseits natürlich von der Funktion selbst ab. Aber zu einem großen Teil auch von der gewählten Form des Netzes und den Inputdaten. Ich habe vor für das Experiment verschiedene Ausprägungen eines Typs von NN zu testen. Grundsätzlich wird es wie gesagt ein mehrschichtiges feedforward NN. Die Anzahl der Schichten und Anzahl der Neuronen auf den Schichten werde ich variabel gestalten und im Laufe der Tests dann schaun welche Kombination am besten funktioniert. Die Funktion als solches wird ein "simpler" Klassifikator (darin sind NNs besonders gut). Sprich +1 wenn der nächste Bar min X Einheiten nach oben geht und nicht mehr als Y Einheiten fällt, -1 bei entsprechender Konstellation für Short und 0 sonst. Sicherheitshalber werde ich es auch mit 3 Outputneuronen (einen für Long, einen für Short, einen für Flat) testen um zu sehen wie hier die Unterschiede sind. Kurzer Exkurs zu NN als Klassifikator Will man ein NN nutzen um Inputs (zB Bilder) in gewisse Kategorien zu trennen, geht man meist so vor: Das NN hat gleich viele Outputneuronen wie es Kategorien gibt. Beim Trainieren setzt man dann den Output der korrekten Kategorie auf 1 und alle anderen auf 0. Ein neuer Input wird dann jener Kategorie zugeordnet deren zugehöriges Outputneuron den höchsten Wert hat. Weiter im Text: Also ein Klassifikator mit 3 Kategorien (entweder über 1 oder 3 Outputneuronen realisiert). nächste große Frage: Was wird klassifiziert/ Was sind die Inputs Einerseits werden natürlich die Daten der letzten X Bars (O/H/L/C + Volume) als Input dienen. Zusätzlich werd ich aber auch "jede Menge" halbwegs sinnvolle Vorverarbeitungen (Indikatoren) in das NN schmeißen und schaun was passiert. Aber: Mit genug Schichten kann sich das NN zwar aus den Inputs beliebige gewichtete Kombination bauen. die 2. Schicht kann also schon inputs der Form (C-O) oder (H-C) etc. kriegen. Aber es gibt (zumindest mit den Übergangsfunktionen die ich nehmen werd) keine Relationen zwischen den Inputs. Also (C-O)/ATR muss ich selber als Input definieren. Bei Inputs sollte man grundsätzlich sowieso immer darauf achten das alle auf ein einheitliches Maß "normiert" werden (meist zw. -1 und +1). Da in einem Chart ja stärker die relative Bewegung als die absoluten Zahlen zählen, werd ich die Bars auch entsprechend vorverarbeiten: Open_NN wird (Open- Close[1])/M wobei M wiederum per Parameter entschieden wird, entweder das Open des Bars (prozentuale Veränderung) oder ATR(13) (relative Bewegung) High_NN = (High - Open)/M Low_NN = (Open-Low)/M Close_NN= (Close-Open)/M Welche Indis noch dazukommen weiß ich noch nicht genau, aber auf jeden Fall: RSI, (Diff zw. zwei SMA)/M... Die Übergangsfunktionen Da ich noch keine Ahnung habe welche Übergangsfunktionen für diese Anwendung gut funktionieren, wirds auch hier im Sinne eines Forschungsprojekts zuerst einen Fundus geben. Nur das hier jedes NN gleich mit allen ausgestattet sind. Nach dem lernen werd ich mir die Gewichte anschauen, falls die Ausgangsgewichte eines Neurons alle nahe 0 sind, ist das Neuron für die Klassifikation offensichtlich umsonst. Dann kann ich es weglassen. Im ersten Schritt wird es also auf jedem Layer 3* an Neuronen geben. jeweils 1 lineare Übergangsfunktion, 1 Heavyside und eine sigmoide. Wie man merkt gibt es noch viele "Unbekannte" die im Laufe des Experiments gelöst werden sollen. Aber dafür ist so ein Projekt ja da ;)
-
Neuronale Netze - Hintergrund
Dann starten wir mal mit ein bisschen Theorie. Heute: Neuronale Netze. Wer sich "ernsthaft" mit dem Thema auseinandersetzen will, findet im Netz jede Menge "serious shit" zum Thema. Ich will hier eher einen (weiteren) kurzen Streifzug zu dem Thema geben, damit der geneigte Leser im weiteren Verlauf die Grundmechanismen versteht die ich zu verwenden gedenke. Was ist ein Neuronales Netz Prinzipiell ist ein NN nichts anderes als die Approximation einer (beliebig komplexen) Funktion. Also eine Black-Box mit einer fixen Menge an Eingabeparametern und einer fixen Menge an Ausgaben. Man versucht also eine Funktion zu "schätzen" deren wahre Logik man nicht kennt, von der man aber eine gewisse Menge an Beispielwerten (Eingabeparameter mit dazugehörigen Ausgaben) hat. Aber warum heißt es dann "neuronales Netz"? Ein NN ist eine Menge von Knoten (genannt "Neuronen") die miteinander in einer Netzartigen Struktur verbunden sind. Jede Verbindung kann man sich wie eine Datenübertragungsleitung vorstellen über die jeweils eine Zahl übertragen wird. Ein Neuron hat eine beliebige Anzahl an Eingangsverbindungen (über die jeweils eine Zahl an das Neuron geschickt wird). Aus all diesen Zahlen berechnet das Neuron eine neue Zahl (mit der sogenannten Übergangsfunktion). Diese Zahl sendet das Neuron dann über seine Ausgangsverbindungen an die nächsten Neuronen weiter. Damit es nicht zu kompliziert wird sind die Neuronen in Schichten (Layern) angeordnet. Die erste Schicht besteht aus den Input-Neuronen, diese erhalten ihre Werte aus den Inputparametern. Die letzte Schicht sind die Output-Neuronen, deren Ergebnisse sind die Ausgaben des NN. Feed Forward Um es nicht unnötig kompliziert zu machen, verwende ich ein feed forward NN. Das bedeutet einfach, das es innerhalb des Netzwerks nur Verbindungen von einer Schicht zu Neuronen in der direkt nächsten Schicht gibt. Also weder Verbindungen die "zurück zum Eingang" laufen, noch Verbindungen die Schichten überspringen. Die Übergangsfunktionen Nicht unwesentlich für die Funktion des NN sind die Übergangsfunktionen. In der Regel haben sie eine sehr einfache Form: Jede Eingangsverbindung erhält ein fixes Gewicht. Kommt ein neuer Satz von Eingangswerten (also 1 Zahl pro Verbindung) daher, wird der jeweilige Wert mit dem entsprechenden Gewicht multipliziert und die Ergebnisse summiert. Sprich es wird eine gewichtete Summe gebildet. Dieser Summenwert ist nun der Input für die Aktivierungsfunktion, deren Ergebnis auch schon der Ausgangswert des Neurons ist. Typische Beispiele für diese Aktivierungsfunktion ist zB die sogenannte HeavySide Funktion die 0 liefert solang der Wert unter einem fixen Schwellwert ist und 1 wenn er darüber ist. Oder die lineare Aktivierungsfunktion die einfach den Eingangswert 1:1 als Ausgang zurückgibt. Wie lernt ein NN Die gerade beschriebenen Gewichte sind auch schon der Schlüssel wie ein NN nun lernt. Einfach ausgedrückt so wie ein Mensch auch: Man zeigt ihm eine Reihe von Beispielhaften Inputdaten und die dazugehörigen Outputs. Das NN rechnet nun für diese Inputdaten aus welche Outputs es gerade liefern würde, vergleicht diese mit den gewünschten Outputs und passt alle internen Gewichte so an, das ab sofort für diese Inputs die gewünschten Outputs geliefert werden. (Der eigentlich Lernalgorithmus ist etwas komplizierter, aber das ist die Grundidee) Auswendig lernen Man kennt es vom optimieren: Overfitting. Auch beim NN gibt es dieses Problem: Man hat ein hochkomplexes NN mit massig Neuronen und Schichten und trainiert es auf wenige Testdaten (Inputs mit entsprechenden Outputs). Nun besteht das große Risiko, dass das Netz einfach diese Testdaten auswendig lernt. Somit für die Testmenge perfekte Ergebnisse liefert, aber für leicht veränderte Daten komplett daneben liegt. Verhindert wird es mit der gleichen Technik wie beim Trading: Out of Sample. Man teilt die Menge der vorhandenen Testdaten auf. Mit der einen Menge trainiert man das Netz, nach jeder Lerniteration (davon braucht man meist sehr viele) testet man das Netz an der zweiten Menge (lässt es aber nicht "lernen" also die Gewichte bleiben unverändert). Zu Beginn sollte der Fehler nun bei beiden Mengen sinken. Sobald der Fehler bei der zweiten Menge wieder steigt, beginnt offensichtlich das overfitten und man stoppt den Lernalgorithmus. Nun sollte das NN bestmöglich trainiert sein. Soweit der Überblick über neuronale Netze. Wie ich ein solches Netz jetzt im Trading anwenden will, gibts in einem nächsten Eintrag.
- Die Grundannahmen
-
Die Eckpfeiler, ab hier gehts zweigleisig
Also die Eckpfeiler. Ich möchte das System eigentlich möglichst "einfach" halten. Vor allem zu Beginn und solange noch nicht klar ist was die Ansätze überhaupt können. Die Eckpfeiler bestehen für mich (u.a.) aus diesen Punkten: Welcher Markt/Märkte Welcher Timeframe Einstiegslogik Ausstiegslogik MM/RM Markt: Da die Systeme selbstlernend werden (sollen), werde ich mich auf keinen Markt einschränken, sondern die Systeme dann mit diversen Märkten füttern und schaun was passiert, Schwerpunkt aber FOREX. Timeframe ergibt sich aus der Grundannahme: M1 bzw. ggf. M5. Die Systeme werde ich Timeframe unabhängig bauen damit man auch Test auf höheren Timeframes machen kann, aber der produktive Einsatz ist für kleine TF geplant. Die Ausstiege und MM/RM wird wie gesagt sehr simpel sein, jede Position bekommt TP und SL (abhängig von dem erkannten Muster). Später werde ich sicher auch mit trailing und BreakEven testen, aber derweil reicht TP und SL. Risiko bekommt jeder Trade gleich viel, es soll ja jeder die gleiche Chance haben. Kommt das spannenste hier: die Einstiege. Wie schon gesagt wird alles auf Mustererkennung beruhen. Eigentlich macht das fast jedes Handelssystem, also konkreter: Muster in der PriceAction. Der Knackpunkt ist natürlich "wie" diese Muster erkannt werden und welches Muster wie gehandelt werden soll. Und hier beginnen sich die beiden Systeme zu unterscheiden: klassische KI Der KI Teil, wird ein neuronales Netz bekommen, mit diversen (noch zu definierenden Eingängen) und damit klassifizieren ob der Kurs innerhalb der nächsten 2 Bars nach oben oder nach unten geht. (Details dazu später). Entsprechend dieser Klassifikation wird dann die Position eröffnet. Evolution Beim Evolutionsteil wird es noch etwas leichter sein zu verstehen warum das System etwas macht. Hier versuche ich zuerst einige mögliche Muster zu definieren. Das System wird dann diese Muster in der Vergangenheit testen und analysieren welche "funktionieren" und welche nicht. Die "guten Muster" werden dann einerseits gehandelt, andererseits werden durch Mutation und Vermischung neue "Muster" definiert die bei Erfolg automatisch zum Pool hinzugefügt werden. (Auch hier Details später). Um mir für das ganze rundherum die Arbeit zu erleichtern, werde ich das ganze in das EA-Kitchen Framework einbetten.