//+------------------------------------------------------------------+ //| NeuronProof.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Подключаем библиотеки | //+------------------------------------------------------------------+ #include "neuronbase.mqh" #include //+------------------------------------------------------------------+ //| Class CNeuronProof | //| Назначение: Класс организации работы подвыборочного слоя | //+------------------------------------------------------------------+ class CNeuronProof : public CNeuronBase { protected: uint m_iWindow; //Размер окна на входе нейронного слоя uint m_iStep; //Размер шага входного окна uint m_iNeurons; //Размер выхода одного фильтра uint m_iWindowOut; //Количество фильтров/ ENUM_ACTIVATION m_eActivation; //Функция активации public: CNeuronProof(void); ~CNeuronProof(void) {}; //--- virtual bool Init(CLayerDescription *description); virtual bool FeedForward(CNeuronBase *prevLayer); virtual bool CalcOutputGradient(CBufferDouble *target) { return false;} virtual bool CalcHiddenGradient(CNeuronBase *prevLayer); virtual bool CalcDeltaWeights(CNeuronBase *prevLayer) { return true; } virtual bool UpdateWeights(int batch_size, double learningRate, double &Beta[], double &Lambda[]) { return true; } //--- virtual CBufferDouble *GetWeights(void) const { return(NULL); } virtual CBufferDouble *GetDeltaWeights(void) const { return(NULL); } virtual uint GetNeurons(void) const { return m_iNeurons; } //--- Методы работы с файлами virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); //--- Метод идентификации объекта virtual int Type(void) const { return(defNeuronProof); } }; //+------------------------------------------------------------------+ //| Конструктор класса | //+------------------------------------------------------------------+ CNeuronProof::CNeuronProof(void) : m_eActivation(ACT_MAX_POOLING), m_iWindow(2), m_iStep(1), m_iWindowOut(1), m_iNeurons(0) { } //+------------------------------------------------------------------+ //| Метод инициализации класса | //+------------------------------------------------------------------+ bool CNeuronProof::Init(CLayerDescription *description) { //--- Блок контролей if(CheckPointer(description) == POINTER_INVALID || description.type != Type() || description.count <= 0) return false; //--- Сохраняем константы m_iWindow = description.window; m_iStep = description.step; m_iWindowOut = description.window_out; m_iNeurons = description.count; if(m_iWindow <= 0 || m_iStep <= 0 || m_iWindowOut <= 0 || m_iNeurons <= 0) return false; //--- Проверка функции потерь switch(description.activation) { case ACT_AVERAGE_POOLING: case ACT_MAX_POOLING: m_eActivation = description.activation; break; default: return false; break; } //--- Инициализируем буфер результатов if(CheckPointer(m_cOutputs) == POINTER_INVALID) { m_cOutputs = new CBufferDouble(); if(CheckPointer(m_cOutputs) == POINTER_INVALID) return false; } if(!m_cOutputs.BufferInit(m_iNeurons * m_iWindowOut, 0)) return false; //--- Инициализируем буфер градиентов ошибки if(CheckPointer(m_cGradients) == POINTER_INVALID) { m_cGradients = new CBufferDouble(); if(CheckPointer(m_cGradients) == POINTER_INVALID) return false; } if(!m_cGradients.BufferInit(m_iNeurons * m_iWindowOut, 0)) return false; //--- m_eOptimization = None; //--- Удаляем не используемые объекты if(CheckPointer(m_cActivation) != POINTER_INVALID) delete m_cActivation; if(CheckPointer(m_cWeights) != POINTER_INVALID) delete m_cWeights; if(CheckPointer(m_cDeltaWeights) != POINTER_INVALID) delete m_cDeltaWeights; for(int i = 0; i < 2; i++) if(CheckPointer(m_cMomenum[i]) != POINTER_INVALID) delete m_cMomenum[i]; //--- return true; } //+------------------------------------------------------------------+ //| Метод прямого прохода | //+------------------------------------------------------------------+ bool CNeuronProof::FeedForward(CNeuronBase *prevLayer) { //--- Блок контролей if(CheckPointer(prevLayer) == POINTER_INVALID || CheckPointer(m_cOutputs) == POINTER_INVALID || CheckPointer(prevLayer.GetOutputs()) == POINTER_INVALID) return false; CBufferDouble *input_data = prevLayer.GetOutputs(); //--- Разветвление алгоритма в зависимости от устройста выполнениия операций if(CheckPointer(m_cOpenCL) == POINTER_INVALID) { uint input_total = input_data.Total(); uint output_total = m_cOutputs.Total(); uint input_neurons = input_total; if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof) { CNeuronProof *temp = prevLayer; input_neurons = temp.GetNeurons(); } double array[]; if(ArrayResize(array, m_iWindow) < 0) return false; for(uint f = 0; f < m_iWindowOut; f++) { uint shift_inp = f * input_neurons; uint shift_out = f * m_iNeurons; if(shift_inp >= input_total) { for(uint i = shift_out; i < output_total; i++) m_cOutputs.Update(i, 0); break; } for(uint o = 0; o < m_iNeurons; o++) { uint shift = o * m_iStep; for(uint i = 0; i < m_iWindow; i++) array[i] = ((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons ? 0 : input_data.At(shift_inp + shift + i)); switch(m_eActivation) { case ACT_MAX_POOLING: m_cOutputs.Update(shift_out + o, array[ArrayMaximum(array)]); break; case ACT_AVERAGE_POOLING: m_cOutputs.Update(shift_out + o, MathMean(array)); break; default: return false; } } } } else // Блок работы с OpenCL { //--- Создание и загрузка содержимое буферов в контекст OpenCL if(input_data.GetIndex() < 0 && !input_data.BufferCreate(m_cOpenCL)) return false; if(m_cOutputs.GetIndex() < 0 && !m_cOutputs.BufferCreate(m_cOpenCL)) return false; //--- Передача параметров в кернел if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofFeedForward, def_prff_inputs, input_data.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofFeedForward, def_prff_outputs, m_cOutputs.GetIndex())) return false; if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_inputs_total, input_data.Total())) return false; if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_window, m_iWindow)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_step, m_iStep)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_window_out, m_iWindowOut)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_activation, (int)m_eActivation)) return false; //--- Постановка кернела в очередь на выполнение uint input_neurons = input_data.Total(); if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof) { CNeuronProof *temp = prevLayer; input_neurons = temp.GetNeurons(); } if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_input_neurons, input_neurons)) return false; int off_set[] = {0}; int NDRange[] = {(int)m_iNeurons}; if(!m_cOpenCL.Execute(def_k_ProofFeedForward, 1, off_set, NDRange)) return false; //--- Загрузка результатов расчёта if(!m_cOutputs.BufferRead()) return false; input_data.BufferFree(); } //--- return true; } //+------------------------------------------------------------------+ //| Метод распределения градиента ошибки через скрытый слой | //+------------------------------------------------------------------+ bool CNeuronProof::CalcHiddenGradient(CNeuronBase *prevLayer) { //--- Блок контролей if(CheckPointer(prevLayer) == POINTER_INVALID || CheckPointer(m_cOutputs) == POINTER_INVALID || CheckPointer(m_cGradients) == POINTER_INVALID || CheckPointer(prevLayer.GetOutputs()) == POINTER_INVALID || CheckPointer(prevLayer.GetGradients()) == POINTER_INVALID) return false; CBufferDouble *input_data = prevLayer.GetOutputs(); CBufferDouble *input_gradient = prevLayer.GetGradients(); if(!input_gradient.BufferInit(input_data.Total(), 0)) return false; //--- Разветвление алгоритма в зависимости от устройста выполнениия операций if(CheckPointer(m_cOpenCL) == POINTER_INVALID) { uint input_total = input_data.Total(); uint output_total = m_cOutputs.Total(); uint input_neurons = input_total; if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof) { CNeuronProof *temp = prevLayer; input_neurons = temp.GetNeurons(); } for(uint f = 0; f < m_iWindowOut; f++) { uint shift_inp = f * input_neurons; uint shift_out = f * m_iNeurons; if(shift_inp >= input_total) break; for(uint o = 0; o < m_iNeurons; o++) { uint shift = o * m_iStep; double out = m_cOutputs.At(shift_out + o); double gradient = m_cGradients.At(shift_out + o); switch(m_eActivation) { case ACT_MAX_POOLING: for(uint i = 0; i < m_iWindow; i++) { if((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons) break; if(input_data.At(shift_inp + shift + i) == out) { input_gradient.Update(shift_inp + shift + i, input_gradient.At(shift_inp + shift + i) + gradient); break; } } break; case ACT_AVERAGE_POOLING: gradient /= (double)m_iWindow; for(uint i = 0; i < m_iWindow; i++) { if((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons) break; input_gradient.Update(shift_inp + shift + i, input_gradient.At(shift_inp + shift + i) + gradient); } break; default: return false; } } } } else // Блок работы с OpenCL { //--- Создание и загрузка содержимое буферов в контекст OpenCL if(input_data.GetIndex() < 0 && !input_data.BufferCreate(m_cOpenCL)) return false; if(m_cOutputs.GetIndex() < 0 && !m_cOutputs.BufferCreate(m_cOpenCL)) return false; if(input_gradient.GetIndex() < 0 && !input_gradient.BufferCreate(m_cOpenCL)) return false; if(m_cGradients.GetIndex() < 0 && !m_cGradients.BufferCreate(m_cOpenCL)) return false; //--- Передача параметров в кернел if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_inputs, input_data.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_outputs, m_cOutputs.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_gradients, m_cGradients.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_gradient_inputs, input_gradient.GetIndex())) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_inputs_total, input_data.Total())) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_window, m_iWindow)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_step, m_iStep)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_window_out, m_iWindowOut)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_activation, (int)m_eActivation)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_neurons, m_iNeurons)) return false; if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_outputs_total, m_cOutputs.Total())) return false; //--- Постановка кернела в очередь на выполнене uint input_neurons = input_data.Total(); if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof) { CNeuronProof *temp = prevLayer; input_neurons = temp.GetNeurons(); } int off_set[] = {0}; int NDRange[] = {(int)input_neurons}; if(!m_cOpenCL.Execute(def_k_ProofHiddenGradients, 1, off_set, NDRange)) return false; //--- Загрузка результатов if(!input_gradient.BufferRead()) return false; //--- Очистка памяти контекста OpenCL input_data.BufferFree(); m_cOutputs.BufferFree(); m_cGradients.BufferFree(); } //--- return true; } //+------------------------------------------------------------------+ //| Метод сохранения элементов касса в файл | //+------------------------------------------------------------------+ bool CNeuronProof::Save(const int file_handle) { //--- Блок контролей if(file_handle == INVALID_HANDLE) return false; //--- Сохраняем константы if(FileWriteInteger(file_handle, Type()) <= 0) return false; if(FileWriteInteger(file_handle, (int)m_iWindow) <= 0) return false; if(FileWriteInteger(file_handle, (int)m_iStep) <= 0) return false; if(FileWriteInteger(file_handle, (int)m_iWindowOut) <= 0) return false; if(FileWriteInteger(file_handle, (int)m_iNeurons) <= 0) return false; if(FileWriteInteger(file_handle, (int)m_eActivation) <= 0) return false; //--- return true; } //+------------------------------------------------------------------+ //| Метод восстановления работы класса из файла | //+------------------------------------------------------------------+ bool CNeuronProof::Load(const int file_handle) { //--- Блок контролей if(file_handle == INVALID_HANDLE) return false; //--- Загружаем константы m_iWindow = (uint)FileReadInteger(file_handle); m_iStep = (uint)FileReadInteger(file_handle); m_iWindowOut = (uint)FileReadInteger(file_handle); m_iNeurons = (uint)FileReadInteger(file_handle); m_eActivation = (ENUM_ACTIVATION)FileReadInteger(file_handle); //--- Инициализтируем и загружаем буфер результатов if(CheckPointer(m_cOutputs) == POINTER_INVALID) { m_cOutputs = new CBufferDouble(); if(CheckPointer(m_cOutputs) == POINTER_INVALID) return false; } if(!m_cOutputs.BufferInit(m_iNeurons * m_iWindowOut, 0)) return false; //--- Инициализируем и загружаем буфер градиентов ошибки if(CheckPointer(m_cGradients) == POINTER_INVALID) { m_cGradients = new CBufferDouble(); if(CheckPointer(m_cGradients) == POINTER_INVALID) return false; } if(!m_cGradients.BufferInit(m_iNeurons * m_iWindowOut, 0)) return false; //--- return true; } //+------------------------------------------------------------------+