//+------------------------------------------------------------------+ //| 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, MATRIX &output, 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(ulong rows, ulong columns) { return CBufferDouble::BufferInit(rows, columns, 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(void) : m_eFunction(ACT_SWISH) { m_adParams[0] = 1; m_adParams[1] = 0; } //+------------------------------------------------------------------+ //| Функция инициализации класса | //+------------------------------------------------------------------+ void CActivation::SetFunction(ENUM_ACTIVATION function, MATRIX &output, double param1 = 1.0, double param2 = 0.0) { m_eFunction = function; m_mMatrix = output; 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(m_cOpenCL) delete m_cOpenCL; else { if(!CBufferDouble::BufferCreate(opencl)) { delete m_cOpenCL; return false; } } m_cOpenCL = opencl; } //--- return(!!m_cOpenCL); } //+------------------------------------------------------------------+ //| Диспетчерская функция определения вычисления функции активации | //+------------------------------------------------------------------+ 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(!buffer || buffer.Total() <= 0) return false; //--- switch(m_eFunction) { case ACT_None: break; case ACT_SOFTMAX: { for(ulong r = 0; r < buffer.m_mMatrix.Rows(); r++) { double sum = 0; VECTOR temp = buffer.m_mMatrix.Row(r); for(ulong i = 0; i < temp.Size(); i++) sum += temp[i] = MathExp(temp[i]); //--- Нормализация temp /= sum; sum = temp.Sum(); if(!buffer.m_mMatrix.Row(temp, r)) return false; } } break; case ACT_SWISH: m_mMatrix = buffer.m_mMatrix; for(uint i = 0; i < Total(); i++) { if(!buffer.m_mMatrix.Flat(i, SwishActivation(buffer.m_mMatrix.Flat(i)))) return false; } break; default: for(uint i = 0; i < Total(); i++) { if(!buffer.m_mMatrix.Flat(i, Activation(buffer.m_mMatrix.Flat(i)))) return false; } break; } //--- return true; } //+------------------------------------------------------------------+ //| Диспетчерская функция определения производной функции активации | //+------------------------------------------------------------------+ bool CActivation::Derivative(CBufferDouble *outputs, CBufferDouble *gradient) { if(!outputs || !gradient) return false; //--- if(!m_cOpenCL) { switch(m_eFunction) { case ACT_None: break; case ACT_SWISH: if(m_mMatrix.Rows() != outputs.m_mMatrix.Rows() || m_mMatrix.Cols() != outputs.m_mMatrix.Cols()) return false; for(ulong r = 0; r < outputs.m_mMatrix.Rows(); r++) for(ulong c = 0; c < m_mMatrix.Cols(); c++) gradient.m_mMatrix[r, c] *= SwishDerivative(outputs.m_mMatrix[r, c], m_mMatrix[r, c]); break; case ACT_SOFTMAX: { MATRIX e; if(!e.Init(Total(), Total())) return false; e.Identity(); for(ulong r = 0; r < Total(); r++) if(!e.Row(e.Row(r) - outputs.m_mMatrix.Row(0), r)) return false; gradient.m_mMatrix = (outputs.m_mMatrix * gradient.m_mMatrix).MatMul(e); } break; default: for(ulong r = 0; r < outputs.m_mMatrix.Rows(); r++) for(ulong i = 0; i < outputs.m_mMatrix.Cols(); i++) gradient.m_mMatrix[r, i] *= Derivative(outputs.m_mMatrix[r, i]); break; } } else { //--- Деактивация градиента ошибки //--- проверяем буфера данных if(gradient.GetIndex() < 0) return false; if(outputs.GetIndex() < 0) return false; if(m_myIndex < 0) return false; if(m_eFunction == ACT_SOFTMAX) { m_mMatrix = gradient.m_mMatrix; BufferWrite(); } //--- Передача параметров кернелу 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 NDRange[] = { ((int)outputs.Total() + 3) / 4 }; int off_set[] = {0}; if(!m_cOpenCL.Execute(def_k_DeActivateGradient, 1, off_set, NDRange)) return false; //--- Получение результатов операций if(!gradient.BufferRead()) return false; } //--- 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; } //+------------------------------------------------------------------+