//+------------------------------------------------------------------+ //| activation.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include "bufferdouble.mqh" //+------------------------------------------------------------------+ //| Class CActivation | //| Назначение: Класс для реализации алгоритмов функции активации | //| и её производной | //+------------------------------------------------------------------+ class CActivation : protected CBufferDouble { protected: ENUM_ACTIVATION m_eFunction; double m_adParams[2]; //--- Функции активации double LineActivation(double value); //Линейная функция активации double SigmoidActivation(double value); //Сигмоида double TanhActivation(double value); //TANH double LReLUActivation(double value); //LReLU double SwishActivation(double value); //Swish //--- Производные функций активации double LineDerivative(double value); double SigmoidDerivative(double value); double TanhDerivative(double value); double LReLUDerivative(double value); double SwishDerivative(double value, double input_value); public: CActivation(void); ~CActivation(void) {}; //--- void SetFunction(ENUM_ACTIVATION value, double param1 = 1, double param2 = 0); ENUM_ACTIVATION GetFunction(double ¶ms[]); ENUM_ACTIVATION GetFunction(void) { return GetFunction(m_adParams); } double Activation(double value); bool Activation(CBufferDouble *buffer); double Derivative(double value, double input_value = 1); bool Derivative(CBufferDouble *outputs, CBufferDouble *gradient); //--- virtual bool SetOpenCL(CMyOpenCL *opencl); virtual bool BufferCreate(void) { return CBufferDouble::BufferCreate(m_cOpenCL);} virtual bool BufferInit(uint count) { return CBufferDouble::BufferInit(count, 0.0);} virtual bool BufferRead(void) { return CBufferDouble::BufferRead();} virtual bool BufferFree(void) { return CBufferDouble::BufferFree();} virtual int GetIndex(void) { return CBufferDouble::GetIndex();} //--- methods for working with files virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); //--- method of identifying the object virtual int Type(void) const { return defActivation; } }; //+------------------------------------------------------------------+ //| Конструктор класса | //+------------------------------------------------------------------+ CActivation::CActivation() : m_eFunction(ACT_SWISH) { m_adParams[0] = 1; m_adParams[1] = 0; } //+------------------------------------------------------------------+ //| Функция инициализации класса | //+------------------------------------------------------------------+ void CActivation::SetFunction(ENUM_ACTIVATION function, double param1 = 1.0, double param2 = 0.0) { m_eFunction = function; switch(function) { case ACT_SOFTMAX: m_adParams[0] = 1; m_adParams[1] = 0; break; default: m_adParams[0] = param1; m_adParams[1] = param2; break; } } //+------------------------------------------------------------------+ //| Функция возвращает используемую функцию активации | //+------------------------------------------------------------------+ ENUM_ACTIVATION CActivation::GetFunction(double ¶ms[]) { if(ArrayCopy(params, m_adParams) <= 0) return ACT_None; return m_eFunction; } //+------------------------------------------------------------------+ //| Установка используемого контекста OpenCL | //+------------------------------------------------------------------+ bool CActivation::SetOpenCL(CMyOpenCL *opencl) { if(m_cOpenCL != opencl) { if(CheckPointer(m_cOpenCL) != POINTER_INVALID) delete m_cOpenCL; m_cOpenCL = opencl; } //--- return(CheckPointer(m_cOpenCL) != POINTER_INVALID); } //+------------------------------------------------------------------+ //| Диспетчерская функция определения вычисления функции активации | //+------------------------------------------------------------------+ double CActivation::Activation(double value) { double result = 0; switch(m_eFunction) { case ACT_LINE: result = LineActivation(value); break; case ACT_SIGMOID: result = SigmoidActivation(value); break; case ACT_TANH: result = TanhActivation(value); break; case ACT_LReLU: result = LReLUActivation(value); break; case ACT_SWISH: result = SwishActivation(value); break; default: result = value; break; } //--- return result; } //+------------------------------------------------------------------+ //| Диспетчерская функция определения производной функции активации | //+------------------------------------------------------------------+ double CActivation::Derivative(double value, double input_value = 1) { double result = 1; switch(m_eFunction) { case ACT_LINE: result = LineDerivative(value); break; case ACT_SIGMOID: case ACT_SOFTMAX: result = SigmoidDerivative(value); break; case ACT_TANH: result = TanhDerivative(value); break; case ACT_LReLU: result = LReLUDerivative(value); break; case ACT_SWISH: result = SwishDerivative(value, input_value); break; default: result = 1; break; } //--- return result; } //+------------------------------------------------------------------+ //| Диспетчерская функция определения вычисления функции активации | //+------------------------------------------------------------------+ bool CActivation::Activation(CBufferDouble *buffer) { if(CheckPointer(buffer) == POINTER_INVALID || buffer.m_data_total <= 0) return false; //--- switch(m_eFunction) { case ACT_None: break; case ACT_SOFTMAX: { double sum = 0; for(int i = 0; i < buffer.m_data_total; i++) sum += buffer.m_data[i] = MathExp(buffer.m_data[i]); //--- Нормализация for(int i = 0; i < buffer.m_data_total; i++) buffer.m_data[i] /= sum; } break; case ACT_SWISH: if(!Reserve(buffer.m_data_total)) return false; for(int i = 0; i < buffer.m_data_total; i++) { m_data[i] = buffer.m_data[i]; buffer.m_data[i] = SwishActivation(buffer.m_data[i]); } m_data_total = buffer.m_data_total; break; default: for(int i = 0; i < buffer.m_data_total; i++) buffer.m_data[i] = Activation(buffer.m_data[i]); break; } //--- return true; } //+------------------------------------------------------------------+ //| Диспетчерская функция определения производной функции активации | //+------------------------------------------------------------------+ bool CActivation::Derivative(CBufferDouble *outputs, CBufferDouble *gradient) { if(CheckPointer(outputs) == POINTER_INVALID || CheckPointer(gradient) == POINTER_INVALID) return false; //--- if(CheckPointer(m_cOpenCL) == POINTER_INVALID) { switch(m_eFunction) { case ACT_None: break; case ACT_SWISH: if(m_data_total < outputs.m_data_total) return false; for(int i = 0; i < outputs.m_data_total; i++) gradient.m_data[i] *= SwishDerivative(outputs.m_data[i], m_data[i]); break; case ACT_SOFTMAX: if(!AssignArray(gradient)) return false; for(int i = 0; i < outputs.m_data_total; i++) { double grad = 0; for(int j = 0; j < outputs.m_data_total; j++) grad += outputs.m_data[j] * ((int)(i == j) - outputs.m_data[i]) * m_data[j]; gradient.m_data[i] = grad; } break; default: for(int i = 0; i < outputs.m_data_total; i++) gradient.m_data[i] *= Derivative(outputs.m_data[i]); break; } } else { //--- Деактивация градиента ошибки //--- Создание буферов данных if(gradient.GetIndex() < 0) if(!gradient.BufferCreate(m_cOpenCL)) return false; if(outputs.GetIndex() < 0) if(!outputs.BufferCreate(m_cOpenCL)) return false; if(m_eFunction == ACT_SOFTMAX) if(!AssignArray(gradient)) return false; if(m_data_total != outputs.Total()) { if(!BufferInit(outputs.Total())) return false; } if(!BufferCreate()) return false; //--- Передача параметров кернелу if(!m_cOpenCL.SetArgumentBuffer(def_k_DeActivateGradient, def_deactgr_sums, GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_DeActivateGradient, def_deactgr_outputs, outputs.GetIndex())) return false; if(!m_cOpenCL.SetArgumentBuffer(def_k_DeActivateGradient, def_deactgr_gradients, gradient.GetIndex())) return false; if(!m_cOpenCL.SetArgument(def_k_DeActivateGradient, def_deactgr_outputs_total, outputs.Total())) return false; if(!m_cOpenCL.SetArgument(def_k_DeActivateGradient, def_deactgr_activation, (int)m_eFunction)) return false; if(!m_cOpenCL.SetArgument(def_k_DeActivateGradient, def_deactgr_act_param_a, m_adParams[0])) return false; if(!m_cOpenCL.SetArgument(def_k_DeActivateGradient, def_deactgr_act_param_b, m_adParams[1])) return false; //--- Постановка кернела в очередь выполнения int s = outputs.Total(); int d = s % 4; s = (s - d) / 4 + (d > 0 ? 1 : 0); int NDRange[] = {s}; int off_set[] = {0}; if(!m_cOpenCL.Execute(def_k_DeActivateGradient, 1, off_set, NDRange)) return false; //--- Получение результатов операций if(!gradient.BufferRead()) return false; BufferFree(); outputs.BufferFree(); gradient.BufferFree(); } //--- return true; } //+------------------------------------------------------------------+ //| Линейная функция активации | //| Параметры 'value' Взвешенная сумма исходных данных для акивации | //| 'm_adParams[0]' коэффициент наклона линии | //| 'm_adParams[1]' - вертикальный сдвиг линии | //+------------------------------------------------------------------+ double CActivation::LineActivation(double value) { return (m_adParams[0] * value + m_adParams[1]); } //+------------------------------------------------------------------+ //| Производная линейной функции активации возвращает Parameter[0] | //+------------------------------------------------------------------+ double CActivation::LineDerivative(double value) { return m_adParams[0]; } //+------------------------------------------------------------------+ //| Сигмоидная функция актиавции | //| Параметры 'value' Взвешенная сумма исходных данных для акивации | //| 'm_adParams[0]' определяет диапазон значений функции активции | //| от '0' до 'm_adParams[0]' | //| 'm_adParams[1]' - вертикальный сдвиг значения функции | //+------------------------------------------------------------------+ double CActivation::SigmoidActivation(double value) { return (m_adParams[0] / (1 + exp(-value)) - m_adParams[1]); } //+------------------------------------------------------------------+ //| Проиизводная сигмоидной функции активации | //| Параметры 'value' текущее значениие функции активации | //| 'm_adParams[0]' определяет диапазон значений функции активции | //| от '0' до 'm_adParams[0]' | //| 'm_adParams[1]' - вертикальный сдвиг значения функции | //+------------------------------------------------------------------+ double CActivation::SigmoidDerivative(double value) { double z = MathMax(MathMin(m_adParams[0], value + m_adParams[1]), 0); return (z * (1 - z / m_adParams[0])); } //+------------------------------------------------------------------+ //| TANH | //| Параметры 'value' Взвешенная сумма исходных данных для акивации | //+------------------------------------------------------------------+ double CActivation::TanhActivation(double value) { return MathTanh(value); } //+------------------------------------------------------------------+ //| Производная TANH | //| Параметры 'value' текущее значениие функции активации | //+------------------------------------------------------------------+ double CActivation::TanhDerivative(double value) { return (1 - MathPow(value, 2)); } //+------------------------------------------------------------------+ //| LReLU | //| Параметры 'value' Взвешенная сумма исходных данных для акивации | //| 'm_adParams[0]' коэффициент утечки | //+------------------------------------------------------------------+ double CActivation::LReLUActivation(double value) { return (value > 0 ? value : m_adParams[0] * value); } //+------------------------------------------------------------------+ //| Производная LReLU | //| Параметры 'value' текущее значениие функции активации | //| 'm_adParams[0]' коэффициент утечки | //+------------------------------------------------------------------+ double CActivation::LReLUDerivative(double value) { return (value > 0 ? 1 : m_adParams[0]); } //+------------------------------------------------------------------+ //| Swish | //| Параметры 'value' Взвешенная сумма исходных данных для акивации | //| 'm_adParams[0]' коэффициент не линейностит функции | //+------------------------------------------------------------------+ double CActivation::SwishActivation(double value) { return value / (1 + exp(-value * m_adParams[0])); } //+------------------------------------------------------------------+ //| Производная Swish | //| Параметры 'value' текущее значениие функции активации | //| 'value_input' Взвешенная сумма исходных данных для акивации | //| 'm_adParams[0]' коэффициент не линейностит функции | //+------------------------------------------------------------------+ double CActivation::SwishDerivative(double value, double input_value) { if(input_value == 0) return 0.5; //--- double by = m_adParams[0] * value; return (by + (value / input_value * (1 - by))); } //+------------------------------------------------------------------+ //| Метод сохранения класса | //+------------------------------------------------------------------+ bool CActivation::Save(const int file_handle) { if(file_handle == INVALID_HANDLE) return false; if(FileWriteInteger(file_handle, (int)m_eFunction) <= 0 || FileWriteArray(file_handle, m_adParams) <= 0) return false; //--- return true; } //+------------------------------------------------------------------+ //| Метод восстановления элементов класса по ранее сохранённым данным| //+------------------------------------------------------------------+ bool CActivation::Load(const int file_handle) { if(file_handle == INVALID_HANDLE) return false; m_eFunction = (ENUM_ACTIVATION)FileReadInteger(file_handle); if(FileReadArray(file_handle, m_adParams) <= 0) return false; //--- return true; } //+------------------------------------------------------------------+