//+------------------------------------------------------------------+ //| NeuronDropout.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Подключаем библиотеки | //+------------------------------------------------------------------+ #include "neuronbase.mqh" //+------------------------------------------------------------------+ //| Class CNeuronDropout | //| Назначение: Класс реализации метода Dropout | //+------------------------------------------------------------------+ class CNeuronDropout : public CNeuronBase { protected: double m_dOutProbability; int m_iOutNumber; double m_dInitValue; CBufferDouble *m_cDropOutMultiplier; public: CNeuronDropout(void); ~CNeuronDropout(void); //--- virtual bool Init(CLayerDescription *description); virtual bool FeedForward(CNeuronBase *prevLayer); 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 bool Save(const int file_handle); virtual bool Load(const int file_handle); //--- Метод идентификации объекта virtual int Type(void) const { return(defNeuronDropout); } }; //+------------------------------------------------------------------+ //| Конструктор класса | //+------------------------------------------------------------------+ CNeuronDropout::CNeuronDropout(void) : m_dInitValue(1.0), m_dOutProbability(0), m_iOutNumber(0) { m_cDropOutMultiplier = new CBufferDouble(); m_bTrain = false; } //+------------------------------------------------------------------+ //| Деструктор класса | //+------------------------------------------------------------------+ CNeuronDropout::~CNeuronDropout(void) { if(CheckPointer(m_cDropOutMultiplier) != POINTER_INVALID) delete m_cDropOutMultiplier; } //+------------------------------------------------------------------+ //| Метод инициализации класса | //+------------------------------------------------------------------+ bool CNeuronDropout::Init(CLayerDescription *description) { //--- Блок контролей if(!CheckPointer(description) == POINTER_INVALID || description.count != description.window) return false; description.window = 0; if(!CNeuronBase::Init(description)) return false; //--- Расчёт коэффициентов m_dOutProbability = MathMin(description.probability, 0.9); if(m_dOutProbability < 0) return false; m_iOutNumber = (int)(m_cOutputs.Total() * m_dOutProbability); m_dInitValue = 1.0 / (1.0 - m_dOutProbability); if(CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) { m_cDropOutMultiplier = new CBufferDouble(); if(CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) return false; } //--- Инициируем буфер маскирования if(!m_cDropOutMultiplier.BufferInit(m_cOutputs.Total(), m_dInitValue)) return false; m_bTrain = true; //--- return true; } //+------------------------------------------------------------------+ //| Метод прямого прохода | //+------------------------------------------------------------------+ bool CNeuronDropout::FeedForward(CNeuronBase *prevLayer) { //--- Блок контролей if(CheckPointer(prevLayer) == POINTER_INVALID || CheckPointer(prevLayer.GetOutputs()) == POINTER_INVALID || CheckPointer(m_cOutputs) == POINTER_INVALID || CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) return false; //--- Провеверка флага режима работы if(!m_bTrain) return m_cOutputs.AssignArray(prevLayer.GetOutputs()); //--- Генерируем тензор маскирования данных int total = m_cOutputs.Total(); if(!m_cDropOutMultiplier.BufferInit(total, m_dInitValue)) return false; for(int i = 0; i < m_iOutNumber; i++) { int pos = (int)(MathRand() * MathRand() / MathPow(32767.0, 2) * total); if(m_cDropOutMultiplier[pos] == 0) { i--; continue; } if(!m_cDropOutMultiplier.Update(pos, 0)) return false; } //--- Разветвление алгоритма в зависимости от устройста выполнениия операций if(CheckPointer(m_cOpenCL) == POINTER_INVALID) { double out[]; double prev[]; if(ArrayResize(out, total) < total) return false; if(!prevLayer.GetOutputs().GetData(prev, false) || ArraySize(prev) != total) return false; for(int i = 0; i < total; i++) out[i] = prev[i] * m_cDropOutMultiplier[i]; if(!m_cOutputs.AssignArray(out)) return false; } else // Блок OpenCL { //--- Создание буферов данных CBufferDouble *inputs = prevLayer.GetOutputs(); if(inputs.GetIndex() < 0 && !inputs.BufferCreate(m_cOpenCL)) return false; if(m_cDropOutMultiplier.GetIndex() < 0 && !m_cDropOutMultiplier.BufferCreate(m_cOpenCL)) return false; if(m_cOutputs.GetIndex() < 0 && !m_cOutputs.BufferCreate(m_cOpenCL)) return false; //--- Передача параметров кернелу if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_inputs, inputs.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_mask, m_cDropOutMultiplier.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_outputs, m_cOutputs.GetIndex())) return false; if(!m_cOpenCL.SetArgument(def_k_MaskMult, def_mask_total, total)) return false; //--- Постановка в очередь выполнения int off_set[] = {0}; int s = total; int d = s % 4; s = (s - d) / 4 + (d > 0 ? 1 : 0); int NDRange[] = {s}; if(!m_cOpenCL.Execute(def_k_MaskMult, 1, off_set, NDRange)) return false; //--- Получение результатов if(!m_cOutputs.BufferRead()) return false; //--- Очистка памяти кернела inputs.BufferFree(); m_cDropOutMultiplier.BufferFree(); } //--- return true; } //+------------------------------------------------------------------+ //| Метод распределения градиента через скрытый слой | //+------------------------------------------------------------------+ bool CNeuronDropout::CalcHiddenGradient(CNeuronBase *prevLayer) { //--- Блок контролей if(CheckPointer(prevLayer) == POINTER_INVALID || CheckPointer(prevLayer.GetGradients()) == POINTER_INVALID || CheckPointer(m_cGradients) == POINTER_INVALID || CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) return false; //--- Провеверка флага режима работы if(!m_bTrain) return prevLayer.GetGradients().AssignArray(m_cGradients); //--- Разветвление алгоритма в зависимости от устройста выполнениия операций int total = m_cOutputs.Total(); if(CheckPointer(m_cOpenCL) == POINTER_INVALID) { double gradient[]; double prev[]; if(ArrayResize(prev, total) < total) return false; if(!m_cGradients.GetData(gradient, false) || ArraySize(gradient) != total) return false; for(int i = 0; i < total; i++) prev[i] = gradient[i] * m_cDropOutMultiplier[i]; if(!prevLayer.GetGradients().AssignArray(prev)) return false; } else // Блок OpenCL { //--- Создание буферов данных CBufferDouble *prev = prevLayer.GetGradients(); if(prev.GetIndex() < 0 && !prev.BufferCreate(m_cOpenCL)) return false; if(m_cDropOutMultiplier.GetIndex() < 0 && !m_cDropOutMultiplier.BufferCreate(m_cOpenCL)) return false; if(m_cGradients.GetIndex() < 0 && !m_cGradients.BufferCreate(m_cOpenCL)) return false; //--- Передача параметров кернелу if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_inputs, m_cGradients.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_mask, m_cDropOutMultiplier.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_MaskMult, def_mask_outputs, prev.GetIndex())) return false; if(!m_cOpenCL.SetArgument(def_k_MaskMult, def_mask_total, total)) return false; //--- Постановка в очередь выполнения int off_set[] = {0}; int s = total; int d = s % 4; s = (s - d) / 4 + (d > 0 ? 1 : 0); int NDRange[] = {s}; if(!m_cOpenCL.Execute(def_k_MaskMult, 1, off_set, NDRange)) return false; //--- Получение результатов if(!prev.BufferRead()) return false; //--- Очистка памяти кернела m_cGradients.BufferFree(); m_cDropOutMultiplier.BufferFree(); } //--- return true; } //+------------------------------------------------------------------+ //| Метод сохранения элементов класса в файл | //+------------------------------------------------------------------+ bool CNeuronDropout::Save(const int file_handle) { //--- Вызов метода родительского класса if(!CNeuronBase::Save(file_handle)) return false; //--- Сохраняем константу вероятности "выкидывания" элементов if(FileWriteDouble(file_handle, m_dOutProbability) <= 0) return false; //--- return true; } //+------------------------------------------------------------------+ //| Метод восстановления класса из сохранённых данных | //+------------------------------------------------------------------+ bool CNeuronDropout::Load(const int file_handle) { //--- Вызов метода родительского класса if(!CNeuronBase::Load(file_handle)) return false; //--- Считывание и восстановление констант m_dOutProbability = FileReadDouble(file_handle); m_iOutNumber = (int)(m_cOutputs.Total() * m_dOutProbability); m_dInitValue = 1.0 / (1.0 - m_dOutProbability); //--- Инициализация буфера маскированиия данных if(CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) { m_cDropOutMultiplier = new CBufferDouble(); if(CheckPointer(m_cDropOutMultiplier) == POINTER_INVALID) return false; } if(!m_cDropOutMultiplier.BufferInit(m_cOutputs.Total(), m_dInitValue)) return false; //--- return true; } //+------------------------------------------------------------------+