English Русский 中文 Español 日本語 Português
preview
Neuronale Netze leicht gemacht (Teil 26): Reinforcement-Learning

Neuronale Netze leicht gemacht (Teil 26): Reinforcement-Learning

MetaTrader 5Handel | 9 November 2022, 11:07
297 0
Dmitriy Gizlyk
Dmitriy Gizlyk

Inhalt


Einführung

In den vorangegangenen Artikeln dieser Reihe haben wir bereits Algorithmen für überwachtes und unüberwachtes Lernen kennengelernt. Dieser Artikel schlägt ein weiteres Kapitel des maschinellen Lernens auf: Reinforcement-Learning. Diese Algorithmen beruhen auf der Umsetzung des Lernens durch Versuch und Irrtum, das mit dem Lernsystem lebender Organismen verglichen werden kann. Diese Eigenschaft ermöglicht die Verwendung solcher Algorithmen für die Lösung von Problemen, die die Entwicklung bestimmter Strategien erfordern. Offensichtlich kann der Handel auf solche Probleme zurückgeführt werden, da alle Händler bestimmte Strategien für einen erfolgreichen Handel verfolgen. Daher kann der Einsatz einer solchen Technologie für unser spezielles Gebiet nützlich sein.


1. Grundlagen des verstärkenden Lernens

Bevor wir uns mit spezifischen Algorithmen befassen, sollten wir die grundlegenden Konzepte und die Philosophie hinter dem Reinforcement-Learning studieren. Zunächst einmal ist zu beachten, dass beim Reinforcement-Learning das Modell nicht als etwas Separates behandelt wird. Sie befasst sich mit der Interaktion zwischen den Prozessbeteiligten. Zum besseren Verständnis des Gesamtprozesses wäre es sinnvoll, sich eine Person als einen der Prozessbeteiligten vorzustellen. Wir sind uns unseres Handelns sehr wohl bewusst. In diesem Fall wird es für uns einfacher sein, das Verhalten des Modells zu verstehen.

Wir leben also in einer sich ständig verändernden Welt. Die Veränderungen hängen bis zu einem gewissen Grad von uns und unserem Handeln ab. Aber die Veränderungen hängen nicht nur von uns ab. Das liegt daran, dass Millionen anderer Menschen auf der Welt leben und bestimmte Handlungen ausführen. Außerdem gibt es viele Faktoren, die nicht von den Menschen abhängen.

In ähnlicher Weise gibt es beim Verstärkungslernen die Umgebung, die als Personifizierung unserer Welt verstanden wird. Ein Agent interagiert mit der Umwelt. Der Agent kann mit einer Person verglichen werden, die in dieser Umgebung lebt. Die Umwelt verändert sich ständig.

Wir schauen uns ständig um, beurteilen Gegenstände durch Berührung und hören auf Geräusche. Wir bewerten unsere Welt also jeden Moment mit unseren Sinnen. In unseren Köpfen legen wir seinen Zustand fest.

In ähnlicher Weise erzeugt die Umwelt ihren Zustand (State), der vom Agenten bewertet wird.

Der Agent führt eine Aktion gemäß seiner Politik (Strategie) aus, ähnlich wie wir in Übereinstimmung mit unserer Weltsicht handeln.

Die Auswirkungen führen dazu, dass sich die Umwelt mit einer gewissen Wahrscheinlichkeit verändert. Für jede Aktion erhält der Agent von der Umwelt Belohnungen. Die Belohnungen können positiv oder negativ sein. Anhand der Belohnungen kann der Agent die Nützlichkeit der durchgeführten Aktionen bewerten.

Reinforcement-Learning

Beachten Sie, dass die Belohnungspolitik unterschiedlich sein kann. Es kann Ein-Schritt-Optionen geben, wenn die Umgebung die Belohnungen nach jeder Aktion erteilt. Aber es gibt viele andere Aufgaben, bei denen es schwierig oder unmöglich ist, jede einzelne Handlung zu bewerten. Die Belohnungen können nur am Ende der Sitzung gewährt werden. Betrachten wir beispielsweise eine Schachpartie, so können wir versuchen, jeden Zug fachmännisch danach zu bewerten, ob sich die Stellung verbessert oder verschlechtert hat. Aber das Hauptziel ist es, das Spiel zu gewinnen. Dies ist die Belohnung, die alle möglichen früheren Berichte abdeckt. Andernfalls wird das Modell zu einem „spielen, um zu spielen“ und würde einen Weg finden, sich in eine Schleife zu begeben, in der es darum geht, die maximale Belohnung zu erhalten, anstatt ins Finale zu kommen.

In ähnlicher Weise kann eine auf dem Markt eröffnete Position zu einem bestimmten Zeitpunkt positiv und zu einem anderen negativ sein. Das Ergebnis eines Handelsgeschäfts kann jedoch erst nach dessen Ende bewertet werden.

Fälle wie diese zeigen, dass oft Belohnungen nicht von einzelnen Aktionen abhängen. Sie hängen jedoch von einer Reihe von aufeinander folgenden Aktionen ab.

Dies bestimmt das Ziel der Modellschulung. So wie wir beim Handel den maximalen Gewinn anstreben, wird das Modell so trainiert, dass es die maximalen Belohnungen für ein bestimmtes endliches Intervall erzielt. Das kann ein Spiel oder eine Handelssitzung sein. Oder auch nur einen Zeitraum.

Bitte beachten Sie hier zwei zusammenhängende Voraussetzungen für den Einsatz von Methoden des Reinforcement-Learnings.

Erstens muss der zu untersuchende Prozess die Anforderungen des so genannten Markov Decision Process erfüllen. Einfach ausgedrückt, jede Aktion des Agenten hängt nur von dem aktuellen Zustand ab. Keine der zuvor durchgeführten Aktionen oder beobachteten Zustände wirkt sich weiter auf die Agentenhandlungen und die Änderungen der Umgebungen. Ihr gesamter Einfluss wurde bereits im aktuellen Zustand berücksichtigt.

Natürlich ist es unter realen Bedingungen schwierig, ein Verfahren zu finden, das diese Anforderung erfüllt. Und es geht definitiv nicht um den Handel. Bevor sie eine Handelsoperation durchführen, analysieren die Händler sorgfältig die Daten eines bestimmten Zeitintervalls mit verschiedenen Details. Der Einfluss aller Ereignisse nimmt jedoch allmählich ab und wird durch neue Ereignisse kompensiert. Daher können wir immer sagen, dass der aktuelle Zustand Ereignisse für ein bestimmtes Zeitintervall umfasst. Wir können auch Zeitstempel hinzufügen, um anzugeben, wie lange ein Ereignis zurückliegt.

Die zweite Voraussetzung ist die Endlichkeit des Prozesses. Wie bereits erwähnt, besteht das Ziel der Ausbildung des Modells darin, die Belohnungen zu maximieren. Und wir können die Rentabilität der Strategie nur für gleiche Zeiträume bewerten. Wenn eine Strategie dauerhaft gut funktioniert, wird sie über einen längeren Zeitraum mehr Gewinn bringen. Dies bedeutet, dass bei unendlichen Prozessen die gesamten Belohnungen gegen Unendlich tendieren könnten.

Bei unendlichen Prozessen (auf die sich auch der Handel bezieht) müssen wir daher die Größe der Trainingsstichprobe auf einen bestimmten Zeitrahmen beschränken, um diese Anforderung zu erfüllen. Andererseits muss die Ausbildungsstichprobe repräsentativ sein. Denn wir brauchen eine Strategie, die zumindest für einige Zeit nach dem Ende der Ausbildungszeit funktioniert. Je länger die Laufzeit des entsprechenden Modells ist, desto besser.

Erinnern Sie sich daran, dass wir über dasselbe gesprochen haben, als wir mit der Untersuchung von Algorithmen für überwachtes Lernen begannen. Wir werden später noch auf die Unterschiede zwischen den verschiedenen Lehrmethoden eingehen.

Nun möchte ich erwähnen, wie wichtig es ist, die richtige Belohnungspolitik für die durchgeführten Aktionen zu entwickeln. Sie haben wahrscheinlich schon von der Bedeutung einer angemessenen Organisation des Belohnungssystems im Personalmanagement gehört. Ein angemessenes Belohnungssystem regt das Personal des Unternehmens dazu an, die Produktivität und die Qualität der geleisteten Arbeit zu steigern. In ähnlicher Weise kann das richtige Belohnungssystem der Schlüssel zum Training des Modells und zum Erreichen des gewünschten Ergebnisses sein. Dies ist besonders wichtig bei Aufgaben mit verzögerter Belohnung.

Es kann schwierig sein, die endgültige Belohnung korrekt auf alle Aktionen zu verteilen, die auf dem Weg zum Endziel unternommen werden. Zum Beispiel, wie der Gewinn oder Verlust aus der Transaktion zwischen dem Eröffnen und dem Schließen einer Position aufgeteilt werden soll. Das erste, was einem in den Sinn kommt, ist, beiden Operationen gleichviel zuzuteilen. Doch leider ist dies nicht immer der Fall. Sie können eine Position zum richtigen Zeitpunkt eröffnen, da sich der Kurs in Ihre Richtung bewegt. Wie kann man den Zeitpunkt des Ausstiegs bestimmen? Wir können die Position früh schließen, wodurch uns ein Teil des potenziellen Gewinns entgeht. Oder wir halten die Position zu lange und schließen sie, nachdem der Kurs begonnen hat, sich gegen uns zu entwickeln. Dann würde uns ein Teil des möglichen Gewinns entgehen. Im schlimmsten Fall könnt wir sogar mit einem Verlust aussteigen. In diesem Fall erhält eine scheinbar gute Positionseröffnung eine negative Belohnung aufgrund einer schlechten Positionsschließung. Ist das nicht ungerecht? Außerdem wird das Modell aufgrund dieser negativen Belohnung diesen Eintrag beim nächsten Mal als nicht geeignet betrachten. So entgeht uns noch mehr Gewinn.

Natürlich ist auch der umgekehrte Fall möglich: Wir eröffnen eine erfolglose Position. Wir bezweifeln sie nicht. Aber zum Glück kehrt sich der Kurs nach einiger Zeit um und beginnt, sich in unsere Richtung zu bewegen. Es gelingt uns also, die Position mit einem Gewinn zu schließen. Das Modell erhält eine positive Belohnung, betrachtet einen solchen Einstieg als erfolgreich und eröffnet, wenn ein solches Muster auftritt, erneut eine Position. Aber dieses Mal haben wir kein Glück und der Preis kehrt sich nicht um.

Es stimmt, das Modell lernt aus mehreren Handelsgeschäften. Es sammelt Handelsstatistiken und erstellt Durchschnittswerte. Aber ein falsches Belohnungssystem kann alles verderben und das Modelltraining in eine falsche Richtung lenken.

Daher sollten Sie bei der Entwicklung von Modellen für überwachtes Lernen sehr vorsichtig sein, wenn Sie ein Belohnungssystem entwickeln.


2. Unterschied zu den bisher betrachteten Methoden

Betrachten wir den Unterschied zwischen Methoden des Reinforcement-Learnings und den zuvor besprochenen Algorithmen des überwachten und unbeaufsichtigten Lernens. Alle Methoden haben ein Modell und ein Trainingsmuster, an dem sie lernen. Bei der Verwendung von Methoden des überwachten Lernens umfasst die Trainingsstichprobe Paare von Ausgangszuständen und richtigen Antworten, die vom „Überwacher“ bereitgestellt werden. Beim unüberwachten Lernen haben wir nur eine Trainingsstichprobe — die Algorithmen suchen nach internen Ähnlichkeiten und Strukturen der einzelnen Zustände, um sie zu trennen. Die Trainingsstichprobe ist in beiden Fällen statisch. Die Modelloperation ändert das Modell nie.

Im Falle des Verstärkungslernens haben wir keine Trainingsstichprobe im üblichen Sinne. Wir haben eine Umgebung die den aktuellen Zustand erzeugt. Nun, wir können verschiedene Umgebungszustände für die Trainingsstichprobe auswählen. Aber es gibt noch eine andere Beziehung zwischen der Umwelt und dem Agenten. Nachdem er den Zustand des Systems bewertet hat, führt der Agent eine Aktion durch. Diese Maßnahme wirkt sich auf die Umwelt aus und kann sie verändern. Außerdem muss die Umwelt eine Reaktion auf die Aktion in Form von Belohnungen.

Die von der Umwelt erhaltene Belohnung kann mit der „Referenzreaktion des Betreuers“ bei überwachten Lernmethoden verglichen werden. Aber es gibt einen grundlegenden Unterschied. Beim überwachten Lernen haben wir für jede Situation die einzig richtige Antwort und lernen daraus. Beim Verstärkungslernen erhalten wir nur eine Reaktion auf die Aktion des Agenten. Wir wissen nicht, wie die Belohnung zustande kommt. Außerdem wissen wir nicht, ob es sich um ein Maximum oder ein Minimum handelt, oder wie weit es von den Extremwerten entfernt ist. Mit anderen Worten: Wir kennen die Aktion des Agenten und seine Einschätzung. Aber wir wissen nicht, was die „Referenz“ gewesen sein soll. Um dies herauszufinden, müssen wir alle möglichen Aktionen von einem bestimmten Zustand aus durchführen. Und in diesem Fall erhalten wir die Auswertung aller Aktionen in einem Zustand.

Erinnern wir uns nun daran, dass wir in der nächsten Zeitperiode in einen neuen Umgebungszustand gelangen, der von der Aktion abhängt, die der Agent im vorherigen Schritt durchgeführt hat.

Und wir bemühen uns, die maximale Gesamtvergütung für den gesamten analysierten Zeitraum zu erhalten. Um also eine Referenzaktion für jeden Zustand zu erhalten, benötigen wir eine große Anzahl vollständiger Durchläufe für alle möglichen Zustände der Umgebung für alle möglichen Aktionen des Agenten.

Ein solcher Ansatz wäre extrem langwierig und arbeitsintensiv. Um optimale Strategien zu finden, werden wir daher einige Heuristiken verwenden, auf die wir etwas später eingehen werden.

Lassen Sie uns zusammenfassen.

Supervised Learning (Überwachtes Lernen) Unsupervised Learning (Unüberwachtes Lernen) Reinforcement-Learning (Lernen durch Verstärkung)
 Auf ungefähre Referenzwerte trainiert  Lernt die Datenstruktur  Durch Versuch und Irrtum trainiert, bis die maximale Belohnung erreicht ist
 Referenzwerte erforderlich  Referenzwerte nicht erforderlich  Reaktion der Umwelt auf Aktionen des Agenten erforderlich
 Das Modell hat keinen Einfluss auf die Originaldaten  Das Modell hat keinen Einfluss auf die Originaldaten  Der Agent kann die Umwelt beeinflussen


3. Kreuzentropie-Methode

Ich schlage vor, dass wir unsere Bekanntschaft mit den Algorithmen des Reinforcement-Learnings mit der Kreuzentropie-Methode beginnen. Bitte beachten Sie, dass die Anwendung dieser Methode mit einigen Einschränkungen verbunden ist. Um sie richtig zu nutzen, muss die Umwelt eine endliche Anzahl von Zuständen haben. Außerdem ist der Agent auf eine begrenzte Anzahl von möglichen Aktionen beschränkt. Natürlich müssen wir die oben genannten Anforderungen des Markov-Prozesses und die begrenzte Trainingszeit einhalten.

Diese Methode steht in vollem Einklang mit der Ideologie von Versuch und Irrtum. Erinnern Sie sich daran, wie Sie, wenn Sie in eine unbekannte Umgebung kommen, anfangen, verschiedene Handlungen auszuführen, um die neue Umgebung zu erkunden. Diese Aktionen können entweder zufällig sein oder auf Ihrer Erfahrung in ähnlichen Situationen basieren.

In ähnlicher Weise macht der Agent mehrere Durchgänge von Anfang bis Ende in der untersuchten Umgebung. In jedem Zustand führt er eine bestimmte Aktion aus. Die Aktion kann entweder völlig willkürlich sein oder von einer bestimmten Richtlinie diktiert werden, die bei der Initialisierung in den Agenten aufgenommen wird. Die Anzahl dieser Durchgänge kann unterschiedlich sein; sie ist ein Hyperparameter, der vom Modellarchitekten festgelegt wird.

Bei jedem Durchgang durch die zu erforschende Umgebung speichern wir jeden Zustand, die durchgeführte Aktion und die Gesamtbelohnung jedes Durchgangs.

Aus allen Durchläufen wählen wir zwischen 20 und 50 % der besten Durchläufe nach der Gesamtbelohnung aus und aktualisieren die Strategie des Agenten auf der Grundlage der Ergebnisse. Die Formel für die Aktualisierung der Politik ist unten dargestellt.

Aktualisierung der Politik

Nach der Aktualisierung der Richtlinie wiederholen wir die Durchläufe durch die Umwelt. Wir wählen die besten Passagen aus und aktualisieren die Agentenpolitik.

Wir wiederholen diesen Vorgang, bis das gewünschte Ergebnis erreicht ist oder die Rentabilität des Modells nicht mehr steigt.

3.1. Implementierung mittels MQL5

Der Algorithmus der Kreuzentropie-Methode mag einfach klingen, aber seine Umsetzung mit MQL5-Tools ist nicht so einfach. Diese Methode geht von einer endlichen Anzahl möglicher Umweltzustände und Agentenaktionen aus. Die Handlungen des Agenten sind klar, aber es gibt ein großes Problem mit der Endlichkeit der Anzahl der möglichen Zustände der Umwelt.

Kehren wir zu den Problemen des unüberwachten Lernens zurück. Insbesondere zu den Problemen des Clustering. Bei der Untersuchung der k-means-Methode haben wir alle möglichen Zustände in 500 Cluster eingeteilt. Ich denke, dies ist eine akzeptable Lösung für das Problem der Endlichkeit der Anzahl der möglichen Zustände.

Dies reicht aus, um den Algorithmus zu demonstrieren. Auf den Einfluss der Handlungen des Agenten auf den Systemzustand wollen wir hier nicht näher eingehen.

Der MQL5-Code für die Implementierung des Algorithmus der Kreuzentropie-Methode ist in der EA-Datei „crossenteopy.mq5“ enthalten. Binden wir zu Beginn die erforderlichen Bibliotheken ein. In diesem Fall werden wir eine tabellarische Version der Kreuzentropie-Methode implementieren, sodass wir die Bibliothek für neuronale Netze nicht verwenden. Wir werden ihre Verwendung in Algorithmen des verstärkten Lernens in den nächsten Artikeln dieser Reihe untersuchen.

#include "..\Unsupervised\K-means\kmeans.mqh"
#include <Trade\SymbolInfo.mqh>
#include <Indicators\Oscilators.mqh>

Als Nächstes deklarieren wir externe Variablen, die fast identisch sind mit denen, die in der EA zur Demonstration der k-means-Methode verwendet werden. Dies ist nur natürlich, da wir die Methode verwenden werden, um die grafischen Muster der Marktsituation zu clustern.

input int                  StudyPeriod =  15;            //Study period, years
input uint                 HistoryBars =  20;            //Depth of history
input int                  Clusters    =  500;           //Clusters
ENUM_TIMEFRAMES            TimeFrame   =  PERIOD_CURRENT;
//---
input int                  Samples     =  100;
input int                  Percentile  =  70;
      int                  Actions     =  3;
//---
input group                "---- RSI ----"
input int                  RSIPeriod   =  14;            //Period
input ENUM_APPLIED_PRICE   RSIPrice    =  PRICE_CLOSE;   //Applied price
//---
input group                "---- CCI ----"
input int                  CCIPeriod   =  14;            //Period
input ENUM_APPLIED_PRICE   CCIPrice    =  PRICE_TYPICAL; //Applied price
//---
input group                "---- ATR ----"
input int                  ATRPeriod   =  14;            //Period
//---
input group                "---- MACD ----"
input int                  FastPeriod  =  12;            //Fast
input int                  SlowPeriod  =  26;            //Slow
input int                  SignalPeriod =  9;            //Signal
input ENUM_APPLIED_PRICE   MACDPrice   =  PRICE_CLOSE;   //Applied price

Zur Umsetzung der Kreuzentropie-Methode wurden drei Variablen hinzugefügt:

  • Samples — die Anzahl der Durchläufe für jede Iteration der Richtlinienaktualisierung
  • Percentile — das Perzentil zur Auswahl der Referenzpässe für die Richtlinienaktualisierung
  • Actions — die Anzahl der möglichen Aktionen des Agenten

Die Anzahl der möglichen Zustände des Systems wird durch die Anzahl der mit der k-means-Methode erstellten Cluster bestimmt.

In der EA-Initialisierungsmethode initialisieren wir die Objekte für die Arbeit mit Indikatoren und das Objekt für das Clustering grafischer Muster.

int OnInit()
  {
//---
   Symb = new CSymbolInfo();
   if(CheckPointer(Symb) == POINTER_INVALID || !Symb.Name(_Symbol))
      return INIT_FAILED;
   Symb.Refresh();
//---
   RSI = new CiRSI();
   if(CheckPointer(RSI) == POINTER_INVALID || !RSI.Create(Symb.Name(), TimeFrame, RSIPeriod, RSIPrice))
      return INIT_FAILED;
//---
   CCI = new CiCCI();
   if(CheckPointer(CCI) == POINTER_INVALID || !CCI.Create(Symb.Name(), TimeFrame, CCIPeriod, CCIPrice))
      return INIT_FAILED;
//---
   ATR = new CiATR();
   if(CheckPointer(ATR) == POINTER_INVALID || !ATR.Create(Symb.Name(), TimeFrame, ATRPeriod))
      return INIT_FAILED;
//---
   MACD = new CiMACD();
   if(CheckPointer(MACD) == POINTER_INVALID || !MACD.Create(Symb.Name(), TimeFrame, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice))
      return INIT_FAILED;
//---
   Kmeans = new CKmeans();
   if(CheckPointer(Kmeans) == POINTER_INVALID)
      return INIT_FAILED;
//---
   bool    bEventStudy = EventChartCustom(ChartID(), 1, 0, 0, "Init");
//---
   return(INIT_SUCCEEDED);
  }

In der EA-Deinitialisierungsmethode löschen wir alle oben erstellten Objekte. Wir wissen, dass die Deinitialisierungsmethode aufgerufen wird, wenn das Programm geschlossen wird. Es ist also eine gute Praxis, dort eine Speicherbereinigung zu implementieren.

void OnDeinit(const int reason)
  {
//---
   if(CheckPointer(Symb) != POINTER_INVALID)
      delete Symb;
//---
   if(CheckPointer(RSI) != POINTER_INVALID)
      delete RSI;
//---
   if(CheckPointer(CCI) != POINTER_INVALID)
      delete CCI;
//---
   if(CheckPointer(ATR) != POINTER_INVALID)
      delete ATR;
//---
   if(CheckPointer(MACD) != POINTER_INVALID)
      delete MACD;
//---
   if(CheckPointer(Kmeans) != POINTER_INVALID)
      delete Kmeans;
//---
  }

Die Implementierung des Algorithmus und das Training des Modells sind in der Funktion Train enthalten. Zu Beginn der Funktion werden vorbereitende Arbeiten durchgeführt. Es beginnt mit dem Erstellen eines Objekts für die Arbeit mit einem OpenCL-Gerät. Diese Technologie wird zur Implementierung des k-means-Algorithmus verwendet. Einzelheiten zur Implementierung finden Sie in dem Artikel „Neuronale Netze leicht gemacht (Teil 15): Datenclustering mit MQL5".

void Train(void)
  {
   COpenCLMy *opencl = OpenCLCreate(cl_unsupervised);
   if(CheckPointer(opencl) == POINTER_INVALID)
     {
      ExpertRemove();
      return;
     }
   if(!Kmeans.SetOpenCL(opencl))
     {
      delete opencl;
      ExpertRemove();
      return;
     }

Als Nächstes aktualisieren wir die historischen Daten.

   MqlDateTime start_time;
   TimeCurrent(start_time);
   start_time.year -= StudyPeriod;
   if(start_time.year <= 0)
      start_time.year = 1900;
   datetime st_time = StructToTime(start_time);
//---
   int bars = CopyRates(Symb.Name(), TimeFrame, st_time, TimeCurrent(), Rates);
   if(!RSI.BufferResize(bars) || !CCI.BufferResize(bars) || !ATR.BufferResize(bars) || !MACD.BufferResize(bars))
     {
      ExpertRemove();
      return;
     }
   if(!ArraySetAsSeries(Rates, true))
     {
      ExpertRemove();
      return;
     }
//---
   RSI.Refresh();
   CCI.Refresh();
   ATR.Refresh();
   MACD.Refresh();

Dann laden wir das trainierte k-means-Modell. Vergessen wir nicht, die Ergebnisse bei jedem Schritt zu kontrollieren.

   int handl = FileOpen(StringFormat("kmeans_%d.net", Clusters), FILE_READ | FILE_BIN);
   if(handl == INVALID_HANDLE)
     {
      ExpertRemove();
      return;
     }
   if(FileReadInteger(handl) != Kmeans.Type())
     {
      ExpertRemove();
      return;
     }
   bool result = Kmeans.Load(handl);
   FileClose(handl);
   if(!result)
     {
      ExpertRemove();
      return;
     }

Nach dem erfolgreichem Abschluss der oben genannten Vorgänge prüfen wir, ob genügend historische Daten vorhanden sind.

   int total = bars - (int)HistoryBars - 480;
   double data[], fractals[];
   if(ArrayResize(data, total * 8 * HistoryBars) <= 0 ||
      ArrayResize(fractals, total * 3) <= 0)
     {
      ExpertRemove();
      return;
     }

Als Nächstes erstellen wir eine historische Stichprobe, die geclustert werden soll.

   for(int i = 0; (i < total && !IsStopped()); i++)
     {
      Comment(StringFormat("Create data: %d of %d", i, total));
      for(int b = 0; b < (int)HistoryBars; b++)
        {
         int bar = i + b + 480;
         int shift = (i * (int)HistoryBars + b) * 8;
         double open = Rates[bar]
                       .open;
         data[shift] = open - Rates[bar].low;
         data[shift + 1] = Rates[bar].high - open;
         data[shift + 2] = Rates[bar].close - open;
         data[shift + 3] = RSI.GetData(MAIN_LINE, bar);
         data[shift + 4] = CCI.GetData(MAIN_LINE, bar);
         data[shift + 5] = ATR.GetData(MAIN_LINE, bar);
         data[shift + 6] = MACD.GetData(MAIN_LINE, bar);
         data[shift + 7] = MACD.GetData(SIGNAL_LINE, bar);
        }
      int shift = i * 3;
      int bar = i + 480;
      fractals[shift] = (int)(Rates[bar - 1].high <= Rates[bar].high && Rates[bar + 1].high < Rates[bar].high);
      fractals[shift + 1] = (int)(Rates[bar - 1].low >= Rates[bar].low && Rates[bar + 1].low > Rates[bar].low);
      fractals[shift + 2] = (int)((fractals[shift] + fractals[shift]) == 0);
     }
   if(IsStopped())
     {
      ExpertRemove();
      return;
     }
   CBufferFloat *Data = new CBufferFloat();
   if(CheckPointer(Data) == POINTER_INVALID ||
      !Data.AssignArray(data))
      return;
   CBufferFloat *Fractals = new CBufferFloat();
   if(CheckPointer(Fractals) == POINTER_INVALID ||
      !Fractals.AssignArray(fractals))
      return;

Und wir führen das Clustering durch.

//---
   ResetLastError();
   Data = Kmeans.SoftMax(Data);

Als nächstes können wir mit der Kreuzentropie-Methode arbeiten. Zunächst bereiten wir die erforderlichen Variablen vor. Hier werden wir der Matrix der Systemzustände, states, und der der durchgeführten Aktionen, actions, Nullen zuweisen. Die Zeilen der Matrixdaten entsprechen den Durchgängen. Ihre Spalten stehen für jeden Schritt des entsprechenden Durchgangs. Damit werden die Zustände bei jedem Schritt des entsprechenden Durchgangs speichern. Die Matrix der Aktionen speichert die im jeweiligen Schritt durchgeführte Aktion.

Der Vektor CumRewards wird verwendet, um die Belohnung für jeden Durchgang zu kumulieren.

Die Politik des Agenten wird mit gleichen Wahrscheinlichkeiten für jede Aktion initialisiert.

   vector   env = vector::Zeros(Data.Total() / Clusters);
   vector   target = vector::Zeros(env.Size());
   matrix   states = matrix::Zeros(Samples, env.Size());
   matrix   actions = matrix::Zeros(Samples, env.Size());
   vector   CumRewards = vector::Zeros(Samples);
   double   average = 1.0 / Actions;
   matrix   policy = matrix::Full(Clusters, Actions, average);

Bitte beachten Sie, dass das obige Beispiel ziemlich „unrealistisch“ ist und nur zur Demonstration der Technologie dient. Um zu vermeiden, dass sich die Anzahl der möglichen Systemzustände erhöht, habe ich daher den Einfluss der Aktionen des Agenten auf die Änderung des nachfolgenden Zustands ausgeschlossen. Dies ermöglichte die Erstellung einer Sequenz aller Systemzustände für den analysierten Zeitraum im Vektor env. Die Verwendung der Zieldaten aus dem Problem des überwachten Lernens ermöglicht es uns, den Vektor target von Zielwerten zu erstellen. Bei der Lösung praktischer Probleme wird es diese Möglichkeit nicht geben. Um diese Daten zu erhalten, müssen wir uns daher jedes Mal auf die Umwelt beziehen.

   for(ulong state = 0; state < env.Size(); state++)
     {
      ulong shift = state * Clusters;
      env[state] = (double)(Data.Maximum((int)shift, Clusters) - shift);
      shift = state * Actions;
      target[state] = Fractals.Maximum((int)shift, Actions) - shift;
     }

Damit ist die Vorbereitungsphase abgeschlossen. Gehen wir nun zur direkten Implementierung des Algorithmus der Kreuzentropie-Methode über. Wie oben beschrieben, wird der Algorithmus in einem Schleifensystem implementiert.

Die äußere Schleife zählt die Anzahl der Iterationen im Zusammenhang mit der Aktualisierung der Agentenpolitik. In dieser Schleife werden zunächst die kumulativen Belohnungsvektoren (CumRewards) zurückgesetzt.

   for(int iter = 0; iter < 200; iter++)
     {
      CumRewards.Fill(0);

Dann erstellen wir eine verschachtelte Schleife für die Durchläufe durch den analysierten Prozess.

      for(int sampl = 0; sampl < Samples; sampl++)
        {

Jeder Durchlauf enthält seinerseits eine verschachtelte Schleife, die alle Schritte des Prozesses durchläuft. Bei jedem Schritt wählen wir die wahrscheinlichste Aktion für den aktuellen Zustand. Wenn dieser Zustand neu ist, wird eine neue Aktion zufällig ausgewählt. Anschließend übergeben wir die ausgewählte Aktion an die Umwelt und erhalten eine Belohnung.

In dieser Implementierung habe ich eine Belohnung von 1 für die richtige Aktion (entspricht der Referenz) und -1 für die anderen Fälle vergeben.

Den aktuellen Zustand und die Aktion speichern wir und gehen zum nächsten Schritt über (eine neue Iteration der Schleife).

         for(ulong state = 0; state < env.Size(); state++)
           {
            ulong a = policy.Row((int)env[state]).ArgMax();
            if(policy[(int)env[state], a] <= average)
               a = (int)(MathRand() / 32768.0 * Actions);
            if(a == target[state])
               CumRewards[sampl] += 1;
            else
               CumRewards[sampl] -= 1;
            actions[sampl, state] = (double)a;
            states[sampl,state]=env[state];
           }

Nachdem wir alle Durchgänge absolviert haben, bestimmen wir die Belohnungsstufe für die Referenzdurchgänge.

      double percentile = CumRewards.Percentile(Percentile);

Dann aktualisieren wir die Agentenpolitik. Zu diesem Zweck sollten wir ein System von Schleifen durch alle durchgeführten Durchgänge einrichten und nur die Referenzdurchgänge auswählen.

Für Referenzdurchläufe werden alle abgeschlossenen Schritte in einer Schleife durchlaufen und für jeden besuchten Zustand der Zähler der entsprechenden abgeschlossenen Aktion um 1 erhöht. Da wir nur die Referenzdurchläufe prüfen, halten wir die durchgeführten Aktionen für korrekt. Weil sie die höchsten Belohnungen erzielten.

      policy.Fill(0);
      for(int sampl = 0; sampl < Samples; sampl++)
        {
         if(CumRewards[sampl] < percentile)
            continue;
         for(int state = 0; state < env.Size(); state++)
            policy[(int)states[sampl, state], (int)actions[sampl, state]] += 1;
        }

Nach dem Zählen der Aktionen der Referenzstrategien normalisieren wir die Strategie so, dass die Gesamtwahrscheinlichkeit der Aktionen für jeden Zustand 1 ist. Dazu gehen wir in einer Schleife durch alle Zeilen der Politikmatrix policy. Jede Zeile dieser Matrix entspricht einem bestimmten Zustand des Systems, und die Spalte entspricht der durchgeführten Aktion. Wenn für einen Zustand keine Aktionen gespeichert wurden, gelten alle Aktionen für diesen Zustand als gleichermaßen möglich.

      for(int row = 0; row < Clusters; row++)
        {
         vector temp = policy.Row(row);
         double sum = temp.Sum();
         if(sum > 0)
            temp = temp / sum;
         else
            temp.Fill(average);
         if(!policy.Row(temp, row))
            break;
        }

Nach Abschluss der Schleifenwiederholungen erhalten wir die aktualisierte Politik unseres Agenten.

Zur Information geben wir die maximale erhaltene Belohnung aus und fahren mit einer neuen Iteration durch die analysierte Umgebung fort.

      PrintFormat("Iteration %d, Max reward %.0f", iter, CumRewards.Max());
     }

Nach Abschluss des Modelltrainings löschen wir die Objekte der Trainingsstichprobe und rufen die Prozedur auf, um die EA-Arbeit zu beenden.

   if(CheckPointer(Data) == POINTER_DYNAMIC)
      delete Data;
   if(CheckPointer(Fractals) == POINTER_DYNAMIC)
      delete Fractals;
   if(CheckPointer(opencl) == POINTER_DYNAMIC)
      delete opencl;
   Comment("");
//---
   ExpertRemove();
  }

Im Anhang finden Sie den gesamten EA-Code und alle Bibliotheken.


Schlussfolgerung

Dieser Artikel hat ein neues Kapitel in unserer Studie zum maschinellen Lernen aufgeschlagen: Reinforcement-Learning. Dieser Ansatz kommt der Art und Weise, wie lebende Organismen lernen, am nächsten. Sie ist nach dem Prinzip von Versuch und Irrtum aufgebaut. Der Ansatz ist recht vielversprechend, da er es ermöglicht, logisch fundierte Strategien für das Modellverhalten auf der Grundlage von unbeschrifteten Daten zu entwickeln. Dies erfordert jedoch eine gründliche Vorbereitung des Belohnungssystems des Modells.

In diesem Artikel haben wir einen der Algorithmen des Lernen durch Verstärkung besprochen, die Kreuzentropie-Methode. Diese Methode ist einfach zu verstehen, hat aber eine Reihe von Einschränkungen. Ein eher vereinfachtes Beispiel für die Umsetzung zeigt jedoch das große Potenzial dieses Ansatzes.

In den nächsten Artikeln werden wir uns weiter mit dem Verstärkungslernen beschäftigen und andere Algorithmen kennenlernen. Einige von ihnen verwenden neuronale Netze, um den Agenten zu trainieren.

Liste der Referenzen

  1. Neuronale Netze leicht gemacht (Teil 14): Datenclustering
  2. Neuronale Netze leicht gemacht (Teil 15): Datenclustering mit MQL5
  3. Neuronale Netze leicht gemacht (Teil 16): Praktische Anwendung des Clustering

Programme, die im diesem Artikel verwendet werden

# Name Typ Beschreibung
1 crossenteopy.mq5 EA Expert Advisor zum Trainieren des Modells 
2 kmeans.mqh  Klassenbibliothek Bibliothek zur Implementierung der k-means-Methode 
3 unsupervised.cl Code Base
OpenCL-Programmcode-Bibliothek zur Implementierung der k-means-Methode


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/11344

Beigefügte Dateien |
MQL5.zip (85.7 KB)
DoEasy. Steuerung (Teil 17): Beschneiden unsichtbarer Objektteile, Hilfspfeiltasten WinForms-Objekte DoEasy. Steuerung (Teil 17): Beschneiden unsichtbarer Objektteile, Hilfspfeiltasten WinForms-Objekte
In diesem Artikel werde ich die Funktionalität zum Ausblenden von Objektabschnitten, die sich außerhalb ihrer Container befinden, erstellen. Außerdem werde ich zusätzliche Pfeiltastenobjekte erstellen, die als Teil anderer WinForms-Objekte verwendet werden können.
Neuronale Netze leicht gemacht (Teil 25): Praxis des Transfer-Learnings Neuronale Netze leicht gemacht (Teil 25): Praxis des Transfer-Learnings
In den letzten beiden Artikeln haben wir ein Tool zur Erstellung und Bearbeitung von Modellen neuronaler Netze entwickelt. Nun ist es an der Zeit, die Einsatzmöglichkeiten der Technologie des Transfer-Learnings anhand praktischer Beispiele zu bewerten.
DoEasy. Steuerung (Teil 18): Funktionsweise für scrollende Registerkarten in TabControl DoEasy. Steuerung (Teil 18): Funktionsweise für scrollende Registerkarten in TabControl
In diesem Artikel werde ich die Schaltflächen der Kopfzeilen-Scroll-Steuerung im TabControl WinForms-Objekt platzieren, für den Fall, dass die Kopfzeile nicht in die Größe des Steuerelements passt. Außerdem werde ich die Verschiebung der Kopfleiste beim Klicken auf die abgeschnittene Registerkartenüberschrift implementieren.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 28): Der Zukunft entgegen (III) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 28): Der Zukunft entgegen (III)
Es gibt noch eine Aufgabe, der unser Auftragssystem nicht gewachsen ist, aber wir werden das ENDLICH verstehen. Der MetaTrader 5 bietet ein Ticketsystem, das die Erstellung und Korrektur von Auftragswerten ermöglicht. Die Idee ist, einen Expert Advisor zu haben, der das gleiche Ticketsystem schneller und effizienter machen würde.