ニューラル ネットワーク: EAの自己最適化

Jose Miguel Soriano | 21 10月, 2016


イントロダクション

EAの努力を完全に無効にするかもしれない 2 つの問題に直面します。

  • 最適なインプット値とは何でしょう。
  • どのくらい値が信頼できるのでしょうか。いつ再最適化を実行する必要があるのか。
定義済みのパラメータ (シンボル、期間等) から離れて (編集可能) の他の設定があります: インジケーター計算期間、買いする/売るレベル、TP ・ SL レベルなど。EA を使用する場合、問題があります。

ポジションを最適化し、定義された間隔で条件を終了することができるEAを開発します。

履歴を分析し、戦略を実現するためのモジュールとして、多層パーセプトロンの形でニューラル ネットワーク (NN) を実装します。毎月 (毎週、毎日、または毎時) ニューラル ネットワークを最適化し、その後稼働する EA を作成します。自己最適化 EA の作成に考えが着きます。

これは初めてではなく、トレーディング コミュニティ「MQL ・ ニューラル ネットワーク」に関するトピックが発生したからあります。ただし、ほとんどの場合、外部ニューラル ネットワークによって (時々 手動で) 得られたデータは МetaTrader 4/МetaTrader 5 オプティマイザーによるニューラル ネットワークの最適化で削減されます。最後に、この記事でEA のインプットに置き換えられます。

現在では、 トレードロボットを記述するつもりはありません。代わりに、 実装 (モジュール) で mql5 を開発した多層パーセプトロン (MLP) を使用して、上記のアルゴリズムを実行する EA のレイアウトALGLIBライブラリを使用します。その後、ニューラル ネットワークは、結果の異なるアルゴリズムを使用して、シンプルにチェックできる 2 つの演算タスクを解決します。MLP は、その内部構造によると、計画ソリューションとタスク分析およびデータインプットモジュールを変更するだけで EA を実装する条件を取得します。レイアウトは、自己最適化問題を解決する必要があります。

読者は、その構造と組織フォーム、レイヤー数のニューロンの数を含む、ニューラル ネットワークの一般理論に精通している前提です。いずれの場合も、適切な記事に必要な情報を見つけることができるでしょう。


1. 基本的なアルゴリズム

  1. ニューラル ネットワークを作成します。
  2. 配列にデータをダウンロードすることによってインプット (適切な出力) を準備しています。
  3. 特定の範囲のデータの正規化 (通常は、[0, 1] または [-1, 1])。
  4. トレーニングとニューラル ネットワークの最適化。
  5. ネットワークの予測および EAの戦略による適用を計算。
  6. 自己最適化: 返す点 2 と OnTimer() 関数を含むプロセスを繰り返し強調します。

ロボットは、ユーザーが指定した時間の間隔と記述されているアルゴリズムによると定期的に最適化を実行します。ユーザーは、心配する必要はありません。手順 5 は繰り返しプロセスに含まれていません。EA は常に最適化中であっても予測値を持ちます。どのように起こるかを見てみましょう。

 

2. ALGLIB ライブラリ

ライブラリは、Sergey Bochkanovのパブリケーション及び、ALGLIB プロジェクトのウェブサイト http://www.alglib.net/で公開され、クロスプラット フォームの解析とデータ処理ライブラリとして説明された場所で説明されています。さまざまなプログラミング言語 (C++、c#、パスカル、VBA) およびオペレーティング システム (Windows、Linux、Solaris) と互換性があります。ALGLIB は、豊富な関数を備えています。内容:

  • 線形代数 (直接アルゴリズム、EVD/SVD)
  • 線形および非線形方程式のソリューション ウィザード
  • 補間
  • 最適化
  • 高速フーリエ変換
  • 数値積分
  • 線形および非線形の正方形最安値
  • 常微分方程式
  • 特別な関数
  • 統計情報 (記述統計、仮説検定)
  • データ解析 (分類/回帰、ニューラル ネットワークなど)
  • (MPFR 使用) 高精度演算で線形代数, 補間, 他のアルゴリズムを実装します。

CAlglib クラスの静的関数は、ライブラリで動作するように使用されます。このクラスには、すべてのライブラリ関数があります。

シンプルな usealglib.mq5 デモ スクリプトと一緒に testclasses.mq5 と testinterfaces.mq5 のテスト スクリプトを備えています。(Testclasses.mqh と testinterfaces.mqh) 同じ名前のインクルード ファイルは、テストを起動する際、使用されます。\MQL5\Scripts\Alglib\Testcases\ に配置する必要があります。

ライブラリ ファイルと関数の数百のうち、次のものに関心があります。

パッケージ 
説明 
alglib.mqh
主なライブラリのパッケージには、カスタム関数があります。ライブラリを操作するため、関数を呼び出す必要があります。
dataanalysis.mqh データ分析のクラス:
  1. CMLPBase — 多層パーセプトロン。
  2. CMLPTrain — 多層パーセプトロンのトレーニング。
  3. ニューラル ネットワークの CMLPE-を設定します。

ライブラリをダウンロードする場合、ファイルは MQL5\Include\Math\Alglib\ に送信されます。使用するプログラム コードに次のコマンドが含まれます。

#include<Math\Alglib\alglib.mqh>

CAlglib クラスの関数は、提案されたソリューションに適用されます。

//---神経ネットワークを作成
static void    MLPCreate0(const int nin,const int nout,CMultilayerPerceptronShell &network);
static void    MLPCreate1(const int nin,int nhid,const int nout,CMultilayerPerceptronShell &network);
static void    MLPCreate2(const int nin,const int nhid1,const int nhid2,const int nout,CMultilayerPerceptronShell &network);
static void    MLPCreateR0(const int nin,const int nout,double a,const double b,CMultilayerPerceptronShell &network);
static void    MLPCreateR1(const int nin,int nhid,const int nout,const double a,const double b,CMultilayerPerceptronShell &network);
static void    MLPCreateR2(const int nin,const int nhid1,const int nhid2,const int nout,const double a,const double b,CMultilayerPerceptronShell &network);
static void    MLPCreateC0(const int nin,const int nout,CMultilayerPerceptronShell &network);
static void    MLPCreateC1(const int nin,int nhid,const int nout,CMultilayerPerceptronShell &network);
static void    MLPCreateC2(const int nin,const int nhid1,const int nhid2,const int nout,CMultilayerPerceptronShell &network)

MLPCreate 関数は、線形出力ニューラル ネットワークを作成します。本稿の例でこのネットワークのタイプを作ります。

MLPCreateR 関数内で出力とニューラル ネットワークが作成されます、[a,b] 間隔。

MLPCreateC 関数は、出力の「クラス」分類とニューラル ネットワークを作成されます (0 または 1 。 -1, 0 または 1). 

//---プロパティとニューラル ネットワークの誤差
static void    MLPProperties(CMultilayerPerceptronShell &network,int &nin,int &nout,int &wcount);
static int     MLPGetLayersCount(CMultilayerPerceptronShell &network);
static int     MLPGetLayerSize(CMultilayerPerceptronShell &network,const int k);
static void    MLPGetInputScaling(CMultilayerPerceptronShell &network,const int i,double &mean,double &sigma);
static void    MLPGetOutputScaling(CMultilayerPerceptronShell &network,const int i,double &mean,double &sigma);
static void    MLPGetNeuronInfo(CMultilayerPerceptronShell &network,const int k,const int i,int &fkind,double &threshold);
static double  MLPGetWeight(CMultilayerPerceptronShell &network,const int k0,const int i0,const int k1,const int i1);
static void    MLPSetNeuronInfo(CMultilayerPerceptronShell &network,const int k,const int i,int fkind,double threshold);
static void    MLPSetWeight(CMultilayerPerceptronShell &network,const int k0,const int i0,const int k1,const int i1,const double w);
static void    MLPActivationFunction(const double net,const int k,double &f,double &df,double &d2f);
static void    MLPProcess(CMultilayerPerceptronShell &network,double &x[],double &y[]);
static double  MLPError(CMultilayerPerceptronShell &network,CMatrixDouble &xy,const int ssize);
static double  MLPRMSError(CMultilayerPerceptronShell &network,CMatrixDouble &xy,const int npoints);



//---ニューラル ネットワークのトレーニング
static void    MLPTrainLM(CMultilayerPerceptronShell &network,CMatrixDouble &xy,const int npoints,const double decay,const int restarts,int &info,CMLPReportShell &rep);
static void    MLPTrainLBFGS(CMultilayerPerceptronShell &network,CMatrixDouble &xy,const int npoints,const double decay,const int restarts,const double wstep,int maxits,int &info,CMLPReportShell &rep);

これらの関数は、2 から 4 つの層 (インプット層、出力層、ゼロの層と同様 1 つまたは 2 つの非表示のレイヤー) に含まれているニューラル ネットワークを最適化することができます。メインのインプットの名前は一目瞭然のとおりです。

  • nin: インプット層ニューロンの数。
  • nout: 出力レイヤー。
  • nhid1: 非表示レイヤー 1。
  • nhid2: 非表示レイヤー 2。
  • ネットワーク: ニューロンとその活性化関数の重みと接続の定義を有効にするのは、CMultilayerPerceptronShell クラスのオブジェクトです。
  • xy: トレーニングを行い最適化ニューラル ネットワークにインプット/出力データを有効にするのは、CMatrixDouble クラスのオブジェクトです。

トレーニング/最適化レーベンバーグ ・ マルカート (MLPTrainLM()) または L BFGS アルゴリズム (MLPTrainLBFGS()) を使用して実行します。最後の 1 つがネットワークに 500 接続含まれている場合に使用され: 関数データの何百もの「ネットワーク」の警告を行います。このアルゴリズムは、いわゆる「バックプロパゲーション」NN で一般的に使用するよりも効率的です。ライブラリでは、他の最適化関数もあります。上記 2 つの関数を使用して目的を達成していない場合は、検討します。

 

3. MQL の実装

外部インプットとして各層のニューロンの数を定義してみましょう。正規化パラメータを含む変数を定義する必要があります。

input int nNeuronEntra= 35;      //インプット層のニューロン数
input int nNeuronSal= 1;         //出力層のニューロン数
input int nNeuronCapa1= 45;      //1 非表示層のニューロンの数 (< 1にすることはできません )
input int nNeuronCapa2= 10;      //2 非表示層のニューロンの数 (< 1にすることはできません )

input string intervEntrada= "0;1";        //正規化をインプット: min と max  (空正規化 = なし)
input string intervSalida= "";            //正規化を出力: min と max  (空正規化 = なし)

その他の外部の変数:

input int velaIniDesc= 15;
input int historialEntrena= 1500;

バーのインデックスを指定できます (velaIniDesc)、ネットワークのトレーニングだけでなく、バーのデータ量の合計の履歴データがアップロード (historialEntrena) にあるためです。

ネットワーク オブジェクトと"arDatosAprende"ダブル配列オブジェクト オープンのグローバル変数として定義します
CMultilayerPerceptronShell *objRed;
CMatrixDouble arDatosAprende(0, 0);

「arDatosAprende」ネットワークのトレーニングのインプット/出力データ文字列が含まれます。2次元動的ダブル型行列 (mql5 は、1 次元動的配列を作成することができます。多次元配列を作成するには、すべてのディメンションを定義する)。 

主なアルゴリズムのポイント 1-4 は、"gestionRed()"関数で実装されます。

//---最適化ニューラル ネットワーク--
bool gestionRed(CMultilayerPerceptronShell &objRed, string simb, bool normEntrada= true , bool normSalida= true,
                bool imprDatos= true, bool barajar= true)
{
   double tasaAprende= 0.001;             //ネットワーク トレーニング比率
   int ciclosEntren= 2;                   //トレーニング サイクル数
   ResetLastError();
   bool creada= creaRedNeuronal(objRed);                                //ニューラル ネットワークを作成します。
  if(creada) 
   {
      preparaDatosEntra(objRed, simb, arDatosAprende);                  //arDatosAprende のインプット/出力データをダウンロード
      if(imprDatos) imprimeDatosEntra(simb, arDatosAprende);            //信頼性を評価するためデータを表示
      if(normEntrada || normSalida) normalizaDatosRed(objRed, arDatosAprende, normEntrada, normSalida); //オプションインプット/出力データの正規化
      if(barajar) barajaDatosEntra(arDatosAprende, nNeuronEntra+nNeuronSal);    //データ配列の文字列を反復処理
      errorMedioEntren= entrenaEvalRed(objRed, arDatosAprende, ciclosEntren, tasaAprende);      //トレーニング/最適化を実行
      salvaRedFich(arObjRed[codS], "copiaSegurRed_"+simb);      //ネットワークをディスク ファイルに保存
   }
   else infoError(GetLastError(), __FUNCTION__);
   
   return(_LastError==0);
}


ここでは、関数に、NN を作成 (creaRedNeuronal(objRed)); その後、 は"arDatosAprende"を使用してデータをダウンロード、 preparaDatosEntra() 関数信頼性評価を使用してデータを表示する、 imprimeDatosEntra() 関数。インプットと出力データをする必要があるかどうかは、 normalizaDatosRed() 関数を使います。また、最適化の前にデータ配列の文字列を反復処理する場合は、 barajaDatosEntra()を実行します。トレーニングを用いて得られた最適化エラーを返しますentrenaEvalRed()。最後に、ネットワークのディスクに保存のポテンシャルを作成し、再び最適化することなくリカバリーします

GestionRed() 関数の先頭には、2 つの変数 (tasaAprende、ciclosEntrena) がある NN 率を定義して、サイクルをトレーニングします。ALGLIB は、通常関数を反映する値で使用され、警告しています。しかし、 2 つの提案の最適化アルゴリズムと複数のテストを行っている変数の値の変更は結果に実質的に影響ありません。最初に紹介した、インプットとして変数が、(に無意味) 関数の内部で移動します。インプットのように扱われるべきかどうかを決定することができます。

normalizaDatosRed() 関数は、NN 予報をリクエストするための実際のデータがまた範囲内に存在する場合にのみ、定義された範囲内で NN トレーニングインプットデータを正規化します。それ以外の場合、正規化は必要ではありません。また、トレーニングデータが既に正規化されている場合、予測リクエストの前に実際のデータ正規化は行われません。

3.1 ニューラル ネットワーク (NN) の作成。 

//--------------------------------- CREATE THE NETWORK --------------------------------------
bool creaRedNeuronal (CMultilayerPerceptronShell ・ objRed)
{
   bool creada= false;
   int nEntradas= 0, nSalidas= 0, nPesos= 0;
   if(nNeuronCapa1<1 && nNeuronCapa2<1) CAlglib::MLPCreate0(nNeuronEntra, nNeuronSal, objRed);   //線形出力
   else if(nNeuronCapa2<1) CAlglib::MLPCreate1(nNeuronEntra, nNeuronCapa1, nNeuronSal, objRed);   //線形出力
   else CAlglib::MLPCreate2(nNeuronEntra, nNeuronCapa1, nNeuronCapa2, nNeuronSal, objRed);   		//線形出力
   creada= existeRed(objRed);
   if(!creada) Print("Error creating a NEURAL NETWORK ==> ", __FUNCTION__, " ", _LastError);
   else
   {
      CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos);
      Print("Created the network with nº layers", propiedadRed(objRed, N_CAPAS));
      Print("Nº neurons in the input layer ", nEntradas);
      Print("Nº neurons in the hidden layer 1 ", nNeuronCapa1);
      Print("Nº neurons in the hidden layer 2 ", nNeuronCapa2);
      Print("Nº neurons in the output layer ", nSalidas);
      Print("Nº of the weight", nPesos);
   }
   return(creada);
}

上記関数は、必要な層とニューロン数の NN を作成します(nNeuronEntra、nNeuronCapa1、nNeuronCapa2、nNeuronSal) 、関数を使用して、ネットワークの作成の妥当性をチェックします:

//--------------------------------- EXISTING NETWORK --------------------------------------------
bool existeRed (CMultilayerPerceptronShell ・ objRed)
{
   bool resp= false;
   int nEntradas= 0, nSalidas= 0, nPesos= 0;
   CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos);
   resp= nEntradas>0 && nSalidas>0;
   return(resp);
}

ネットワークが正しく設定されている場合、この関数は、ALGLIB で見つけることができます。 CAlglib クラスの MLPProperties() 関数を使用してそのパラメータのユーザーを通知します。

2 章ですでに述べたように、NN の分類の作成を許可する他の関数を特徴とALGLIB (クラスのラベルは結果として得られる) または (特定の数値が結果として得られた) 回帰課題解決のネットワークです。

NN を作成した後は、他の EA の部分でそのパラメータのいくつかを取得するには、"propiedadRed()"関数を定義できます。

enum mis_PROPIEDADES_RED {N_CAPAS, N_NEURONAS, N_ENTRADAS, N_SALIDAS, N_PESOS};

//---------------------------------- NETWORK PROPERTIES  -------------------------------------------
int propiedadRed(CMultilayerPerceptronShell &objRed, mis_PROPIEDADES_RED prop= N_CAPAS, int numCapa= 0)
{           //N_NEURONAS ニューロンの数がリクエストされる場合、numCapa 画層インデックスを設定します
   int resp= 0, numEntras= 0, numSals= 0, numPesos= 0;
   if(prop>N_NEURONAS) CAlglib::MLPProperties(objRed, numEntras, numSals, numPesos);    
   switch(prop)
   {
      case N_CAPAS:
         resp= CAlglib::MLPGetLayersCount(objRed);
         break;
      ケースN_NEURONAS:
         resp= CAlglib::MLPGetLayerSize(objRed, numCapa);
         break;
      case N_ENTRADAS:
         resp= numEntras;
         break;
      ケースN_SALIDAS:
         resp= numSals;
         break;
      case N_PESOS:
         resp= numPesos;
   }
   return(resp);
}  

3.2 準備インプット/出力データ 

提案する関数は、データの量と種類によって変更できます。

//---------------------------------- PREPARE INPUT/OUTPUT DATA --------------------------------------------------
void preparaDatosEntra(CMultilayerPerceptronShell &objRed, string simb, CMatrixDouble &arDatos, bool normEntrada= true , bool normSalida= true)
{
   int fin= 0, fila= 0, colum= 0,
       nEntras= propiedadRed(objRed, N_ENTRADAS),
       nSals= propiedadRed(objRed, N_SALIDAS);
   double valor= 0, arResp[];   
   arDatos.Resize(historialEntrena, nEntras+nSals);
   fin= velaIniDesc+historialEntrena;
   for(fila= velaIniDesc; fila<fin; fila++)
   {                   
      for(colum= 0; colum<NUM_INDIC;  colum++)
      {
         valor= valorIndic(codS, fila, colum);
         arDatos[fila-1].Set(colum, valor);
      }
      calcEstrat(fila-nVelasPredic, arResp);
      for(colum= 0; colum<nSals; colum++) arDatos[fila-1].Set(colum+nEntras, arResp[colum]);
   }
   return;
}

このプロセスでは、「velaIniDesc + historialEntrena」まで"velaIniDesc"から全体のヒストリーに沿って渡し、各バーの戦略で使用される各インジケーターの値 (NUM_INDIC) を受け取ります。その後、値が CMatrixDouble の2次元行列の適切な列にダウンロードされます。また、インプット戦略結果 (各指定されたインジケーターの値に対応するバーの calcEstrat()")」を入力します。"NVelasPredic"変数は、インジケーター値 n の前方を外挿することができます。"nVelasPredic"は、通常、外部パラメータとして定義します。

CMatrixDouble クラスの各"arDatos"配列の文字列がインプットまたは、戦略だけでなく、定義されている出力データ量で使用されるインジケーター値の数に一致する列の数が含まれることを意味します。"arDatos"は"historialEntrena"値によって定義されている文字列の番号を持ちます。

3.3Printインプット/出力データ配列

インプットと出力データの精度をチェックする次元の行列の内容をPrintする場合には、"imprimeDatosEntra()"関数を使用します。

//---------------------------------- DISPLAY INPUT/OUTPUT DATA --------------------------------------------------
void imprimeDatosEntra (文字列simb、CMatrixDouble ・ arDatos)
{
   string encabeza= "indic1;indic2;indic3...;resultEstrat",     //「;」で区切られたインジケーター名
          fichImprime= "dataEntrenaRed_"+simb+".csv";
   bool entrar= false, copiado= false;
   int fila= 0, colum= 0, resultEstrat= -1, nBuff= 0,
       nFilas= arDatos.Size(),
       nColum= nNeuronEntra+nNeuronSal,
       puntFich= FileOpen(fichImprime, FILE_WRITE|FILE_CSV|FILE_COMMON);
   FileWrite(puntFich, encabeza);
   for(fila= 0; fila<nFilas; fila++)
   {
      linea= IntegerToString(fila)+";"+TimeToString(iTime(simb, PERIOD_CURRENT, velaIniDesc+fila), TIME_MINUTES)+";";                
      for(colum= 0; colum<nColum;  colum++) 
         linea= linea+DoubleToString(arDatos[fila][colum], 8)+(colum<(nColum-1)?";": "");
      FileWrite(puntFich, linea);
   }
   FileFlush(puntFich);
   FileClose(puntFich);
   Alert("Download file= ", fichImprime);
   Alert("Path= ", TerminalInfoString(TERMINAL_COMMONDATA_PATH)+"\\Files");
   return;
}

この関数は、文字列の作成「リネア」で区切られた文字列内のすべての列値を持つ各ステップで文字列";"マトリックスに沿って渡します。これらのデータは、FileOpen() 関数を使用して作成した .csv ファイルに渡されます。現在の記事の主題の2次関数です。Excel を使用して、.csv ファイルを確認することができます。

3.4 特定の間隔内のデータの正規化

通常は、ネットワークの最適化を開始する前にある特定の範囲内のインプットデータを正規化する。そには、CMatrixDouble クラスの"arDatos"配列にある (選択)、インプットまたは出力データの正規化を実行する次の関数を使用します。

//---インプット/出力データ---を正規化します。
void normalizaDatosRed(CMultilayerPerceptronShell &objRed, CMatrixDouble &arDatos, bool normEntrada= true, bool normSalida= true)
{
   int fila= 0, colum= 0, maxFila= arDatos.Size(),
       nEntradas= propiedadRed(objRed, N_ENTRADAS),
       nSalidas= propiedadRed(objRed, N_SALIDAS);
   double maxAbs= 0, minAbs= 0, maxRel= 0, minRel= 0, arMaxMinRelEntra[], arMaxMinRelSals[];
   ushort valCaract= StringGetCharacter(";", 0);
   if(normEntrada) StringSplit(intervEntrada, valCaract, arMaxMinRelEntra);
   if(normSalida) StringSplit(intervSalida, valCaract, arMaxMinRelSals);
   for(colum= 0; normEntrada && colum<nEntradas; colum++)
   {
      maxAbs= arDatos[0][colum];
      minAbs= arDatos[0][colum];
      minRel= StringToDouble(arMaxMinRelEntra[0]);
      maxRel= StringToDouble(arMaxMinRelEntra[1]); 
      for(fila= 0; fila<maxFila; fila++)                //maxAbs と minAbs の各データ列の定義
      {
         if(maxAbs<arDatos[fila][colum]) maxAbs= arDatos[fila][colum];
         if(minAbs>arDatos[fila][colum]) minAbs= arDatos[fila][colum];            
      }
      for(fila= 0; fila<maxFila; fila++)                //新しいの正規化された値を設定する
         arDatos[fila].Set(colum, normValor(arDatos[fila][colum], maxAbs, minAbs, maxRel, minRel));
   }
   for(colum= nEntradas; normSalida && colum<(nEntradas+nSalidas); colum++)
   {
      maxAbs= arDatos[0][colum];
      minAbs= arDatos[0][colum];
      minRel= StringToDouble(arMaxMinRelSals[0]);
      maxRel= StringToDouble(arMaxMinRelSals[1]);
      for(fila= 0; fila<maxFila; fila++)
      {
         if(maxAbs<arDatos[fila][colum]) maxAbs= arDatos[fila][colum];
         if(minAbs>arDatos[fila][colum]) minAbs= arDatos[fila][colum];            
      }
      minAbsSalida= minAbs;
      maxAbsSalida= maxAbs;
      for(fila= 0; fila<maxFila; fila++)
         arDatos[fila].Set(colum, normValor(arDatos[fila][colum], maxAbs, minAbs, maxRel, minRel));
   }
   return;
}

繰り返しは、一定間隔で NN トレーニングインプットデータを正規化する決定をした場合に実行されます。予測 NN をリクエストするために使用する実際のデータも範囲内であることを確認します。それ以外の場合、正規化する必要がありません。

"IntervEntrada"と"intervSalida"がインプット (「実装で mql5 を」セクション冒頭を参照) として定義されている文字列型変数であることに注意してください。例えば「0; 1」または「-1; 1」、すなわち相対的な高値と安値があります。"StringSplit()"関数は、相対的な極端な値が含まれている配列に文字列を渡します。次を行ってください。

  1. 絶対値でハイとロー ("maxAbs"と"minAbs"変数) を定義します。
  2. "MaxRel"と"minRel"の値を正規化全体の列に沿ってパス: 下の"normValor()"関数を参照してください。
  3. CMatrixDouble クラスの .set メソッドを使用して"arDatos"の新しい値を設定します。
//------正規化関数
double normValor(double valor, double maxAbs, double minAbs, double maxRel= 1, double minRel= -1)
{
   double valorNorm= 0;
   if(maxAbs>minAbs) valorNorm= (valor-minAbs)*(maxRel-minRel))/(maxAbs-minAbs) + minRel;
   return(valorNorm);
}
3.5インプット/出力データの反復

データ配列内の潜在的な値の継承を避けるために 任意に変更する (反復) 配列内の文字列のシーケンスです。CMatrixDouble 配列の文字列を反復処理し、各文字列の各行のデータのポジションを考え、バブル法 ("filaTmp"変数) を使用してデータを移動し、新しいターゲット文字列を定義し、"barajaDatosEntra"関数を適用します。 

//---繰り返し処理インプット/出力データ文字列--
void barajaDatosEntra (CMatrixDouble ・ arDatos、 int nColum)
{
   int fila= 0, colum= 0, filaDestino= 0, nFilas= arDatos.Size();
   double filaTmp[];
   ArrayResize(filaTmp, nColum);
   MathSrand(GetTickCount());          //リセット ランダム孫シリーズ
   while(fila<nFilas)
   {
      filaDestino= randomEntero(0, nFilas-1);   //任意のメソッドで対象の文字列を受け取る
      if(filaDestino!=fila)
      {
         for(colum= 0; colum<nColum; colum++) filaTmp[colum]= arDatos[filaDestino][colum];
         for(colum= 0; colum<nColum; colum++) arDatos[filaDestino].Set(colum, arDatos[fila][colum]);
         for(colum= 0; colum<nColum; colum++) arDatos[fila].Set(colum, filaTmp[colum]);
         fila++;
      }
   }
   return;
}

ランダムな"MathSrand(GetTcikCount())"の子シリーズをリセットした後"randomEntero()"関数の文字列がランダムに移動先を担当します。

//---------------------------------- RANDOM MOVING -------------------------------------------------------
int randomEntero(int minRel= 0, int maxRel= 1000)
{
   int num= (int)MathRound(randomDouble((double)minRel, (double)maxRel));
   return(num);
}

3.6 ニューラル ネットワーク学習/最適化
ALGLIB ライブラリにより、トレーニングと最適化-多層パーセプトロンに適用される従来のシステムと比較すると、「バックプロパゲーション」に削減ネットワーク構成アルゴリズムを使用します。すでに言及しているとおり下記を使用します。

2 番目のアルゴリズムは、500 を超える数のネットワークを最適化するためです。

//---------------------------------- NETWORK TRAINING-------------------------------------------
double entrenaEvalRed(CMultilayerPerceptronShell &objRed, CMatrixDouble &arDatosEntrena, int ciclosEntrena= 2, double tasaAprende= 0.001)
{
   bool salir= false;
   double errorMedio= 0; string mens= "Entrenamiento Red";
   int k= 0, i= 0, codResp= 0,
       historialEntrena= arDatosEntrena.Size();
   CMLPReportShell infoEntren;
   ResetLastError();
   datetime tmpIni= TimeLocal();
   Alert("Neural network optimization start...");
   Alert("Wait a few minutes according to the amount of applied history.");
   Alert("...///...");
   if(propiedadRed(objRed, N_PESOS)<500)
      CAlglib::MLPTrainLM(objRed, arDatosEntrena, historialEntrena, tasaAprende, ciclosEntrena, codResp, infoEntren);
   else
      CAlglib::MLPTrainLBFGS(objRed, arDatosEntrena, historialEntrena, tasaAprende, ciclosEntrena, 0.01, 0, codResp, infoEntren);
   if(codResp==2 || codResp==6) errorMedio= CAlglib::MLPRMSError(objRed, arDatosEntrena, historialEntrena);
   else Print("Cod entrena Resp: ", codResp);
   datetime tmpFin= TimeLocal();
   Alert("NGrad ", infoEntren.GetNGrad(), " NHess ", infoEntren.GetNHess(), " NCholesky ", infoEntren.GetNCholesky());
   Alert("codResp ", codResp," Average training error "+DoubleToString(errorMedio, 8), " ciclosEntrena ", ciclosEntrena);
   Alert("tmpEntren ", DoubleToString(((double)(tmpFin-tmpIni))/60.0, 2), " min", "---> tmpIni ", TimeToString(tmpIni, _SEG), " tmpFin ", TimeToString(tmpFin, _SEG));
   infoError(GetLastError(), __FUNCTION__);
   return(errorMedio);
}

「ネットワーク オブジェクト」と既に正規化されたインプット/出力データ行列をインプットとして受け取る関数。また定義サイクル、またはトレーニング元 ("ciclosEntrena"; アルゴリズムは少なくとも「トレーニング エラー」を探してフィッティングを行います);マニュアルが推奨している値は 2 です。テストはトレーニングの数を増加した後改善された結果を示しませんでした。

"InfoEntren"のオブジェクトを定義してみましょう (CMLPReportShell クラス)トレーニング結果データを関数の先頭にします。その後、GetNGrad() と GetNCholesky() メソッドを使用してを取得します。"MLPRMSError()"関数を使用して平均の学習誤差 (アルゴリズムで処理後に得られる出力データを基準にしてすべての出力データの平均2乗誤差) を取得します。その上、時間の最適化が通知されます。初期および終了に使用する tmpIni と tmpFin 変数の時間。

最適化関数は、次の値を取ることができる実行エラー コード ("codResp") を返します。

  • -2 トレーニング サンプルに出力層のニューロン数を超える出力データの数がある場合。
  • -1 一部がインプット関数が正しくありません。
  • 2 は、適切な実行を意味します。エラー スケールとなってストップ ("MLPTrainLM()")。
  • 6は"MLPTrainLBFGS()"関数と同じ。

したがって、適切な実行は 2 または最適化されたネットワークの重みの数によると 6 を返します。 

このアルゴリズムは、トレーニング サイクル ("ciclosEntrena"変数) の繰り返しが得られた精度を大幅変更できます。「バック伝搬」アルゴリズムとは異なり、取得エラーに及ぼす影響はほとんどなく、実行します。35 の 4 層から成るネットワーク、45、10、2 ニューロンと 2000 文字列のインプット行列を最適化する約 2-4-1/100000 のエラーで 4-6 分 です。(I5、4 コア、メモリ 8 gb)

3.7テキスト ファイルにネットワークを保存または復元

この時点で、 NN を作成し、インプット/出力データを準備、ネットワーク トレーニングを実行します。セキュリティ上の理由から、EAの操作中に予期しないエラーが発生した場合に備えて、ネットワークはディスクに保存してください。ネットワーク特性と内部値 (層と各層のニューロンの数)、重量、等の値を受信し、データをディスクにあるテキスト ファイルに書き込む ALGLIB によって提供される関数を使用する必要があります。

//------ディスクにネットワークを保存します。
bool salvaRedFich(CMultilayerPerceptronShell &objRed, string nombArch= "")
{
   bool redSalvada= false;
   int k= 0, i= 0, j= 0, numCapas= 0, arNeurCapa[], neurCapa1= 1, funcTipo= 0, puntFichRed= 9999;
   double umbral= 0, peso= 0, media= 0, sigma= 0;
   if(nombArch=="") nombArch= "copiaSegurRed";
   nombArch= nombArch+".red";
   FileDelete(nombArch, FILE_COMMON);
   ResetLastError();
   puntFichRed= FileOpen(nombArch, FILE_WRITE|FILE_BIN|FILE_COMMON);
   redSalvada= puntFichRed!=INVALID_HANDLE;
   if(redSalvada)
   {
      numCapas= CAlglib::MLPGetLayersCount(objRed);   
      redSalvada= redSalvada && FileWriteDouble(puntFichRed, numCapas)>0;
      ArrayResize(arNeurCapa, numCapas);
      for(k= 0; redSalvada && k<numCapas; k++)
      {
         arNeurCapa[k]= CAlglib::MLPGetLayerSize(objRed, k);
         redSalvada= redSalvada && FileWriteDouble(puntFichRed, arNeurCapa[k])>0;
      }
      for(k= 0; redSalvada && k<numCapas; k++)
      {
         for(i= 0; redSalvada && i<arNeurCapa[k]; i++)
         {
            if(k==0)
            {
               CAlglib::MLPGetInputScaling(objRed, i, media, sigma);
               FileWriteDouble(puntFichRed, media);
               FileWriteDouble(puntFichRed, sigma);
            }
            else if(k==numCapas-1)
            {
               CAlglib::MLPGetOutputScaling(objRed, i, media, sigma);
               FileWriteDouble(puntFichRed, media);
               FileWriteDouble(puntFichRed, sigma);
            }
            CAlglib::MLPGetNeuronInfo(objRed, k, i, funcTipo, umbral);
            FileWriteDouble(puntFichRed, funcTipo);
            FileWriteDouble(puntFichRed, umbral);
            for(j= 0; redSalvada && k<(numCapas-1) && j<arNeurCapa[k+1]; j++)
            {
               peso= CAlglib::MLPGetWeight(objRed, k, i, k+1, j);
               redSalvada= redSalvada && FileWriteDouble(puntFichRed, peso)>0;
            }
         }      
      }
      FileClose(puntFichRed);
   }
   if(!redSalvada) infoError(_LastError, __FUNCTION__);
   return(redSalvada);
} 

6 番目のコード文字列を見ることができ、 赤色の拡張関数は、将来の検索とチェックを簡素化するファイルに割り当てられます。この関数のデバッグ時間を費やしています。

EA をストップするイベントの後、 上記のものとは反対の関数を使用してディスク上のファイルからネットワークを復元します。この関数は、ネットワーク オブジェクトを作成し、NN の保存テキスト ファイルから読み取ってデータで塗りつぶします。

//------ディスクからネットワークを復元。
bool recuperaRedFich(CMultilayerPerceptronShell &objRed, string nombArch= "")
{
   bool exito= false;
   int k= 0, i= 0, j= 0, nEntradas= 0, nSalidas= 0, nPesos= 0,
       numCapas= 0, arNeurCapa[], funcTipo= 0, puntFichRed= 9999;
   double umbral= 0, peso= 0, media= 0, sigma= 0;
   if(nombArch=="") nombArch= "copiaSegurRed";
   nombArch= nombArch+".red";
   puntFichRed= FileOpen(nombArch, FILE_READ|FILE_BIN|FILE_COMMON);
   exito= puntFichRed!=INVALID_HANDLE;
   if(exito)
   {
      numCapas= (int)FileReadDouble(puntFichRed);
      ArrayResize(arNeurCapa, numCapas);
      for(k= 0; k<numCapas; k++) arNeurCapa[k]= (int)FileReadDouble(puntFichRed); 
      if(numCapas==2) CAlglib::MLPCreate0(nNeuronEntra, nNeuronSal, objRed);
      else if(numCapas==3) CAlglib::MLPCreate1(nNeuronEntra, nNeuronCapa1, nNeuronSal, objRed);
      else if(numCapas==4) CAlglib::MLPCreate2(nNeuronEntra, nNeuronCapa1, nNeuronCapa2, nNeuronSal, objRed);
      exito= existeRed(arObjRed[0]);
      if(!exito) Print("neural network generation error ==> ", __FUNCTION__, " ", _LastError);
      else
      {
         CAlglib::MLPProperties(objRed, nEntradas, nSalidas, nPesos);
         Print("Restored the network having nº layers", propiedadRed(objRed, N_CAPAS));
         Print("Nº neurons in the input layer ", nEntradas);
         Print("Nº neurons in the hidden layer 1 ", nNeuronCapa1);
         Print("Nº neurons in the hidden layer 2 ", nNeuronCapa2);
         Print("Nº neurons in the output layer ", nSalidas);
         Print("Nº of the weight", nPesos);
         for(k= 0; k<numCapas; k++)
         {
            for(i= 0; i<arNeurCapa[k]; i++)
            {
               if(k==0)
               {
                  media= FileReadDouble(puntFichRed);
                  sigma= FileReadDouble(puntFichRed);
                  CAlglib::MLPSetInputScaling(objRed, i, media, sigma);
               }
               else if(k==numCapas-1)
               {
                  media= FileReadDouble(puntFichRed);
                  sigma= FileReadDouble(puntFichRed);
                  CAlglib::MLPSetOutputScaling(objRed, i, media, sigma);
               }
               funcTipo= (int)FileReadDouble(puntFichRed);
               umbral= FileReadDouble(puntFichRed);
               CAlglib::MLPSetNeuronInfo(objRed, k, i, funcTipo, umbral);
               for(j= 0; k<(numCapas-1) && j<arNeurCapa[k+1]; j++)
               {
                  peso= FileReadDouble(puntFichRed);
                  CAlglib::MLPSetWeight(objRed, k, i, k+1, j, peso);
               }
            }      
         }
      }
   }
   FileClose(puntFichRed);
   return(exito);
} 

予測データのダウンロード時のネットワークを取得するには、"respuestaRed()"関数を呼び出します。

//------ネットワーク応答をリクエスト
double respuestaRed(CMultilayerPerceptronShell &ObjRed, double &arEntradas[], double &arSalidas[], bool desnorm= false)
{
   double resp= 0, nNeuron= 0;
   CAlglib::MLPProcess(ObjRed, arEntradas, arSalidas);   
   if(desnorm)             //出力データの正規化が変更される場合
   {
      nNeuron= ArraySize(arSalidas);
      for(int k= 0; k<nNeuron; k++)
         arSalidas[k]= desNormValor(arSalidas[k], maxAbsSalida, minAbsSalida, arMaxMinRelSals[1], arMaxMinRelSals[0]);
   }
   resp= arSalidas[0];
   return(resp);
}

この関数では、トレーニング行列の出力データに適用する正規化を変更する能力を前提としています。

 

4. 自己最適化

最適化にニューラル ネットワーク (適用インプット)操作中に戦略テスターに組み込まれている最適化なしで、セクション 1 で説明した基本的なアルゴリズムを繰り返す必要があります。

加えて、 EA は、コンピューティング リソースの偉大な量を含む NN 最適化中にその制御を失うことがなく相場を継続的に監視する必要があります。

毎日、選択的に基本的なアルゴリズムを繰り返す、ユーザーが選択できる時間間隔を示す mis_PLAZO_OPTIM 列挙型を設定してみましょう。EA がネットワーク「オプティマイザー」または「アクチュエータ」の戦略として関数するかどうかを決定する別の列挙可能ユーザーも設定する必要があります。

enum mis_PLAZO_OPTIM {_DIARIO, _DIA_ALTERNO, _FIN_SEMANA};
enum mis_TIPO_EAred {_OPTIMIZA, _EJECUTA};

МetaTrader 5 で開いているグラフで同時に EA を実行することができます。したがって、最初のグラフの実行モードで EA を起動し、2 つ目の最適化モードでを実行します。最初のグラフの EAは、2 番目の中にのみ、ニューラル ネットワークを最適化します。したがって、2 番目の問題は解決します。最初のグラフの EAを使用して読み込んだNN を最適化するたびに「オプティマイザー」モードで生成されたテキスト ファイルからニューラル ネットワークを生成します。

最適化テストに計算時間の約 4-6 分かかったことを指しています。このメソッドがアジアや欧州の相場活動時間に応じて 8-15 分を取るプロセスをわずかに増加しました。

次のインプットを定義する必要があります。

input mis_TIPO_EAred tipoEAred            = _OPTIMIZA;        //タスクの種類を実行
input mis_PLAZO_OPTIM plazoOptim          = _DIARIO;          //ネットワークの最適化の時間間隔
input int horaOptim                       = 3;                //ネットワークの最適化の現地時間

"horaOptim"パラメータは、ローカルに最適化する時間を節約できます。時刻がゼロの相場活動で対応する必要があります: たとえば、ヨーロッパで早朝 (3:00 既定値として) をする必要があります。定義された時間と日を待たず、EA を起動するたびに、最適化を実行する場合は、次のよう指定して下さい。

input bool optimInicio                    = true;         //EA を起動するとニューラル ネットワークの最適化

ネットワークの最適化 (「最適化」モード) とみなされ、読み込みの時間 (「アクチュエータ」モード) のネットワーク ファイルを定義する必要があります。

double fechaUltLectura;
bool reOptimizada= false;

OnTimer() 関数に指定したメソッド処理ブロックを設定する最初の問題を解決するために順番に設定されている OnInit() の EventSetTimer(tmp) を介して、少なくとも毎時"tmp"期間に従って実行されます。したがって、tmp 秒ごと「オプティマイザー」EA 、「アクチュエータ」EA を確認し、「オプティマイザー」によって更新されたので、ネットワーク ファイルが再び読み込まれる場合かどうかネットワークの再最適化する必要があります。

/---------------------------------- ON TIMER --------------------------------------
void OnTimer()
{
   bool existe= false;
   string fichRed= "";
   if(tipoEAred==_OPTIMIZA)            //「オプティマイザー」モードで動作する EA
   {
      bool optimizar= false;
      int codS= 0,
          hora= infoFechaHora(TimeLocal(), _HORA);    //完全な現在の時間が表示される
      if(!redOptimizada) optimizar= horaOptim==hora && permReoptimDia();
      fichRed= "copiaSegurRed_"+Symbol()+".red";      //ニューラル ネットワークのファイル名を定義する
      existe= buscaFich(fichRed, "*.red");            //ニューラル ネットワークが保存されているファイルのディスクを検索
      if(!existe || optimizar)
         redOptimizada= gestionRed(objRed, simb, intervEntrada!="", intervSalida!="", imprDatosEntrena, barajaDatos);
      if(hora>(horaOptim+6)) redOptimizada= false;    //予定時刻の 6 時間は、最適化されたネットワークの遅れが考慮される
      guardaVarGlobal(redOptimizada);                 //ディスク上の"reoptimizada"(再最適化された) 値を保存
   }
   else if(tipoEAred==_EJECUTA)        //「アクチュエータ」モードで動作する EA
   {
      datetime fechaUltOpt= 0;
      fichRed= "copiaSegurRed_"+Symbol()+".red";      //ニューラル ネットワーク ファイルの名前を定義する
      existe= buscaFich(fichRed, "*.red");            //ニューラル ネットワークが保存されているファイルのディスクを検索
      if(existe)
      {
         fechaUltOpt= fechaModifFich(0, fichRed);     //最後の最適化の日付 (ネットワーク ファイルの変更) を定義する
         if(fechaUltOpt>fechaUltLectura)              //最適化日付が最後の読み込みよりも後の場合
         {
            recuperaRedFich(objRed, fichRed);         //新しいニューラル ネットワークを生成する
            fechaUltLectura= (double)TimeCurrent();
            guardaVarGlobal(fechaUltLectura);         //新しい保存ディスクに日付を読み込み
            Print("Network restored after optimization... "+simb);      //画面にメッセージを表示
         }
      }
      else Alert("tipoEAred==_EJECUTA --> Neural network file not found: "+fichRed+".red");
   }
   return;
}

コメント化されていない次の追加関数は以下のとおりです。

//------再最適化を有効にします。
bool permReoptimDia()
{
   int diaSemana= infoFechaHora(TimeLocal(), _DSEM);
   bool permiso= (plazoOptim==_DIARIO && diaSemana!=6 && diaSemana!=0) ||     //[火曜日から土曜日からまで毎日]最適化
                 (plazoOptim==_DIA_ALTERNO && diaSemana%2==1) ||              //[火・木・土曜日] 最適化
                 (plazoOptim==_FIN_SEMANA && diaSemana==5);                   //[土曜日] を最適化
   return(permiso);
}

//-------------------------------------- LOOK FOR FILE --------------------------------------------
bool buscaFich(string fichBusca, string filtro= "*.*", int carpeta= FILE_COMMON)
{
   bool existe= false;
   string fichActual= "";
   long puntBusca= FileFindFirst(filtro, fichActual, carpeta);
   if(puntBusca!=INVALID_HANDLE)
   {
      ResetLastError();
      while(!existe)
      {
         FileFindNext(puntBusca, fichActual);
         existe= fichActual==fichBusca;
      }
      FileFindClose(puntBusca);
   }
   else Print("File not found!");
   infoError(_LastError, __FUNCTION__);
   return(existe);

このアルゴリズムは現在、全体の戦略を管理することができる EA で使用します。毎晩、3:00 から3ヶ月前 (現地時間)、ネットワークが Н1 データの再最適化をします: 35 ニューロンのインプットレイヤー、8 番目の隠された層と出力層の 2 の最初の非表示レイヤーに 45 のもの最適化は、35 〜 45 分を実行されます。

5. タスク 1: 2進 10 進数コンバーター

システムをチェックするためには、すでに既知の解決策 (適切なアルゴリズムがある) を持っており、をニューラル ネットワークによって提供される比較作業を解決できます。2進ー10 進数コンバーターを作成してみましょう。次のスクリプトは、テストされています。

#propertyscript_show_confirm
#propertyscript_show_inputs

#defineFUNC_CAPA_OCULTA 1  
#defineFUNC_SALIDA -5
            //1= hyperbolic tangent; 2= e^(-x^2); 3= x>=0 raizC(1+x^2) x<0 e^x; 4= sigmoidal function;
            //5= binomial x>0.5? 1: 0; -5= linear function
#include<Math\Alglib\alglib.mqh>

enum mis_PROPIEDADES_RED {N_CAPAS, N_NEURONAS, N_ENTRADAS, N_SALIDAS, N_PESOS};
//---------------------------------  Inputs  ---------------------
sinput int nNeuronEntra= 10;                 //インプット層ニューロンの数
                                             //2 ^8 = 256 2 ^9 = 512;2 ^10 = 1024;2 ^12 = 4096。2 ^14 = 16384 
sinput int nNeuronCapa1= 0;                  //最初の非表示層のニューロンの数 ( < 1にすることはできません)
sinput int nNeuronCapa2= 0;                  //2 番目の隠れ層のニューロンの数 ( < 1にすることはできません)//2^8= 256 2 ^9 = 512;2 ^10 = 1024;2 ^12 = 4096。2 ^14 = 16384
sinput int nNeuronSal= 1;                    //出力層のニューロン数

sinput int    historialEntrena= 800;         //トレーニング履歴
sinput int    historialEvalua= 200;          //評価履歴
sinput int    ciclosEntrena= 2;              //サイクル トレーニング
sinput double tasaAprende= 0.001;            //ネットワーク トレーニング レベル
sinput string intervEntrada= "";             //正規化をインプット: min と max を希望 (空 = 正規化なし)
sinput string intervSalida= "";              //正規化を出力: min と max を希望 (空 = 正規化なし)
sinput bool   imprEntrena= true;             //トレーニング/評価データを表示
      
//------------------------------ GLOBAL VARIABLES -----------------------------     
int puntFichTexto= 0;
ulong contFlush= 0; 
CMultilayerPerceptronShell redNeuronal;
CMatrixDouble arDatosAprende(0, 0);
CMatrixDouble arDatosEval(0, 0);
double minAbsSalida= 0, maxAbsSalida= 0;
string nombreEA= "ScriptBinDec";

//+------------------------------------------------------------------+
void OnStart()              //2進ー10 進数コンバーター
{
   string mensIni= "Script conversor BINARIO-DECIMAL",
          mens= "", cadNumBin= "", cadNumRed= "";
   int contAciertos= 0, arNumBin[],
       inicio= historialEntrena+1,
       fin= historialEntrena+historialEvalua;
   double arSalRed[], arNumEntra[], salida= 0, umbral= 0, peso= 0;
   double errorMedioEntren= 0;
   bool normEntrada= intervEntrada!="", normSalida= intervSalida!="", correcto= false,
        creada= creaRedNeuronal(redNeuronal);        
   if(creada) 
   {
      iniFichImprime(puntFichTexto, nombreEA+"-infRN", ".csv",mensIni);
      preparaDatosEntra(redNeuronal, arDatosAprende, intervEntrada!="", intervSalida!="");
      normalizaDatosRed(redNeuronal, arDatosAprende, normEntrada, normSalida);
      errorMedioEntren= entrenaEvalRed(redNeuronal, arDatosAprende);
      escrTexto("-------------------------", puntFichTexto);
      escrTexto("RESPUESTA RED------------", puntFichTexto);
      escrTexto("-------------------------", puntFichTexto);
      escrTexto("numBinEntra;numDecSalidaRed;correcto", puntFichTexto);
      for(int k= inicio; k<=fin; k++)
      {
         cadNumBin= dec_A_baseNumerica(k, arNumBin, 2, nNeuronEntra);
         ArrayCopy(arNumEntra, arNumBin);
         salida= respuestaRed(redNeuronal, arNumEntra, arSalRed);
         salida= MathRound(salida);
         correcto= k==(int)salida;
         escrTexto(cadNumBin+";"+IntegerToString((int)salida)+";"+correcto, puntFichTexto);
         cadNumRed= "";
      }
   }      
   deIniFichImprime(puntFichTexto);
   return;
}

NN を作成した後 (10 文字、10 インプットニューロンと 1 つの出力ニューロン) バイナリ形式で 800 最初の自然数をトレーニングします。その後、(バイナリ形式で801から 1000 ) バイナリ形式に200 自然数を変換し、NN で予測したものと実際の結果を比較する必要があります。たとえば、ネットワーク (820 のバイナリ フォーム; 10 文字、10 インプットニューロン) に 1100110100 を設定した場合、ネットワークの 820 または他の図が表示されます。上記のメソッドは受信ネットワークが 200 の数字に関する予測と推定されるものと期待される結果を比較します。

指定されたパラメータ (非表示のレイヤー、10 インプットニューロンと出力ニューロンの 1 なし NN) でスクリプトを実行した後は、素晴らしい結果を取得します。ScriptBinDec infRN.csv は、Terminal\Common\Files ディレクトリに、生成されます。


 

スクリプトはバイナリ (インプット) と 10 進数 (出力) フォームで 800 までトレーニング行列をプリントしました。NN にトレーニングされ、801 から始まる答えを出力しています。3 番目の列には、'true' が含まれています。予想と実際の結果の比較の結果です。すでに述べたように、良い結果です。

しかし、 「10 のインプットニューロン、20 の最初非表示層のニューロン、8 第 2 中間層ニューロン、1 つの出力ニューロン」として NN 構造を定義する場合、次を取得します。


 

これは受け入れ難い結果です!ここで ニューラル ネットワークの処理中に深刻な問題に直面します。: 最適な内部構成 (層ニューロンと活性化関数の数) は何ですか?この問題は、適切な記事を読んで、たとえば「機械学習モデル変数の選択と評価」で実施した何千もの確かな経験によってのみ解決できます。その上で mql5 を実装する前に、最も効率的な構造を見つけるためにラピッドマイナー統計解析ソフトウェアのトレーニング マトリックス データを適用しました。

 

6. タスク 2: 素数検出器

似たようなタスクを考えてみましょう。今回は、NN がある数が素数であるかどうかを定義します。トレーニングマトリックス 800 台列か素数 (「1」) であるかどうかを示す (「0」) までバイナリ形式で各々 の自然数の 10 文字で 10 列が含まれます。言い換えると、800 の行と 11 列があります。次に、NN (1000 801) からバイナリ形式で次 200 自然数字を分析し、どの数値が素数か定義をしなければなりません。このタスクは難しいので、得られた統計情報をPrintしてみましょう。

#include<Math\Alglib\alglib.mqh>

enum mis_PROPIEDADES_RED {N_CAPAS, N_NEURONAS, N_ENTRADAS, N_SALIDAS, N_PESOS};
//---------------------------------  Inputs ----------------------- ---------------------
sinput int nNeuronEntra= 10;                 //インプット層ニューロンの数
                                             //2 ^8 = 256 2 ^9 = 512;2 ^10 = 1024;2 ^12 = 4096。2 ^14 = 16384 
sinput int nNeuronCapa1= 20;                 //1 非表示層のニューロンの数 (< 1にすることはできません )
sinput int nNeuronCapa2= 0;                  //2 非表示層のニューロンの数 (< 1にすることはできません )//2^8= 256 2 ^9 = 512;2 ^10 = 1024;2 ^12 = 4096。2 ^14 = 16384
sinput int nNeuronSal= 1;                    //出力層のニューロン数

sinput int    historialEntrena= 800;         //トレーニング履歴
sinput int    historialEvalua= 200;          //ヒストリーを予測
sinput int    ciclosEntrena= 2;              //サイクル トレーニング
sinput double tasaAprende= 0.001;            //ネットワーク トレーニング比率
sinput string intervEntrada= "";             //正規化をインプット: min と max を希望 (空 = 正規化なし)
sinput string intervSalida= "";              //正規化を出力: min と max を希望 (空 = 正規化なし)
sinput bool   imprEntrena= true;             //トレーニング/評価データを表示
      
//------------------------------ GLOBAL VARIABLES ----------------------------------------     
int puntFichTexto= 0;
ulong contFlush= 0; 
CMultilayerPerceptronShell redNeuronal;
CMatrixDouble arDatosAprende(0, 0);
double minAbsSalida= 0, maxAbsSalida= 0;
string nombreEA= "ScriptNumPrimo";

//+----------------------- Prime number detector -------------------------------------------------+
void OnStart()
{
   string mensIni= "Script comprobación NÚMEROS PRIMOS", cadNumBin= "", linea= "";
   int contAciertos= 0, totalPrimos= 0, aciertoPrimo= 0, arNumBin[],
       inicio= historialEntrena+1,
       fin= historialEntrena+historialEvalua;
   double arSalRed[], arNumEntra[], numPrimoRed= 0;
   double errorMedioEntren= 0;
   bool correcto= false,
        esNumPrimo= false, 
        creada= creaRedNeuronal(redNeuronal);        
   if(creada) 
   {
      iniFichImprime(puntFichTexto, nombreEA+"-infRN", ".csv",mensIni);
      preparaDatosEntra(redNeuronal, arDatosAprende, intervEntrada!="", intervSalida!="");
      normalizaDatosRed(redNeuronal, arDatosAprende, normEntrada, normSalida);
      errorMedioEntren= entrenaEvalRed(redNeuronal, arDatosAprende);
      escrTexto("-------------------------", puntFichTexto);
      escrTexto("RESPUESTA RED------------", puntFichTexto);
      escrTexto("-------------------------", puntFichTexto);
      escrTexto("numDec;numBin;numPrimo;numPrimoRed;correcto", puntFichTexto);
      for(int k= inicio; k<=fin; k++)
      {
         cadNumBin= dec_A_baseNumerica(k, arNumBin, 2, nNeuronEntra);
         esNumPrimo= esPrimo(k);
         ArrayCopy(arNumEntra, arNumBin);
         numPrimoRed= respuestaRed(redNeuronal, arNumEntra, arSalRed);
         numPrimoRed= MathRound(numPrimoRed);
         correcto= esNumPrimo==(int)numPrimoRed;
         if(esNumPrimo)
         {
            totalPrimos++;
            if(correcto) aciertoPrimo++;  
         }
         if(correcto) contAciertos++;
         linea= IntegerToString(k)+";"+cadNumBin+";"+esNumPrimo+";"+(numPrimoRed==0?"false": "true")+";"+correcto;
         escrTexto(linea, puntFichTexto);
      }
   }     
   escrTexto("porc Aciertos / total;"+DoubleToString((double)contAciertos/(double)historialEvalua*100, 2)+" %", puntFichTexto); 
   escrTexto("Aciertos primos;"+IntegerToString(aciertoPrimo)+";"+"total primos;"+IntegerToString(totalPrimos), puntFichTexto); 
   escrTexto("porc Aciertos / total primos;"+DoubleToString((double)aciertoPrimo/(double)totalPrimos*100, 2)+" %", puntFichTexto); 
   deIniFichImprime(puntFichTexto);
   return;
}

(非表示のレイヤーなし NN、10 のインプットニューロン、20 ニューロンの最初の非表示レイヤー、出力レイヤーの 1) は、指定されたパラメータでスクリプトを実行した後、結果は 1 つ前のより悪いです。ScriptNumPrimo infRN.csv Terminal\Common\Files ディレクトリに生成され、次のデータを提供します。


 

ここで、 800 (809) の後、最初の素数がないネットワークで検出されたことを見ることができます ( = true)。統計情報の要約を次に示します。


 

レポートの状態評価間隔 (801 から 200) で 200数 の 78% を推測する NN が管理しています。しかし、29の 素数の間隔に存在する、 13 (44.83%) のみを検出してはいます。

次のネットワーク構造を持つテストを実施かどうか。:"10 のインプット層ニューロン、35 の最初非表示層のニューロン、10 第 2 中間層ニューロン、1 出力層ニューロン」、スクリプトの実行中に次のデータが表示されます。


 

下の画像を見てとれるように、結果は 0.53 分の平均トレーニングエラー 0.04208383 に等しい時間、悪化しています。


 

ネットワークの内部構造を定義する最善の方法は何でしょう。

 

結論

自己最適化 EA を扱いながら、 mql5 をプログラムを使用してALGLIBライブラリのニューラル ネットワークの最適化コードを実装しました。EA が大規模なコンピューティングリソースを含むネットワーク構成を実行するときに、トレード戦略を妨げる問題の解決策を提案しています。

その後、 mql5プログラムから 2 つのタスクを解決するために提案されたコードの一部を使用し、 2進ー10 進数変換、素数の検出と追跡の nn は、内部構造によると結果がでました。

これは、より良い効率的なトレード戦略に有効でしょうか。それはまだわからりませんが、引き続き、取り組んでいきます。この段階では、このトピックは、良いスタートだと言えるでしょう。