forked from mnbvc188199/Warrior_EA
652 lines
47 KiB
MQL5
652 lines
47 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| NetGenetic.mqh |
| |
//| Copyright DNG® |
| |
//| https://www.mql5.com/en/users/dng |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright DNG®"
| |
#property link "https://www.mql5.com/en/users/dng"
| |
#property version "1.00"
| |
#include "..\NeuroNet_DNG\NeuroNet.mqh"
| |
//---
| |
#define MaxMutation 0.5f
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class CNetGenetic : public CNet
| |
{
| |
protected:
| |
uint i_PopulationSize;
| |
vectorf v_Probability;
| |
vectorf v_Rewards;
| |
matrixf m_Weights;
| |
matrixf m_WeightsConv;
| |
| |
//---
| |
virtual bool CreatePopulation(void);
| |
virtual int GetAction(CBufferFloat * probability);
| |
virtual bool GetWeights(uint layer);
| |
float NextGenerationWeight(matrixf &array, uint shift, vectorf &probability);
| |
float GenerateWeight(uint total);
| |
| |
public:
| |
CNetGenetic();
| |
~CNetGenetic();
| |
//---
| |
virtual bool Create(CArrayObj *Description, uint population_size);
| |
virtual bool SetPopulationSize(uint size);
| |
virtual bool feedForward(CArrayFloat *inputVals, int window = 1, bool tem = true);
| |
virtual bool Rewards(CArrayFloat *rewards);
| |
virtual bool NextGeneration(float quantile, float mutation, float &average, float &mamximum);
| |
virtual bool Load(string file_name, uint population_size, bool common = true);
| |
virtual bool SaveModel(string file_name, int model, bool common = true);
| |
//---
| |
virtual bool CopyModel(CArrayLayer *source, uint model);
| |
virtual bool Detach(void);
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
CNetGenetic::CNetGenetic() : i_PopulationSize(100)
| |
{
| |
SetPopulationSize(i_PopulationSize);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
CNetGenetic::~CNetGenetic()
| |
{
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::Create(CArrayObj *Description, uint population_size)
| |
{
| |
if(CheckPointer(Description) == POINTER_INVALID)
| |
return false;
| |
//---
| |
if(!SetPopulationSize(population_size))
| |
return false;
| |
CNet::Create(Description);
| |
return CreatePopulation();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::SetPopulationSize(uint size)
| |
{
| |
i_PopulationSize = size;
| |
v_Probability = vectorf::Zeros(i_PopulationSize);
| |
v_Rewards = vectorf::Zeros(i_PopulationSize);
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::Load(string file_name, uint population_size, bool common = true)
| |
{
| |
float temp;
| |
datetime dt;
| |
if(!CNet::Load(file_name, temp, temp, temp, dt, common))
| |
return false;
| |
//---
| |
if(!SetPopulationSize(population_size))
| |
return false;
| |
return CreatePopulation();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::CreatePopulation(void)
| |
{
| |
if(!layers || layers.Total() < 2)
| |
return false;
| |
//---
| |
CLayer *layer = layers.At(0);
| |
if(!layer || !layer.At(0))
| |
return false;
| |
//---
| |
CNeuronBaseOCL *neuron_ocl = layer.At(0);
| |
int prev_count = neuron_ocl.Neurons();
| |
for(int i = 1; i < layers.Total(); i++)
| |
{
| |
layer = layers.At(i);
| |
if(!layer || !layer.At(0))
| |
return false;
| |
//---
| |
neuron_ocl = layer.At(0);
| |
CLayerDescription *desc = neuron_ocl.GetLayerInfo();
| |
int outputs = neuron_ocl.getConnections();
| |
//---
| |
for(uint n = layer.Total(); n < i_PopulationSize; n++)
| |
{
| |
CNeuronConvOCL *neuron_conv_ocl = NULL;
| |
CNeuronProofOCL *neuron_proof_ocl = NULL;
| |
CNeuronAttentionOCL *neuron_attention_ocl = NULL;
| |
CNeuronMLMHAttentionOCL *neuron_mlattention_ocl = NULL;
| |
CNeuronDropoutOCL *dropout = NULL;
| |
CNeuronBatchNormOCL *batch = NULL;
| |
CVAE *vae = NULL;
| |
CNeuronLSTMOCL *lstm = NULL;
| |
switch(layer.At(0).Type())
| |
{
| |
case defNeuron:
| |
case defNeuronBaseOCL:
| |
neuron_ocl = new CNeuronBaseOCL();
| |
if(CheckPointer(neuron_ocl) == POINTER_INVALID)
| |
return false;
| |
if(!neuron_ocl.Init(outputs, n, opencl, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_ocl;
| |
return false;
| |
}
| |
neuron_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_ocl))
| |
{
| |
delete neuron_ocl;
| |
return false;
| |
}
| |
neuron_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronConvOCL:
| |
neuron_conv_ocl = new CNeuronConvOCL();
| |
if(CheckPointer(neuron_conv_ocl) == POINTER_INVALID)
| |
return false;
| |
if(!neuron_conv_ocl.Init(outputs, n, opencl, desc.window, desc.step, desc.window_out, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_conv_ocl;
| |
return false;
| |
}
| |
neuron_conv_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_conv_ocl))
| |
{
| |
delete neuron_conv_ocl;
| |
return false;
| |
}
| |
neuron_conv_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronProofOCL:
| |
neuron_proof_ocl = new CNeuronProofOCL();
| |
if(!neuron_proof_ocl)
| |
return false;
| |
if(!neuron_proof_ocl.Init(outputs, n, opencl, desc.window, desc.step, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_proof_ocl;
| |
return false;
| |
}
| |
neuron_proof_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_proof_ocl))
| |
{
| |
delete neuron_proof_ocl;
| |
return false;
| |
}
| |
neuron_proof_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronAttentionOCL:
| |
neuron_attention_ocl = new CNeuronAttentionOCL();
| |
if(CheckPointer(neuron_attention_ocl) == POINTER_INVALID)
| |
return false;
| |
if(!neuron_attention_ocl.Init(outputs, n, opencl, desc.window, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_attention_ocl;
| |
return false;
| |
}
| |
neuron_attention_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_attention_ocl))
| |
{
| |
delete neuron_attention_ocl;
| |
return false;
| |
}
| |
neuron_attention_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronMHAttentionOCL:
| |
neuron_attention_ocl = new CNeuronMHAttentionOCL();
| |
if(CheckPointer(neuron_attention_ocl) == POINTER_INVALID)
| |
return false;
| |
if(!neuron_attention_ocl.Init(outputs, n, opencl, desc.window, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_attention_ocl;
| |
return false;
| |
}
| |
neuron_attention_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_attention_ocl))
| |
{
| |
delete neuron_attention_ocl;
| |
return false;
| |
}
| |
neuron_attention_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronMLMHAttentionOCL:
| |
neuron_mlattention_ocl = new CNeuronMLMHAttentionOCL();
| |
if(CheckPointer(neuron_mlattention_ocl) == POINTER_INVALID)
| |
return false;
| |
if(!neuron_mlattention_ocl.Init(outputs, n, opencl, desc.window, desc.window_out, desc.step, desc.count, desc.layers, desc.optimization, desc.batch))
| |
{
| |
delete neuron_mlattention_ocl;
| |
return false;
| |
}
| |
neuron_mlattention_ocl.SetActivationFunction(desc.activation);
| |
if(!layer.Add(neuron_mlattention_ocl))
| |
{
| |
delete neuron_mlattention_ocl;
| |
return false;
| |
}
| |
neuron_mlattention_ocl = NULL;
| |
break;
| |
//---
| |
case defNeuronDropoutOCL:
| |
dropout = new CNeuronDropoutOCL();
| |
if(CheckPointer(dropout) == POINTER_INVALID)
| |
return false;
| |
if(!dropout.Init(outputs, n, opencl, desc.count, desc.probability, desc.optimization, desc.batch))
| |
{
| |
delete dropout;
| |
return false;
| |
}
| |
if(!layer.Add(dropout))
| |
{
| |
delete dropout;
| |
return false;
| |
}
| |
dropout = NULL;
| |
break;
| |
//---
| |
case defNeuronBatchNormOCL:
| |
batch = new CNeuronBatchNormOCL();
| |
if(CheckPointer(batch) == POINTER_INVALID)
| |
return false;
| |
if(!batch.Init(outputs, n, opencl, desc.count, desc.batch, desc.optimization))
| |
{
| |
delete batch;
| |
return false;
| |
}
| |
batch.SetActivationFunction(desc.activation);
| |
if(!layer.Add(batch))
| |
{
| |
delete batch;
| |
return false;
| |
}
| |
batch = NULL;
| |
break;
| |
//---
| |
case defNeuronVAEOCL:
| |
vae = new CVAE();
| |
if(!vae)
| |
return false;
| |
if(!vae.Init(outputs, n, opencl, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete vae;
| |
return false;
| |
}
| |
if(!layer.Add(vae))
| |
{
| |
delete vae;
| |
return false;
| |
}
| |
vae = NULL;
| |
break;
| |
case defNeuronLSTMOCL:
| |
lstm = new CNeuronLSTMOCL();
| |
if(!lstm)
| |
return false;
| |
if(!lstm.Init(outputs, n, opencl, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete lstm;
| |
return false;
| |
}
| |
if(!lstm.SetInputs(prev_count))
| |
{
| |
delete lstm;
| |
return false;
| |
}
| |
if(!layer.Add(lstm))
| |
{
| |
delete lstm;
| |
return false;
| |
}
| |
lstm = NULL;
| |
break;
| |
//---
| |
case defNeuronSoftMaxOCL:
| |
neuron_ocl = new CNeuronSoftMaxOCL();
| |
if(!neuron_ocl)
| |
return false;
| |
if(!neuron_ocl.Init(outputs, n, opencl, desc.count, desc.optimization, desc.batch))
| |
{
| |
delete neuron_ocl;
| |
return false;
| |
}
| |
if(!layer.Add(neuron_ocl))
| |
{
| |
delete neuron_ocl;
| |
return false;
| |
}
| |
neuron_ocl = NULL;
| |
break;
| |
//---
| |
default:
| |
return false;
| |
break;
| |
}
| |
}
| |
if(layer.Total() > (int)i_PopulationSize)
| |
layer.Resize(i_PopulationSize);
| |
delete desc;
| |
}
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::feedForward(CArrayFloat *inputVals, int window = 1, bool tem = true)
| |
{
| |
if(CheckPointer(layers) == POINTER_INVALID || CheckPointer(inputVals) == POINTER_INVALID || layers.Total() <= 1)
| |
return false;
| |
//---
| |
CLayer *previous = NULL;
| |
CLayer *current = layers.At(0);
| |
int total = MathMin(current.Total(), inputVals.Total());
| |
CNeuronBase *neuron = NULL;
| |
if(CheckPointer(opencl) == POINTER_INVALID)
| |
return false;
| |
CNeuronBaseOCL *neuron_ocl = current.At(0);
| |
CBufferFloat *inputs = neuron_ocl.getOutput();
| |
int total_data = inputVals.Total();
| |
if(!inputs.Resize(total_data))
| |
return false;
| |
for(int d = 0; d < total_data; d++)
| |
{
| |
int pos = d;
| |
int dim = 0;
| |
if(window > 1)
| |
{
| |
dim = d % window;
| |
pos = (d - dim) / window;
| |
}
| |
float value = pos / pow(10000, (2 * dim + 1) / (float)(window + 1));
| |
value = (float)(tem ? (dim % 2 == 0 ? sin(value) : cos(value)) : 0);
| |
value += inputVals.At(d);
| |
if(!inputs.Update(d, value))
| |
return false;
| |
}
| |
if(!inputs.BufferWrite())
| |
return false;
| |
//---
| |
CNeuronBaseOCL *current_ocl;
| |
for(int l = 1; l < layers.Total(); l++)
| |
{
| |
previous = current;
| |
current = layers.At(l);
| |
if(CheckPointer(current) == POINTER_INVALID)
| |
return false;
| |
//---
| |
for(uint n = 0; n < i_PopulationSize; n++)
| |
{
| |
current_ocl = current.At(n);
| |
if(!current_ocl.FeedForward(previous.At(l == 1 ? 0 : n)))
| |
return false;
| |
continue;
| |
}
| |
}
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::Rewards(CArrayFloat *rewards)
| |
{
| |
if(!rewards || !layers || layers.Total() < 2)
| |
return false;
| |
//---
| |
CLayer *output = layers.At(layers.Total() - 1);
| |
if(!output)
| |
return false;
| |
//---
| |
for(int i = 0; i < output.Total(); i++)
| |
{
| |
CNeuronBaseOCL *neuron = output.At(i);
| |
if(!neuron)
| |
continue;
| |
int action = GetAction(neuron.getOutput());
| |
v_Rewards[i] += rewards.At(action >= 0 ? action : rewards.Minimum(0, WHOLE_ARRAY));
| |
}
| |
//---
| |
v_Probability = v_Rewards - v_Rewards.Min();
| |
if(!v_Probability.Clip(0, v_Probability.Max()))
| |
return false;
| |
v_Probability = v_Probability / v_Probability.Sum();
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
int CNetGenetic::GetAction(CBufferFloat * probability)
| |
{
| |
vectorf prob;
| |
if(!probability.GetData(prob))
| |
return -1;
| |
prob = prob.CumSum();
| |
prob = prob / prob.Max();
| |
int err_code;
| |
float random = (float)Math::MathRandomNormal(0.5, 0.5, err_code);
| |
if(random >= 1)
| |
return (int)prob.Size() - 1;
| |
for(int i = 0; i < (int)prob.Size(); i++)
| |
if(random <= prob[i] && prob[i] > 0)
| |
return i;
| |
//---
| |
return -1;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::NextGeneration(float quantile, float mutation, float &average, float &maximum)
| |
{
| |
maximum = v_Rewards.Max();
| |
mutation = MathMin(mutation, MaxMutation);
| |
v_Probability = v_Rewards - v_Rewards.Quantile(quantile);
| |
if(!v_Probability.Clip(0, v_Probability.Max()))
| |
return false;
| |
v_Probability = v_Probability / v_Probability.Sum();
| |
average = v_Rewards.Average(v_Probability);
| |
//---
| |
if(!v_Probability.Resize(i_PopulationSize + 1))
| |
return false;
| |
v_Probability[i_PopulationSize] = mutation;
| |
v_Probability = (v_Probability / (1 + mutation)).CumSum();
| |
//---
| |
for(int l = 1; l < layers.Total(); l++)
| |
{
| |
if(!GetWeights(l))
| |
{
| |
PrintFormat("Error of load weights from layer %d", l);
| |
return false;
| |
}
| |
CLayer* layer = layers.At(l);
| |
if(!layer)
| |
return false;
| |
for(uint i = 0; i < i_PopulationSize; i++)
| |
{
| |
CNeuronBaseOCL* neuron = layer.At(i);
| |
CBufferFloat* weights = neuron.getWeights();
| |
if(!!weights)
| |
{
| |
for(int w = 0; w < weights.Total(); w++)
| |
if(!weights.Update(w, NextGenerationWeight(m_Weights, w, v_Probability)))
| |
{
| |
Print("Error updating the weights");
| |
return false;
| |
}
| |
weights.BufferWrite();
| |
}
| |
if(neuron.Type() != defNeuronConvOCL)
| |
continue;
| |
CNeuronConvOCL* temp = neuron;
| |
weights = temp.GetWeightsConv();
| |
for(int w = 0; w < weights.Total(); w++)
| |
if(!weights.Update(w, NextGenerationWeight(m_WeightsConv, w, v_Probability)))
| |
{
| |
Print("Error updating the weights");
| |
return false;
| |
}
| |
weights.BufferWrite();
| |
}
| |
}
| |
v_Rewards.Fill(0);
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
float CNetGenetic::NextGenerationWeight(matrixf &array, uint shift, vectorf &probability)
| |
{
| |
int err_code;
| |
float random = (float)Math::MathRandomNormal(0.5, 0.5, err_code);
| |
for(int i = 0; i < (int)probability.Size(); i++)
| |
if(probability[i] > 0 && random <= probability[i] && i < (int)array.Rows())
| |
{
| |
return array[i, shift];
| |
}
| |
//---
| |
return GenerateWeight((uint)array.Cols());
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::GetWeights(uint layer)
| |
{
| |
if(!layers)
| |
return false;
| |
CLayer *current_layer = layers.At(layer);
| |
if(!current_layer)
| |
return false;
| |
int total = current_layer.Total();
| |
m_Weights = matrixf::Zeros(total, 0);
| |
m_WeightsConv = matrixf::Zeros(total, 0);
| |
for(int m = 0; m < total; m++)
| |
{
| |
CNeuronBaseOCL *neuron = current_layer.At(m);
| |
if(!neuron)
| |
return false;
| |
vectorf buffer;
| |
if(!!neuron.getWeights())
| |
{
| |
neuron.getWeights().GetData(buffer);
| |
if(m_Weights.Cols() <= 0)
| |
if(!m_Weights.Resize(total, buffer.Size()))
| |
return false;
| |
if(!m_Weights.Row(buffer, m))
| |
continue;
| |
}
| |
//---
| |
if(neuron.Type() != defNeuronConvOCL)
| |
continue;
| |
CNeuronConvOCL *temp = neuron;
| |
if(temp.GetWeightsConv().GetData(buffer) <= 0)
| |
return false;
| |
if(m_WeightsConv.Cols() != buffer.Size())
| |
if(!m_WeightsConv.Resize(total, buffer.Size()))
| |
return false;
| |
if(!m_WeightsConv.Row(buffer, m))
| |
return false;
| |
}
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::SaveModel(string file_name, int model, bool common = true)
| |
{
| |
if(model < 0 || model >= (int)i_PopulationSize)
| |
model = (int)v_Probability.ArgMax();
| |
//---
| |
CNetGenetic *new_model = new CNetGenetic();
| |
if(!new_model)
| |
return false;
| |
if(!new_model.CopyModel(layers, model))
| |
{
| |
new_model.Detach();
| |
delete new_model;
| |
return false;
| |
}
| |
bool result = new_model.Save(file_name, 0, 0, 0, 0, common);
| |
new_model.Detach();
| |
delete new_model;
| |
//---
| |
return result;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::CopyModel(CArrayLayer *source, uint model)
| |
{
| |
if(!source)
| |
return false;
| |
if(!layers)
| |
{
| |
layers = new CArrayLayer();
| |
if(!layers)
| |
return false;
| |
}
| |
else
| |
layers.Clear();
| |
for(int l = 0; l < source.Total(); l++)
| |
{
| |
CLayer* source_layer = source.At(l);
| |
if(!source_layer)
| |
return false;
| |
CLayer* new_layer = new CLayer(source_layer.Outputs(), -1, opencl);
| |
if(!new_layer)
| |
return false;
| |
if(!new_layer.Add(source_layer.At(l == 0 ? 0 : model)))
| |
return false;
| |
if(!layers.Add(new_layer))
| |
return false;
| |
}
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CNetGenetic::Detach(void)
| |
{
| |
if(!layers || layers.Total() <= 0)
| |
return true;
| |
int total = layers.Total();
| |
for(int i = 0; i < total; i++)
| |
{
| |
CLayer *layer = layers.At(i);
| |
if(!layer)
| |
continue;
| |
for(int n = layer.Total() - 1; n >= 0; n--)
| |
CObject *temp = layer.Detach(n);
| |
}
| |
//---
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
float CNetGenetic::GenerateWeight(uint total)
| |
{
| |
xor128;
| |
float result = (float)rnd_w / UINT_MAX;
| |
if(result == 0)
| |
return GenerateWeight(total);
| |
float k = (float)(1 / sqrt(total + 1));
| |
result = (2 * result * k - k) * WeightsMultiplier;
| |
//---
| |
return result;
| |
}
| |
//+------------------------------------------------------------------+
|