English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
회귀 메트릭을 사용하여 ONNX 모델 평가하기

회귀 메트릭을 사용하여 ONNX 모델 평가하기

MetaTrader 5 | 12 2월 2024, 10:06
268 0
MetaQuotes
MetaQuotes

소개

회귀는 레이블이 지정되지 않은 예제에서 실제의 값을 예측하는 작업입니다. 회귀 분석의 예로서 잘 알려진 것은 크기, 무게, 색상, 투명도 등의 특성을 기반으로 다이아몬드의 가치를 추정하는 것입니다.

회귀 메트릭 회귀 모델 예측의 정확도를 평가하는 데 사용됩니다. 유사한 알고리즘에도 불구하고 회귀 지표는 손실 함수와는 의미적으로 다릅니다. 여러분들은 이 둘의 차이점을 이해해야 합니다. 차이점은 다음과 같이 공식화할 수 있습니다:

  • 손실 함수는 모델 구축 문제를 최적화 문제로 축소하는 순간 발생합니다. 일반적으로 좋은 속성(예: 차별성)이 있어야 합니다.

  • 메트릭은 일반적으로 모델의 매개변수가 아닌 예측된 값에만 의존하는 외부의 객관적인 품질 기준입니다.


MQL5의 회귀 메트릭

MQL5 언어에는 다음과 같은 메트릭이 있습니다:

  • 평균 절대 오차, MAE
  • 평균 제곱오차, MSE
  • 평균 제곱근 오차, RMSE
  • R-제곱, R2
  • 평균 절대 백분율 오류, MAPE
  • 평균 제곱 백분율 오차, MSPE
  • 평균 제곱 로그 오차, RMSLE

앞으로 MQL5의 회귀 메트릭 수는 늘어날 것입니다.


회귀 메트릭의 간략한 특징

MAE는 절대 오차, 즉 예측된 수치가 실제 수치와 얼마나 차이가 나는지를 측정합니다. 오차는 목적 함수의 값과 동일한 단위로 측정됩니다. 오류 값은 가능한 값의 범위에 따라 해석됩니다. 예를 들어 목표 값이 1에서 1.5 범위인 경우 값이 10인 평균 절대 오차는 매우 큰 오차이며, 10000...15000 범위의 경우에는 상당히 허용 가능한 오차입니다. 회귀 메트릭은 값의 확산이 큰 예측을 평가하는 데는 적합하지 않습니다.

MSE에서 각 오류는 제곱으로 인해 고유한 가중치를 갖습니다. 이로 인해 예측과 현실 사이의 큰 불일치가 훨씬 더 눈에 띕니다.

RMSE 는 MSE와 동일한 장점이 있지만 오차가 목적 함수의 값과 동일한 단위로 측정되기 때문에 이해하기가 더 편리합니다. RSME는 비정상과 스파이크에 매우 민감합니다. MAE 와 RMSE를 함께 사용하여 예측 집합의 오류 변동을 확인할 수 있습니다. RMSE는 항상 MAE보다 크거나 같아야 합니다. 그 차이가 클수록 샘플에서는 개별 오류의 확산이 더 커집니다. RMSE = MAE인 경우 모든 오류의 크기는 동일합니다.

R2 - 결정비는 두 무작위 변수 간의 관계 강도를 나타냅니다. 결정비는 모델이 설명할 수 있는 데이터 다양성의 비중을 파악하는 데 도움이 됩니다. 모델이 항상 정확하게 예측하는 경우 메트릭은 1입니다. 좋지 않은 모델의 경우 0입니다. 모델이 데이터 추세를 따르지 않으면서 좋지 않은 모델보다 더 나쁘게 예측하는 경우 메트릭 값은 음수가 될 수 있습니다.

MAPE - 오류는 차원이 없으며 해석하기가 매우 쉽습니다. MAPE는 소수점과 백분율로 모두 표현할 수 있습니다. MQL5에서는 소수점으로 표현됩니다. 예를 들어 값이 0.1이면 오차가 실제 값의 10%임을 나타냅니다. 이 지표의 기본 개념은 상대적 편차에 대한 민감도입니다. 실제 측정 단위로 작업해야 하는 작업에는 적합하지 않습니다.

MSPE는 가중치가 관찰된 값의 제곱에 반비례하는 MSE의 가중치 버전으로 간주될 수 있습니다. 따라서 관측값이 증가함에 따라 오차는 감소하는 경향이 있습니다.

실제 값이 몇 배 이상의 크기정도로 확장될 때 RMSLE을 사용합니다. 정의에 따르면 예측값과 실제 관측값은 음수가 될 수 없습니다.

위의 모든 메트릭을 계산하는 알고리즘은 소스 파일 VectorRegressionMetric.mqh에 있습니다.


ONNX 모델

우리는 이전 일의 바에서 당일 종가(EURUSD, D1)를 예측하는 4개의 회귀 모델을 사용했습니다. 우리는 이전 기사에서 이러한 모델을 살펴보았습니다: "클래스에 ONNX 모델 래핑하기", "MQL5에서 ONNX 모델을 앙상블하는 방법의 예" 및 "MQL5에서 ONNX 모델을 사용하는 방법"을 읽어보세요. 이글에서는 모델 학습에 사용된 규칙을 반복하지 않겠습니다. 모든 모델을 학습하기 위한 스크립트는 이 문서에 첨부된 zip 아카이브의 Python 하위 폴더에 있습니다. 훈련된 onnx 모델 - model.eurusd.D1.10, model.eurusd.D1.30, model.eurusd.D1.52 및 model.eurusd.D1.63도 여기에 있습니다.


클래스에서 ONNX 모델 래핑하기

이전 글에서 우리는 ONNX 모델의 기본 클래스와 분류 모델을 위한 파생 클래스들을 살펴보았습니다. 우리는 베이스 클래스의 유연성을 높이기 위해 몇 가지 작은 변경 사항을 구현했었습니다.

//+------------------------------------------------------------------+
//|                                            ModelSymbolPeriod.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+

//--- price movement prediction
#define PRICE_UP   0
#define PRICE_SAME 1
#define PRICE_DOWN 2

//+------------------------------------------------------------------+
//| Base class for models based on trained symbol and period         |
//+------------------------------------------------------------------+
class CModelSymbolPeriod
  {
protected:
   string            m_name;             // model name
   long              m_handle;           // created model session handle
   string            m_symbol;           // symbol of trained data
   ENUM_TIMEFRAMES   m_period;           // timeframe of trained data
   datetime          m_next_bar;         // time of next bar (we work at bar begin only)
   double            m_class_delta;      // delta to recognize "price the same" in regression models

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
   CModelSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES period,const double class_delta=0.0001)
     {
      m_name="";
      m_handle=INVALID_HANDLE;
      m_symbol=symbol;
      m_period=period;
      m_next_bar=0;
      m_class_delta=class_delta;
     }

   //+------------------------------------------------------------------+
   //| Destructor                                                       |
   //+------------------------------------------------------------------+
   ~CModelSymbolPeriod(void)
     {
      Shutdown();
     }

   //+------------------------------------------------------------------+
   //|                                                                  |
   //+------------------------------------------------------------------+
   string GetModelName(void)
     {
      return(m_name);
     }

   //+------------------------------------------------------------------+
   //| virtual stub for Init                                            |
   //+------------------------------------------------------------------+
   virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period)
     {
      return(false);
     }

   //+------------------------------------------------------------------+
   //| Check for initialization, create model                           |
   //+------------------------------------------------------------------+
   bool CheckInit(const string symbol, const ENUM_TIMEFRAMES period,const uchar& model[])
     {
      //--- check symbol, period
      if(symbol!=m_symbol || period!=m_period)
        {
         PrintFormat("Model must work with %s,%s",m_symbol,EnumToString(m_period));
         return(false);
        }

      //--- create a model from static buffer
      m_handle=OnnxCreateFromBuffer(model,ONNX_DEFAULT);
      if(m_handle==INVALID_HANDLE)
        {
         Print("OnnxCreateFromBuffer error ",GetLastError());
         return(false);
        }

      //--- ok
      return(true);
     }

   //+------------------------------------------------------------------+
   //| Release ONNX session                                             |
   //+------------------------------------------------------------------+
   void Shutdown(void)
     {
      if(m_handle!=INVALID_HANDLE)
        {
         OnnxRelease(m_handle);
         m_handle=INVALID_HANDLE;
        }
     }

   //+------------------------------------------------------------------+
   //| Check for continue OnTick                                        |
   //+------------------------------------------------------------------+
   virtual bool CheckOnTick(void)
     {
      //--- check new bar
      if(TimeCurrent()<m_next_bar)
         return(false);
      //--- set next bar time
      m_next_bar=TimeCurrent();
      m_next_bar-=m_next_bar%PeriodSeconds(m_period);
      m_next_bar+=PeriodSeconds(m_period);

      //--- work on new day bar
      return(true);
     }

   //+------------------------------------------------------------------+
   //| virtual stub for PredictPrice (regression model)                 |
   //+------------------------------------------------------------------+
   virtual double PredictPrice(datetime date)
     {
      return(DBL_MAX);
     }

   //+------------------------------------------------------------------+
   //| Predict class (regression ~> classification)                     |
   //+------------------------------------------------------------------+
   virtual int PredictClass(datetime date,vector& probabilities)
     {
      date-=date%PeriodSeconds(m_period);
      double predicted_price=PredictPrice(date);
      if(predicted_price==DBL_MAX)
         return(-1);

      double last_close[2];
      if(CopyClose(m_symbol,m_period,date,2,last_close)!=2)
         return(-1);
      double prev_price=last_close[0];

      //--- classify predicted price movement
      int    predicted_class=-1;
      double delta=prev_price-predicted_price;
      if(fabs(delta)<=m_class_delta)
         predicted_class=PRICE_SAME;
      else
        {
         if(delta<0)
            predicted_class=PRICE_UP;
         else
            predicted_class=PRICE_DOWN;
        }

      //--- set predicted probability as 1.0
      probabilities.Fill(0);
      if(predicted_class<(int)probabilities.Size())
         probabilities[predicted_class]=1;
      //--- and return predicted class
      return(predicted_class);
     }
  };
//+------------------------------------------------------------------+

PredictPrice 및 PredictClass 메서드에 날짜/시간 매개변수를 추가했습니다. 이는 현재 시점 뿐만 아니라 모든 시점에 대한 예측을 수행할 수 있도록 하기 위해서 였습니다. 이는 예측 벡터를 형성하는 데 유용합니다.


D1_10 모델 클래스

첫 번째 모델은 model.eurusd.D1.10.onnx입니다. 10개의 OHLC 가격 시리즈에 대해 EURUSD D1에서 학습된 회귀 모델입니다.
//+------------------------------------------------------------------+
//|                                             ModelEurusdD1_10.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ModelSymbolPeriod.mqh"

#resource "Python/model.eurusd.D1.10.onnx" as uchar model_eurusd_D1_10[]

//+------------------------------------------------------------------+
//| ONNX-model wrapper class                                         |
//+------------------------------------------------------------------+
class CModelEurusdD1_10 : public CModelSymbolPeriod
  {
private:
   int               m_sample_size;

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
   CModelEurusdD1_10(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1)
     {
      m_name="D1_10";
      m_sample_size=10;
     }

   //+------------------------------------------------------------------+
   //| ONNX-model initialization                                        |
   //+------------------------------------------------------------------+
   virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period)
     {
      //--- check symbol, period, create model
      if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_10))
        {
         Print("model_eurusd_D1_10 : initialization error");
         return(false);
        }

      //--- since not all sizes defined in the input tensor we must set them explicitly
      //--- first index - batch size, second index - series size, third index - number of series (OHLC)
      const long input_shape[] = {1,m_sample_size,4};
      if(!OnnxSetInputShape(m_handle,0,input_shape))
        {
         Print("model_eurusd_D1_10 : OnnxSetInputShape error ",GetLastError());
         return(false);
        }
   
      //--- since not all sizes defined in the output tensor we must set them explicitly
      //--- first index - batch size, must match the batch size of the input tensor
      //--- second index - number of predicted prices
      const long output_shape[] = {1,1};
      if(!OnnxSetOutputShape(m_handle,0,output_shape))
        {
         Print("model_eurusd_D1_10 : OnnxSetOutputShape error ",GetLastError());
         return(false);
        }
      //--- ok
      return(true);
     }

   //+------------------------------------------------------------------+
   //| Predict price                                                    |
   //+------------------------------------------------------------------+
   virtual double PredictPrice(datetime date)
     {
      static matrixf input_data(m_sample_size,4);    // matrix for prepared input data
      static vectorf output_data(1);                 // vector to get result
      static matrix  mm(m_sample_size,4);            // matrix of horizontal vectors Mean
      static matrix  ms(m_sample_size,4);            // matrix of horizontal vectors Std
      static matrix  x_norm(m_sample_size,4);        // matrix for prices normalize
   
      //--- prepare input data
      matrix rates;
      //--- request last bars
      date-=date%PeriodSeconds(m_period);
      if(!rates.CopyRates(m_symbol,m_period,COPY_RATES_OHLC,date-1,m_sample_size))
         return(DBL_MAX);
      //--- get series Mean
      vector m=rates.Mean(1);
      //--- get series Std
      vector s=rates.Std(1);
      //--- prepare matrices for prices normalization
      for(int i=0; i<m_sample_size; i++)
        {
         mm.Row(m,i);
         ms.Row(s,i);
        }
      //--- the input of the model must be a set of vertical OHLC vectors
      x_norm=rates.Transpose();
      //--- normalize prices
      x_norm-=mm;
      x_norm/=ms;
   
      //--- run the inference
      input_data.Assign(x_norm);
      if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data))
         return(DBL_MAX);

      //--- denormalize the price from the output value
      double predicted=output_data[0]*s[3]+m[3];
      //--- return prediction
      return(predicted);
     }
  };
//+------------------------------------------------------------------+

이 모델은 공개 프로젝트 MQL5\Shared Projects\ONNX.Price.Prediction에 게시된 첫 번째 모델과 유사합니다.

10개의 OHLC 가격 계열은 훈련시와 동일한 방식으로 정규화해야 합니다. 즉, 계열의 평균 가격과의 편차를 계열의 표준 편차로 나눕니다. 따라서 우리는 평균이 0이고 스프레드가 1인 특정 범위로 시리즈를 설정해서 훈련 중 수렴을 개선하는 효과를 얻습니다.


D1_30 모델 클래스

두 번째 모델은 model.eurusd.D1.30.onnx입니다. 회귀 모델은 30개의 종가 시리즈와 평균 기간이 21 및 34인 두 개의 단순 이동 평균을 사용하여 EURUSD D1에 대해 학습했습니다.

//+------------------------------------------------------------------+
//|                                             ModelEurusdD1_30.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ModelSymbolPeriod.mqh"

#resource "Python/model.eurusd.D1.30.onnx" as uchar model_eurusd_D1_30[]

//+------------------------------------------------------------------+
//| ONNX-model wrapper class                                         |
//+------------------------------------------------------------------+
class CModelEurusdD1_30 : public CModelSymbolPeriod
  {
private:
   int               m_sample_size;
   int               m_fast_period;
   int               m_slow_period;
   int               m_sma_fast;
   int               m_sma_slow;

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
   CModelEurusdD1_30(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1)
     {
      m_name="D1_30";
      m_sample_size=30;
      m_fast_period=21;
      m_slow_period=34;
      m_sma_fast=INVALID_HANDLE;
      m_sma_slow=INVALID_HANDLE;
     }

   //+------------------------------------------------------------------+
   //| ONNX-model initialization                                        |
   //+------------------------------------------------------------------+
   virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period)
     {
      //--- check symbol, period, create model
      if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_30))
        {
         Print("model_eurusd_D1_30 : initialization error");
         return(false);
        }

      //--- since not all sizes defined in the input tensor we must set them explicitly
      //--- first index - batch size, second index - series size, third index - number of series (Close, MA fast, MA slow)
      const long input_shape[] = {1,m_sample_size,3};
      if(!OnnxSetInputShape(m_handle,0,input_shape))
        {
         Print("model_eurusd_D1_30 : OnnxSetInputShape error ",GetLastError());
         return(false);
        }
   
      //--- since not all sizes defined in the output tensor we must set them explicitly
      //--- first index - batch size, must match the batch size of the input tensor
      //--- second index - number of predicted prices
      const long output_shape[] = {1,1};
      if(!OnnxSetOutputShape(m_handle,0,output_shape))
        {
         Print("model_eurusd_D1_30 : OnnxSetOutputShape error ",GetLastError());
         return(false);
        }
      //--- indicators
      m_sma_fast=iMA(m_symbol,m_period,m_fast_period,0,MODE_SMA,PRICE_CLOSE);
      m_sma_slow=iMA(m_symbol,m_period,m_slow_period,0,MODE_SMA,PRICE_CLOSE);
      if(m_sma_fast==INVALID_HANDLE || m_sma_slow==INVALID_HANDLE)
        {
         Print("model_eurusd_D1_30 : cannot create indicator");
         return(false);
        }
      //--- ok
      return(true);
     }

   //+------------------------------------------------------------------+
   //| Predict price                                                    |
   //+------------------------------------------------------------------+
   virtual double PredictPrice(datetime date)
     {
      static matrixf input_data(m_sample_size,3);    // matrix for prepared input data
      static vectorf output_data(1);                 // vector to get result
      static matrix  x_norm(m_sample_size,3);        // matrix for prices normalize
      static vector  vtemp(m_sample_size);
      static double  ma_buffer[];
   
      //--- request last bars
      date-=date%PeriodSeconds(m_period);
      if(!vtemp.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,date-1,m_sample_size))
         return(DBL_MAX);
      //--- get series Mean
      double m=vtemp.Mean();
      //--- get series Std
      double s=vtemp.Std();
      //--- normalize
      vtemp-=m;
      vtemp/=s;
      x_norm.Col(vtemp,0);
      //--- fast sma
      if(CopyBuffer(m_sma_fast,0,date-1,m_sample_size,ma_buffer)!=m_sample_size)
         return(-1);
      vtemp.Assign(ma_buffer);
      m=vtemp.Mean();
      s=vtemp.Std();
      vtemp-=m;
      vtemp/=s;
      x_norm.Col(vtemp,1);
      //--- slow sma
      if(CopyBuffer(m_sma_slow,0,date-1,m_sample_size,ma_buffer)!=m_sample_size)
         return(-1);
      vtemp.Assign(ma_buffer);
      m=vtemp.Mean();
      s=vtemp.Std();
      vtemp-=m;
      vtemp/=s;
      x_norm.Col(vtemp,2);
   
      //--- run the inference
      input_data.Assign(x_norm);
      if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data))
         return(DBL_MAX);

      //--- denormalize the price from the output value
      double predicted=output_data[0]*s+m;
      //--- return prediction
      return(predicted);
     }
  };
//+------------------------------------------------------------------+

이전 클래스에서와 마찬가지로 CheckInit 베이스 클래스 메서드는 Init 메서드에서 호출됩니다. 베이스 클래스 메서드에서는 ONNX 모델에 대한 세션이 생성되고 입력 및 출력 텐서의 크기가 명시적으로 설정됩니다.

PredictPrice 메서드는 30개의 이전 종가 및 계산된 이동 평균을 제공합니다. 데이터는 학습과 동일한 방식으로 정규화 됩니다.

이 모델은 "클래스에 ONNX 모델 래핑하기" 문서를 위해 개발되었으며 분류에서 회귀로 변환되었습니다.


D1_52 모델 클래스

세 번째 모델은 model.eurusd.D1.52.onnx입니다. 회귀 모델은 일련의 52개 종가에 대해 EURUSD D1을 학습시켰습니다.

//+------------------------------------------------------------------+
//|                                             ModelEurusdD1_52.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ModelSymbolPeriod.mqh"

#resource "Python/model.eurusd.D1.52.onnx" as uchar model_eurusd_D1_52[]

//+------------------------------------------------------------------+
//| ONNX-model wrapper class                                         |
//+------------------------------------------------------------------+
class CModelEurusdD1_52 : public CModelSymbolPeriod
  {
private:
   int               m_sample_size;

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
   CModelEurusdD1_52(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1,0.0001)
     {
      m_name="D1_52";
      m_sample_size=52;
     }

   //+------------------------------------------------------------------+
   //| ONNX-model initialization                                        |
   //+------------------------------------------------------------------+
   virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period)
     {
      //--- check symbol, period, create model
      if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_52))
        {
         Print("model_eurusd_D1_52 : initialization error");
         return(false);
        }

      //--- since not all sizes defined in the input tensor we must set them explicitly
      //--- first index - batch size, second index - series size, third index - number of series (only Close)
      const long input_shape[] = {1,m_sample_size,1};
      if(!OnnxSetInputShape(m_handle,0,input_shape))
        {
         Print("model_eurusd_D1_52 : OnnxSetInputShape error ",GetLastError());
         return(false);
        }
   
      //--- since not all sizes defined in the output tensor we must set them explicitly
      //--- first index - batch size, must match the batch size of the input tensor
      //--- second index - number of predicted prices (we only predict Close)
      const long output_shape[] = {1,1};
      if(!OnnxSetOutputShape(m_handle,0,output_shape))
        {
         Print("model_eurusd_D1_52 : OnnxSetOutputShape error ",GetLastError());
         return(false);
        }
      //--- ok
      return(true);
     }

   //+------------------------------------------------------------------+
   //| Predict price                                                    |
   //+------------------------------------------------------------------+
   virtual double PredictPrice(datetime date)
     {
      static vectorf output_data(1);            // vector to get result
      static vector  x_norm(m_sample_size);     // vector for prices normalize
   
      //--- set date to day begin
      date-=date%PeriodSeconds(m_period);
      //--- check for calculate min and max
      double price_min=0;
      double price_max=0;
      GetMinMaxClose(date,price_min,price_max);
      //--- check for normalization possibility
      if(price_min>=price_max)
         return(DBL_MAX);
      //--- request last bars
      if(!x_norm.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,date-1,m_sample_size))
         return(DBL_MAX);
      //--- normalize prices
      x_norm-=price_min;
      x_norm/=(price_max-price_min);
      //--- run the inference
      if(!OnnxRun(m_handle,ONNX_DEFAULT,x_norm,output_data))
         return(DBL_MAX);

      //--- denormalize the price from the output value
      double predicted=output_data[0]*(price_max-price_min)+price_min;
      //--- return prediction
      return(predicted);
     }

private:
   //+------------------------------------------------------------------+
   //| Get minimal and maximal Close for last 52 weeks                  |
   //+------------------------------------------------------------------+
   void GetMinMaxClose(const datetime date,double& price_min,double& price_max)
     {
      static vector close;

      close.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,date,m_sample_size*7+1);
      price_min=close.Min();
      price_max=close.Max();
     }
  };
//+------------------------------------------------------------------+

모델을 제출하기 전의 가격 정규화는 이전의 것과는 다릅니다. 훈련 중에 MinMaxScaler가 사용되었습니다. 따라서 우리는 예측일 전 52주 동안의 최소 및 최대 가격을 취합니다.

이 모델은 "MQL5에서 ONNX 모델을 사용하는 방법"이란 글에 설명된 모델과 유사합니다.


D1_63 모델 클래스

마지막으로 네 번째 모델은 model.eurusd.D1.63.onnx입니다. 회귀 모델은 63개의 종가 시리즈에 대해 EURUSD D1을 학습시켰습니다.

//+------------------------------------------------------------------+
//|                                             ModelEurusdD1_63.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ModelSymbolPeriod.mqh"

#resource "Python/model.eurusd.D1.63.onnx" as uchar model_eurusd_D1_63[]

//+------------------------------------------------------------------+
//| ONNX-model wrapper class                                         |
//+------------------------------------------------------------------+
class CModelEurusdD1_63 : public CModelSymbolPeriod
  {
private:
   int               m_sample_size;

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
   CModelEurusdD1_63(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1)
     {
      m_name="D1_63";
      m_sample_size=63;
     }

   //+------------------------------------------------------------------+
   //| ONNX-model initialization                                        |
   //+------------------------------------------------------------------+
   virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period)
     {
      //--- check symbol, period, create model
      if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_63))
        {
         Print("model_eurusd_D1_63 : initialization error");
         return(false);
        }

      //--- since not all sizes defined in the input tensor we must set them explicitly
      //--- first index - batch size, second index - series size
      const long input_shape[] = {1,m_sample_size};
      if(!OnnxSetInputShape(m_handle,0,input_shape))
        {
         Print("model_eurusd_D1_63 : OnnxSetInputShape error ",GetLastError());
         return(false);
        }
   
      //--- since not all sizes defined in the output tensor we must set them explicitly
      //--- first index - batch size, must match the batch size of the input tensor
      //--- second index - number of predicted prices
      const long output_shape[] = {1,1};
      if(!OnnxSetOutputShape(m_handle,0,output_shape))
        {
         Print("model_eurusd_D1_63 : OnnxSetOutputShape error ",GetLastError());
         return(false);
        }
      //--- ok
      return(true);
     }

   //+------------------------------------------------------------------+
   //| Predict price                                                    |
   //+------------------------------------------------------------------+
   virtual double PredictPrice(datetime date)
     {
      static vectorf input_data(m_sample_size);  // vector for prepared input data
      static vectorf output_data(1);             // vector to get result
   
      //--- request last bars
      date-=date%PeriodSeconds(m_period);
      if(!input_data.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,date-1,m_sample_size))
         return(DBL_MAX);
      //--- get series Mean
      float m=input_data.Mean();
      //--- get series Std
      float s=input_data.Std();
      //--- normalize prices
      input_data-=m;
      input_data/=s;
   
      //--- run the inference
      if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data))
         return(DBL_MAX);

      //--- denormalize the price from the output value
      double predicted=output_data[0]*s+m;
      //--- return prediction
      return(predicted);
     }
  };
//+------------------------------------------------------------------+

PredictPrice 메서드는 63개의 이전 종가 시리즈를 제공합니다. 데이터는 첫 번째 및 두 번째 모델과 동일한 방식으로 정규화됩니다.

이 모델은 이미 "MQL5에서 ONNX 모델을 앙상블하는 방법의 예”라는 기사를 위해 개발되었습니다.


모든 모델을 하나의 스크립트로 결합합니다. 현실과, 예측 및 회귀 메트릭

회귀 지표를 적용하기 위해서는 일정 수의 예측(vector_pred)을 하고 동일한 날짜에 대한 실제 데이터(vector_true)를 가져와야 합니다.

모든 모델이 동일한 기본 클래스에서 파생된 클래스로 래핑되어 있으므로 우리는 한 번에 모두를 평가할 수 있습니다.

스크립트는 매우 간단합니다.

//+------------------------------------------------------------------+
//|                                    ONNX.eurusd.D1.4M.Metrics.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#define MODELS 4
#include "ModelEurusdD1_10.mqh"
#include "ModelEurusdD1_30.mqh"
#include "ModelEurusdD1_52.mqh"
#include "ModelEurusdD1_63.mqh"

#property script_show_inputs
input datetime InpStartDate = D'2023.01.01';
input datetime InpStopDate  = D'2023.01.31';

CModelSymbolPeriod *ExtModels[MODELS];

struct PredictedPrices
  {
   string            model;
   double            pred[];
  };
PredictedPrices ExtPredicted[MODELS];

double ExtClose[];

struct Metrics
  {
   string            model;
   double            mae;
   double            mse;
   double            rmse;
   double            r2;
   double            mape;
   double            mspe;
   double            rmsle;
  };
Metrics ExtMetrics[MODELS];

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- init section
   if(!Init())
      return;

//--- predictions test loop
   datetime dates[];
   if(CopyTime(_Symbol,_Period,InpStartDate,InpStopDate,dates)<=0)
     {
      Print("Cannot get data from ",InpStartDate," to ",InpStopDate);
      return;
     }
   for(uint n=0; n<dates.Size(); n++)
      GetPredictions(dates[n]);
      
   CopyClose(_Symbol,_Period,InpStartDate,InpStopDate,ExtClose);
   CalculateMetrics();

//--- deinit section
   Deinit();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool Init()
  {
   ExtModels[0]=new CModelEurusdD1_10;
   ExtModels[1]=new CModelEurusdD1_30;
   ExtModels[2]=new CModelEurusdD1_52;
   ExtModels[3]=new CModelEurusdD1_63;

   for(long i=0; i<ExtModels.Size(); i++)
     {
      if(!ExtModels[i].Init(_Symbol,_Period))
        {
         Deinit();
         return(false);
        }
     }

   for(long i=0; i<ExtModels.Size(); i++)
      ExtPredicted[i].model=ExtModels[i].GetModelName();

   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Deinit()
  {
   for(uint i=0; i<ExtModels.Size(); i++)
      delete ExtModels[i];
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void GetPredictions(datetime date)
  {
//--- collect predicted prices
   for(uint i=0; i<ExtModels.Size(); i++)
      ExtPredicted[i].pred.Push(ExtModels[i].PredictPrice(date));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CalculateMetrics()
  {
   vector vector_pred,vector_true;
   vector_true.Assign(ExtClose);

   for(uint i=0; i<ExtModels.Size(); i++)
     {
      ExtMetrics[i].model=ExtPredicted[i].model;
      vector_pred.Assign(ExtPredicted[i].pred);
      ExtMetrics[i].mae  =vector_pred.RegressionMetric(vector_true,REGRESSION_MAE);
      ExtMetrics[i].mse  =vector_pred.RegressionMetric(vector_true,REGRESSION_MSE);
      ExtMetrics[i].rmse =vector_pred.RegressionMetric(vector_true,REGRESSION_RMSE);
      ExtMetrics[i].r2   =vector_pred.RegressionMetric(vector_true,REGRESSION_R2);
      ExtMetrics[i].mape =vector_pred.RegressionMetric(vector_true,REGRESSION_MAPE);
      ExtMetrics[i].mspe =vector_pred.RegressionMetric(vector_true,REGRESSION_MSPE);
      ExtMetrics[i].rmsle=vector_pred.RegressionMetric(vector_true,REGRESSION_RMSLE);
     }

   ArrayPrint(ExtMetrics);
  }
//+------------------------------------------------------------------+

EURUSD D1 차트에서 스크립트를 실행하고 날짜를 2023년 1월 1일부터 1월 31일까지로 설정해 보겠습니다. 무엇을 볼 수 있나요?

    [model]   [mae]   [mse]  [rmse]     [r2]  [mape]  [mspe] [rmsle]
[0] "D1_10" 0.00381 0.00003 0.00530  0.77720 0.00356 0.00002 0.00257
[1] "D1_30" 0.01809 0.00039 0.01963 -2.05545 0.01680 0.00033 0.00952
[2] "D1_52" 0.00472 0.00004 0.00642  0.67327 0.00440 0.00004 0.00311
[3] "D1_63" 0.00413 0.00003 0.00559  0.75230 0.00385 0.00003 0.00270

음의 R-제곱 값은 두 번째 행에서 즉시 눈에 띄게 나타납니다. 이는 모델이 작동하지 않는다는 의미입니다. 예측 그래프를 보는 것은 흥미롭습니다.

네 가지 모델 예측

실제 종가 및 기타 예측과는 거리가 먼 차트 D1_30을 볼 수 있습니다. 이 모델에 대한 지표 중에 그 어느 것도 고무적이지 않습니다. MAE는 1809개 가격대의 예측 정확도를 보여줍니다! 그러나 이 모델은 처음 개발시에는 회귀 모델이 아닌 분류 모델로 개발되었다는 점에 유의하세요. 예는 꽤 명확합니다.

다른 모델을 살펴 보겠습니다.

분석해 볼 첫번째는 D1_10입니다.

    [model]   [mae]   [mse]  [rmse]     [r2]  [mape]  [mspe] [rmsle]
[0] "D1_10" 0.00381 0.00003 0.00530  0.77720 0.00356 0.00002 0.00257

이 모델이 예측한 가격 차트를 살펴보겠습니다.

D1_10 예측

1.05에서 1.09 사이의 스프레드가 하나의 크기 정도보다 훨씬 작기 때문에 RMSLE 메트릭은 큰 의미가 없습니다. MAPE 및 MSPE 지표는 EURUSD 환율이 1에 가깝다는 특성으로 인해 MAE 및 MSE에 근접한 값을 보입니다. 그러나 백분율 편차를 계산할 때는 절대 편차를 계산할 때 나타나지 않는 뉘앙스가 있습니다.

MAPE = |(y_true-y_pred)/y_true|

if y_true = 10 and y_pred = 5
MAPE = 0.5

if y_true = 5 and y_pred = 10
MAPE = 1.0

즉 이 지표(MSPE와 같은)는 비대칭적입니다. 즉 예측이 실제보다 높을 경우 오차가 더 커집니다.

순전히 방법론적인 목적, 즉 MQL5에서 ONNX 모델로 작업하는 방법을 여러분에게 보여주기 위해 조합한 간단한 모델에서 R-제곱 메트릭의 좋은 결과를 얻을 수 있었습니다.


두 번째 후보 - D1_63

    [model]   [mae]   [mse]  [rmse]     [r2]  [mape]  [mspe] [rmsle]
[3] "D1_63" 0.00413 0.00003 0.00559  0.75230 0.00385 0.00003 0.00270

D1_63 예측

예측은 시각적으로 이전 예측과 매우 유사합니다. 메트릭 값으로 유사성 확인

[0] "D1_10" 0.00381 0.00003 0.00530  0.77720 0.00356 0.00002 0.00257
[3] "D1_63" 0.00413 0.00003 0.00559  0.75230 0.00385 0.00003 0.00270

다음으로 같은 기간 동안 테스터에서 어떤 모델이 더 나은 성능을 보여주는지를 살펴보겠습니다.


이제 D1_52

    [model]   [mae]   [mse]  [rmse]     [r2]  [mape]  [mspe] [rmsle]
[2] "D1_52" 0.00472 0.00004 0.00642  0.67327 0.00440 0.00004 0.00311

R-제곱이 0.5보다 크다는 이유만으로 선택한 것입니다.

D1_52 예측

최악의 경우처럼 거의 모든 예상 가격이 종가 차트 아래에 있습니다. 이 모델은 앞의 두 모델과 비슷한 메트릭 값에도 불구하고 낙관적인 전망을 불러일으키지는 않습니다. 이부분에 대해서는 다음 단락에서 살펴보겠습니다.


테스터에서 ONNX 모델 실행

다음은 테스터에서 모델을 확인하기 위한 매우 간단한 EA입니다.

//+------------------------------------------------------------------+
//|                                    ONNX.eurusd.D1.Prediction.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2023, MetaQuotes Ltd."
#property link        "https://www.mql5.com"
#property version     "1.00"

#include "ModelEurusdD1_10.mqh"
#include "ModelEurusdD1_30.mqh"
#include "ModelEurusdD1_52.mqh"
#include "ModelEurusdD1_63.mqh"
#include <Trade\Trade.mqh>

input double InpLots = 1.0;    // Lots amount to open position

//CModelEurusdD1_10 ExtModel;
//CModelEurusdD1_30 ExtModel;
CModelEurusdD1_52 ExtModel;
//CModelEurusdD1_63 ExtModel;
CTrade            ExtTrade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(!ExtModel.Init(_Symbol,_Period))
      return(INIT_FAILED);
   Print("model ",ExtModel.GetModelName());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtModel.Shutdown();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!ExtModel.CheckOnTick())
      return;

//--- predict next price movement
   vector prob(3);
   int    predicted_class=ExtModel.PredictClass(TimeCurrent(),prob);
   Print("predicted class ",predicted_class);
//--- check trading according to prediction
   if(predicted_class>=0)
      if(PositionSelect(_Symbol))
         CheckForClose(predicted_class);
      else
         CheckForOpen(predicted_class);
  }
//+------------------------------------------------------------------+
//| Check for open position conditions                               |
//+------------------------------------------------------------------+
void CheckForOpen(const int predicted_class)
  {
   ENUM_ORDER_TYPE signal=WRONG_VALUE;
//--- check signals
   if(predicted_class==PRICE_DOWN)
      signal=ORDER_TYPE_SELL;    // sell condition
   else
     {
      if(predicted_class==PRICE_UP)
         signal=ORDER_TYPE_BUY;  // buy condition
     }

//--- open position if possible according to signal
   if(signal!=WRONG_VALUE && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      double price=SymbolInfoDouble(_Symbol,(signal==ORDER_TYPE_SELL) ? SYMBOL_BID : SYMBOL_ASK);
      ExtTrade.PositionOpen(_Symbol,signal,InpLots,price,0,0);
     }
  }
//+------------------------------------------------------------------+
//| Check for close position conditions                              |
//+------------------------------------------------------------------+
void CheckForClose(const int predicted_class)
  {
   bool bsignal=false;
//--- position already selected before
   long type=PositionGetInteger(POSITION_TYPE);
//--- check signals
   if(type==POSITION_TYPE_BUY && predicted_class==PRICE_DOWN)
      bsignal=true;
   if(type==POSITION_TYPE_SELL && predicted_class==PRICE_UP)
      bsignal=true;

//--- close position if possible
   if(bsignal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      ExtTrade.PositionClose(_Symbol,3);
      //--- open opposite
      CheckForOpen(predicted_class);
     }
  }
//+------------------------------------------------------------------+

실제로 D1_52 모델에 따르면 매도 거래는 한 번만 열렸고 이 모델에 따르면 전체 테스트 기간 동안 추세는 변하지 않았습니다.

테스트 D1_52


2023.06.09 16:18:31.967 Symbols EURUSD: symbol to be synchronized
2023.06.09 16:18:31.968 Symbols EURUSD: symbol synchronized, 3720 bytes of symbol info received
2023.06.09 16:18:32.023 History EURUSD: load 27 bytes of history data to synchronize in 0:00:00.001
2023.06.09 16:18:32.023 History EURUSD: history synchronized from 2011.01.03 to 2023.04.07
2023.06.09 16:18:32.124 History EURUSD,Daily: history cache allocated for 283 bars and contains 260 bars from 2022.01.03 00:00 to 2022.12.30 00:00
2023.06.09 16:18:32.124 History EURUSD,Daily: history begins from 2022.01.03 00:00
2023.06.09 16:18:32.126 Tester  EURUSD,Daily (MetaQuotes-Demo): 1 minutes OHLC ticks generating
2023.06.09 16:18:32.126 Tester  EURUSD,Daily: testing of Experts\article_2\ONNX.eurusd.D1.Prediction.ex5 from 2023.01.01 00:00 to 2023.02.01 00:00 started with inputs:
2023.06.09 16:18:32.126 Tester    InpLots=1.0
2023.06.09 16:18:32.161 ONNX    api version 1.16.0 initialized
2023.06.09 16:18:32.180 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.01 00:00:00   model D1_52
2023.06.09 16:18:32.194 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.02 07:02:00   predicted class 2
2023.06.09 16:18:32.194 Trade   2023.01.02 07:02:00   instant sell 1 EURUSD at 1.07016 (1.07016 / 1.07023 / 1.07016)
2023.06.09 16:18:32.194 Trades  2023.01.02 07:02:00   deal #2 sell 1 EURUSD at 1.07016 done (based on order #2)
2023.06.09 16:18:32.194 Trade   2023.01.02 07:02:00   deal performed [#2 sell 1 EURUSD at 1.07016]
2023.06.09 16:18:32.194 Trade   2023.01.02 07:02:00   order performed sell 1 at 1.07016 [#2 sell 1 EURUSD at 1.07016]
2023.06.09 16:18:32.195 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.02 07:02:00   CTrade::OrderSend: instant sell 1.00 EURUSD at 1.07016 [done at 1.07016]
2023.06.09 16:18:32.196 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.03 00:00:00   predicted class 2
2023.06.09 16:18:32.199 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.04 00:00:00   predicted class 2
2023.06.09 16:18:32.201 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.05 00:00:30   predicted class 2
2023.06.09 16:18:32.203 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.06 00:00:00   predicted class 2
2023.06.09 16:18:32.206 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.09 00:02:00   predicted class 2
2023.06.09 16:18:32.208 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.10 00:00:00   predicted class 2
2023.06.09 16:18:32.210 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.11 00:00:00   predicted class 2
2023.06.09 16:18:32.213 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.12 00:00:00   predicted class 2
2023.06.09 16:18:32.215 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.13 00:00:00   predicted class 2
2023.06.09 16:18:32.217 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.16 00:03:00   predicted class 2
2023.06.09 16:18:32.220 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.17 00:00:00   predicted class 2
2023.06.09 16:18:32.222 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.18 00:00:30   predicted class 2
2023.06.09 16:18:32.224 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.19 00:00:00   predicted class 2
2023.06.09 16:18:32.227 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.20 00:00:30   predicted class 2
2023.06.09 16:18:32.229 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.23 00:02:00   predicted class 2
2023.06.09 16:18:32.231 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.24 00:00:00   predicted class 2
2023.06.09 16:18:32.234 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.25 00:00:00   predicted class 2
2023.06.09 16:18:32.236 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.26 00:00:00   predicted class 2
2023.06.09 16:18:32.238 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.27 00:00:00   predicted class 2
2023.06.09 16:18:32.241 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.30 00:03:00   predicted class 2
2023.06.09 16:18:32.243 ONNX.eurusd.D1.Prediction (EURUSD,D1)   2023.01.31 00:00:00   predicted class 2
2023.06.09 16:18:32.245 Trade   2023.01.31 23:59:59   position closed due end of test at 1.08621 [#2 sell 1 EURUSD 1.07016]
2023.06.09 16:18:32.245 Trades  2023.01.31 23:59:59   deal #3 buy 1 EURUSD at 1.08621 done (based on order #3)
2023.06.09 16:18:32.245 Trade   2023.01.31 23:59:59   deal performed [#3 buy 1 EURUSD at 1.08621]
2023.06.09 16:18:32.245 Trade   2023.01.31 23:59:59   order performed buy 1 at 1.08621 [#3 buy 1 EURUSD at 1.08621]
2023.06.09 16:18:32.245 Tester  final balance 8366.00 USD
2023.06.09 16:18:32.249 Tester  EURUSD,Daily: 123499 ticks, 22 bars generated. Environment synchronized in 0:00:00.043. Test passed in 0:00:00.294 (including ticks preprocessing 0:00:00.016).

이전 섹션에서 언급했듯이 D1_52 모델은 낙관적인 전망과는 거리가 있습니다. 이는 테스트 결과를 통해 확인할 수 있습니다.

코드 두 줄만 변경해 보겠습니다.

#include "ModelEurusdD1_10.mqh"
#include "ModelEurusdD1_30.mqh"
#include "ModelEurusdD1_52.mqh"
#include "ModelEurusdD1_63.mqh"
#include <Trade\Trade.mqh>

input double InpLots = 1.0;    // Lots amount to open position

CModelEurusdD1_10 ExtModel;
//CModelEurusdD1_30 ExtModel;
//CModelEurusdD1_52 ExtModel;
//CModelEurusdD1_63 ExtModel;
CTrade            ExtTrade;

그리고 테스트용 모델 D1_10을 실행합니다.

D1_10 테스트 결과

결과는 좋습니다. 테스트 그래프도 희망적입니다.

D1_10 테스트 그래프


두 줄의 코드를 다시 수정하고 D1_63 모델을 테스트해 보겠습니다.

D1_63 테스트 결과

그래프입니다.

D1_63 테스트 그래프

테스트 그래프는 D1_10 모델보다 많이 나쁩니다.

두 모델 D1_10과 D1_63을 비교하면 첫 번째 모델이 두 번째 모델보다 더 우수한 회귀 메트릭이란 것을 알 수 있습니다. 테스터도 같은 결과를 보여줍니다.

중요 참고 사항: 이 글에서 사용된 모델은 MQL5 언어를 사용하여 ONNX 모델로 작업하는 방법을 시연하기 위한 용도로만 제공되었습니다. Expert Advisor는 실계좌에서 거래할 수 없습니다.


결론

가격 예측 모델을 평가하는 데 가장 적합한 메트릭은 R-제곱입니다. MAE - RMSE - MAPE의 합계를 고려하는 것은 매우 유용할 수 있습니다. 가격 예측 작업에서는 RMSLE 메트릭을 고려하지 않는 것이 좋을 수 있습니다. 수정 사항이 있는 동일한 모델이라도 평가를 위해 여러 모델을 보유하는 것이 매우 유용합니다.

우리는 22개 값의 샘플로는 진지한 연구를 하기에는 부족하다는 것을 알고 있습니다. 여기서 우리는, 통계적 연구를 하려고 한 것은 아닙니다. 우리는 사용 예만 살펴보았습니다.


MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/12772

파일 첨부됨 |
MQL5.zip (1005.2 KB)
Expert Advisor 개발 기초부터(25부): 시스템 견고성 확보(II) Expert Advisor 개발 기초부터(25부): 시스템 견고성 확보(II)
이 글에서는 EA의 성능을 향상하기 위한 마지막 단계를 밟아보겠습니다. 그러니 오랫동안 읽을 준비를 하세요. Expert Advisor의 신뢰성을 높이기 위해 우리는 코드에서 모든 것을 제거합니다. 이 코드는 거래 시스템의 일부가 아닌 코드입니다.
MQL5에서 ONNX 모델을 앙상블하는 방법의 예시 MQL5에서 ONNX 모델을 앙상블하는 방법의 예시
ONNX(Open Neural Network eXchange)는 신경망을 위해 만들어진 개방형 형식입니다. 이 글에서는 하나의 Expert Advisor에서 두 개의 ONNX 모델을 동시에 사용하는 방법을 소개하겠습니다.
클래스에서 ONNX 모델 래핑하기 클래스에서 ONNX 모델 래핑하기
객체 지향 프로그래밍을 사용하면 읽기 쉽고 수정하기 쉬운 보다 간결한 코드를 작성할 수 있습니다. 여기서는 세개의 ONNX 모델에 대한 예제를 살펴보겠습니다.
MQL5에서 행렬 및 벡터: 활성화 함수 MQL5에서 행렬 및 벡터: 활성화 함수
여기서는 머신 러닝에서의 활성화 함수에 대해서만 설명하겠습니다. 인공 신경망에서 뉴런 활성화 함수는 입력 신호 또는 입력 신호 세트의 값을 기반으로 출력 신호의 값을 계산합니다. 우리는 이 프로세스의 내부의 작동 방식에 대해 자세히 살펴볼 것입니다.