363 lines
30 KiB
MQL5
363 lines
30 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| NeuronProof.mqh |
|
|
//| Copyright 2021, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2021, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
//+------------------------------------------------------------------+
|
|
//| Connect libraries |
|
|
//+------------------------------------------------------------------+
|
|
#include "neuronbase.mqh"
|
|
#include <Math\Stat\Math.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| Class CNeuronProof |
|
|
//| Purpose: Class organizing the pooling layer |
|
|
//+------------------------------------------------------------------+
|
|
class CNeuronProof : public CNeuronBase
|
|
{
|
|
protected:
|
|
uint m_iWindow; //Window size at the input of the neural layer
|
|
uint m_iStep; //Input window step size
|
|
uint m_iNeurons; //Output size of one filter
|
|
uint m_iWindowOut; //Number of filters
|
|
ENUM_PROOF m_eActivation; //Activation function
|
|
public:
|
|
CNeuronProof(void);
|
|
~CNeuronProof(void) {};
|
|
//---
|
|
virtual bool Init(const CLayerDescription *desc) override;
|
|
virtual bool FeedForward(CNeuronBase *prevLayer) override;
|
|
virtual bool CalcOutputGradient(CBufferType *target, ENUM_LOSS_FUNCTION loss) override { return false;}
|
|
virtual bool CalcHiddenGradient(CNeuronBase *prevLayer) override;
|
|
virtual bool CalcDeltaWeights(CNeuronBase *prevLayer, bool read) override { return true; }
|
|
virtual bool UpdateWeights(int batch_size, TYPE learningRate, VECTOR &Beta, VECTOR &Lambda) override
|
|
{ return true; }
|
|
//---
|
|
virtual CBufferType *GetWeights(void) override const { return(NULL); }
|
|
virtual CBufferType *GetDeltaWeights(void) override const { return(NULL); }
|
|
virtual uint GetNeurons(void) const { return m_iNeurons; }
|
|
//--- File handling methods
|
|
virtual bool Save(const int file_handle) override;
|
|
virtual bool Load(const int file_handle) override;
|
|
//--- Object identification method
|
|
virtual int Type(void) override const { return(defNeuronProof); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Class constructor |
|
|
//+------------------------------------------------------------------+
|
|
CNeuronProof::CNeuronProof(void) : m_eActivation(AF_MAX_POOLING),
|
|
m_iWindow(2),
|
|
m_iStep(1),
|
|
m_iWindowOut(1),
|
|
m_iNeurons(0)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Class initialization method |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Init(const CLayerDescription *description)
|
|
{
|
|
//--- Control block
|
|
if(!description || description.type != Type() ||
|
|
description.count <= 0)
|
|
return false;
|
|
//--- Save constants
|
|
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;
|
|
//--- Check activation function
|
|
switch((ENUM_PROOF)description.activation)
|
|
{
|
|
case AF_AVERAGE_POOLING:
|
|
case AF_MAX_POOLING:
|
|
m_eActivation = (ENUM_PROOF)description.activation;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
//--- Initialize results buffer
|
|
if(!m_cOutputs)
|
|
if(!(m_cOutputs = new CBufferType()))
|
|
return false;
|
|
if(!m_cOutputs.BufferInit(m_iWindowOut, m_iNeurons, 0))
|
|
return false;
|
|
//--- Initialize the error gradient buffer
|
|
if(!m_cGradients)
|
|
if(!(m_cGradients = new CBufferType()))
|
|
return false;
|
|
if(!m_cGradients.BufferInit(m_iWindowOut, m_iNeurons, 0))
|
|
return false;
|
|
//---
|
|
m_eOptimization = None;
|
|
//--- Delete unused objects
|
|
if(!!m_cActivation)
|
|
delete m_cActivation;
|
|
if(!!m_cWeights)
|
|
delete m_cWeights;
|
|
if(!!m_cDeltaWeights)
|
|
delete m_cDeltaWeights;
|
|
for(int i = 0; i < 2; i++)
|
|
if(!!m_cMomenum[i])
|
|
delete m_cMomenum[i];
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Feed-forward method |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::FeedForward(CNeuronBase *prevLayer)
|
|
{
|
|
//--- Control block
|
|
if(!prevLayer || !m_cOutputs ||
|
|
!prevLayer.GetOutputs())
|
|
return false;
|
|
CBufferType *input_data = prevLayer.GetOutputs();
|
|
//--- Branching of the algorithm depending on the device used for operations
|
|
if(!m_cOpenCL)
|
|
{
|
|
MATRIX inputs = input_data.m_mMatrix;
|
|
if(inputs.Rows() != m_iWindowOut)
|
|
{
|
|
ulong cols = (input_data.Total() + m_iWindowOut - 1) / m_iWindowOut;
|
|
if(!inputs.Reshape(m_iWindowOut, cols))
|
|
return false;
|
|
}
|
|
//--- Create a local matrix to collect data from one filter
|
|
MATRIX array = MATRIX::Zeros(m_iNeurons, m_iWindow);
|
|
m_cOutputs.m_mMatrix.Fill(0);
|
|
//--- Filter iteration loop
|
|
for(uint f = 0; f < m_iWindowOut; f++)
|
|
{
|
|
//--- Loop through the elements of the results buffer
|
|
for(uint o = 0; o < m_iNeurons; o++)
|
|
{
|
|
uint shift = o * m_iStep;
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
array[o, i] = ((shift + i) >= inputs.Cols() ? 0 :
|
|
inputs[f, shift + i]);
|
|
}
|
|
//--- Save the current result in accordance with the activation function
|
|
switch(m_eActivation)
|
|
{
|
|
case AF_MAX_POOLING:
|
|
if(!m_cOutputs.Row(array.Max(1), f))
|
|
return false;;
|
|
break;
|
|
case AF_AVERAGE_POOLING:
|
|
if(!m_cOutputs.Row(array.Mean(1), f))
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else // OpenCL operations block
|
|
{
|
|
//--- check the presence of buffers in the OpenCL context
|
|
if(input_data.GetIndex() < 0)
|
|
return false;
|
|
if(m_cOutputs.GetIndex() < 0)
|
|
return false;
|
|
//--- Pass parameters to the kernel
|
|
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_activation, (int)m_eActivation))
|
|
return false;
|
|
ulong input_neurons = (input_data.Total() + m_iWindowOut - 1) / m_iWindowOut;
|
|
if(!m_cOpenCL.SetArgument(def_k_ProofFeedForward, def_prff_input_neurons, input_neurons))
|
|
return false;
|
|
//--- Place kernel to the execution queue
|
|
uint off_set[] = {0, 0};
|
|
uint NDRange[] = {m_iNeurons, m_iWindowOut};
|
|
if(!m_cOpenCL.Execute(def_k_ProofFeedForward, 2, off_set, NDRange))
|
|
return false;
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method for propagating error gradient through hidden layer |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::CalcHiddenGradient(CNeuronBase *prevLayer)
|
|
{
|
|
//--- Control block
|
|
if(!prevLayer || !m_cOutputs ||
|
|
!m_cGradients || !prevLayer.GetOutputs() ||
|
|
!prevLayer.GetGradients())
|
|
return false;
|
|
CBufferType *input_data = prevLayer.GetOutputs();
|
|
CBufferType *input_gradient = prevLayer.GetGradients();
|
|
if(!input_gradient.BufferInit(input_data.Rows(), input_data.Cols(), 0))
|
|
return false;
|
|
//--- Branching of the algorithm depending on the device used for operations
|
|
if(!m_cOpenCL)
|
|
{
|
|
MATRIX inputs = input_data.m_mMatrix;
|
|
ulong cols = (input_data.Total() + m_iWindowOut - 1) / m_iWindowOut;
|
|
if(inputs.Rows() != m_iWindowOut)
|
|
{
|
|
if(!inputs.Reshape(m_iWindowOut, cols))
|
|
return false;
|
|
}
|
|
//--- Create a local matrix to collect data from one filter
|
|
MATRIX inputs_grad = MATRIX::Zeros(m_iWindowOut, cols);
|
|
//--- Filter iteration loop
|
|
for(uint f = 0; f < m_iWindowOut; f++)
|
|
{
|
|
//--- Loop through the elements of the results buffer
|
|
for(uint o = 0; o < m_iNeurons; o++)
|
|
{
|
|
uint shift = o * m_iStep;
|
|
TYPE out = m_cOutputs.m_mMatrix[f, o];
|
|
TYPE gradient = m_cGradients.m_mMatrix[f, o];
|
|
//--- Transfer gradient in accordance with the activation function
|
|
switch(m_eActivation)
|
|
{
|
|
case AF_MAX_POOLING:
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
{
|
|
if((shift + i) >= cols)
|
|
break;
|
|
if(inputs[f, shift + i] == out)
|
|
{
|
|
inputs_grad[f, shift + i] += gradient;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case AF_AVERAGE_POOLING:
|
|
gradient /= (TYPE)m_iWindow;
|
|
for(uint i = 0; i < m_iWindow; i++)
|
|
{
|
|
if((shift + i) >= cols)
|
|
break;
|
|
inputs_grad[f, shift + i] += gradient;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
//--- copy the gradient matrix to the buffer of the previous neural layer
|
|
if(!inputs_grad.Reshape(input_gradient.Rows(), input_gradient.Cols()))
|
|
return false;
|
|
input_gradient.m_mMatrix = inputs_grad;
|
|
}
|
|
else // OpenCL operation block
|
|
{
|
|
//--- check the presence of buffers in the OpenCL context
|
|
if(input_data.GetIndex() < 0)
|
|
return false;
|
|
if(m_cOutputs.GetIndex() < 0)
|
|
return false;
|
|
if(input_gradient.GetIndex() < 0)
|
|
return false;
|
|
if(m_cGradients.GetIndex() < 0)
|
|
return false;
|
|
//--- Pass parameters to the kernel
|
|
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_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;
|
|
//--- Place kernel to the execution queue
|
|
ulong input_neurons = (input_data.Total() + m_iWindowOut - 1) / m_iWindowOut;
|
|
uint off_set[] = {0, 0};
|
|
uint NDRange[] = {(uint)input_neurons, m_iWindowOut};
|
|
if(!m_cOpenCL.Execute(def_k_ProofHiddenGradients, 2, off_set, NDRange))
|
|
return false;
|
|
input_gradient.BufferRead();
|
|
}
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method for saving class elements to a file |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Save(const int file_handle)
|
|
{
|
|
//--- Control block
|
|
if(file_handle == INVALID_HANDLE)
|
|
return false;
|
|
//--- Save constants
|
|
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;
|
|
//--- Successful completion of the method
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method for restoring the class from a file |
|
|
//+------------------------------------------------------------------+
|
|
bool CNeuronProof::Load(const int file_handle)
|
|
{
|
|
//--- Control block
|
|
if(file_handle == INVALID_HANDLE)
|
|
return false;
|
|
//--- Load constants
|
|
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_PROOF)FileReadInteger(file_handle);
|
|
//--- Initialize and load the results buffer
|
|
if(!m_cOutputs)
|
|
{
|
|
m_cOutputs = new CBufferType();
|
|
if(!m_cOutputs)
|
|
return false;
|
|
}
|
|
if(!m_cOutputs.BufferInit(m_iWindowOut, m_iNeurons, 0))
|
|
return false;
|
|
//--- Initialize and load the error gradient buffer
|
|
if(!m_cGradients)
|
|
{
|
|
m_cGradients = new CBufferType();
|
|
if(!m_cGradients)
|
|
return false;
|
|
}
|
|
if(!m_cGradients.BufferInit(m_iWindowOut, m_iNeurons, 0))
|
|
return false;
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|