895 lines
61 KiB
MQL5
895 lines
61 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| NeuronNet.mqh |
|
|
//| Copyright 2021, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2021, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
//+------------------------------------------------------------------+
|
|
//| Подключаем библиотеки |
|
|
//+------------------------------------------------------------------+
|
|
#include "arraylayers.mqh"
|
|
#include "positionencoder.mqh"
|
|
#include "lossfunction.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Class CNet |
|
|
//| Назначение: Базовій диспетчерский класс организации работы |
|
|
//| нейронной сети |
|
|
//+------------------------------------------------------------------+
|
|
class CNet : public CObject
|
|
{
|
|
protected:
|
|
bool m_bTrainMode;
|
|
CArrayLayers *m_cLayers;
|
|
CMyOpenCL *m_cOpenCL;
|
|
bool m_bOpenCL;
|
|
double m_dNNLoss;
|
|
int m_iLossSmoothFactor;
|
|
CPositionEncoder *m_cPositionEncoder;
|
|
bool m_bPositionEncoder;
|
|
CLossFunction *m_cLossFunction;
|
|
double m_adLambda[2];
|
|
double m_dLearningRate;
|
|
double m_adBeta[2];
|
|
|
|
public:
|
|
CNet(void);
|
|
~CNet(void);
|
|
//--- Методы создания объекта
|
|
bool Create(CArrayObj *descriptions);
|
|
bool Create(CArrayObj *descriptions, double learning_rate,
|
|
double beta1, double beta2);
|
|
bool Create(CArrayObj *descriptions, ENUM_LOSS_FUNCTION loss_function,
|
|
double lambda1, double lambda2);
|
|
bool Create(CArrayObj *descriptions, double learning_rate, double beta1,
|
|
double beta2, ENUM_LOSS_FUNCTION loss_function, double lambda1,
|
|
double lambda2);
|
|
//--- Организация работы с OpenCL
|
|
void UseOpenCL(bool value);
|
|
bool UseOpenCL(void) const { return(m_bOpenCL); }
|
|
bool InitOpenCL(void);
|
|
//--- Методы работы с позиционным колированием
|
|
void UsePositionEncoder(bool value);
|
|
bool UsePositionEncoder(void) const { return(m_bPositionEncoder); }
|
|
//--- Организация основных алгоритмов работы модели
|
|
bool FeedForward(const CBufferDouble *inputs);
|
|
bool Backpropagation(CBufferDouble *target);
|
|
bool UpdateWeights(uint batch_size = 1);
|
|
bool GetResults(CBufferDouble *&result);
|
|
void SetLearningRates(double learning_rate, double beta1 = 0.9,
|
|
double beta2 = 0.999);
|
|
//--- Методы функции потерь
|
|
bool LossFunction(ENUM_LOSS_FUNCTION loss_function, double lambda1 = 0,
|
|
double lambda2 = 0);
|
|
ENUM_LOSS_FUNCTION LossFunction(void) const { return(m_cLossFunction.LossFunction());}
|
|
ENUM_LOSS_FUNCTION LossFunction(double &lambda1, double &lambda2);
|
|
double GetRecentAverageLoss(void) const { return(m_dNNLoss); }
|
|
void LossSmoothFactor(int value) { m_iLossSmoothFactor = value;}
|
|
int LossSmoothFactor(void) const { return(m_iLossSmoothFactor);}
|
|
//--- Управление режимом работы модели
|
|
bool TrainMode(void) const { return m_bTrainMode; }
|
|
void TrainMode(bool mode);
|
|
//--- Методы работы с файлами
|
|
virtual bool Save(string file_name = NULL);
|
|
virtual bool Save(const int file_handle);
|
|
virtual bool Load(string file_name = NULL, bool common = false);
|
|
virtual bool Load(const int file_handle);
|
|
//--- Метод идентификации объекта
|
|
virtual int Type(void) const { return(defNeuronNet); }
|
|
//--- Получение указателей на внутренние объекты
|
|
virtual CBufferDouble *GetGradient(uint layer) const;
|
|
virtual CBufferDouble *GetWeights(uint layer) const;
|
|
virtual CBufferDouble *GetDeltaWeights(uint layer) const;
|
|
virtual int GetGPTUnits(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор класса |
|
|
//+------------------------------------------------------------------+
|
|
CNet::CNet(void) : m_bTrainMode(false),
|
|
m_bOpenCL(false),
|
|
m_bPositionEncoder(false),
|
|
m_dNNLoss(-1),
|
|
m_iLossSmoothFactor(1000),
|
|
m_dLearningRate(3.0e-4),
|
|
m_cLossFunction(NULL)
|
|
{
|
|
ArrayInitialize(m_adLambda, 0);
|
|
ArrayInitialize(m_adBeta, 0);
|
|
m_cLayers = new CArrayLayers();
|
|
m_cOpenCL = new CMyOpenCL();
|
|
m_cPositionEncoder = new CPositionEncoder();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Деструктор класса |
|
|
//+------------------------------------------------------------------+
|
|
CNet::~CNet(void)
|
|
{
|
|
if(m_cLayers)
|
|
delete m_cLayers;
|
|
if(m_cOpenCL)
|
|
delete m_cOpenCL;
|
|
if(m_cPositionEncoder)
|
|
delete m_cPositionEncoder;
|
|
if(m_cLossFunction)
|
|
delete m_cLossFunction;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации класса |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Create(CArrayObj *descriptions)
|
|
{
|
|
//--- Блок контролей
|
|
if(!descriptions)
|
|
return false;
|
|
//--- Проверяем количество создаваемых слоёв
|
|
int total = descriptions.Total();
|
|
if(total < 2)
|
|
return false;
|
|
//--- Инициализируем объеты OpenCL
|
|
if(m_bOpenCL)
|
|
m_bOpenCL = InitOpenCL();
|
|
if(!m_cLayers.SetOpencl(m_cOpenCL))
|
|
m_bOpenCL = false;
|
|
//--- Организовываем цикл для создания нейронных слоёв
|
|
for(int i = 0; i < total; i++)
|
|
{
|
|
CLayerDescription *temp = descriptions.At(i);
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
|
return false;
|
|
if(i == 0)
|
|
{
|
|
if(temp.type != defNeuronBase)
|
|
return false;
|
|
temp.window = 0;
|
|
}
|
|
else
|
|
{
|
|
CLayerDescription *prev = descriptions.At(i - 1);
|
|
if(temp.window <= 0 || temp.window > prev.count || temp.type == defNeuronBase)
|
|
{
|
|
switch(prev.type)
|
|
{
|
|
case defNeuronConv:
|
|
case defNeuronProof:
|
|
temp.window = prev.count * prev.window_out;
|
|
break;
|
|
default:
|
|
temp.window = prev.count;
|
|
break;
|
|
}
|
|
switch(temp.type)
|
|
{
|
|
case defNeuronAttention:
|
|
case defNeuronMHAttention:
|
|
case defNeuronGPT:
|
|
break;
|
|
default:
|
|
temp.step = 0;
|
|
}
|
|
}
|
|
}
|
|
if(!m_cLayers.CreateElement(i, temp))
|
|
return false;
|
|
}
|
|
//--- Инициализируем объекты позиционного кодирования
|
|
if(m_bPositionEncoder)
|
|
{
|
|
if(!m_cPositionEncoder)
|
|
{
|
|
m_cPositionEncoder = new CPositionEncoder();
|
|
if(!m_cPositionEncoder)
|
|
m_bPositionEncoder = false;
|
|
return true;
|
|
}
|
|
CLayerDescription *temp = descriptions.At(0);
|
|
if(!m_cPositionEncoder.InitEncoder(temp.count, temp.window))
|
|
UsePositionEncoder(false);
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации класса |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Create(CArrayObj *descriptions,
|
|
double learning_rate,
|
|
double beta1, double beta2,
|
|
ENUM_LOSS_FUNCTION loss_function,double lambda1, double lambda2)
|
|
{
|
|
if(!Create(descriptions))
|
|
return false;
|
|
SetLearningRates(learning_rate, beta1, beta2);
|
|
if(!LossFunction(loss_function, lambda1, lambda2))
|
|
return false;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации класса |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Create(CArrayObj *descriptions,ENUM_LOSS_FUNCTION loss_function,double lambda1,double lambda2)
|
|
{
|
|
if(!Create(descriptions))
|
|
return false;
|
|
if(!LossFunction(loss_function, lambda1, lambda2))
|
|
return false;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации класса |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Create(CArrayObj *descriptions,
|
|
double learning_rate,
|
|
double beta1, double beta2)
|
|
{
|
|
if(!Create(descriptions))
|
|
return false;
|
|
SetLearningRates(learning_rate, beta1, beta2);
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод прямого прохода |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::FeedForward(const CBufferDouble *inputs)
|
|
{
|
|
//--- Блок контролей
|
|
if(!inputs)
|
|
{
|
|
PrintFormat("%s - %d", __FUNCTION__, __LINE__);
|
|
return false;
|
|
}
|
|
CNeuronBase *InputLayer = m_cLayers.At(0);
|
|
if(!InputLayer)
|
|
{
|
|
PrintFormat("%s - %d", __FUNCTION__, __LINE__);
|
|
return false;
|
|
}
|
|
CBufferDouble *Inputs = InputLayer.GetOutputs();
|
|
if(!Inputs)
|
|
{
|
|
PrintFormat("%s - %d", __FUNCTION__, __LINE__);
|
|
return false;
|
|
}
|
|
if(Inputs.Total() != inputs.Total())
|
|
{
|
|
PrintFormat("%s - %d", __FUNCTION__, __LINE__);
|
|
return false;
|
|
}
|
|
//--- Переносим исходные данные в нейронный слой
|
|
Inputs.m_mMatrix = inputs.m_mMatrix;
|
|
//--- Применяем позиционное кодированиеи
|
|
if(m_bPositionEncoder && !m_cPositionEncoder.AddEncoder(Inputs))
|
|
{
|
|
PrintFormat("%s - %d", __FUNCTION__, __LINE__);
|
|
return false;
|
|
}
|
|
if(m_bOpenCL)
|
|
Inputs.BufferCreate(m_cOpenCL);
|
|
//--- Организовываем цикл с полным перебором всех нейронных слоёв
|
|
//--- и вызовом метода прямого прохода для каждого из них
|
|
CNeuronBase *PrevLayer = InputLayer;
|
|
int total = m_cLayers.Total();
|
|
for(int i = 1; i < total; i++)
|
|
{
|
|
CNeuronBase *Layer = m_cLayers.At(i);
|
|
if(!Layer)
|
|
{
|
|
PrintFormat("%s - %d Layer %d", __FUNCTION__, __LINE__, i);
|
|
return false;
|
|
}
|
|
if(!Layer.FeedForward(PrevLayer))
|
|
{
|
|
PrintFormat("%s - %d Layer %d", __FUNCTION__, __LINE__, i);
|
|
return false;
|
|
}
|
|
PrevLayer = Layer;
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод организации обратного прохода |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Backpropagation(CBufferDouble *target)
|
|
{
|
|
//--- Блок контролей
|
|
if(!target)
|
|
return false;
|
|
int total = m_cLayers.Total();
|
|
CNeuronBase *Output = m_cLayers.At(total - 1);
|
|
if(!Output)
|
|
return false;
|
|
//--- Расчет значения функции потерь
|
|
double loss = m_cLossFunction.Calculate(Output.GetOutputs(),target);
|
|
if(loss == DBL_MAX)
|
|
return false;
|
|
m_dNNLoss = (m_dNNLoss < 0 ? loss : m_dNNLoss + (loss - m_dNNLoss) / m_iLossSmoothFactor);
|
|
//--- Расчет градиента ошибки на выходе нейронной сети
|
|
if(m_cOpenCL)
|
|
{
|
|
if(!target.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
}
|
|
if(!Output.CalcOutputGradient(target))
|
|
{
|
|
return false;
|
|
}
|
|
target.BufferFree();
|
|
//--- Организовываем цикл с перебором всех нейронных слоёв в обратном порядке
|
|
for(int i = total - 2; i >= 0; i--)
|
|
{
|
|
CNeuronBase *temp = m_cLayers.At(i);
|
|
if(!temp)
|
|
return false;
|
|
//--- Вызываем метода распределения градиента ошибки через скрытый слой
|
|
if(!Output.CalcHiddenGradient(temp))
|
|
return false;
|
|
//--- Вызываем метод распределения градиента ошибки до матрицы весов
|
|
if(!Output.CalcDeltaWeights(temp))
|
|
return false;
|
|
Output = temp;
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод обновления матриц весовых коэффициентов |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::UpdateWeights(uint batch_size = 1)
|
|
{
|
|
//--- Блок контролей
|
|
if(batch_size <= 0)
|
|
return false;
|
|
//--- Организовываем цикл перебора всех скрытых слоёв
|
|
int total=m_cLayers.Total();
|
|
|
|
for(int i = 1; i < total; i++)
|
|
{
|
|
//--- Проверяем действительность указателя на объект нейронного слоя
|
|
CNeuronBase *temp = m_cLayers.At(i);
|
|
if(!temp)
|
|
return false;
|
|
//--- Вызываем метод обновления матрицы весов внутреннего слоя
|
|
if(!temp.UpdateWeights(batch_size, m_dLearningRate, m_adBeta, m_adLambda))
|
|
return false;
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод получения результата прямого прохода |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::GetResults(CBufferDouble *&result)
|
|
{
|
|
int total = m_cLayers.Total();
|
|
CNeuronBase *temp = m_cLayers.At(total - 1);
|
|
if(!temp)
|
|
return false;
|
|
CBufferDouble *output=temp.GetOutputs();
|
|
if(!output)
|
|
return false;
|
|
if(!result)
|
|
{
|
|
result = new CBufferDouble();
|
|
if(!result)
|
|
return false;
|
|
}
|
|
//if(!result.m_mMatrix.Init(output.m_mMatrix.Rows(),output.m_mMatrix.Cols()))
|
|
// {
|
|
// delete result;
|
|
// return false;
|
|
// }
|
|
//if(m_cOpenCL)
|
|
// if(!output.BufferRead())
|
|
// return false;
|
|
result.m_mMatrix=output.m_mMatrix;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод сохранение элементов класса в файл |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Save(string file_name = NULL)
|
|
{
|
|
//--- Блок контролей
|
|
if(file_name == NULL || file_name == "")
|
|
file_name = defFileName;
|
|
//--- Окрываем файл для записи
|
|
int handle = FileOpen(file_name, FILE_WRITE | FILE_BIN);
|
|
//--- Вызываем метод сохранения класса по хендлу файла
|
|
bool result = Save(handle);
|
|
//--- Закрываем открытый файл
|
|
FileClose(handle);
|
|
//---
|
|
return result;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод сохранение элементов класса в файл |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Save(const int file_handle)
|
|
{
|
|
//--- Блок контролей
|
|
if(file_handle == INVALID_HANDLE || !m_cLossFunction || !m_cLayers)
|
|
return false;
|
|
//--- Сохраняем константы
|
|
if(!FileWriteInteger(file_handle, (int)m_bOpenCL) ||
|
|
!FileWriteDouble(file_handle, m_dNNLoss) ||
|
|
!FileWriteInteger(file_handle, m_iLossSmoothFactor) ||
|
|
!FileWriteInteger(file_handle, (int)m_bPositionEncoder) ||
|
|
!FileWriteDouble(file_handle, m_dLearningRate) ||
|
|
!FileWriteArray(file_handle, m_adBeta, 0) ||
|
|
!FileWriteArray(file_handle, m_adLambda, 0) ||
|
|
!FileWriteInteger(file_handle, (int)m_cLossFunction.LossFunction()))
|
|
return false;
|
|
//--- Сохраняем объект позиционного кодирования при необходимости
|
|
if(m_bPositionEncoder)
|
|
{
|
|
if(CheckPointer(m_cPositionEncoder) == POINTER_INVALID ||
|
|
!m_cPositionEncoder.Save(file_handle))
|
|
return false;
|
|
}
|
|
//--- Вызываем метод сохранения данных динамического массива нейронных слоёв
|
|
return m_cLayers.Save(file_handle);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод восстановления класса из сохранённых данных |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Load(string file_name = NULL, bool common = false)
|
|
{
|
|
//--- Блок контролей
|
|
string path = TerminalInfoString(TERMINAL_COMMONDATA_PATH);
|
|
if(!FileIsExist(file_name, (common ? FILE_COMMON : 0)))
|
|
file_name = defFileName;
|
|
//--- Открываем файл и вызываем метод загрузки данных по хендлу файла
|
|
int handle = FileOpen(file_name, FILE_READ | FILE_BIN | FILE_SHARE_READ | (common ? FILE_COMMON : 0));
|
|
bool result = Load(handle);
|
|
FileClose(handle);
|
|
//---
|
|
return result;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод восстановления класса из сохранённых данных |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::Load(const int file_handle)
|
|
{
|
|
//--- Блок контролей
|
|
if(file_handle == INVALID_HANDLE)
|
|
return false;
|
|
//--- Считываем константы
|
|
m_bOpenCL = (bool)FileReadInteger(file_handle);
|
|
m_dNNLoss = FileReadDouble(file_handle);
|
|
m_iLossSmoothFactor = FileReadInteger(file_handle);
|
|
m_bPositionEncoder = (bool)FileReadInteger(file_handle);
|
|
m_dLearningRate = FileReadDouble(file_handle);
|
|
if(FileReadArray(file_handle, m_adBeta, 0) < 2 ||
|
|
FileReadArray(file_handle, m_adLambda, 0) < 2)
|
|
return false;
|
|
ENUM_LOSS_FUNCTION loss=(ENUM_LOSS_FUNCTION) FileReadInteger(file_handle);
|
|
if(!LossFunction(loss,0,0))
|
|
{
|
|
Print("Error, unknown loss function: ",EnumToString(loss));
|
|
return false;
|
|
}
|
|
//--- Загружаем объект позиционного кодирования
|
|
if(m_bPositionEncoder)
|
|
{
|
|
if(!m_cPositionEncoder)
|
|
{
|
|
m_cPositionEncoder = new CPositionEncoder();
|
|
if(!m_cPositionEncoder)
|
|
return false;
|
|
}
|
|
if(!m_cPositionEncoder.Load(file_handle))
|
|
return false;
|
|
}
|
|
//--- Инициализируем объект работы с OpenCL
|
|
if(m_bOpenCL)
|
|
{
|
|
if(!InitOpenCL())
|
|
m_bOpenCL = false;
|
|
}
|
|
else
|
|
if(!!m_cOpenCL)
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
}
|
|
//--- Инициализируем и загружаем данные динамического массива нейронных слоёв
|
|
if(m_cLayers)
|
|
delete m_cLayers;
|
|
m_cLayers = new CArrayLayers(file_handle);
|
|
if(!m_cLayers)
|
|
return false;
|
|
if(m_bOpenCL)
|
|
m_cLayers.SetOpencl(m_cOpenCL);
|
|
//---
|
|
return m_cLayers.Load(file_handle);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации объектов работы с OpenCL |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::InitOpenCL(void)
|
|
{
|
|
//--- Удаляем созданные ранее объекты OpenCL
|
|
if(!!m_cOpenCL)
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
}
|
|
//--- Создаём новый объект для работы с OpenCL
|
|
m_cOpenCL = new CMyOpenCL();
|
|
if(!m_cOpenCL)
|
|
return false;
|
|
//--- Инициализируем объект работы с OpenCL
|
|
if(!m_cOpenCL.Initialize(cl_program, true))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.SetKernelsCount(29))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.SetBuffersCount(10))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
//--- Инициализируем кернелы OpenCL
|
|
if(!m_cOpenCL.KernelCreate(def_k_PerceptronFeedForward, "PerceptronFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_Normalize, "Normalize"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_CalcOutputGradient, "CalcOutputGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_DeActivateGradient, "DeActivateGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_CalcHiddenGradient, "CalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_CalcDeltaWeights, "CalcDeltaWeights"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_SGDUpdate, "SGDUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_MomentumUpdate, "MomentumUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AdaGradUpdate, "AdaGradUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_RMSPropUpdate, "RMSPropUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AdaDeltaUpdate, "AdaDeltaUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AdamUpdate, "AdamUpdate"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_ProofFeedForward, "ProofFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_ProofHiddenGradients, "ProofCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_ConvolutionFeedForward, "ConvolutionFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_ConvolutionHiddenGradients, "ConvolutionCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_ConvolutionDeltaWeights, "ConcolutionCalcDeltaWeights"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_LSTMFeedForward, "LSTMFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_LSTMHiddenGradients, "LSTMCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AttentionFeedForward, "AttentionFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AttentionScoreGradients, "AttentionCalcScoreGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_AttentionHiddenGradients, "AttentionCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_GPTFeedForward, "GPTFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_GPTScoreGradients, "GPTCalcScoreGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_GPTHiddenGradients, "GPTCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_BatchNormFeedForward, "BatchNormFeedForward"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_BatchNormCalcHiddenGradient, "BatchNormCalcHiddenGradient"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_BatchNormCalcDeltaWeights, "BatchNormCalcDeltaWeights"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
if(!m_cOpenCL.KernelCreate(def_k_MaskMult, "MaskMult"))
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
return false;
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод передачи указателя на объект OpenCL до всех |
|
|
//| внутренних объектов |
|
|
//+------------------------------------------------------------------+
|
|
void CNet::UseOpenCL(bool value)
|
|
{
|
|
if(!value)
|
|
{
|
|
if(CheckPointer(m_cOpenCL) == POINTER_INVALID)
|
|
{
|
|
m_bOpenCL = value;
|
|
return;
|
|
}
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
if(CheckPointer(m_cLayers) != POINTER_INVALID)
|
|
m_cLayers.SetOpencl(m_cOpenCL);
|
|
m_bOpenCL = value;
|
|
return;
|
|
}
|
|
//---
|
|
if(CheckPointer(m_cOpenCL) != POINTER_INVALID)
|
|
{
|
|
m_cOpenCL.Shutdown();
|
|
delete m_cOpenCL;
|
|
}
|
|
m_bOpenCL = InitOpenCL();
|
|
if(CheckPointer(m_cLayers) != POINTER_INVALID)
|
|
m_cLayers.SetOpencl(m_cOpenCL);
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод установки параметров обучения |
|
|
//+------------------------------------------------------------------+
|
|
void CNet::SetLearningRates(double learning_rate, double beta1 = 0.900000, double beta2 = 0.999000)
|
|
{
|
|
m_dLearningRate = learning_rate;
|
|
m_adBeta[0] = beta1;
|
|
m_adBeta[1] = beta2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод установки функции потерь |
|
|
//+------------------------------------------------------------------+
|
|
bool CNet::LossFunction(ENUM_LOSS_FUNCTION loss_function,double lambda1=0.000000,double lambda2 = 0.000000)
|
|
{
|
|
//--- сохраним параметры функции потерь
|
|
m_adLambda[0]=lambda1;
|
|
m_adLambda[1]=lambda2;
|
|
//--- удаляем старую функцию
|
|
if(m_cLossFunction)
|
|
{
|
|
delete m_cLossFunction;
|
|
m_cLossFunction=NULL;
|
|
}
|
|
//--- создаём новую функцию
|
|
switch(loss_function)
|
|
{
|
|
case LOSS_MSE:
|
|
m_cLossFunction=new CLoss_MSE();
|
|
break;
|
|
case LOSS_MAE:
|
|
m_cLossFunction=new CLoss_MAD();
|
|
break;
|
|
case LOSS_BCE:
|
|
m_cLossFunction=new CLoss_BCE();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
//--- проверим что функция потерь создана
|
|
return(m_cLossFunction!=NULL);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод получениия указателя на буфер градиентов по номеру слоя |
|
|
//+------------------------------------------------------------------+
|
|
CBufferDouble *CNet::GetGradient(uint layer) const
|
|
{
|
|
if(layer >= (uint)m_cLayers.Total())
|
|
return NULL;
|
|
//---
|
|
CNeuronBase *l = m_cLayers.At(layer);
|
|
return l.GetGradients();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод получения указателя на матрицу весов по номеру слоя |
|
|
//+------------------------------------------------------------------+
|
|
CBufferDouble *CNet::GetWeights(uint layer) const
|
|
{
|
|
if(layer >= (uint)m_cLayers.Total())
|
|
return NULL;
|
|
//---
|
|
CNeuronBase *l = m_cLayers.At(layer);
|
|
return l.GetWeights();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод получения указателя на буфер накопленных градиентов |
|
|
//| ошибки на уровне матрицы весов по номеру слоя |
|
|
//+------------------------------------------------------------------+
|
|
CBufferDouble *CNet::GetDeltaWeights(uint layer)const
|
|
{
|
|
if(layer >= (uint)m_cLayers.Total())
|
|
return NULL;
|
|
//---
|
|
CNeuronBase *l = m_cLayers.At(layer);
|
|
return l.GetDeltaWeights();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Установка режима работы модели |
|
|
//+------------------------------------------------------------------+
|
|
void CNet::TrainMode(bool mode)
|
|
{
|
|
m_bTrainMode = mode;
|
|
int total = m_cLayers.Total();
|
|
for(int i = 0; i < total; i++)
|
|
{
|
|
if(CheckPointer(m_cLayers.At(i)) == POINTER_INVALID)
|
|
continue;
|
|
CNeuronBase *temp = m_cLayers.At(i);
|
|
temp.TrainMode(mode);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод получения глубины используемых блоков GPT |
|
|
//+------------------------------------------------------------------+
|
|
//int CNet::GetGPTUnits(void)
|
|
// {
|
|
// int result = 0;
|
|
// if(CheckPointer(m_cLayers) == POINTER_INVALID)
|
|
// return result;
|
|
// int total = m_cLayers.Total();
|
|
// for(int i = 0; i < total; i++)
|
|
// {
|
|
// if(CheckPointer(m_cLayers.At(i)) == POINTER_INVALID)
|
|
// continue;
|
|
// if(m_cLayers.At(i).Type() == defNeuronGPT)
|
|
// {
|
|
// CNeuronGPT *temp = m_cLayers.At(i);
|
|
// result += temp.GetUnits() * temp.GetLayers();
|
|
// }
|
|
// if(m_cLayers.At(i).Type() == defNeuronLSTM)
|
|
// {
|
|
// CNeuronLSTM *temp = m_cLayers.At(i);
|
|
// result += temp.GetDepth();
|
|
// }
|
|
// }
|
|
////---
|
|
// return result;
|
|
// }
|
|
//+------------------------------------------------------------------+
|
|
//| Метод устоновки флага использования позиционного кодирования |
|
|
//+------------------------------------------------------------------+
|
|
void CNet::UsePositionEncoder(bool value)
|
|
{
|
|
m_bPositionEncoder = value;
|
|
if(!m_bPositionEncoder)
|
|
{
|
|
if(CheckPointer(m_cPositionEncoder) != POINTER_INVALID)
|
|
delete m_cPositionEncoder;
|
|
return;
|
|
}
|
|
//---
|
|
if(CheckPointer(m_cPositionEncoder) == POINTER_INVALID)
|
|
m_cPositionEncoder = new CPositionEncoder();
|
|
if(CheckPointer(m_cLayers) == POINTER_INVALID || m_cLayers.Total() < 1)
|
|
return;
|
|
CNeuronBase *temp = m_cLayers.At(0);
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
|
return;
|
|
if(!m_cPositionEncoder.InitEncoder(1, temp.GetOutputs().Total()))
|
|
UsePositionEncoder(false);
|
|
//---
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|