English Deutsch
preview
純粋なMQL5におけるエネルギーベースの学習を用いた特徴量選択アルゴリズム

純粋なMQL5におけるエネルギーベースの学習を用いた特徴量選択アルゴリズム

MetaTrader 5 | 8 7月 2024, 11:23
22 0
Francis Dube
Francis Dube

はじめに

アルゴリズム取引の分野では、機械学習の普及により、金融データの隠れたパターンを発見するためのデータマイニング技術の導入が進んでいます。このような状況の中で、実務家は多くの変数を選別し、特定の目標の達成や特定の問題の解決に最も有益と思われる変数を特定するという課題に取り組むことが多いです。この記事では、与えられた予測タスクに対する候補変数のセットの関連性を評価することを目的とした、特徴量選択アルゴリズムの実装を探求します。

Yun Li、Jennie Si、Guojing Zhou、Shasha Huang、Songcan Chenは研究論文「FREL: A Stable Feature Selection Algorithm」を共著しました。この論文では、正則化エネルギーベース学習(Feature Weighting as Regularized Energy-Based Learning:FREL)と名付けられたアルゴリズムが紹介されてます。このアルゴリズムは、精度と安定性の両方を提供するように設計された特徴量選択または重み付け技術として機能します。この議論では、正規化エネルギーベース学習と特徴の重み付けの背後にある理論的根拠の概要を示します。さらに、スクリプトとして作成されたMQL5プログラムの例を通して、提案されたアプローチの有効性を説明し、特徴量選択ツールとしてのこの手法の可能性を強調します。


加重最近傍分類

FRELの背後にある概念は、データセット内の点間の距離を活用して予測をおこなう、重み付きk近傍法として知られる技法からインスピレーションを得ています。各特徴量に対して適切な重みを決定することで、この方法は予測精度を高めます。重み付き最近傍分類は、機械学習で分類タスクに広く使われているk-最近傍(k-NN)アルゴリズムのバリエーションです。標準的なk-NN分類では、アルゴリズムは新しいデータ点を分類する際に、訓練セットの中で最も近いk個のデータ点を調べ、最終的にこれらの近傍データ点の中で多数派のクラスを新しいデータ点に割り当てます。

しかし、重み付き最近傍分類では、単に最近傍の票を集計するのではなく、各近傍の票は新しいデータ点からの距離に応じて重み付けされます。その根拠は、より近い隣人は、より遠い隣人よりも、分類決定に強い影響を及ぼすはずだということです。この重み付けプロセスでは、新しいデータ点と訓練セット内の各点との距離を計算します。よく使用される距離測定基準には、ユークリッド距離、マンハッタン距離、コサイン類似度などがあり、データの特徴量に基づいて選択されます。この文脈では、データ点間のマンハッタン距離(都市ブロック距離とも呼ばれる)を利用します。この距離の計算式を以下に示します。ここで、wは重みであり、テストケースは訓練ケースとして与えられた他の訓練データと比較して評価されます。

加重最近傍距離の公式


エネルギーベースモデリングについて

機械学習におけるエネルギーベースモデリング(英語)は、教師あり学習タスクと教師なし学習タスクの両方に適用できる汎用的なフレームワークとして機能します。エネルギーベースモデリングは、様々なデータ構成にエネルギー値を割り当て、望ましい構成と望ましくない構成を区別できるモデルを学習するという原理で動作します。これは、観測されたデータのエネルギーを最小化する一方で、観測されていない、あるいは望ましくないデータ構成のエネルギーを最大化することで達成されます。

エネルギーベースのモデルの核心は、E()と表記されるエネルギー関数の定義にあります。この関数は,モデルパラメータの集合とともに,入力変数または予測変数の設定を入力として受け取ります。エネルギー関数の出力は、入力変数の構成の関連性を示します。例えば、回帰モデルを評価する場合、エネルギー関数は平均二乗誤差として表されます。関連性の高い予測変数が平均2乗誤差式に入力されると、出力値はより小さくなる傾向があり、より高い関連性を反映します。逆に、悪い予測因子は平均二乗誤差を大きくします。エネルギー関数は、考えられる変数の各構成にスカラー値を割り当てます。

エネルギーベースのモデルを学習する目的は、エネルギー関数のパラメータを学習し、関連性のある入力変数には低いエネルギーを、関連性のない入力変数には高いエネルギーを割り当てるようにすることです。これは、正しい変数には高いエネルギーを、正しくない変数には低いエネルギーをペナルティーとして課す目的関数を定義することを必要とします。これを達成するために、目標は、モデルから誤った予測を引き起こす可能性の高いサンプルを表す、最も低いエネルギーをもたらす不正確な変数の構成を特定することです。以下の関数は、正確な予測を生み出す入力変数の構成と区別するために、誤った値yを出力する入力xとモデルパラメータwの構成のエネルギーが低すぎることを表しています。 

エネルギーが最も低い誤った構成のエネルギー公式

最終的に、目的関数は、最も低いエネルギーを持つ誤った構成と、最も近い正しい変数構成との間の不一致を最大化することを目指します。このような構成から得られるエネルギーを以下に示します。

最も近い正しい変数構成のエネルギー公式

損失関数として知られる目的関数は、サンプルごとに平均化された損失関数で構成されます。対数損失は以下の通りです。

サンプルあたりの平均損失関数(対数損失)

サンプルごとの損失関数としては、ハインゼル損失、ロジット損失、二乗損失、二乗指数損失など、さまざまな損失基準を用いることができ、用途に応じて選択することができます。

まとめると、これらがFRELの根底にある基本的な概念です。続くセクションでは、アルゴリズムそのものの詳細を掘り下げていきます。


FRELアルゴリズム

FRELアルゴリズムを効果的に適用するためには、特定の基本的な考慮事項に従わなければなりません。まず、訓練データを慎重に評価することが重要です。FRELは、候補変数のセットを単一の目標に写像するデータセットに理想的に適しています。同様に重要なのは、変数の規模が似ていることです。FRELを一貫性のないスケーリングを持つ予測変数の候補にさらすと、最終的な結果を大きく歪める可能性があります。

第二に、FRELがエネルギーに基づく学習法であることを考えると、重み付けパラメータを組み込んだエネルギー関数を定義する必要があります。したがって、使用するモデルは、対応する重み付けとともに、候補となる変数のセットを受け入れるように設定する必要があります。例えば、平均二乗誤差をエネルギー関数として考え、モデルが回帰モデルである場合、重み付けパラメータを組み込むことは比較的簡単です。それぞれの重みは、候補となる予測因子と対になっています。

最後に、損失関数を決定するために、サンプルごとの損失関数を選択しなければなりません。モデルの重み付けパラメータを組み込んだ損失関数は、最適な重みを得るために最小化される(関数の)関数です。

FRELアルゴリズムの核となるステップは以下の通りです。

訓練データ行列

  1. n個の目標値に対応するd個の予測子候補を持つn個の観測からなる訓練データ集合から始めます。目標は、目標値を決定するために、d個の候補の集合から最も関連性の高い予測子を確認することです。これは,d個の予測変数の各々に重みを割り当て,他の変数との相対的な変数の重要性を示します。重みが大きいほど、目標値を定義する際の関連性が高いことを意味します。 
  2. 最初は、すべての重みに1の値を割り当てます。
  3.  訓練データの各観測に重み付き最近傍分類を適用し、最も低いエネルギーをもたらす不正確な変数の構成と、高いエネルギーを持つ正しい変数の最も近い構成を特定します。これらのエネルギー値を利用して、選択した損失関数を使用してサンプルごとの損失を計算します。
  4.  最後に、適切な最適化手順を用いて、正則化を用いて目的損失関数を最小化します(オプション)。これがFRELのコアとなるアルゴリズムです。
研究論文の中で、著者はブートストラッピングを採用することで、コアアルゴリズムを強化したことも示しています。サンプリングは置換なしでおこなわれなければならず,結果として得られる重み付けは,各ブートストラップ標本からの平均値です。


MQL5でのFRELの実装

このテキストで紹介するFRELの実装は、パウエルの最適化法を利用しています。使用する特定の最適化手法が重要なわけではありませんが、結果は手法間で比較的一貫しているはずです。この実装では、パウエル法はPowells.mqhで定義されたクラスPowellsMethodとして表現されます。FRELアルゴリズムは、frel.mqhで指定されているPowellsMethodの子孫であるクラスFRELにカプセル化されています。

//+------------------------------------------------------------------+
   //| constructor                                                      |
   //+------------------------------------------------------------------+

                     FREL(matrix &in_data,int numboot=1, int bootsize=0)
     {
      m_data = in_data;
      m_num_boot=(numboot>0)?numboot:1;
      m_bootsize=(bootsize>2 && bootsize<=int(m_data.Rows()) && m_num_boot>1)?bootsize:int(m_data.Rows());
      
      
      if(ArrayResize(m_indices, int(m_data.Rows()))!=int(m_data.Rows()) ||
         ArrayResize(m_target_bin, int(m_data.Rows()))!=int(m_data.Rows()) ||
         ArrayResize(m_trial_weights, int(m_data.Cols()-1))!=int(m_data.Cols()-1) ||
         ArrayResize(m_work_weights, int(m_data.Cols()-1))!=int(m_data.Cols()-1)
        )
        {
         Print(__FUNCTION__, " error ", GetLastError());
         m_memory_allocated = false;
        }
      else
         m_memory_allocated = true;
     }

パラメトリックコンストラクタの説明を掘り下げてみましょう。少なくとも1つのパラメータ、すなわち学習データの行列を指定して起動します。訓練データを行列でどのように構成するかが重要です。各行は観測または1つの標本を表し,列は評価される候補変数または予測変数を表します。目標は行列の最後の列に存在すると予想されます。コンストラクタのオプションパラメータについては、以下の表で詳しく説明します。

 パラメータ名
 データ型
デフォルト値
 詳細
numboot
integer
1
numbootは、実施するブートストラップの数を設定します。
bootsize
integer
0
bootsizeは、各ブートストラップのサイズを定義します。このパラメータの設定には注意してください。行列の観測数より大きな値が使用された場合、numbootは自動的に1にフォールバックし、bootsizeは観測数にフォールバックします。

FRELクラスを利用するために、ユーザーが熟知しておく必要があるメソッドはただひとつ、WeighVars()です。

//+-----------------------------------------------------------------------+
   //| Find the most relevant variables from a dataset of candidate variables|
   //+-----------------------------------------------------------------------+

   bool               WeighVars(int num_bins_target, double reg_factor,int &index[],double &weights[])
     {
      
      if(!m_memory_allocated)
        {
         Print(" INTERNAL ERROR ");
         return false;
        }
        
        
      if(num_bins_target<=1 || num_bins_target>int(m_data.Rows()))
       {
        Print(__FUNCTION__, " invalid function parameter: num_bins_target. Parameter should be >=2 ");
        return false;
       }  

      int ret=0;
      double target[], target_thresholds[] ;
      double sum ;

      int n_cases = int(m_data.Rows());
      m_reg_factor = MathAbs(reg_factor);
      m_loss = 0.0;


      if(ArrayResize(index,int(m_data.Cols()-1))!=int(m_data.Cols()-1) ||
         !np::vecAsArray(m_data.Col(m_data.Cols()-1),target)
        )
        {
         Print(__FUNCTION__, " error ", GetLastError());
         return false;
        }

      int k = num_bins_target ;
      if(!bin_array(target, k, target_thresholds, m_target_bin))
         return false;

      if(k<num_bins_target)
        {
         Print("error bins of target vector ", num_bins_target," : ", k);
         return false;
        }

      for(int i=0 ; i<n_cases ; i++)
        {
         if(m_target_bin[i] >= num_bins_target)
           {
            Print("error m_target_bin array at index ", i, " is ",m_target_bin[i], " should be less than ", num_bins_target);
            return false;
           }
        }

      ret = calc_wt(num_bins_target,m_loss,weights);

      if(ret<0)
         return false;

      sum = 0.0 ;
      for(ulong var=0 ; var<m_data.Cols()-1 ; var++)
        {
         weights[var] = m_data.Col(var).Std() * exp(weights[var]);
         sum += weights[var] ;
        }

      for(ulong var=0 ; var<m_data.Cols()-1 ; var++)
        {
         weights[var] *= 100.0 / sum ;
         index[var] = int(var) ;
        }

      MathQuickSortDescending(weights,index,0,int(weights.Size()-1)) ;

      return true;
     }

このメソッドはコンストラクタで指定された訓練データを評価します。これはブール値を返し、falseはプロシージャが完了できなかったことを示します。このメソッドのパラメータは以下の通りです。

  • num_bins_target:目標値が分割されるビンの数を定義する整数。このパラメータは、2以上かつ訓練データの観測の数以下の任意の整数に設定されるべきです。
  • reg_factor:正則化の度合いを制御する正のdouble値。値0は正則化を無効にします。
  • index[]:操作結果の一部を書き込む整数配列。これは、コンストラクタに供給されたオリジナルの列インデックスを、目標との関連性が高い順に並べたものです。
  • weights[]:最適な重み付けを降順に並べたdouble配列

WeighVars()が呼び出されると、privateメソッドbin_array()の呼び出しに備えて、目標値が行列から抽出され、配列に入れられます。このメソッドは、配列をほぼ等しい大きさのカテゴリにセグメント化し、成功すると2つの配列を出力します。upperbound_thresholds[]は、各セグメントに対する上限しきい値の配列で、整数配列categories[]は、対応する目標値が属するセグメントを表すインデックス値を含みます。これらの各値は、すべての目標値が正しくビニングされたことを確認するために確認されます。

//+------------------------------------------------------------------+
   //| calculates the optimal weights of candidate variables            |
   //+------------------------------------------------------------------+

   int               calc_wt(int num_bins_target,double &loss_value, double &w[])
     {
      int ret,rand_error, class_count[] ;

      ret = 0;

      if(ArrayResize(class_count,num_bins_target)!=num_bins_target || (w.Size()!=uint(m_data.Cols()-1) && ArrayResize(w,int(m_data.Cols()-1))!=int(m_data.Cols()-1)))
        {
         Print(__FUNCTION__, " error ", GetLastError());
         return -1;
        }

      ArrayInitialize(w,0.0);
      loss_value = 0.0 ;

      for(ulong i=0 ; i<m_data.Rows() ; i++)
         m_indices[i] = int(i) ;
     
      for(int ibootstrap=0 ; ibootstrap<m_num_boot; ibootstrap++)
        { 
         Comment(" Bootstrap iteration ", ibootstrap+1);
           
         ArrayInitialize(class_count,0);

         int ii, j, k, m;

         ii = int (m_data.Rows()) ;
         while(ii > 1)
           {
            m = int (m_data.Rows()) - ii ;
            if(m >= m_bootsize)
               break ;
            j = (int)(MathRandomUniform(0.0,1.0,rand_error) * ii) ;
            if(j >= ii)
               j = ii - 1 ;
            k = m_indices[m] ;
            m_indices[m] = m_indices[m+j] ;
            m_indices[m+j] = k ;
            --ii ;
            ++class_count[m_target_bin[m_indices[m]]] ;
           }
           

         for(int i=0 ; i<num_bins_target ; i++)
           {
            if(class_count[i] < 2)
               Print(__FUNCTION__, "  class at ", i, " has less than 2 members. Consider adjusting Frel parameters. (number of partitions or bootstrap sample size)");
           }
            
         ArrayInitialize(m_trial_weights,0.0);

         ret += Optimize(m_trial_weights);
         loss_value += PowellsMethod::GetFret() ;

         for(ulong i=0 ; i<m_data.Cols()-1 ; i++)
            w[i] += m_trial_weights[i] ; 

        }

      for(ulong i=0 ; i<m_data.Cols()-1; i++)
        w[i] /= double(m_num_boot) ;

      return ret ;

     }

重みの推定はcalc_wt()の呼び出しから始まります。ここでは、ブートストラップサンプリングをおこない、初期重みを最適化する前にデータをシャッフルします。最適化は、親クラスのメソッドOptimize()によって実行されます。各ブートストラップの最適重みはw[]に合計され、calc_wt()を終了する前に平均されます。

//+------------------------------------------------------------------+
   //| function minimized by Powells optimization method                |
   //+------------------------------------------------------------------+
   virtual double          func(const double& p[])
     {
      double pen = 0.0 ;

      for(ulong i=0 ; i<m_data.Cols()-1 ; i++)
        {
         if(p[i] > 4.0)
           {
            m_work_weights[i] = exp(4.0) + p[i] - 4.0 ;
            pen += (p[i] - 4.0) * (p[i] - 4.0) ;
           }
         else
            if(p[i] < -3.0)
              {
               m_work_weights[i] = exp(-3.0) + p[i] + 3.0 ;
               pen += (p[i] + 3.0) * (p[i] + 3.0) ;
              }
            else
               m_work_weights[i] = exp(p[i]) ;
        }

      return (loss(m_work_weights) + pen) ;
     }

最小化される関数は損失関数であり、親クラスのfunc()と呼ばれるオーバーライドされたメソッドで表されることを覚えておいてください。

//+------------------------------------------------------------------+
   //| calculates the loss function                                     |
   //+------------------------------------------------------------------+
   double            loss(double &w[])
     {
      double totaloss = total_loss(w);

      totaloss/=double(m_data.Rows());

      if(m_reg_factor>0.0)
        {
         for(ulong i=0; i<m_data.Cols()-1;i++)
            totaloss+=m_reg_factor*pow(w[i],2.0);
        }

      return totaloss;
     }

func()内で、メソッドloss()が実行され、privateメソッドtotal_loss()として実装されたサンプルごとの損失関数の計算がトリガーされます。

//+------------------------------------------------------------------+
   //|  loss over all data                                              |
   //+------------------------------------------------------------------+

   double            total_loss(double &w[])
     {
      int category,first, other ;
      double  distance, top, bottom, loss ;

      loss = 0.0 ;
      for(int i=0; i<m_bootsize; i++)
        {
         other = m_indices[i] ;
         category = m_target_bin[other] ;
         top = bottom = DBL_MAX ;

         for(int iother=0 ; iother<m_bootsize; iother++)
           {
            first = m_indices[iother] ;
            if(first == other)
               continue ;

            distance = 0.0 ;
            for(ulong v=0 ; v<m_data.Cols()-1; v++)
              {
               distance += w[v] * fabs(m_data[other][v] - m_data[first][v]) ;
              }

            if(m_target_bin[first] == category)
              {
               if(distance < top)
                  top = distance ;
              }
            else
              {
               if(distance < bottom)
                  bottom = distance ;
              }
           }

         distance = top - bottom ;
         if(distance > 30.0)
            loss += distance ;
         else
            loss += log(1.0 + exp(distance));
        }

      return loss ;
     }

すべてのブートストラップが完了すると、平均化された最適な重みが、ユーザーから与えられたweights[]配列に書き込まれます。降順に並び替える前に、重みが合計で100になるように変換され、解釈しやすくなっています。


FRELによる特徴量選択例

FRELアルゴリズムを実証するために、スクリプトFrelExample.mq5を提供します。このスクリプトは、FRELを利用して、候補変数と目標からなるランダムに生成されたデータセットを分析し、最良の予測因子を特定します。ユーザーは、FRELアルゴリズムのすべてのパラメータと合成データセットの特定の特性を調整することができます。これは観測の合計数(num_observations)と予測変数の候補数(num_candidate_predictors)を含みます。以下は、ユーザーが調整可能なスクリプトの入力を示すスニペットです。

//---user adjustable input parameters
input int number_of_partitions = 8; //Number of partitions
input double regularization_factor = 0.0; //Regularization factor
input int number_of_bootstraps = 1;
input int bootstrap_size = 0;
input ulong num_observations = 2000;// Sample size of random dataset
input ulong num_candidate_predictors = 10;// Maximum number of candidate predictors in dataset

このスクリプトは、num_observations行と num_candidate_predictors + 1列の乱数行列を生成します。最後の列は、インデックス1、3、5、7の列の合計で上書きされ、データセットの目標変数となります。

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   srand(126);
//---check user input parameters
   if(number_of_partitions<2 || num_observations<10 || num_candidate_predictors<8)
     {
      Print("Invalid input parameters");
      return;
     }
//---the data
   matrix dataset;
//---initialize size of random dataset
   dataset.Init(num_observations,num_candidate_predictors+1);
//---fill dataset with random data
   dataset.Random(1.0,10.0);
//---set the target variable in the last column
   if(!dataset.Col(dataset.Col(1) + dataset.Col(3) + dataset.Col(5) + dataset.Col(7),num_candidate_predictors))
     {
      Print("error ", GetLastError());
      return;
     }
//---initialize Frel object
   FREL frel(dataset,number_of_bootstraps,bootstrap_size);
//---declare containers to recieve output from Frel operation
   double optimal_weights[];
   int index[];
//---
   ulong timeIT = GetTickCount64();
//---find the most relevant predictors
   if(!frel.WeighVars(number_of_partitions,regularization_factor,index,optimal_weights))
      return;
//---calculate runtime
   Print("Runtime of FREL ", GetTickCount64() - timeIT, " ms");
//---display results
   for(uint i = 0; i<optimal_weights.Size(); i++)
      Print("Predictor at Column index ", index[i], " weight ", optimal_weights[i]);

  }
//+------------------------------------------------------------------+

目的は、FRELが変数を適切に重み付けできるかどうかを観測することであり、1、3、5、7列目を目標との関係が最も強いと指定します。最初に、正則化が無効で、ブートストラップが1つだけ指定されていることに注意して、デフォルトのパラメータでスクリプトを実行します。

Frelスクリプトのデフォルト設定

出力

ON      0       18:12:30.906    FrelExample (BTCUSD,D1) Runtime of FREL 273375 ms
GD      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 7 weight 24.46987538756267
IH      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 3 weight 24.22319404776024
EL      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 5 weight 22.26820806768701
LP      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 1 weight 22.13748732798876
DD      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 0 weight 1.162036446785271
KK      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 8 weight 1.1532145209345603
RO      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 4 weight 1.1496286906955606
RS      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 2 weight 1.1472521997561425
NG      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 6 weight 1.14561384476096
DK      0       18:12:30.906    FrelExample (BTCUSD,D1) Predictor at Column index 9 weight 1.14348946606884

次に、正則化の度合いを0.1と1.0にして、正則化が推定重みに与える影響を調べました。

正則化係数0.1設定のFrel

出力

MQ      0       18:19:03.951    FrelExample (BTCUSD,D1) Runtime of FREL 331296 ms
QD      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 3 weight 19.63442784832085
PK      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 5 weight 19.009699240770477
GO      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 7 weight 18.823288529399388
GQ      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 1 weight 18.18026689510982
NE      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 0 weight 4.106428447842871
KI      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 8 weight 4.075425288243113
OM      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 2 weight 4.070169243578418
MQ      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 6 weight 4.051103060690134
FE      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 9 weight 4.025271426001863
FJ      0       18:19:03.951    FrelExample (BTCUSD,D1) Predictor at Column index 4 weight 4.0239200200430805

正則化係数1.0設定のFrel

出力

HP      0       18:25:43.421    FrelExample (BTCUSD,D1) Runtime of FREL 362984 ms
FF      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 3 weight 10.353013480731704
JJ      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 7 weight 10.227015183302557
IM      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 5 weight 10.213781888319609
KQ      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 1 weight 10.079770794877978
PF      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 0 weight 9.948300319843046
QJ      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 8 weight 9.938367489770178
KN      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 2 weight 9.897336276433514
DQ      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 6 weight 9.79559491756489
EF      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 9 weight 9.774541742551756
CI      0       18:25:43.421    FrelExample (BTCUSD,D1) Predictor at Column index 4 weight 9.77227790660475

正則化テストの結果は、重みが他の変数に分散し、正しい変数から乖離していることを示しています。正則化の度合いを大きく指定すると、重みがはっきりしなくなり、有用な変数と無関係な変数を区別するのが難しくなると思われます。

テストの実行結果を調べると、FRELの動作が比較的遅いことがわかります。このボトルネックは、おそらくtotal_loss()関数に起因していると思われます。この関数は、オプティマイザが実行される際に、データセット全体を何度も繰り返し処理しなければなりません。実行効率を向上させるために、少ないサンプルサイズで何度もブートストラップをおこないます。以下の結果は、40サンプルの100ブートストラップから得られたものです。

ブートストラップFrelの設定

出力

IN      0       18:30:55.441    FrelExample (BTCUSD,D1) Runtime of FREL 22985 ms
OK      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 3 weight 18.706272752181135
OL      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 1 weight 18.32079620338284
RS      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 5 weight 18.194009676469012
HG      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 7 weight 16.298306686632337
MI      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 4 weight 5.838867272535404
LM      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 9 weight 5.249285089162589
FQ      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 8 weight 4.791606631149278
DE      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 6 weight 4.770223641360407
KI      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 0 weight 3.974977300216029
KM      0       18:30:55.441    FrelExample (BTCUSD,D1) Predictor at Column index 2 weight 3.855654746910961


結論

本稿では、正則化エネルギーベースのモデリングを用いた特徴量重み付けのMQL5実装を紹介しました。アルゴリズムの理論的基礎について簡単に説明し、合成データセットでの有効性を紹介しました。結果は有望でしたが、このアルゴリズムには大きな計算コストがかかり、解析に時間がかかることが確認されました。この問題に対処するために、より小さなサンプルサイズの複数のブートストラップの利用を提案し、アルゴリズムの全体的な実行速度を顕著に改善しました。ただし、私たちの実装は、マルチスレッドやGPUアクセラレーションから大きな恩恵を受ける可能性があります。とはいえ、この方法に興味のある読者は、ご自分のニーズに応じてコードをカスタマイズすることが奨励されます。この記事で説明したコードはすべて含まれています。各ソースファイルは以下の表に詳述されています。

ソースファイル
詳細
Mql5\include\np.mqh
様々なベクトル行列操作ツールのヘッダファイル
Mql5\include\Powells.mqh
Powells関数最小化法を実装するPowellsMethodクラスの定義が含まれる
Mql5\include\frel.mqh
正則化エネルギーに基づく学習アルゴリズムとしての特徴量重み付けを表すFRELクラスの定義が含まれる
Mql5\script\FrelExample.mq5
FRELクラスの使用法を示すスクリプト


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/14865

添付されたファイル |
frel.mqh (14.08 KB)
Powells.mqh (17.57 KB)
np.mqh (37.73 KB)
FrelExample.mq5 (2.52 KB)
Mql5.zip (12.76 KB)
予測による三角裁定取引 予測による三角裁定取引
この記事では、三角裁定を簡略化し、市場に慣れていない方でも、予測や専用ソフトを使用してより賢く通貨を取引する方法をご紹介します。専門知識を駆使して取引する準備はできていますか?
知っておくべきMQL5ウィザードのテクニック(第20回):関数同定問題 知っておくべきMQL5ウィザードのテクニック(第20回):関数同定問題
関数同定問題は、研究対象のデータセットをマッピングする基本モデルがどのようなものであるかについて、最小限の仮定から始める回帰の形式です。ベイズ法やニューラルネットワークでも実装可能ですが、ここでは遺伝的アルゴリズムによる実装が、MQL5ウィザードで使用可能なExpertSignalクラスのカスタマイズにどのように役立つかを見ていきます。
予測による統計的裁定取引 予測による統計的裁定取引
統計的裁定取引について調べ、共和分で相関する銘柄をPythonで検索し、ピアソン係数の指標を作成し、PythonとONNX モデルで予測をおこなって統計的裁定取引を行うEAを作成します。
知っておくべきMQL5ウィザードのテクニック(第19回):ベイズ推定 知っておくべきMQL5ウィザードのテクニック(第19回):ベイズ推定
ベイズ推定とは、新しい情報が入手可能になったときに確率仮説を更新するためにベイズの定理を採用することです。これは直感的に時系列分析への適応につながるので、シグナルだけでなく、資金管理やトレーリングストップのためのカスタムクラスを構築する際に、これをどのように利用できるか見てみましょう。