243 lines
8.1 KiB
MQL5
243 lines
8.1 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| NeuronBase.mqh |
|
||
|
|
//| |
|
||
|
|
//| CNeuronBase - the pure-MQL5 dense neuron: init, forward, |
|
||
|
|
//| gradients, activation, persistence. |
|
||
|
|
//| |
|
||
|
|
//| Included from AI\Network.mqh AFTER every class declaration - |
|
||
|
|
//| bodies only, no declarations. Relocation is behaviour-neutral by |
|
||
|
|
//| construction: nothing here is reachable until Network.mqh ends. |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#ifndef WARRIOR_AI_IMPL_NEURONBASE_MQH
|
||
|
|
#define WARRIOR_AI_IMPL_NEURONBASE_MQH
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double CNeuronBase::alpha = momentum; // momentum
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CNeuronBase::CNeuronBase(void) :
|
||
|
|
outputVal(1),
|
||
|
|
gradient(0),
|
||
|
|
activation(TANH),
|
||
|
|
t(1),
|
||
|
|
optimization(SGD)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CNeuronBase::~CNeuronBase(void)
|
||
|
|
{
|
||
|
|
if(CheckPointer(Connections) != POINTER_INVALID)
|
||
|
|
delete Connections;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronBase::Init(uint numOutputs, uint myIndex, ENUM_OPTIMIZATION optimization_type, double weighScale = -1.0)
|
||
|
|
{
|
||
|
|
if(CheckPointer(Connections) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
Connections = new CArrayCon();
|
||
|
|
if(CheckPointer(Connections) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
if(!Connections.Reserve(fmax(numOutputs, 1)))
|
||
|
|
{
|
||
|
|
Print(__FUNCTION__ + ": Connections.Reserve failed (allocation failure?) - neuron would silently end up with 0 connections");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for(uint c = 0; c < numOutputs; c++)
|
||
|
|
{
|
||
|
|
if(!Connections.CreateElementScaled(c, weighScale))
|
||
|
|
return false;
|
||
|
|
Connections.IncreaseTotal();
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
m_myIndex = myIndex;
|
||
|
|
optimization = optimization_type;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronBase::feedForward(CObject *&SourceObject)
|
||
|
|
{
|
||
|
|
bool result = false;
|
||
|
|
//---
|
||
|
|
if(CheckPointer(SourceObject) == POINTER_INVALID)
|
||
|
|
return result;
|
||
|
|
//---
|
||
|
|
CLayer *temp_l;
|
||
|
|
CNeuronPool *temp_n;
|
||
|
|
switch(SourceObject.Type())
|
||
|
|
{
|
||
|
|
case defLayer:
|
||
|
|
temp_l = SourceObject;
|
||
|
|
result = feedForward(temp_l);
|
||
|
|
break;
|
||
|
|
case defNeuronConv:
|
||
|
|
case defNeuronPool:
|
||
|
|
case defNeuronLSTM:
|
||
|
|
temp_n = SourceObject;
|
||
|
|
result = feedForward(temp_n.getOutputLayer());
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronBase::updateInputWeights(CObject *SourceObject)
|
||
|
|
{
|
||
|
|
bool result = false;
|
||
|
|
//---
|
||
|
|
if(CheckPointer(SourceObject) == POINTER_INVALID)
|
||
|
|
return result;
|
||
|
|
//---
|
||
|
|
CLayer *temp_l;
|
||
|
|
CNeuronPool *temp_n;
|
||
|
|
switch(SourceObject.Type())
|
||
|
|
{
|
||
|
|
case defLayer:
|
||
|
|
temp_l = SourceObject;
|
||
|
|
result = updateInputWeights(temp_l);
|
||
|
|
break;
|
||
|
|
case defNeuronConv:
|
||
|
|
case defNeuronPool:
|
||
|
|
case defNeuronLSTM:
|
||
|
|
temp_n = SourceObject;
|
||
|
|
temp_l = temp_n.getOutputLayer();
|
||
|
|
result = updateInputWeights(temp_l);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronBase::calcHiddenGradients(CObject *&TargetObject)
|
||
|
|
{
|
||
|
|
bool result = false;
|
||
|
|
//---
|
||
|
|
if(CheckPointer(TargetObject) == POINTER_INVALID)
|
||
|
|
return result;
|
||
|
|
//---
|
||
|
|
CLayer *temp_l;
|
||
|
|
CNeuronPool *temp_n;
|
||
|
|
switch(TargetObject.Type())
|
||
|
|
{
|
||
|
|
case defLayer:
|
||
|
|
temp_l = TargetObject;
|
||
|
|
result = calcHiddenGradients(temp_l);
|
||
|
|
break;
|
||
|
|
case defNeuronConv:
|
||
|
|
case defNeuronPool:
|
||
|
|
case defNeuronLSTM:
|
||
|
|
switch(Type())
|
||
|
|
{
|
||
|
|
case defNeuron:
|
||
|
|
temp_n = TargetObject;
|
||
|
|
result = temp_n.calcInputGradients(GetPointer(this), m_myIndex);
|
||
|
|
break;
|
||
|
|
case defNeuronLSTM:
|
||
|
|
temp_n = TargetObject;
|
||
|
|
temp_l = getOutputLayer();
|
||
|
|
if(!temp_n.calcInputGradients(temp_l))
|
||
|
|
{
|
||
|
|
result = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
result = calcHiddenGradients(temp_l);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
temp_l =getOutputLayer();
|
||
|
|
temp_n = TargetObject;
|
||
|
|
result = temp_n.calcInputGradients(temp_l);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronBase::Save(int file_handle)
|
||
|
|
{
|
||
|
|
if(file_handle == INVALID_HANDLE)
|
||
|
|
return false;
|
||
|
|
if(FileWriteInteger(file_handle, Type()) < INT_VALUE)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
if(FileWriteInteger(file_handle, (int)activation, INT_VALUE) < INT_VALUE)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
if(FileWriteInteger(file_handle, (int)optimization, INT_VALUE) < INT_VALUE)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
if(FileWriteInteger(file_handle, t, INT_VALUE) < INT_VALUE)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
return Connections.Save(file_handle);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double CNeuronBase::activationFunction(double x)
|
||
|
|
{
|
||
|
|
switch(activation)
|
||
|
|
{
|
||
|
|
case NONE:
|
||
|
|
return(x);
|
||
|
|
break;
|
||
|
|
case TANH:
|
||
|
|
return TanhFunction(x);
|
||
|
|
break;
|
||
|
|
case SIGMOID:
|
||
|
|
return SigmoidFunction(x);
|
||
|
|
break;
|
||
|
|
case PRELU:
|
||
|
|
// fixed 0.01 slope, matches CNeuronConv::activationFunction's `param` default -
|
||
|
|
// this case was previously missing here, so any generic Dense (defNeuron) layer
|
||
|
|
// given PRELU silently fell through to the `return x` below (pure linear identity,
|
||
|
|
// no nonlinearity at all) instead of leaky-ReLU.
|
||
|
|
return(x >= 0 ? x : 0.01 * x);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return x;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double CNeuronBase::activationFunctionDerivative(double x)
|
||
|
|
{
|
||
|
|
switch(activation)
|
||
|
|
{
|
||
|
|
case NONE:
|
||
|
|
return(1);
|
||
|
|
break;
|
||
|
|
case TANH:
|
||
|
|
return TanhFunctionDerivative(x);
|
||
|
|
break;
|
||
|
|
case SIGMOID:
|
||
|
|
return SigmoidFunctionDerivative(x);
|
||
|
|
break;
|
||
|
|
case PRELU:
|
||
|
|
// See activationFunction() above - was previously missing here too, defaulting
|
||
|
|
// to a derivative of 1 (which happens to be right for x>=0, but wrong for x<0,
|
||
|
|
// where it must be the 0.01 leak slope, not 1).
|
||
|
|
return(x >= 0 ? 1.0 : 0.01);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#endif
|