Esecuzione di un modello

Per eseguire un modello ONNX in MQL5, completare 3 passaggi:

  1. Caricare il modello da un file *.onnx utilizzando la funzione OnnxCreate o da un array utilizzando OnnxCreateFromBuffer.
  2. Specificare le dimensioni dei dati di ingresso e di uscita utilizzando le funzioni OnnxSetInputShape e OnnxSetOutputShape.
  3. Eseguire il modello utilizzando la funzione OnnxRun, passando i parametri in ingresso e in uscita pertinenti.
  4. Quando necessario, è possibile terminare l'operatività del modello utilizzando la funzione OnnxRelease.

 

Quando si crea un modello ONNX, si dovrebbero considerare i limiti e le restrizioni esistenti, descritti in https://github.com/microsoft/onnxruntime/blob/rel-1.14.0/docs/OperatorKernels.md

Di seguito sono riportati alcuni esempi di tali restrizioni:

Operazione

Tipi di dati supportati

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)

 

Di seguito è riportato un esempio di codice MQL5 dal progetto pubblico ONNX.Price.Prediction.

const long   ExtOutputShape[] = {1,1};    // dimensione del modello d'uscita
const long   ExtInputShape [] = {1,10,4}; // dimensione del modello d'ingresso
#resource "Python/model.onnx" as uchar ExtModel[]// modello come risorsa
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   matrix rates;
//-- ottenere 10 barre
   if(!rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,2,10))
      return(-1);
//---inserire un insieme di vettori OHLC
   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);
//-- riempire le matrici di normalizzazione
   for(int i=0i<10i++)
     {
      mm.Row(m,i);
      ms.Row(s,i);
     }
//-- normalizzare i dati d'ingresso
   x_norm-=mm;
   x_norm/=ms;
//-- creare il modello
   long handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
//-- specificare la dimensione dei dati d'ingresso
   if(!OnnxSetInputShape(handle,0,ExtInputShape))
     {
      Print("OnnxSetInputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- specificare la dimensione dei dati d'uscita
   if(!OnnxSetOutputShape(handle,0,ExtOutputShape))
     {
      Print("OnnxSetOutputShape failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- convertire i dati d'ingresso normalizzati in tipo float
   matrixf x_normf;
   x_normf.Assign(x_norm);
//-- ottenere i dati d'uscita del modello qui, cioè la previsione dei prezzi
   vectorf y_norm(1);
//-- eseguire il modello
   if(!OnnxRun(handle,ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION,x_normf,y_norm))
     {
      Print("OnnxRun failed, error ",GetLastError());
      OnnxRelease(handle);
      return(-1);
     }
//-- stampa il valore di uscita del modello nel log
   Print(y_norm);
//-- fare la trasformazione inversa per ottenere il prezzo previsto
   double y_pred=y_norm[0]*s[3]+m[3];
   Print("price predicted:",y_pred);
//-- operazione completata
   OnnxRelease(handle);
   return(0);
  }

Esempio di esecuzione di uno script:

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

Il terminale MetaTrader 5 ha selezionato l'esecutore ottimale per i calcoli — ONNX Runtime Execution Provider. In questo esempio, il modello è stato eseguito sulla CPU.

Modifichiamo lo script per calcolare la percentuale di successo delle previsioni dei prezzi di Chiusura effettuate in base ai valori delle precedenti 10 barre.

#resource "Python/model.onnx" as uchar ExtModel[]// modello come risorsa
 
#define TESTS 10000  // numero di set di dati del test
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
//-- creare il modello
   long session_handle=OnnxCreateFromBuffer(ExtModel,ONNX_DEBUG_LOGS);
   if(session_handle==INVALID_HANDLE)
     {
      Print("Cannot create model. Error ",GetLastError());
      return(-1);
     }
 
//--- poiché la dimensione del tensore d'ingresso non è definita per il modello, specificarlo esplicitamente
//--- il primo indice è la dimensione del lotto, il secondo indice è la dimensione della serie, terzo indice è il numero delle serie (OHLC)
   const long input_shape[]={1,10,4};
   if(!OnnxSetInputShape(session_handle,0,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(-2);
     }
 
//--- poiché la dimensione del tensore d'uscita non è definita per il modello, specificarlo esplicitamente
//--- il primo indice è la dimensione del lotto, deve coincidere alla dimensione del lotto nel tensore d'ingresso
//--- il secondo indice è il numero dei prezzi previsti (solo la Chiusura è prevista qui)
   const long output_shape[]={1,1};
   if(!OnnxSetOutputShape(session_handle,0,output_shape))
     {
      Print("OnnxSetOutputShape error ",GetLastError());
      return(-3);
     }
//--- escuzione dei test
   vector closes(TESTS);      // vettore per memorizzare i prezzi di convalida
   vector predicts(TESTS);    // vettore per memorizzare le previsioni ottenute
   vector prev_closes(TESTS); // vettore per memorizzare i prezzi precedenti
 
   matrix rates;              // matrice per ottenere la serie OHLC
   matrix splitted[2];        // due sottomatrici per dividere la serie in test e validazione
   ulong  parts[]={10,1};     // dimensioni delle sottomatrici divise
 
//-- parte dalla barra precedente
   for(int i=1i<=TESTSi++)
     {
      //--- ottiene 11 barre
      rates.CopyRates("EURUSD",PERIOD_H1,COPY_RATES_OHLC,i,11);
      //--- dividere la matrice in test e validazione
      rates.Vsplit(parts,splitted);
      //--- prendere il prezzo di Chiusura dalla matrice di convalida
      closes[i-1]=splitted[1][3][0];
      //---l'ultima Chiusura nella serie testata
      prev_closes[i-1]=splitted[0][3][9];
 
      //--- sottoporre al test la matrice di prova di 10 bar
      predicts[i-1]=PricePredictionTest(session_handle,splitted[0]);
      //--- errore di runtime
      if(predicts[i-1]<=0)
        {
         OnnxRelease(session_handle);
         return(-4);
        }
     }
//-- operazione completata
   OnnxRelease(session_handle);
//-- valutare se il movimento dei prezzi è stato previsto correttamente
   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);
  }
//+------------------------------------------------------------------+
// Preparare i dati ed eseguire il modello                           |
//+------------------------------------------------------------------+
double PricePredictionTest(const long session_handle,matrixrates)
  {
   static matrixf input_data(10,4); // matrice per l'ingresso trasformato
   static vectorf output_data(1);   // vettore per ricevere il risultato
   static matrix mm(10,4);          // matrice di vettori orizzontali Mean</T11><segmento 0258 >
   static matrix ms(10,4);          // matrice di vettori orizzontali Std
 
//-- un insieme di vettori verticali OHLC deve essere inserito nel modello
   matrix x_norm=rates.Transpose();
//-- normalizzare i prezzi
   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;
 
//-- eseguire il modello
   input_data.Assign(x_norm);
   if(!OnnxRun(session_handle,ONNX_DEBUG_LOGS,input_data,output_data))
     {
      Print("OnnxRun error ",GetLastError());
      return(0);
     }
//--- normalizzare il prezzo dal valore di uscita
   double y_pred=output_data[0]*s[3]+m[3];
 
   return(y_pred);
  }

Esegui lo script: l'accuratezza della previsione è di circa il 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 %