Ausführung eines Modells

Um ein ONNX-Modell in MQL5 auszuführen, führen Sie 3 Schritte aus:

  1. Laden Sie das Modell aus einer *.onnx-Datei mit der Funktion OnnxCreate oder aus einem Array mit OnnxCreateFromBuffer.
  2. Spezifizieren Sie die Formen der Eingangs- und Ausgangsdaten mit den Funktionen OnnxSetInputShape und OnnxSetOutputShape.
  3. Starten Sie das Modell mit der Funktion OnnxRun und übergeben Sie ihr die entsprechenden Eingangs- und Ausgangsparameter.
  4. Bei Bedarf können Sie den Modellbetrieb mit der Funktion OnnxRelease beenden.

 

Bei der Erstellung eines ONNX-Modells sollten Sie die bestehenden Grenzen und Einschränkungen berücksichtigen, die unter https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs/OperatorKernels.md beschrieben sind.

Einige Beispiele für solche Einschränkungen sind im Folgenden aufgeführt:

Bedienung

Unterstützte Datentypen

ReduceSum

tensor(double), tensor(float), tensor(int32), tensor(int64)

Mul

tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)

 

Nachfolgend ein MQL5-Codebeispiel aus dem öffentlichen Projekt ONNX.Price.Prediction.

const long   ExtOutputShape[] = {1,1};    // die Ausgangsform des Modells
const long   ExtInputShape [] = {1,10,4}; // Eingangsform des Modells
#resource "Python/model.onnx" as uchar ExtModel[]// Modell als Ressource
//+------------------------------------------------------------------+
//| Skript Programm Start Funktion                                   |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//--- Abrufen von 10 Balken
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//--- Eingang einer Reihe von OHLC-Vektoren
   matrix x_norm=rates.Transpose();
   vector m=x_norm.Mean(0);               
   vector s=x_norm.Std(0);
   matrix mm(10,4);
   matrix ms(10,4);
//--- die Normalisierungsmatrizen ausfüllen
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//--- Normalisieren der Eingangsdaten
   x_norm-=mm;
   x_norm/=ms;
//--- Modell erstellen
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//--- Die Form der Eingangsdaten spezifizieren
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("OnnxSetInputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- Spezifikation der Form der Ausgangsdaten
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("OnnxSetOutputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- konvertieren der normalisierten Eingänge in den Type float
   matrixf x_normf;
   x_normf.Assign(x_norm);
//--- hier die Ausgangsdaten des Modells abrufen, d. h. die Preisvorhersage
   vectorf y_norm(1);
//--- Ausführen des Modells
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("OnnxRun failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//--- Ausdrucken der Ausgangswerte des Modells in das Protokoll
   Print(y_norm);
//--- die Rücktransformation durchführen, um den voraussichtlichen Preis zu erhalten
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("price predicted:",y_pred);
//--- Operation beenden
   OnnxRelease(handle);
   return(0);
  }

Ausführungsbeispiel eines Skripts:

ONNXCreating and using per session threadpools since use_per_session_threads_ is true
ONNXDynamic block base set to 0
ONNXInitializing session.
ONNXAdding default CPU execution provider.
ONNXTotal shared scalar initializer count0
ONNXTotal fused reshape node count0
ONNXTotal shared scalar initializer count0
ONNXTotal fused reshape node count0
ONNXUse DeviceBasedPartition as default
ONNXSaving initialized tensors.
ONNXDone saving initialized tensors
ONNXSession successfully initialized.
[0.28188983]
predicted 1.0559258806393044

Das MetaTrader 5 Terminal hat die optimale Ausführung für die Berechnungen ausgewählt — ONNX Runtime Execution Provider. In diesem Beispiel wurde das Modell auf der CPU ausgeführt.

Ändern wir das Skript, um den Prozentsatz erfolgreicher Prognose der Schlusskurse zu berechnen, die auf den Werten der vorangegangenen 10 Balken basieren.

#resource "Python/model.onnx" as uchar ExtModel[]// Modell als Ressource
 
#define TESTS 10000  // Anzahl der Testdatensätze
//+------------------------------------------------------------------+
//| Skript Programm Start Funktion                                   |
//+------------------------------------------------------------------+
int OnStart()
  {
//--- Modell erstellen
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Modell konnte nicht erstellt werden. Fehler: ",GetLastError());
      return(-1);
     }
 
//--- da die Größe des Eingangstensors für das Modell nicht definiert ist, muss das explizit angegeben werden
//--- der erste Index ist die Größe des Pakets, der zweite Index ist die Seriengröße und der dritte ist die Anzahl der Serien (OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(-2);
     }
 
//--- da die Größe des Ausgangstensors für das Modell nicht definiert ist, muss sie explizit angegeben werden
//--- der erste Index ist die Größe des Pakets, er muss mit der Größe des Pakets im Eingangstensor übereinstimmen
//--- der zweite Index ist die Anzahl der vorhergesagten Preise (hier wird nur Close vorhergesagt)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("OnnxSetOutputShape Fehler: ",GetLastError());
      return(-3);
     }
//--- Durchführen der Tests
   vector closes(TESTS);      // Vektor zum Speichern von Validierungspreisen
   vector predicts(TESTS);    // Vektor zum Speichern der erhaltenen Vorhersagen
   vector prev_closes(TESTS); // Vektor zum Speichern vorheriger Preise
 
   matrix rates;              // Matrix zur Ermittlung der OHLC-Reihen
   matrix splitted[2];        // zwei Teilmatrizen, um die Reihen in Test und Validierung zu unterteilen
   ulong  parts[]={10,1};     // Größen der unterteilten Submatrizen
 
//--- Start vom vorherigen Balken
   for(int i=1i<=TESTSi++)
     {
      //--- Abrufen von 11 Balken
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- Aufteilung der Matrix in Test und Validierung
      rates.Vsplit(parts,splitted);
      //--- Auswählen des Schlusskurses aus der Validierungsmatrix
      closes[i-1]=splitted[1][3][0];
      //--- letzter Schlusskurs in den getesteten Serien
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- die Testmatrix von 10 Balken für den Test
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- Laufzeitfehler
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//--- Operation beenden
   OnnxRelease(session_handle);
//--- Bewerten, ob die Preisentwicklung richtig vorhergesagt wurde
   int    right_directions=0;
   vector delta_predicts=prev_closes-predicts;
   vector delta_actuals=prev_closes-closes;
 
   for(int i=0i<TESTSi++)
      if((delta_predicts[i]>0 && delta_actuals[i]>0) || (delta_predicts[i]<0 && delta_actuals[i]<0))
         right_directions++;
   PrintFormat("right direction predictions = %.2f%%",(right_directions*100.0)/double(TESTS));
//--- 
   return(0);
  }
//+------------------------------------------------------------------+
//|  Vorbereitung der Daten und Ausführung des Modells               |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrixrates)
  {
   static matrixf input_data(10,4); // Matrix für die transformierte Eingangs
   static vectorf output_data(1);   // Vektor zur Aufnahme des Ergebnisses
   static matrix mm(10,4);          // Matrix der Mittelwerte der horizontalen Vektoren
   static matrix ms(10,4);          // Matrix der Std der horizontalen Vektoren
 
//--- eine Reihe von vertikalen OHLC-Vektoren muss in das Modell eingegeben werden
   matrix x_norm=rates.Transpose();
//--- Normalisierung der Preise
   vector m=x_norm.Mean(0);
   vector s=x_norm.Std(0);
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
   x_norm-=mm;
   x_norm/=ms;
 
//--- Ausführen des Modells
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("OnnxRun Fehler: ",GetLastError());
      return(0);
     }
//--- De-Normalisieren der Preise von den Ausgangswerten
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

Führen Sie das Skript aus: Die Vorhersagegenauigkeit beträgt etwa 51%.

ONNX: Creating and using per session threadpools since use_per_session_threads_ is true
ONNX: Dynamic block base set to 0
ONNX: Initializing session.
ONNX: Adding default CPU execution provider.
ONNX: Total shared scalar initializer count: 0
ONNX: Total fused reshape node count: 0
ONNX: Total shared scalar initializer count: 0
ONNX: Total fused reshape node count: 0
ONNX: Use DeviceBasedPartition as default
ONNX: Saving initialized tensors.
ONNX: Done saving initialized tensors
ONNX: Session successfully initialized.
right direction predictions = 51.34 %