//+------------------------------------------------------------------+ //| 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