527 lines
19 KiB
MQL5
527 lines
19 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| NeuronLSTM.mqh |
|
||
|
|
//| |
|
||
|
|
//| CNeuronLSTM - the pure-MQL5 LSTM cell (gate layers, BPTT, |
|
||
|
|
//| 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_NEURONLSTM_MQH
|
||
|
|
#define WARRIOR_AI_IMPL_NEURONLSTM_MQH
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CNeuronLSTM::CNeuronLSTM(void)
|
||
|
|
{
|
||
|
|
ForgetGate = new CLayer();
|
||
|
|
InputGate = new CLayer();
|
||
|
|
OutputGate = new CLayer();
|
||
|
|
NewContent = new CLayer();
|
||
|
|
Memory = new CArrayDouble();
|
||
|
|
PrevMemory = new CArrayDouble();
|
||
|
|
Input = new CArrayDouble();
|
||
|
|
InputGradient = new CArrayDouble();
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CNeuronLSTM::~CNeuronLSTM(void)
|
||
|
|
{
|
||
|
|
if(CheckPointer(ForgetGate) != POINTER_INVALID)
|
||
|
|
delete ForgetGate;
|
||
|
|
if(CheckPointer(InputGate) != POINTER_INVALID)
|
||
|
|
delete InputGate;
|
||
|
|
if(CheckPointer(OutputGate) != POINTER_INVALID)
|
||
|
|
delete OutputGate;
|
||
|
|
if(CheckPointer(NewContent) != POINTER_INVALID)
|
||
|
|
delete NewContent;
|
||
|
|
if(CheckPointer(Memory) != POINTER_INVALID)
|
||
|
|
delete Memory;
|
||
|
|
if(CheckPointer(PrevMemory) != POINTER_INVALID)
|
||
|
|
delete PrevMemory;
|
||
|
|
if(CheckPointer(Input) != POINTER_INVALID)
|
||
|
|
delete Input;
|
||
|
|
if(CheckPointer(InputGradient) != POINTER_INVALID)
|
||
|
|
delete InputGradient;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::Init(uint numOutputs, uint myIndex, int window, int step, int units_count, ENUM_OPTIMIZATION optimization_type)
|
||
|
|
{
|
||
|
|
if(units_count <= 0)
|
||
|
|
return false;
|
||
|
|
//--- Init Layers
|
||
|
|
if(!CNeuronPool::Init(numOutputs, myIndex, window, step, units_count, optimization_type))
|
||
|
|
return false;
|
||
|
|
if(!InitLayer(ForgetGate, units_count, window + units_count, optimization_type))
|
||
|
|
return false;
|
||
|
|
if(!InitLayer(InputGate, units_count, window + units_count, optimization_type))
|
||
|
|
return false;
|
||
|
|
if(!InitLayer(OutputGate, units_count, window + units_count, optimization_type))
|
||
|
|
return false;
|
||
|
|
if(!InitLayer(NewContent, units_count, window + units_count, optimization_type))
|
||
|
|
return false;
|
||
|
|
if(!Memory.Reserve(units_count))
|
||
|
|
return false;
|
||
|
|
if(!PrevMemory.Reserve(units_count))
|
||
|
|
return false;
|
||
|
|
CNeuron *temp;
|
||
|
|
for(int i = 0; i < units_count; i++)
|
||
|
|
{
|
||
|
|
if(!Memory.Add(0))
|
||
|
|
return false;
|
||
|
|
if(!PrevMemory.Add(0))
|
||
|
|
return false;
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
temp.setOutputVal(0);
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::InitLayer(CLayer *layer, int numUnits, int numOutputs, ENUM_OPTIMIZATION optimization_type)
|
||
|
|
{
|
||
|
|
if(CheckPointer(layer) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
layer = new CLayer(numOutputs);
|
||
|
|
if(CheckPointer(layer) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
layer.Clear();
|
||
|
|
if(!layer.Reserve(numUnits))
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
//--- LeCun-uniform init, matching CNeuronLSTMOCL::Init's gate weighScale rationale - fan-in is
|
||
|
|
//--- hidden units + input width (window+units_count, i.e. this InitLayer() call's own numOutputs
|
||
|
|
//--- param, since each gate neuron's Connections array IS its fan-in weight vector here, not a
|
||
|
|
//--- fan-out to a next layer).
|
||
|
|
double gateScale = 1.0 / MathSqrt((double)numOutputs + 1.0);
|
||
|
|
CNeuron *temp;
|
||
|
|
for(int i = 0; i < numUnits; i++)
|
||
|
|
{
|
||
|
|
temp = new CNeuron();
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
if(!temp.Init(numOutputs + 1, i, optimization_type, gateScale))
|
||
|
|
return false;
|
||
|
|
if(!layer.Add(temp))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::feedForward(CLayer *prevLayer)
|
||
|
|
{
|
||
|
|
if(CheckPointer(prevLayer) == POINTER_INVALID || prevLayer.Total() <= 0)
|
||
|
|
return false;
|
||
|
|
CNeuronBase *temp;
|
||
|
|
CConnection *temp_con;
|
||
|
|
if(CheckPointer(Input) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
Input = new CArrayDouble();
|
||
|
|
if(CheckPointer(Input) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
Input.Clear();
|
||
|
|
//--- Concatenate input sequence
|
||
|
|
int total = prevLayer.Total();
|
||
|
|
if(!Input.Reserve(total + OutputLayer.Total()))
|
||
|
|
return false;
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = prevLayer.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID || !Input.Add(temp.getOutputVal()))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
total = OutputLayer.Total();
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID || !Input.Add(temp.getOutputVal()))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
int total_data = Input.Total();
|
||
|
|
//--- Calculated forget gate
|
||
|
|
CArrayDouble *forget_gate = CalculateGate(ForgetGate, Input);
|
||
|
|
if(CheckPointer(forget_gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
//--- Calculated input gate
|
||
|
|
CArrayDouble *input_gate = CalculateGate(InputGate, Input);
|
||
|
|
if(CheckPointer(input_gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
//--- Calculated output gate
|
||
|
|
CArrayDouble *output_gate = CalculateGate(OutputGate, Input);
|
||
|
|
if(CheckPointer(output_gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
//--- Calculated new content
|
||
|
|
CArrayDouble *new_content = new CArrayDouble();
|
||
|
|
if(CheckPointer(new_content) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
total = NewContent.Total();
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = NewContent.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
double val = 0;
|
||
|
|
for(int c = 0; c < total_data; c++)
|
||
|
|
{
|
||
|
|
temp_con = temp.Connections.At(c);
|
||
|
|
if(CheckPointer(temp_con) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
val += temp_con.weight * Input.At(c);
|
||
|
|
}
|
||
|
|
val = TanhFunction(val);
|
||
|
|
temp.setOutputVal(val);
|
||
|
|
if(!new_content.Add(val))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
//--- Calculated output sequences
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
if(PrevMemory.Total() <= i)
|
||
|
|
PrevMemory.Add(Memory.At(i));
|
||
|
|
else
|
||
|
|
PrevMemory.Update(i, Memory.At(i));
|
||
|
|
double value = Memory.At(i) * forget_gate.At(i) + new_content.At(i) * input_gate.At(i);
|
||
|
|
if(!Memory.Update(i, value))
|
||
|
|
return false;
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
value = TanhFunction(value) * output_gate.At(i);
|
||
|
|
temp.setOutputVal(value);
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
delete forget_gate;
|
||
|
|
delete input_gate;
|
||
|
|
delete new_content;
|
||
|
|
delete output_gate;
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CArrayDouble *CNeuronLSTM::CalculateGate(CLayer *gate, CArrayDouble *sequence)
|
||
|
|
{
|
||
|
|
CNeuronBase *temp;
|
||
|
|
CConnection *temp_con;
|
||
|
|
CArrayDouble *result = new CArrayDouble();
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return NULL;
|
||
|
|
int total = gate.Total();
|
||
|
|
int total_data = sequence.Total();
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = gate.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
delete result;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
double val = 0;
|
||
|
|
for(int c = 0; c < total_data; c++)
|
||
|
|
{
|
||
|
|
temp_con = temp.Connections.At(c);
|
||
|
|
if(CheckPointer(temp_con) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
delete result;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
val += temp_con.weight * (sequence.At(c) == DBL_MAX ? 1 : sequence.At(c));
|
||
|
|
}
|
||
|
|
val = SigmoidFunction(val);
|
||
|
|
temp.setOutputVal(val);
|
||
|
|
if(!result.Add(val))
|
||
|
|
{
|
||
|
|
delete result;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::calcHiddenGradients(CLayer *&nextLayer)
|
||
|
|
{
|
||
|
|
if(CheckPointer(InputGradient) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
InputGradient = new CArrayDouble();
|
||
|
|
if(CheckPointer(InputGradient) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
InputGradient.Clear();
|
||
|
|
//---
|
||
|
|
int total = OutputLayer.Total();
|
||
|
|
CNeuron *temp;
|
||
|
|
CArrayDouble *MemoryGradient = new CArrayDouble();
|
||
|
|
CNeuron *gate;
|
||
|
|
CConnection *con;
|
||
|
|
//---
|
||
|
|
if(nextLayer != OutputLayer)
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
temp.setGradient(temp.sumDOW(nextLayer));
|
||
|
|
}
|
||
|
|
//--- Calculated memory and output gate gradients
|
||
|
|
if(CheckPointer(MemoryGradient) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
if(!MemoryGradient.Reserve(total))
|
||
|
|
return false;
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
gate = OutputGate.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
double value = temp.getGradient() * gate.getOutputVal();
|
||
|
|
value = TanhFunctionDerivative(Memory.At(i)) * value;
|
||
|
|
if(i >= MemoryGradient.Total())
|
||
|
|
{
|
||
|
|
if(!MemoryGradient.Add(value))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
value = MemoryGradient.At(i) + value;
|
||
|
|
if(!MemoryGradient.Update(i, value))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
gate.setGradient(gate.getOutputVal() != 0 && temp.getGradient() != 0 ? temp.getGradient()*temp.getOutputVal()*SigmoidFunctionDerivative(gate.getOutputVal()) / gate.getOutputVal() : 0);
|
||
|
|
//--- Calcculated gates and new content gradients
|
||
|
|
gate = ForgetGate.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
gate.setGradient(gate.getOutputVal() != 0 && value != 0 ? value * SigmoidFunctionDerivative(gate.getOutputVal()) : 0);
|
||
|
|
gate = InputGate.At(i);
|
||
|
|
temp = NewContent.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
gate.setGradient(gate.getOutputVal() != 0 && value != 0 ? value * temp.getOutputVal()*SigmoidFunctionDerivative(gate.getOutputVal()) : 0);
|
||
|
|
temp.setGradient(temp.getOutputVal() != 0 && value != 0 ? value * gate.getOutputVal()*TanhFunctionDerivative(temp.getOutputVal()) : 0);
|
||
|
|
}
|
||
|
|
//--- Calculated input gradients
|
||
|
|
int total_inp = temp.getConnections().Total();
|
||
|
|
for(int n = 0; n < total_inp; n++)
|
||
|
|
{
|
||
|
|
double value = 0;
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = ForgetGate.At(i);
|
||
|
|
con = temp.getConnections().At(n);
|
||
|
|
value += temp.getGradient() * con.weight;
|
||
|
|
//---
|
||
|
|
temp = InputGate.At(i);
|
||
|
|
con = temp.getConnections().At(n);
|
||
|
|
value += temp.getGradient() * con.weight;
|
||
|
|
//---
|
||
|
|
temp = OutputGate.At(i);
|
||
|
|
con = temp.getConnections().At(n);
|
||
|
|
value += temp.getGradient() * con.weight;
|
||
|
|
//---
|
||
|
|
temp = NewContent.At(i);
|
||
|
|
con = temp.getConnections().At(n);
|
||
|
|
value += temp.getGradient() * con.weight;
|
||
|
|
}
|
||
|
|
if(InputGradient.Total() >= n)
|
||
|
|
{
|
||
|
|
if(!InputGradient.Add(value))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(!InputGradient.Update(n, value))
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
//--- Calculated gradients for prev. state
|
||
|
|
int shift = total_inp - total;
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
if(CheckPointer(temp) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
temp.setGradient(InputGradient.At(shift + i));
|
||
|
|
}
|
||
|
|
//--- Calculated memory and output gate gradients
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
temp = OutputLayer.At(i);
|
||
|
|
gate = OutputGate.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
double value = temp.getGradient() * gate.getPrevVal();
|
||
|
|
value = MemoryGradient.At(i) + TanhFunctionDerivative(PrevMemory.At(i)) * value;
|
||
|
|
if(!MemoryGradient.Update(i, value))
|
||
|
|
return false;
|
||
|
|
gate.setGradient(gate.getGradient() + (gate.getPrevVal() != 0 && temp.getGradient() != 0 ? temp.getGradient()*temp.getPrevVal()*SigmoidFunctionDerivative(gate.getPrevVal()) / gate.getPrevVal() : 0));
|
||
|
|
//--- Calcculated gates and new content gradients
|
||
|
|
gate = ForgetGate.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
gate.setGradient(gate.getGradient() + (gate.getPrevVal() != 0 && value != 0 ? value * SigmoidFunctionDerivative(gate.getPrevVal()) : 0));
|
||
|
|
gate = InputGate.At(i);
|
||
|
|
temp = NewContent.At(i);
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
gate.setGradient(gate.getGradient() + (gate.getPrevVal() != 0 && value != 0 ? value * temp.getPrevVal()*SigmoidFunctionDerivative(gate.getPrevVal()) : 0));
|
||
|
|
temp.setGradient(temp.getGradient() + (temp.getPrevVal() != 0 && value != 0 ? value * gate.getPrevVal()*TanhFunctionDerivative(temp.getPrevVal()) : 0));
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
delete MemoryGradient;
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::updateInputWeights(CLayer *prevLayer)
|
||
|
|
{
|
||
|
|
if(CheckPointer(prevLayer) == POINTER_INVALID || CheckPointer(Input) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
if(!updateInputWeights(ForgetGate, Input) || !updateInputWeights(InputGate, Input) || !updateInputWeights(OutputGate, Input)
|
||
|
|
|| !updateInputWeights(NewContent, Input))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if(optimization == ADAM)
|
||
|
|
t++;
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::updateInputWeights(CLayer *gate, CArrayDouble *input_data)
|
||
|
|
{
|
||
|
|
if(CheckPointer(gate) == POINTER_INVALID || CheckPointer(input_data) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
CNeuronBase *neuron;
|
||
|
|
CConnection *con;
|
||
|
|
int total_n = gate.Total();
|
||
|
|
int total_data = input_data.Total();
|
||
|
|
double lt = eta * sqrt(1 - pow(b2, t)) / (1 - pow(b1, t));
|
||
|
|
for(int n = 0; n < total_n; n++)
|
||
|
|
{
|
||
|
|
neuron = gate.At(n);
|
||
|
|
if(CheckPointer(neuron) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
double g = neuron.getGradient();
|
||
|
|
double g2 = g * g;
|
||
|
|
for(int i = 0; i < total_data; i++)
|
||
|
|
{
|
||
|
|
con = neuron.getConnections().At(i);
|
||
|
|
if(CheckPointer(con) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
double data = input_data.At(i);
|
||
|
|
if(optimization == SGD)
|
||
|
|
con.weight += con.deltaWeight = (g != 0 && data != 0 ? eta * g * (data != DBL_MAX ? data : 1) : 0) + alpha * con.deltaWeight;
|
||
|
|
else
|
||
|
|
{
|
||
|
|
con.mt = b1 * con.mt + (1 - b1) * g;
|
||
|
|
con.vt = b2 * con.vt + (1 - b2) * g2 + 0.00000001;
|
||
|
|
con.deltaWeight = MathMax(-MAX_WEIGHT_DELTA, MathMin(MAX_WEIGHT_DELTA, lt * con.mt / sqrt(con.vt) - lt * WEIGHT_DECAY * con.weight));
|
||
|
|
// Sign-agreement gate removed - see CNeuron::updateInputWeights' comment for why.
|
||
|
|
con.weight += con.deltaWeight;
|
||
|
|
}
|
||
|
|
// See CNeuron::updateInputWeights' matching clamp for why this is needed - matches
|
||
|
|
// AI\Network.cl's LSTM_UpdateWeightsAdam MAX_WEIGHT clamp.
|
||
|
|
con.weight = MathMax(-MAX_WEIGHT, MathMin(MAX_WEIGHT, con.weight));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::calcInputGradients(CNeuronBase *prevNeuron, uint index)
|
||
|
|
{
|
||
|
|
if(CheckPointer(prevNeuron) == POINTER_INVALID || CheckPointer(InputGradient) == POINTER_INVALID || InputGradient.Total() <= (int)index)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
prevNeuron.setGradient(InputGradient.At(index));
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::calcInputGradients(CLayer *prevLayer)
|
||
|
|
{
|
||
|
|
if(CheckPointer(prevLayer) == POINTER_INVALID)
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
int total = prevLayer.Total();
|
||
|
|
if(total <= 0)
|
||
|
|
return false;
|
||
|
|
CNeuronBase *neuron;
|
||
|
|
bool result = true;
|
||
|
|
for(int i = 0; (i < total && result); i++)
|
||
|
|
{
|
||
|
|
neuron = prevLayer.At(i);
|
||
|
|
if(CheckPointer(neuron) == POINTER_INVALID)
|
||
|
|
{
|
||
|
|
result = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
result = calcInputGradients(neuron, i);
|
||
|
|
}
|
||
|
|
//---
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::Save(const int file_handle)
|
||
|
|
{
|
||
|
|
if(!CNeuronPool::Save(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!ForgetGate.Save(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!InputGate.Save(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!OutputGate.Save(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!NewContent.Save(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!Memory.Save(file_handle))
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool CNeuronLSTM::Load(const int file_handle)
|
||
|
|
{
|
||
|
|
if(!CNeuronPool::Load(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!ForgetGate.Load(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!InputGate.Load(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!OutputGate.Load(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!NewContent.Load(file_handle))
|
||
|
|
return false;
|
||
|
|
if(!Memory.Load(file_handle))
|
||
|
|
return false;
|
||
|
|
//---
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
#endif
|