399 lines
34 KiB
MQL5
399 lines
34 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| NeuronProof.mqh |
|
|
//| Copyright 2021, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2021, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
//+------------------------------------------------------------------+
|
|
//| Подключаем библиотеки |
|
|
//+------------------------------------------------------------------+
|
|
#include "neuronbase.mqh"
|
|
#include <Math\Stat\Math.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| Class CNeuronProof |
|
|
//| Назначение: Класс организации работы подвыборочного слоя |
|
|
//+------------------------------------------------------------------+
|
|
class CNeuronProof : public CNeuronBase
|
|
{
|
|
protected:
|
|
uint m_iWindow; //Размер окна на входе нейронного слоя
|
|
uint m_iStep; //Размер шага входного окна
|
|
uint m_iNeurons; //Размер выхода одного фильтра
|
|
uint m_iWindowOut; //Количество фильтров/
|
|
ENUM_ACTIVATION m_eActivation; //Функция активации
|
|
public:
|
|
CNeuronProof(void);
|
|
~CNeuronProof(void) {};
|
|
//---
|
|
virtual bool Init(CLayerDescription *description);
|
|
virtual bool FeedForward(CNeuronBase *prevLayer);
|
|
virtual bool CalcOutputGradient(CBufferDouble *target) { return false;}
|
|
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 CBufferDouble *GetWeights(void) const { return(NULL); }
|
|
virtual CBufferDouble *GetDeltaWeights(void) const { return(NULL); }
|
|
virtual uint GetNeurons(void) const { return m_iNeurons; }
|
|
//--- Методы работы с файлами
|
|
virtual bool Save(const int file_handle);
|
|
virtual bool Load(const int file_handle);
|
|
//--- Метод идентификации объекта
|
|
virtual int Type(void) const { return(defNeuronProof); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор класса |
|
|
//+------------------------------------------------------------------+
|
|
CNeuronProof::CNeuronProof(void) : m_eActivation(ACT_MAX_POOLING),
|
|
m_iWindow(2),
|
|
m_iStep(1),
|
|
m_iWindowOut(1),
|
|
m_iNeurons(0)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод инициализации класса |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Init(CLayerDescription *description)
|
|
{
|
|
//--- Блок контролей
|
|
if(CheckPointer(description) == POINTER_INVALID || description.type != Type() ||
|
|
description.count <= 0)
|
|
return false;
|
|
//--- Сохраняем константы
|
|
m_iWindow = description.window;
|
|
m_iStep = description.step;
|
|
m_iWindowOut = description.window_out;
|
|
m_iNeurons = description.count;
|
|
if(m_iWindow <= 0 || m_iStep <= 0 || m_iWindowOut <= 0 || m_iNeurons <= 0)
|
|
return false;
|
|
//--- Проверка функции потерь
|
|
switch(description.activation)
|
|
{
|
|
case ACT_AVERAGE_POOLING:
|
|
case ACT_MAX_POOLING:
|
|
m_eActivation = description.activation;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
//--- Инициализируем буфер результатов
|
|
if(CheckPointer(m_cOutputs) == POINTER_INVALID)
|
|
{
|
|
m_cOutputs = new CBufferDouble();
|
|
if(CheckPointer(m_cOutputs) == POINTER_INVALID)
|
|
return false;
|
|
}
|
|
if(!m_cOutputs.BufferInit(m_iNeurons * m_iWindowOut, 0))
|
|
return false;
|
|
//--- Инициализируем буфер градиентов ошибки
|
|
if(CheckPointer(m_cGradients) == POINTER_INVALID)
|
|
{
|
|
m_cGradients = new CBufferDouble();
|
|
if(CheckPointer(m_cGradients) == POINTER_INVALID)
|
|
return false;
|
|
}
|
|
if(!m_cGradients.BufferInit(m_iNeurons * m_iWindowOut, 0))
|
|
return false;
|
|
//---
|
|
m_eOptimization = None;
|
|
//--- Удаляем не используемые объекты
|
|
if(CheckPointer(m_cActivation) != POINTER_INVALID)
|
|
delete m_cActivation;
|
|
if(CheckPointer(m_cWeights) != POINTER_INVALID)
|
|
delete m_cWeights;
|
|
if(CheckPointer(m_cDeltaWeights) != POINTER_INVALID)
|
|
delete m_cDeltaWeights;
|
|
for(int i = 0; i < 2; i++)
|
|
if(CheckPointer(m_cMomenum[i]) != POINTER_INVALID)
|
|
delete m_cMomenum[i];
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод прямого прохода |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::FeedForward(CNeuronBase *prevLayer)
|
|
{
|
|
//--- Блок контролей
|
|
if(CheckPointer(prevLayer) == POINTER_INVALID ||
|
|
CheckPointer(m_cOutputs) == POINTER_INVALID ||
|
|
CheckPointer(prevLayer.GetOutputs()) == POINTER_INVALID)
|
|
return false;
|
|
CBufferDouble *input_data = prevLayer.GetOutputs();
|
|
//--- Разветвление алгоритма в зависимости от устройста выполнениия операций
|
|
if(CheckPointer(m_cOpenCL) == POINTER_INVALID)
|
|
{
|
|
uint input_total = input_data.Total();
|
|
uint output_total = m_cOutputs.Total();
|
|
uint input_neurons = input_total;
|
|
if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof)
|
|
{
|
|
CNeuronProof *temp = prevLayer;
|
|
input_neurons = temp.GetNeurons();
|
|
}
|
|
double array[];
|
|
if(ArrayResize(array, m_iWindow) < 0)
|
|
return false;
|
|
for(uint f = 0; f < m_iWindowOut; f++)
|
|
{
|
|
uint shift_inp = f * input_neurons;
|
|
uint shift_out = f * m_iNeurons;
|
|
if(shift_inp >= input_total)
|
|
{
|
|
for(uint i = shift_out; i < output_total; i++)
|
|
m_cOutputs.Update(i, 0);
|
|
break;
|
|
}
|
|
for(uint o = 0; o < m_iNeurons; o++)
|
|
{
|
|
uint shift = o * m_iStep;
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
array[i] = ((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons ? 0 :
|
|
input_data.At(shift_inp + shift + i));
|
|
switch(m_eActivation)
|
|
{
|
|
case ACT_MAX_POOLING:
|
|
m_cOutputs.Update(shift_out + o, array[ArrayMaximum(array)]);
|
|
break;
|
|
case ACT_AVERAGE_POOLING:
|
|
m_cOutputs.Update(shift_out + o, MathMean(array));
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // Блок работы с OpenCL
|
|
{
|
|
//--- Создание и загрузка содержимое буферов в контекст OpenCL
|
|
if(input_data.GetIndex() < 0 && !input_data.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
if(m_cOutputs.GetIndex() < 0 && !m_cOutputs.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
//--- Передача параметров в кернел
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofFeedForward, def_prff_inputs, input_data.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofFeedForward, def_prff_outputs, m_cOutputs.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_inputs_total, input_data.Total()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_window, m_iWindow))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_step, m_iStep))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_window_out, m_iWindowOut))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_activation, (int)m_eActivation))
|
|
return false;
|
|
//--- Постановка кернела в очередь на выполнение
|
|
uint input_neurons = input_data.Total();
|
|
if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof)
|
|
{
|
|
CNeuronProof *temp = prevLayer;
|
|
input_neurons = temp.GetNeurons();
|
|
}
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_input_neurons, input_neurons))
|
|
return false;
|
|
int off_set[] = {0};
|
|
int NDRange[] = {(int)m_iNeurons};
|
|
if(!m_cOpenCL.Execute(def_k_ProofFeedForward, 1, off_set, NDRange))
|
|
return false;
|
|
//--- Загрузка результатов расчёта
|
|
if(!m_cOutputs.BufferRead())
|
|
return false;
|
|
input_data.BufferFree();
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод распределения градиента ошибки через скрытый слой |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::CalcHiddenGradient(CNeuronBase *prevLayer)
|
|
{
|
|
//--- Блок контролей
|
|
if(CheckPointer(prevLayer) == POINTER_INVALID ||
|
|
CheckPointer(m_cOutputs) == POINTER_INVALID ||
|
|
CheckPointer(m_cGradients) == POINTER_INVALID ||
|
|
CheckPointer(prevLayer.GetOutputs()) == POINTER_INVALID ||
|
|
CheckPointer(prevLayer.GetGradients()) == POINTER_INVALID)
|
|
return false;
|
|
CBufferDouble *input_data = prevLayer.GetOutputs();
|
|
CBufferDouble *input_gradient = prevLayer.GetGradients();
|
|
if(!input_gradient.BufferInit(input_data.Total(), 0))
|
|
return false;
|
|
//--- Разветвление алгоритма в зависимости от устройста выполнениия операций
|
|
if(CheckPointer(m_cOpenCL) == POINTER_INVALID)
|
|
{
|
|
uint input_total = input_data.Total();
|
|
uint output_total = m_cOutputs.Total();
|
|
uint input_neurons = input_total;
|
|
if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof)
|
|
{
|
|
CNeuronProof *temp = prevLayer;
|
|
input_neurons = temp.GetNeurons();
|
|
}
|
|
for(uint f = 0; f < m_iWindowOut; f++)
|
|
{
|
|
uint shift_inp = f * input_neurons;
|
|
uint shift_out = f * m_iNeurons;
|
|
if(shift_inp >= input_total)
|
|
break;
|
|
for(uint o = 0; o < m_iNeurons; o++)
|
|
{
|
|
uint shift = o * m_iStep;
|
|
double out = m_cOutputs.At(shift_out + o);
|
|
double gradient = m_cGradients.At(shift_out + o);
|
|
switch(m_eActivation)
|
|
{
|
|
case ACT_MAX_POOLING:
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
{
|
|
if((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons)
|
|
break;
|
|
if(input_data.At(shift_inp + shift + i) == out)
|
|
{
|
|
input_gradient.Update(shift_inp + shift + i,
|
|
input_gradient.At(shift_inp + shift + i) + gradient);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ACT_AVERAGE_POOLING:
|
|
gradient /= (double)m_iWindow;
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
{
|
|
if((shift_inp + shift + i) >= input_total || (shift + i) >= input_neurons)
|
|
break;
|
|
input_gradient.Update(shift_inp + shift + i,
|
|
input_gradient.At(shift_inp + shift + i) + gradient);
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // Блок работы с OpenCL
|
|
{
|
|
//--- Создание и загрузка содержимое буферов в контекст OpenCL
|
|
if(input_data.GetIndex() < 0 && !input_data.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
if(m_cOutputs.GetIndex() < 0 && !m_cOutputs.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
if(input_gradient.GetIndex() < 0 && !input_gradient.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
if(m_cGradients.GetIndex() < 0 && !m_cGradients.BufferCreate(m_cOpenCL))
|
|
return false;
|
|
//--- Передача параметров в кернел
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_inputs, input_data.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_outputs, m_cOutputs.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_gradients, m_cGradients.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgumentBuffer(def_k_ProofHiddenGradients, def_prhgr_gradient_inputs, input_gradient.GetIndex()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_inputs_total, input_data.Total()))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_window, m_iWindow))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_step, m_iStep))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_window_out, m_iWindowOut))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_activation, (int)m_eActivation))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_neurons, m_iNeurons))
|
|
return false;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofHiddenGradients, def_prhgr_outputs_total, m_cOutputs.Total()))
|
|
return false;
|
|
//--- Постановка кернела в очередь на выполнене
|
|
uint input_neurons = input_data.Total();
|
|
if(prevLayer.Type() == defNeuronConv || prevLayer.Type() == defNeuronProof)
|
|
{
|
|
CNeuronProof *temp = prevLayer;
|
|
input_neurons = temp.GetNeurons();
|
|
}
|
|
int off_set[] = {0};
|
|
int NDRange[] = {(int)input_neurons};
|
|
if(!m_cOpenCL.Execute(def_k_ProofHiddenGradients, 1, off_set, NDRange))
|
|
return false;
|
|
//--- Загрузка результатов
|
|
if(!input_gradient.BufferRead())
|
|
return false;
|
|
//--- Очистка памяти контекста OpenCL
|
|
input_data.BufferFree();
|
|
m_cOutputs.BufferFree();
|
|
m_cGradients.BufferFree();
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод сохранения элементов касса в файл |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Save(const int file_handle)
|
|
{
|
|
//--- Блок контролей
|
|
if(file_handle == INVALID_HANDLE)
|
|
return false;
|
|
//--- Сохраняем константы
|
|
if(FileWriteInteger(file_handle, Type()) <= 0)
|
|
return false;
|
|
if(FileWriteInteger(file_handle, (int)m_iWindow) <= 0)
|
|
return false;
|
|
if(FileWriteInteger(file_handle, (int)m_iStep) <= 0)
|
|
return false;
|
|
if(FileWriteInteger(file_handle, (int)m_iWindowOut) <= 0)
|
|
return false;
|
|
if(FileWriteInteger(file_handle, (int)m_iNeurons) <= 0)
|
|
return false;
|
|
if(FileWriteInteger(file_handle, (int)m_eActivation) <= 0)
|
|
return false;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Метод восстановления работы класса из файла |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Load(const int file_handle)
|
|
{
|
|
//--- Блок контролей
|
|
if(file_handle == INVALID_HANDLE)
|
|
return false;
|
|
//--- Загружаем константы
|
|
m_iWindow = (uint)FileReadInteger(file_handle);
|
|
m_iStep = (uint)FileReadInteger(file_handle);
|
|
m_iWindowOut = (uint)FileReadInteger(file_handle);
|
|
m_iNeurons = (uint)FileReadInteger(file_handle);
|
|
m_eActivation = (ENUM_ACTIVATION)FileReadInteger(file_handle);
|
|
//--- Инициализтируем и загружаем буфер результатов
|
|
if(CheckPointer(m_cOutputs) == POINTER_INVALID)
|
|
{
|
|
m_cOutputs = new CBufferDouble();
|
|
if(CheckPointer(m_cOutputs) == POINTER_INVALID)
|
|
return false;
|
|
}
|
|
if(!m_cOutputs.BufferInit(m_iNeurons * m_iWindowOut, 0))
|
|
return false;
|
|
//--- Инициализируем и загружаем буфер градиентов ошибки
|
|
if(CheckPointer(m_cGradients) == POINTER_INVALID)
|
|
{
|
|
m_cGradients = new CBufferDouble();
|
|
if(CheckPointer(m_cGradients) == POINTER_INVALID)
|
|
return false;
|
|
}
|
|
if(!m_cGradients.BufferInit(m_iNeurons * m_iWindowOut, 0))
|
|
return false;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|