Warrior_EA/AI/Impl/Layer.mqh

263 lines
9.6 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| Layer.mqh |
//| |
//| CLayer - neuron container: element construction from a topology |
//| descriptor or a .nnw stream. |
//| |
//| Included from AI\Network.mqh AFTER every class declaration - |
//| bodies only, no declarations. Relocation is behaviour-neutral by |
//| construction: nothing here is reachable until Network.mqh ends. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_AI_IMPL_LAYER_MQH
#define WARRIOR_AI_IMPL_LAYER_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CLayer::CreateElementScaled(int index, double weighScale)
{
if(index >= m_data_max)
return false;
//---
bool result = false;
CNeuronBase *temp = NULL;
CNeuronPool *temp_p = NULL;
CNeuronBaseOCL *temp_ocl = NULL;
if(iFileHandle <= 0)
{
temp = new CNeuron();
if(CheckPointer(temp) == POINTER_INVALID || !temp.Init(iOutputs, index, SGD, weighScale))
return false;
result = true;
}
else
{
int type = FileReadInteger(iFileHandle);
switch(type)
{
case defNeuron:
temp = new CNeuron();
if(CheckPointer(temp) == POINTER_INVALID)
{
result = false;
break;
}
result = temp.Init(iOutputs, index, ADAM);
break;
case defNeuronPool:
temp_p = new CNeuronPool();
if(CheckPointer(temp_p) == POINTER_INVALID)
{
result = false;
break;
}
if(temp_p.Init(iOutputs, index, 1, 1, 1, ADAM))
{
temp = temp_p;
result = true;
}
break;
case defNeuronConv:
temp_p = new CNeuronConv();
if(CheckPointer(temp_p) == POINTER_INVALID)
{
result = false;
break;
}
if(temp_p.Init(iOutputs, index, 1, 1, 1, ADAM))
{
temp = temp_p;
result = true;
}
break;
case defNeuronLSTM:
temp_p = new CNeuronLSTM();
if(CheckPointer(temp_p) == POINTER_INVALID)
{
result = false;
break;
}
if(temp_p.Init(iOutputs, index, 1, 1, 1, ADAM))
{
temp = temp_p;
result = true;
}
break;
case defNeuronBaseOCL:
temp_ocl = new CNeuronBaseOCL();
if(CheckPointer(temp_ocl) == POINTER_INVALID)
{
result = false;
break;
}
//--- Pure-MQL5 inference (no backend): construct host-only. Load() restores the weights into
//--- the host buffers and CNeuronBaseOCL::feedForwardCPU() computes on them. The device Init()
//--- below REQUIRES a backend, so it only runs when one exists (training/optimization/GPU run).
if(CheckPointer(OpenCL) == POINTER_INVALID && CheckPointer(DirectML) == POINTER_INVALID)
{
m_data[index] = temp_ocl;
return true;
}
if(CheckPointer(OpenCL) != POINTER_INVALID
? temp_ocl.Init(iOutputs, index, OpenCL, 1, ADAM)
: temp_ocl.Init(iOutputs, index, DirectML, 1, ADAM))
{
m_data[index] = temp_ocl;
return true;
}
break;
case defNeuronConvOCL:
{
//--- placeholder dims; the real window/step/units are restored by Load() right after
CNeuronConvOCL *temp_conv = new CNeuronConvOCL();
if(CheckPointer(temp_conv) == POINTER_INVALID)
{
result = false;
break;
}
//--- Pure-MQL5 inference (no backend): construct host-only, Load() fills the host buffers,
//--- CNeuronConvOCL::feedForwardCPU() computes on them (device Init below needs a backend).
if(CheckPointer(OpenCL) == POINTER_INVALID && CheckPointer(DirectML) == POINTER_INVALID)
{
m_data[index] = temp_conv;
return true;
}
if(CheckPointer(OpenCL) != POINTER_INVALID
? temp_conv.Init(iOutputs, index, OpenCL, 1, 1, 1, 1, ADAM)
: temp_conv.Init(iOutputs, index, DirectML, 1, 1, 1, 1, ADAM))
{
m_data[index] = temp_conv;
return true;
}
break;
}
case defNeuronBatchNormOCL:
{
//--- Placeholder width of 1; CNeuronBatchNormOCL::Load() replaces both the buffers and the
//--- whole gamma/beta/statistics block with the real ones, then verifies they agree.
CNeuronBatchNormOCL *temp_bn = new CNeuronBatchNormOCL();
if(CheckPointer(temp_bn) == POINTER_INVALID)
{
result = false;
break;
}
//--- Pure-MQL5 inference (no backend): construct host-only (see the defNeuronConvOCL note).
if(CheckPointer(OpenCL) == POINTER_INVALID && CheckPointer(DirectML) == POINTER_INVALID)
{
m_data[index] = temp_bn;
return true;
}
if(CheckPointer(OpenCL) != POINTER_INVALID
? temp_bn.Init(iOutputs, index, OpenCL, 1, 1, ADAM)
: temp_bn.Init(iOutputs, index, DirectML, 1, 1, ADAM))
{
m_data[index] = temp_bn;
return true;
}
break;
}
case defNeuronPoolOCL:
{
CNeuronPoolOCL *temp_pool = new CNeuronPoolOCL();
if(CheckPointer(temp_pool) == POINTER_INVALID)
{
result = false;
break;
}
//--- Pure-MQL5 inference (no backend): construct host-only (see the defNeuronConvOCL note).
if(CheckPointer(OpenCL) == POINTER_INVALID && CheckPointer(DirectML) == POINTER_INVALID)
{
m_data[index] = temp_pool;
return true;
}
if(CheckPointer(OpenCL) != POINTER_INVALID
? temp_pool.Init(iOutputs, index, OpenCL, 1, 1, 1, ADAM)
: temp_pool.Init(iOutputs, index, DirectML, 1, 1, 1, ADAM))
{
m_data[index] = temp_pool;
return true;
}
break;
}
case defNeuronLSTMOCL:
{
CNeuronLSTMOCL *temp_lstm = new CNeuronLSTMOCL();
if(CheckPointer(temp_lstm) == POINTER_INVALID)
{
result = false;
break;
}
//--- Pure-MQL5 inference (no backend): construct host-only (see the defNeuronConvOCL note).
if(CheckPointer(OpenCL) == POINTER_INVALID && CheckPointer(DirectML) == POINTER_INVALID)
{
m_data[index] = temp_lstm;
return true;
}
if(CheckPointer(OpenCL) != POINTER_INVALID
? temp_lstm.Init(iOutputs, index, OpenCL, 1, ADAM)
: temp_lstm.Init(iOutputs, index, DirectML, 1, ADAM))
{
m_data[index] = temp_lstm;
return true;
}
break;
}
default:
result = false;
break;
}
}
if(result)
m_data[index] = temp;
//---
return (result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CLayer::CLayer(uint outputs = 0, int handle = -1, COpenCLMy *opencl = NULL, CDirectMLMy *directml = NULL)
{
iOutputs = outputs;
iFileHandle = handle;
OpenCL = opencl;
DirectML = directml;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CLayer::Load(const int file_handle)
{
iFileHandle = file_handle;
if(!CArrayObj::Load(file_handle))
return false;
// A corrupt/truncated save can produce a 0-element layer here - CArrayObj::Load itself would
// still return true for it, so m_data[0] must be guarded before indexing or MQL5 raises an
// "array out of range" runtime error that aborts the whole CNet::Load call.
if(Total() <= 0 || CheckPointer(m_data[0]) == POINTER_INVALID)
{
Print(__FUNCTION__ + ": layer loaded with no elements (corrupt or truncated save file)");
return false;
}
//---
switch(m_data[0].Type())
{
case defNeuronBaseOCL:
case defNeuronConvOCL:
case defNeuronPoolOCL:
case defNeuronLSTMOCL:
case defNeuronBatchNormOCL:
{
CNeuronBaseOCL *temp = m_data[0];
iOutputs = temp.getConnections();
break;
}
default:
{
CNeuronBase *temp = m_data[0];
iOutputs = temp.getConnections().Total();
break;
}
}
//---
return true;
}
#endif