![ニューラルネットワークが簡単に(第19部):MQL5を使用したアソシエーションルール](https://c.mql5.com/2/49/Neural_Networks_Easy_011_600x314.jpg)
ニューラルネットワークが簡単に(第19部):MQL5を使用したアソシエーションルール
内容
はじめに
前回の記事では、教師なし学習に属するアソシエーションルールマイニングアルゴリズムについて学び始めました。このタイプの問題を解決するための2つのアルゴリズムを検討しました。アプリオンとFP-Growthです。アプリオンアルゴリズムのボトルネックは、頻度の高いパターン候補のサポートを決定することを目的とした多数のデータベース呼び出しです。FP-Growthでは、データベース全体を含む木を構築することで、この問題を解決します。以降のすべての操作は、データベースにアクセスせずにFPツリーで実行されます。FPツリーがRAMに配置されるため、問題解決速度が向上します。データベースにアクセスするのは、データベースを完全に反復処理するよりもはるかに高速です。
1.取引で使用する方法
MQL5を使用したアソシエーションルールマイニングアルゴリズムの構築に進む前に、これを取引でどのように使用できるかを考えてみましょう。
アソシエーションルールマイニングアルゴリズムは、データベース内のバイナリ特徴間の安定した依存関係を検索するために作成されたため、このようなアルゴリズムを使用して、さまざまな機能特徴間の安定した関係を見つけることができます。これは、複数の指標や手段で構成されるさまざまなパターンである可能性があります。それぞれの個別の特徴が異なるメトリックを表しているか異なる時間間隔での同じメトリックの値であるかどうかはアルゴリズムには関係ありません。各特徴は独立しているとして評価されます。したがって、このアルゴリズムを教師あり学習の開発と組み合わせることができます。履歴データの訓練標本にいくつかの目標の特徴を追加してみましょう。アルゴリズムは目標値の形成につながる関連ルールを見つけます。
アルゴリズムがあり、実際の問題を解決するためにそれを使用する方法についてのアイデアもあるので、MQL5を使用して実装する方法を見てみましょう。次に、アイデアを実際にテストします。
2.FP-Growthアルゴリズムの実装
前の記事で検討したFP-Growthアルゴリズムを実装するには、その構築が決定木に基づいていることを思い出してください。MQL5標準ライブラリには、二分木を構築するためのCTreeクラスがあります。残念ながら、FPツリーでは1つのノードからの分岐の数が2(バイナリ実装で利用可能な最大数)を超える可能性があるため、二分木オプションは完全に便利というわけではありません。したがって、アルゴリズム自体を構築する前に、CMyTreeNodeクラスを作成して、複数の分岐を持つツリーノードを実装しましょう。
2.1.ツリーノードクラスの実装
このクラスは、オブジェクトの動的配列の標準MQL5クラスであるCArrayObjから派生します。このクラスが親として選択されたのは、オブジェクトの動的配列の作成と維持に関連する必要な機能を備えているためです。この場合、枝ノードです。
さらに、アルゴリズムに必要な機能を実装するために、3つの新しい変数がクラスに追加されました。
- m_cParent - 前の親ノードのオブジェクトへのポインタ(木の根では空)
- m_iIndex - ソースデータベース内の特徴のインデックス。特徴を識別するために使用
- m_dSupport - 特徴サポート値を受け取る変数
class CMyTreeNode : public CArrayObj { protected: CMyTreeNode *m_cParent; ulong m_iIndex; double m_dSupport; public: CMyTreeNode(); ~CMyTreeNode(); }
クラスコンストラクタで、変数の初期値を設定し、動的配列をクリアします。クラスデストラクタは空のままにします。
CMyTreeNode::CMyTreeNode() : m_iIndex(ULONG_MAX), m_dSupport(0) { Clear(); }
非表示のクラス変数を操作するために、FP-Growthアルゴリズムの作成プロセスで使用されるいくつかのメソッドを作成します。メソッドの目的とその使用法について説明します。
class CMyTreeNode : public CArrayObj { ........ public: ........ //--- methods of access to protected data CMyTreeNode* Parent(void) const { return(m_cParent); } void Parent(CMyTreeNode *node) { m_cParent = node; } void IncreaseSupport(double support) { m_dSupport += support; } double GetSupport(void) { return m_dSupport; } void SetSupport(double support) { m_dSupport = support; } ulong ID(void) { return m_iIndex; } void ID(ulong ID) { m_iIndex = ID; } };
信頼レベルを計算するにはGetConfidenceメソッドを作成しましょう。まず先行ノードへのポインタをチェックし、それが有効な場合は、現在のノードサポートを親ノードサポートで割ります。
FPツリー構築アルゴリズムは、どのノードのサポートも親ノードのサポートより大きくならないように編成されています。したがって、メソッド操作の結果は常に正であり、1より大きくなりません。
既存のトランザクションに基づいてツリーノードが追加されるため、ゼロ除算チェックはありません。したがって、ノードがツリーにある場合、その特徴は少なくとも1回データベースに表示されており、最小限のサポートしかありません。
double CMyTreeNode::GetConfidence(void) { CMyTreeNode *parent = Parent(); if(!parent) return 1; //--- double result = m_dSupport / parent.GetSupport(); return result; }
また、新しい枝ノードを作成するAddNodeメソッドを追加します。メソッドパラメータでは、訓練標本のソースデータベースの特徴IDとノードのサポートを渡します。このメソッドは、作成されたオブジェクトへのポインタを返します。
メソッド本体では、ツリーノードの新しいインスタンスを作成し、すぐに操作結果を確認します。エラーが発生した場合は、無効なオブジェクトポインタを返します。
次に、作成したノードのIDを指定し、現在のオブジェクトへのポインタを親オブジェクトとして渡します。
現在のノードの動的配列に新しいオブジェクトを追加し、操作結果を確認します。配列へのオブジェクトの追加中にエラーが発生した場合は、作成されたオブジェクトを削除してメソッドを終了し、無効なポインタを返します。
メソッドの最後で、パラメータで指定されたサポートを新しいオブジェクトに保存し、メソッドを終了します。
CMyTreeNode *CMyTreeNode::AddNode(const ulong ID, double support = 0) { CMyTreeNode *node = new CMyTreeNode(); if(!node) return node; node.ID(ID); if(!Add(node)) { delete node; return node; } node.Parent(GetPointer(this)); //--- if(support > 0) node.SetSupport(support); return node; }
新しいオブジェクトを作成したら削除できるはずです。動的配列内のインデックスによってオブジェクトを削除するメソッドは、親クラスに既に存在するので、特徴IDでノードを削除するためのDeleteNodeメソッドを作成して、その機能を拡張します。
このメソッドは、削除する特徴のIDを受け取り、操作のブール値の結果を返します。
メソッド本体で、現在のノードの動的配列で指定されたIDを持つノードを検索するループを実装します。ループでは、0からm_data_total変数値までの範囲の要素を反復処理します。変数には、動的配列のアクティブな要素の数が含まれ、親クラスによって制御されます。
メソッド本体で、動的配列から次の要素を抽出し、ポインタを検証します。無効なポインタを持つ要素は、削除する要素の指定されたインデックスを使用して、親クラスのDeleteメソッドを呼び出すことによって直ちに削除されます。
Deleteメソッドは、操作のブール値の結果を返すことに注意してください。要素が動的配列から正常に削除された場合、ループ反復のカウンタを減らし、次の配列に進みます。ループ反復カウンタをデクリメントするだけで、m_data_total変数の値は変更しません。これは、その値がDelete親クラスのメソッドで既に変更されているためです。
動的配列から無効な要素を削除しているときにエラーが発生した場合は、単に配列の次の要素に移動します。このメソッドのタスクは無効なオブジェクトから動的配列をクリアすることではないため、falseの結果でメソッドを終了することはありせん。これは単なるヘルパー機能です。メソッドの主なタスクは、特定の要素を削除することです。したがって、必要な要素が見つかるまでメソッドの実行を続けます。
動的配列の必要な要素が見つかったら、前述の親クラスのDeleteメソッドを呼び出します。今回はメソッドを終了してオブジェクトの削除結果を返します。
動的配列のすべての要素を完全に反復しても要素が見つからない場合は、falseの結果でメソッドを終了します。
bool CMyTreeNode::DeleteNode(const ulong ID) { for(int i = 0; i < m_data_total; i++) { CMyTreeNode *temp = m_data[i]; if(!temp) { if(!Delete(i)) continue; return DeleteNode(ID); } if(temp.ID() != ID) continue; return Delete(i); } //--- return false; }
新しいツリーノードクラスCMyTreeNodeのメソッドについてさらにお話しし、Miningメソッドに注目したいと思います。このメソッドは、分析している特徴までのFPツリー内のパスを見つける役割を果たします。メソッドのアルゴリズムの説明に進む前に、取引での使用目的を考慮して作成されているために、基本的なアルゴリズムから少し逸脱していることを言及しなければなりません。
まず、すべての特徴の関連付けルールを決定するのではなく、ターゲット値のみを決定します。したがって、ルールツリーを構築しているときに、必要な特徴が葉ではなく、パスに沿った後続の要素を含むノードであるという状況に遭遇する可能性が非常に高くなります。ただし、ターゲット結果の可能性を高める可能性があるため、後続のノードを無視することはできません。したがって、解析対象の特徴へのパスを選択する際には、それらを考慮する必要があります。
このメソッドを構築する際に私が注意したもう1つの点は、アルゴリズムによると、まずFPツリーで分析された特徴へのすべてのパスを見つける必要があり、選択したパスの各特徴のサポート値を計算できるのはその後だということです。これら2つのサブタスクを1つのメソッド内で実行することにしました。
FPツリーを構築するのにCMyTreeNodeクラスのインスタンスのみを使用する計画です。したがって、深さ優先検索を実行するには、再帰的なメソッド呼び出しを使用します。
次に、クラスのMiningメソッドでのこれらのタスクの実装を見てみましょう。メソッドのパラメータでは、要素のサポート値を書き込むためのベクトル、パスを書き込むための行列、分析される特徴の識別子、および最小信頼レベルへのポインタを渡します。このメソッドは、操作のブール結果を返します。
メソッド本体では、まず分析されたノードが目的の特徴であるかどうかを確認します。これには、ノードのIDとパラメータで受け取った目的のノードのIDを比較します。それらが等しい場合は、現在の分岐のノードの信頼レベルを確認します。レベルは、前述のGetConfidenceメソッドを使用して決定されます。信頼レベルは、許容される最小値より小さくてはなりません。それ以外の場合は、trueの結果でメソッドを終了します。
bool CMyTreeNode::Mining(vector &supports, matrix &paths, const ulong ID, double min_conf) { if(ID == m_iIndex) if(GetConfidence() < min_conf) return true;
次のブロックは、木の深さに向かってさらに検索を実装します。ここでは、まず現在のノードのサポート値をローカル変数に保存します。次に、ループを実行して、現在のノードから木の深さまでのすべての枝を反復処理します。このメソッドは、すべての枝に対して再帰的に呼び出されます。
再帰的な方法では、対応するノードが見つかるまでのみ、目的のIDを渡すことに注意してください。その後、必要な識別子の代わりに、ULONG_MAX定数を木の深さに渡します。これは、FPツリー構築の仕様により、目的のアイテムへのパスを見つける前に、パターンの信頼度が100%未満になる可能性が高いためです。パスに沿ってさらに進むと、目的の特徴の確率は100%になります。さもなければ、目的のノードをバイパスして、別のパスを構築していたでしょう。
もちろん、カスタムアルゴリズムを使用する場合、このような状況は除外されます。すべての特徴のルールを決定するとき、FPツリーでそれらのいずれかの作業を開始するまでに、それは後続のノードのない葉になります。これは、サポートが低いすべての特徴が処理され、ツリーから削除されるためです。
したがって、アルゴリズムから逸脱した場合は、変更がプロセス全体に与える影響を評価し、アルゴリズムを適切に調整する必要があります。この場合、目的の特徴を含むすべてのパスをリストに追加する必要があります。これは、目的の特徴から木の根までのパスであり、目的の特徴を通過する後続ノードから木の根までのすべてのパスです。この目的のために、ノードと根の間で目的の特徴が見つかったことをさらにノードに通知する必要があります。このようなフラグは、目的の特徴のIDがULONG_MAX定数に変更されたときに発生します。
再帰メソッドの肯定的な結果の後、次のノードについて、現在のノードのサポートを使用してループの前に作成されたローカル変数からサポート値を減算します。次のノードIDが目的のノードIDと等しい場合、処理されたノードを削除します。
double support = m_dSupport; for(int i = 0; i < m_data_total; i++) { CMyTreeNode *temp = m_data[i]; if(!temp) { if(Delete(i)) i--; continue; } if(!temp.Mining(supports, paths, (ID == m_iIndex ? ULONG_MAX : ID), min_conf)) return false; support -= temp.GetSupport(); if(temp.ID() == ID) if(Delete(i)) i--; }
前のブロックでは、後続のノードに対して同じメソッドを再帰的に呼び出しただけで、見つかったパスを保存していないことがわかります。保存は次のメソッドブロックで実行されます。このブロックは、目的の属性を持つノードと後続のノードに対して実行されます。このために、現在のノードのIDとパラメータで受け取ったIDを確認する必要があります。さらに、再帰メソッドの実行後の現在のノードのサポートは、「0」より大きくなければなりません。また、現在のノードを根にすることはできません。これは、少なくとも1つの先行ノードが必要であることを意味します。ルールの前件として何かを使用する必要があるためです。
コントロールが正常に渡された場合は、パス行列のサイズを1行増やし、追加された行にゼロの値を入力します。
次に、現在のノードから木の根への伝播ループを実装します。現在およびすべての先行ノードには、パスライン内の現在のノードの残りのサポートが割り当てられます。また、対応するアイテムの累積サポートベクターに同じ値を追加します。
親の反復が完了したら、メソッドをtrueで終了します。
if(ID == m_iIndex || ID == ULONG_MAX) if(support > 0 && !!m_cParent) { CMyTreeNode *parent = m_cParent; ulong row = paths.Rows(); if(!paths.Resize(row + 1, paths.Cols())) return false; if(!paths.Row(vector::Zeros(paths.Cols()), row)) return false; supports[m_iIndex] += support; while(!!parent) { if(parent.ID() != ULONG_MAX) { supports[parent.ID()] += support; paths[row, parent.ID()] = support; } parent = parent.Parent(); } } //--- return true; }
このメソッドがどのように機能するかを小さな例を使用して説明しましょう。その構造は、上記のPF-Growthアルゴリズムの範囲をわずかに超えているためです。ソースデータベースに次のトランザクションがあるとします。「AB」が2回繰り返され、「ABC」が1つあり、「ABCD」が3回繰り返され、「ABCDE」が1つあります。その結果、パス「A7-B7-C5-D4-E1」がFPツリーに形成されます。「C」を分析するとき、この項目を含むすべてのパスを木から復元する必要があります。
まず、根要素「A」でメソッドを呼び出し、「C」を見つけるように指示します。ここで、ノード「B」のメソッドを再帰的に呼び出し、葉「E」まで続けます。葉「E」には後続ノードがないため、メソッドのブロック2から処理を開始し、パスを記述します。ここでは、最初に「ABCDE」パスを保存し、すべてのノードにサポート1を書き込みます。これは、ソースデータベースにそのようなパスが1つあったことを意味します。次に、メソッドを終了し、制御をより高いレベルに渡します。
ノード「D」レベルで、パス「ABCD」を保存します。ノード「D」のサポートから葉「E」のサポートを引きます(4-1=3)。結果の値をこのパスのすべての項目のサポートとして登録します。ご覧のとおり、これは訓練標本に3つの同一のトランザクションがあった初期データに対応しています。トランザクションを3回繰り返す代わりに、項目のサポート値を使用します。
同様に、サポートが1に等しいパス「ABC」を保存します。パス「AB」は解析された特徴を含まないため保存されません。
以下に添付されているファイルMyTreeNode.mqhで、すべてのクラスメソッドのコード全体を見てください。
2.2.アソシエーションルールマイニングクラスの実装
FPGrowthアソシエーションルールマイニングアルゴリズムの構築を続けましょう。主な機能は、別のクラスCAssocRulesで説明されます。このクラスの構造を以下に示します。ご覧のとおり、ほとんどのメソッドは「ボンネットの下」に隠されています。まず必要なことから始めていきます。
class CAssocRules : public CObject { protected: CMyTreeNode m_cRoot; CMyTreeNode m_cBuyRules; CMyTreeNode m_cSellRules; vector m_vMin; vector m_vStep; int m_iSections; matrix m_mPositions; matrix m_BuyPositions; matrix m_SellPositions; //--- bool NewPath(CMyTreeNode *root, matrix &path); CMyTreeNode *CheckPath(CMyTreeNode *root, vector &path); //--- bool PrepareData(matrix &data, matrix &bin_data, vector &buy, vector &sell, const int sections = 10, const double min_sup = 0.03); matrix CreatePath(vector &bin_data, matrix &positions); matrix CreatePositions(vector &support, const double min_sup = 0.03); bool GrowsTree(CMyTreeNode *root, matrix &bin_data, matrix &positions); double Probability(CMyTreeNode *root, vector &data, matrix &positions); public: CAssocRules(); ~CAssocRules(); //--- bool CreateRules(matrix &data, vector &buy, vector &sell, int sections = 10, double min_freq = 0.03, double min_prob = 0.3); bool Probability(vector &data, double &buy, double &sell); //--- methods for working with files virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual bool Save(const string file_name); virtual bool Load(const string file_name); };
クラス変数の中には、上記のツリーノードのインスタンスが3つあります。
- m_cRoot - FPツリーを書き留めるために使用
- m_cBuyRules - 買いのルールを書き留めるために使用
- m_cSellRules - 売りのルールを書き留めるために使用
行列m_mPositions、m_BuyPositions、m_SellPositionsには、降順で並び替えられた特徴とそのサポート値が含まれます。
以前のすべてのモデルをテストする際に、パターンの3番目のろうそくが形成される前にフラクタルを決定する可能性を確認しました。したがって、フラクタルの売買を定義する2つのルールマイニングツリーを決定します。問題に応じて、より多くのターゲット特徴のルールを定義する必要がある場合は、より多くのプライベートルールツリーを作成する必要があります。
たとえば、ターゲットの売買レベルが複数あることがあります。残念ながら、アソシエーションルールマイニングアルゴリズムは、バイナリテーブルでのみ動作します。したがって、ターゲットレベルごとに個別の項目を作成し、その関連付けルールを見つける必要があります。動的配列を使用して、多数のprivate変数を除外できます。
このクラスでは、ツリー構築オブジェクトへの動的ポインタを使用しなかったため、メソッドコンストラクタとデストラクタは空のままです。
前述のように、アソシエーションルールマイニングアルゴリズムはバイナリ行列でのみ機能します。ただし、市場状況に関するデータをそのように分類することは困難なため、使用前に前処理する必要があります。
クラスのさらなる使用を簡素化するために、アルゴリズムではユーザーからのデータの前処理を必要としません。代わりに、PrepareDataメソッドに実装されています。パラメータでは、メソッドは2つの行列、2つのベクトル、および2つの定数へのポインタを受け取ります。1つの行列には元のデータが含まれ、2番目の行列は処理されたバイナリデータを書き込むために使用されます。ベクトルにはターゲット値が含まれます。私たちの場合、それらはバイナリデータで表されるため、前処理は必要ありません。ソースデータには前処理が必要です。
ソースデータのスカラー単位をバイナリ単位に変換するには、各特徴の値の範囲を使用し、それをいくつかの間隔に分割します。間隔の数は、sectionsパラメータによって設定されます。各特徴の最小値とステップサイズは、対応するベクトルm_vMinとm_vStepに保存されます。ベクトルは、実際の使用中にソースデータをバイナリに変換するために使用されます。
ここでは、必要なサイズを設定し、ゼロで埋めることにより、バイナリ行列を準備します。後で行列の最後の列として追加されるターゲット特徴の識別子を指定することもできます。
bool CAssocRules::PrepareData(matrix &data, matrix &bin_data, vector &buy, vector &sell, const int sections = 10, const double min_sup = 0.03) { //--- m_iSections = sections; m_vMin = data.Min(0); vector max = data.Max(0); vector delt = max - m_vMin; m_vStep = delt / sections + 1e-8; m_cBuyRules.ID(data.Cols() * m_iSections); m_cSellRules.ID(m_cBuyRules.ID() + 1); bin_data = matrix::Zeros(data.Rows(), m_cSellRules.ID() + 1);
次に、入力データ行列のすべての行を通るループを実装します。ループ本体で、各行から最小値のベクトルを減算し、結果をステップサイズで割ります。ベクトル要素の下限値と上限値を制限して、範囲外のデータを除外します。これらの操作の結果、ベクトルの各項目の数値の整数部分は、ソースデータの対応する項目を含める必要がある値の範囲を示します。バイナリ行列の各範囲は、個別の特徴になります。
ネストされたループを実行して、バイナリ行列の関連する行を埋めてみましょう。特徴がアクティブな場合は、その値を「1」に変更します。非アクティブな特徴は値がゼロのままになります。
for(ulong r = 0; r < data.Rows(); r++) { vector pos = (data.Row(r) - m_vMin) / m_vStep; if(!pos.Clip(0, m_iSections - 1)) return false; for(ulong c = 0; c < pos.Size(); c++) bin_data[r, c * sections + (int)pos[c]] = 1; } if(!bin_data.Col(buy, m_cBuyRules.ID()) || !bin_data.Col(sell, m_cSellRules.ID())) return false;
バイナリ行列を埋めた後、すぐに各特徴のサポートを計算し、CreatePositionsメソッドでそれらを降順に並べ替えることができます。並べ替え後、メソッドをtrueで終了します。
vector supp = bin_data.Sum(0) / bin_data.Rows(); m_mPositions = CreatePositions(supp, min_sup); //--- return true; }
CreatePositions特徴の並べ替え方法について言及したので、そのアルゴリズムを考えてみましょう。このメソッドは、パラメータでサポートベクターと最小サポートレベルを受け取ります。
メソッド本体には、準備作業が少し含まれます。これは、受け取ったサポート値がベクトルで表され、項目値にサポート値が含まれているためです。項目のインデックスは特徴を示しています。ベクター項目を単純に並べ替えるだけでは、ソースデータ特徴とのつながりが失われます。したがって、「特徴ID-サポート」のペアを作成する必要があります。ペアデータは行列に保存されます。
これには、最初に2列で行数が元の標本の特徴の数に等しい恒等行列を作成します。次に、項目の累積合計を列ごとに計算し、結果の行列の値を「1」減らします。したがって、行インデックスに対応する「0」から昇順で値が列に含まれる行列が得られます。結果のサポートベクターで1つの列を置き換えるだけで、行列を取得します。各行には、特徴識別子とそれに対応するサポート値が含まれます。
matrix CAssocRules::CreatePositions(vector &support, const double min_sup = 0.03) { matrix result = matrix::Ones(support.Size(), 2); result = result.CumSum(0) - 1; if(!result.Col(support, 1)) return matrix::Zeros(0, 0);
行列の行をサポートの降順で並べ替えます。これには、バブルソートアルゴリズムを使用してループを実装します。
bool change = false; do { change = false; ulong total = result.Rows() - 1; for(ulong i = 0; i < total; i++) { if(result[i, 1] >= result[i + 1, 1]) continue; if(result.SwapRows(i, i + 1)) change = true; } } while(change);
ループを終了すると、並べ替えられた特徴を含む行列が得られます。この行列から最小サポート要件を満たさない特徴のみを削除する必要があります。これには、最小サポートレベルより下の最初の特徴を見つけ、このレベルより下のすべての特徴を「切り捨て」ます。
int i = 0; while(result[i, 1] >= min_sup) i++; if(!result.Resize(i, 2)) return matrix::Zeros(0, 0); //--- return result; }
行列のサイズ変更に成功したら、メソッドを終了し、結果の行列を返します。
publicメソッドに移る前に、いくつかの繰り返し機能が実行されるいくつかのメソッドを見てみましょう。バイナリデータからFPツリーに転送するためのパスを作成する必要があります。この機能はCreatePathメソッドで実行されます。メソッドは、バイナリデータのベクトルと並べ替えられた特徴の行列へのポインタを受け取ります。次に、FPツリーに追加される「特徴ID-サポート」のペアを含むパス行列を返します。
データを準備するときに取得した並び替えられた特徴行列と、FPツリーへのパスを追加するための行列の違いに注意してください。両方の行列には「特徴ID-サポート」のペアが含まれます。ただし、最初の行列には、ソースデータで使用できるすべての特徴と、訓練標本でのそれらのサポートが含まれています。パス行列には、分析されたトランザクションに存在する特徴と、バイナリ行列で示されるこのトランザクションからのサポートのみが含まれます。
バイナリ行列を扱っているため、各トランザクションの特徴サポート値は同じでなければなりません。後で、同じ方法を使用してプライベートルールツリーを構築します。前にCMyTreeNode::Miningメソッドを説明する際に例を考えました。その例では、1つのパスを繰り返す代わりにサポートレベルを数回使用しました。したがって、操作を統一するために、2つのサブプロセスで1つのメソッドを使用します。この場合、サポートレベルを導入すると非常に役立ちます。
メソッドの開始時に、ソースデータベクトルのサイズと分析された特徴の数をローカル変数に保存します。これは、ソースデータベクトルのサイズより、サポートが最小値を下回るランダムな特徴の数だけ少なくなります。
また、結果を書くための行列を用意します。分析特徴行列より大きくすることはできません。また、パスのサイズを示す変数を導入します。この段階では「0」です。
次に、解析されたすべての特徴をサポートの降順でループします。ループ本体では、次にチェックされた特徴の識別子を抽出します。バイナリソースデータベクトルでその値を確認します。特徴がアクティブでない場合は、次の特徴に進みます。
特徴がアクティブな場合は、特徴IDとそのサポートをバイナリソースデータベクトルからパス行列に追加します。その後、パスサイズ変数を増やします。
ループを終了した後、パス行列のサイズを入力された要素の数に減らして、メソッドを終了します。
matrix CAssocRules::CreatePath(vector &bin_data, matrix &positions) { ulong size = bin_data.Size(); //--- ulong total = positions.Rows(); int vect_pos = 0; matrix path = matrix::Zeros(2, total); for(ulong c = 0; c < total; c++) { ulong pos = (ulong)positions[c, 0]; if(pos >= size) continue; if(bin_data[pos] == 0) continue; path[0, vect_pos] = (double)pos; path[1, vect_pos] = bin_data[pos]; vect_pos++; } if(!path.Resize(2, vect_pos)) return matrix::Zeros(0, 0); //--- return path; }
必要なもう1つのメソッドは、FPツリーにパスを追加するNewPathです。このメソッドは、木の根ノードへのポインタと、以前に作成されたパス行列を受け取って、操作のブール結果を返します。メソッド本体では、まず結果のパスのサイズを確認します。0より大きい必要があります。次に、根ノードのサポートを増やし、パスのすべての項目をループします。
ループ本体で、必要なIDを持つ次のノードの存在を確認し、必要に応じて新しいノードを作成します。次に、ノードサポートサイズを増やします。そして、パス内の次のノードの検索に進みます。
パスのすべての項目を反復処理したら、メソッドを終了します。
bool CAssocRules::NewPath(CMyTreeNode *root, matrix &path) { ulong total = path.Cols(); if(total <= 0) return false; CMyTreeNode *parent = root; root.IncreaseSupport(path[1, 0]); for(ulong i = 0; i < total; i++) { CMyTreeNode *temp = parent.GetNext((ulong)path[0, i]); if(!temp) { temp = parent.AddNode((int)path[0, i], 0); if(!temp) return false; } temp.IncreaseSupport(path[1, i]); parent = temp; } //--- return true; }
最後に、FPツリーを成長させるGrowsTreeメソッドに進みます。パラメータとして、木の根ノードへのポインタ、ソースデータのバイナリ行列、および並べ替えられた分析された特徴の行列を受け取ります。メソッド内で、ソースデータのすべての行に対してループを実行します。
ループ本体で、訓練標本から次のトランザクションをキャプチャし、CreatePathメソッドを使用して木に追加するパスを作成します。受け取った部分が0より大きいことを確認してください。次に、NewPathメソッドを呼び出して、作成したパスをFPツリーに追加します。操作実行結果の確認も忘れてはいけません。
ソースデータからのすべてのトランザクションを正常に反復した後、メソッドをtrueで終了します。
bool CAssocRules::GrowsTree(CMyTreeNode * root, matrix & bin_data, matrix &positions) { ulong rows = bin_data.Rows(); for(ulong r = 0; r < rows; r++) { matrix path = CreatePath(bin_data.Row(r), positions); ulong size = path.Cols(); if(size <= 0) continue; if(!NewPath(root, path)) return false; } //--- return true; }
ここで、上記のすべてのメソッドをpublicメソッドCreateRulesに結合しましょう。メソッドパラメータでは、スカラーソースデータ(バイナリではない)の行列、ターゲット値のバイナリベクトル、スカラー値をバイナリに変換する間隔の数、最小サポート、および最小信頼度を渡します。
メソッド本体では、まず受信したソースデータを確認します。主に、取得した行列ベクトルの次元の対応と、0より大きくなければならない間隔の数を確認します。
確認ブロックの後、スカラーソースデータをバイナリ形式に変換します。これは、前述のPrepareDataメソッドを使用しておこなわれます。
bool CAssocRules::CreateRules(matrix &data, vector &buy, vector &sell, int sections = 10, double min_sup = 0.03, double min_conf = 0.3) { if(data.Rows() <= 0 || data.Cols() <= 0 || sections <= 0 || data.Rows() != buy.Size() || data.Rows() != sell.Size()) return false; //--- matrix binary_data; if(!PrepareData(data, binary_data, buy, sell, sections)) return false;
さらに、相対単位の平面に移動するために、バイナリ行列値を訓練標本のトランザクション数で割り、GrowsTreeメソッドを使用してFPツリーを構築します。
double k = 1.0 / (double)(binary_data.Rows()); if(!GrowsTree(GetPointer(m_cRoot), binary_data * k, m_mPositions)) return false;
FPツリーを構築したら、ルールの作成に進むことができます。まず、サポートを書き込むベクトルとパスを書き込む行列を用意します。次に、FPツリーのMiningメソッドを呼び出して、買いの特徴を使用してすべてのパスを見つけます。
vector supports = vector::Zeros(binary_data.Cols()); binary_data = matrix::Zeros(0, binary_data.Cols()); if(!m_cRoot.Mining(supports, binary_data, m_cBuyRules.ID(),min_conf)) return false;
すべてのパスを正常に抽出した後、買いの特徴のサポートをリセットして、すべてのパスの処理から削除します。また、プライベートサポートを降順に並べ替えます。CreatePositionsメソッドを呼び出し、結果をm_BuyPositions行列に書き込みます。特徴を並べ替えた後もルールを構築する能力がある場合(並べ替えられた行列には、ルールの前件として使用する特徴がまだ残っている場合)、ツリー成長メソッドを呼び出し、以前に選択したパスをそれに入力します。
これらの操作の結果、m_cBuyRulesノードを根とするプライベートルールツリーが得られます。
supports[m_cBuyRules.ID()] = 0; m_BuyPositions = CreatePositions(supports, min_sup); if(m_BuyPositions.Rows() > 0) if(!GrowsTree(GetPointer(m_cBuyRules), binary_data, m_BuyPositions)) return false;
同様に、売りの特徴のルールツリーを作成します。
supports = vector::Zeros(binary_data.Cols()); binary_data = matrix::Zeros(0, binary_data.Cols()); if(!m_cRoot.Mining(supports, binary_data, m_cSellRules.ID(),min_conf)) return false; supports[m_cSellRules.ID()] = 0; m_SellPositions = CreatePositions(supports, min_sup); if(m_SellPositions.Rows() > 0) if(!GrowsTree(GetPointer(m_cSellRules), binary_data, m_SellPositions)) return false; //--- m_cRoot.Clear(); //--- return true; }
すべてのルールを選択したら、ソースFPツリーオブジェクトをクリアして、コンピューターのリソースを解放します。次に、メソッドをtrueで終了します。
「Probability」メソッドは、実用化のために作成されました。メソッドはパラメータとして、ソースデータのスカラーベクトルとdouble型の2つの変数へのポインタを受け取ります。これらは、特定のパターンの信頼度を格納するために使用されます。メソッドアルゴリズムは、上で説明したすべてのメソッドを使用します。添付ファイルでご覧ください。
すべてのクラスとそのメソッドの完全なコードは、添付ファイルにあります。
3.テスト
実際のデータを使用してクラスをテストするためのエキスパートアドバイザー(EA)(assocrules.mq5)を作成しました。EAは、以前のテストで使用されたすべてのパラメータに完全に準拠してテストされました。この方法ですべてのフラクタルをエラーなしで決定したとは言えませんが、作成されたEAは、以下のスクリーンショットに示されている興味深い結果を示しました。
結論
この記事では、教師なし学習法によって解決される別のタイプの問題を検討しました。アソシエーションルールマイニングです。FPGrowthアルゴリズムを使用してアソシエーションルールを構築するためのクラスを作成しました。EAを使用してテストすると、興味深い結果が示されました。したがって、そのようなアルゴリズムを使用して、取引における実際の問題を解決できると結論付けることができます。
参考文献リスト
- ニューラルネットワークが簡単に(第14部):データクラスタリング
- ニューラルネットワークが簡単に(第15部):MQL5によるデータクラスタリング
- ニューラルネットワークが簡単に(第16部):クラスタリングの実用化
- ニューラルネットワークが簡単に(第17部):次元削減
- ニューラルネットワークが簡単に(第18部):アソシエーションルール
記事で使用されているプログラム
# | 名前 | タイプ | 詳細 |
---|---|---|---|
1 | assocrules.mq5 | EA | モデルの訓練とテストのためのEA |
2 | AssocRules.mqh | クラスライブラリ | FP-Growthアルゴリズムを編成するためのクラスライブラリ |
3 | MyTreeNode.mqh | クラスライブラリ | ツリーノード編成クラスライブラリ |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/11141
![DoEasy-コントロール(第9部):WinFormsオブジェクトメソッド、RadioButtonおよびButtonコントロールの再配置](https://c.mql5.com/2/47/MQL5-avatar-doeasy-library-2__3.png)
![ウィリアムズPRによる取引システムの設計方法を学ぶ](https://c.mql5.com/2/47/why-and-how__4.png)
![一目均衡表による取引システムの設計方法を学ぶ](https://c.mql5.com/2/47/why-and-how__3.png)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索