- Moves CLayer neuron construction to AI/Impl/Layer.mqh to keep Network.mqh clean - Unifies four previously duplicated architecture initialisation blocks (MLP/CONV/LSTM/HYBRID) into a single shared function - Eliminates risk of behavioural drift where one architecture missed a setter, causing mismatched feature sets or targets
263 lines
9.6 KiB
MQL5
263 lines
9.6 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|