431 lines
35 KiB
MQL5
431 lines
35 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|