forked from kelmut/NeuroBook
295 lines
22 KiB
MQL5
295 lines
22 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| convolution_test3.mq5 |
| |
//| Copyright 2021, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2021, MetaQuotes Ltd."
| |
#property link "https://www.mql5.com"
| |
#property version "1.00"
| |
#property script_show_inputs
| |
//+------------------------------------------------------------------+
| |
//| External parameters for script operation |
| |
//+------------------------------------------------------------------+
| |
input string StudyFileName = "study_data.csv"; // Name of file with the training dataset
| |
input string OutputFileName = "loss_study_conv3.csv";// File name to write error dynamics
| |
input int BarsToLine = 40; // Number of historical bars in one pattern
| |
input int NeuronsToBar = 4; // Number of input layer neurons per 1 bar
| |
input bool UseOpenCL = false; // Use of OpenCL
| |
input int BatchSize = 10000; // Batch size to update the weight matrix
| |
input double LearningRate = 0.00003; // Learning rate
| |
input int HiddenLayers = 1; // Number of hidden layers
| |
input int HiddenLayer = 40; // Number of neurons in one hidden layer
| |
input int Epochs = 1000; // Number of learning epochs
| |
//+------------------------------------------------------------------+
| |
//| Connect the neural network library |
| |
//+------------------------------------------------------------------+
| |
#include "..\..\Include\realization\neuronnet.mqh"
| |
//+------------------------------------------------------------------+
| |
//| Script program start |
| |
//+------------------------------------------------------------------+
| |
void OnStart(void)
| |
{
| |
vectorf loss_history;
| |
//--- prepare vector for storing the history of network errors
| |
if(!loss_history.Resize(0, Epochs))
| |
{
| |
Print("Not enough memory for loss history");
| |
return;
| |
}
| |
CNet net;
| |
//--- 1. Network initialization
| |
if(!NetworkInitialize(net))
| |
return;
| |
//--- 2. Loading training set data
| |
CArrayObj data;
| |
CArrayObj result;
| |
if(!LoadTrainingData(StudyFileName, data, result))
| |
return;
| |
//--- 3. Training the network
| |
if(!NetworkFit(net, data, result, loss_history))
| |
return;
| |
//--- 4. Save the history of network errors
| |
SaveLossHistory(OutputFileName, loss_history);
| |
//--- 5. Save the resulting network
| |
net.Save("Study.net");
| |
Print("Done");
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Load training data |
| |
//+------------------------------------------------------------------+
| |
bool LoadTrainingData(string path, CArrayObj &data, CArrayObj &result)
| |
{
| |
CBufferType *pattern;
| |
CBufferType *target;
| |
//--- open the file with the training dataset
| |
int handle = FileOpen(path, FILE_READ | FILE_CSV | FILE_ANSI | FILE_SHARE_READ, ",", CP_UTF8);
| |
if(handle == INVALID_HANDLE)
| |
{
| |
PrintFormat("Error of open study data file: %d", GetLastError());
| |
return false;
| |
}
| |
//--- display the progress of loading training data in the chart comment
| |
uint next_comment_time = 0;
| |
enum
| |
{
| |
OutputTimeout = 250 // no more than 1 time every 250 milliseconds
| |
};
| |
//--- organize loop to load training dataset
| |
while(!FileIsEnding(handle) && !IsStopped())
| |
{
| |
if(!(pattern = new CBufferType()))
| |
{
| |
PrintFormat("Error creating Pattern data array: %d", GetLastError());
| |
return false;
| |
}
| |
if(!pattern.BufferInit(1, NeuronsToBar * BarsToLine))
| |
return false;
| |
if(!(target = new CBufferType()))
| |
{
| |
PrintFormat("Error creating Pattern Target array: %d", GetLastError());
| |
return false;
| |
}
| |
if(!target.BufferInit(1, 2))
| |
return false;
| |
for(int i = 0; i < NeuronsToBar * BarsToLine; i++)
| |
pattern.m_mMatrix[0, i] = (TYPE)FileReadNumber(handle);
| |
for(int i = 0; i < 2; i++)
| |
target.m_mMatrix[0, i] = (TYPE)FileReadNumber(handle);
| |
if(!data.Add(pattern))
| |
{
| |
PrintFormat("Error adding training data to array: %d", GetLastError());
| |
return false;
| |
}
| |
if(!result.Add(target))
| |
{
| |
PrintFormat("Error adding training data to array: %d", GetLastError());
| |
return false;
| |
}
| |
//--- show loading progress in the chart comment (no more than 1 time every 250 milliseconds)
| |
if(next_comment_time < GetTickCount())
| |
{
| |
Comment(StringFormat("Patterns loaded: %d", data.Total()));
| |
next_comment_time = GetTickCount() + OutputTimeout;
| |
}
| |
}
| |
FileClose(handle);
| |
return(true);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Initializing the network architecture |
| |
//+------------------------------------------------------------------+
| |
bool CreateLayersDesc(CArrayObj &layers)
| |
{
| |
CLayerDescription *descr;
| |
//--- create input data layer
| |
if(!(descr = new CLayerDescription()))
| |
{
| |
PrintFormat("Error creating CLayerDescription: %d", GetLastError());
| |
return false;
| |
}
| |
descr.type = defNeuronBase;
| |
descr.count = NeuronsToBar * BarsToLine;
| |
descr.window = 0;
| |
descr.activation = AF_NONE;
| |
descr.optimization = None;
| |
if(!layers.Add(descr))
| |
{
| |
PrintFormat("Error adding layer: %d", GetLastError());
| |
return false;
| |
}
| |
//--- Convolutional layer
| |
if(!(descr = new CLayerDescription()))
| |
{
| |
PrintFormat("Error creating CLayerDescription: %d", GetLastError());
| |
return false;
| |
}
| |
descr.type = defNeuronConv;
| |
descr.count = BarsToLine - 2;
| |
descr.window = 3 * NeuronsToBar;
| |
descr.window_out = 8;
| |
descr.step = NeuronsToBar;
| |
descr.activation = AF_SWISH;
| |
descr.optimization = Adam;
| |
descr.activation_params[0] = 1;
| |
if(!layers.Add(descr))
| |
{
| |
PrintFormat("Error adding layer: %d", GetLastError());
| |
delete descr;
| |
return false;
| |
}
| |
//--- Pooling layer
| |
if(!(descr = new CLayerDescription()))
| |
{
| |
PrintFormat("Error creating CLayerDescription: %d", GetLastError());
| |
return false;
| |
}
| |
descr.type = defNeuronProof;
| |
descr.count = BarsToLine - 3;
| |
descr.window = 2;
| |
descr.window_out = 8;
| |
descr.step = 1;
| |
descr.activation = (ENUM_ACTIVATION_FUNCTION)AF_MAX_POOLING;
| |
descr.optimization = None;
| |
descr.activation_params[0] = 0;
| |
if(!layers.Add(descr))
| |
{
| |
PrintFormat("Error adding layer: %d", GetLastError());
| |
delete descr;
| |
return false;
| |
}
| |
//--- Hidden layer
| |
if(!(descr = new CLayerDescription()))
| |
{
| |
PrintFormat("Error creating CLayerDescription: %d", GetLastError());
| |
return false;
| |
}
| |
descr.type = defNeuronBase;
| |
descr.count = HiddenLayer;
| |
descr.activation = AF_SWISH;
| |
descr.optimization = Adam;
| |
descr.activation_params[0] = 1;
| |
for(int i = 0; i < HiddenLayers; i++)
| |
if(!layers.Add(descr))
| |
{
| |
PrintFormat("Error adding layer: %d", GetLastError());
| |
return false;
| |
}
| |
//--- Results layer
| |
if(!(descr = new CLayerDescription()))
| |
{
| |
PrintFormat("Error creating CLayerDescription: %d", GetLastError());
| |
return false;
| |
}
| |
descr.type = defNeuronBase;
| |
descr.count = 2;
| |
descr.activation = AF_LINEAR;
| |
descr.optimization = Adam;
| |
descr.activation_params[0] = 1;
| |
if(!layers.Add(descr))
| |
{
| |
PrintFormat("Error adding layer: %d", GetLastError());
| |
return false;
| |
}
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Network initialization |
| |
//+------------------------------------------------------------------+
| |
bool NetworkInitialize(CNet &net)
| |
{
| |
CArrayObj layers;
| |
//--- create a description of the network layers
| |
if(!CreateLayersDesc(layers))
| |
return false;
| |
//--- initialize the network
| |
if(!net.Create(&layers, (TYPE)LearningRate, (TYPE)0.9, (TYPE)0.999, LOSS_MSE, 0, 0))
| |
{
| |
PrintFormat("Error of init Net: %d", GetLastError());
| |
return false;
| |
}
| |
net.UseOpenCL(UseOpenCL);
| |
net.LossSmoothFactor(BatchSize);
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Network training |
| |
//+------------------------------------------------------------------+
| |
bool NetworkFit(CNet &net, const CArrayObj &data, const CArrayObj &result, vectorf &loss_history)
| |
{
| |
//--- training
| |
int patterns = data.Total();
| |
//--- loop through epochs
| |
for(int epoch = 0; epoch < Epochs; epoch++)
| |
{
| |
ulong ticks = GetTickCount64();
| |
//--- train in batches
| |
for(int i = 0; i < BatchSize; i++)
| |
{
| |
//--- check if training stopped
| |
if(IsStopped())
| |
{
| |
Print("Network fitting stopped by user");
| |
return true;
| |
}
| |
//--- incorrect, sampling into batch must be done before the loop
| |
int k = (int)((double)(MathRand() * MathRand()) / MathPow(32767.0, 2) * patterns);
| |
if(!net.FeedForward(data.At(k)))
| |
{
| |
PrintFormat("Error in FeedForward: %d", GetLastError());
| |
return false;
| |
}
| |
if(!net.Backpropagation(result.At(k)))
| |
{
| |
PrintFormat("Error in Backpropagation: %d", GetLastError());
| |
return false;
| |
}
| |
}
| |
//--- reconfigure network weights
| |
net.UpdateWeights(BatchSize);
| |
printf("Use OpenCL %s, epoch %d, time %.5f sec", (string)UseOpenCL, epoch, (GetTickCount64() - ticks) / 1000.0);
| |
//--- report about a completed epoch
| |
double loss = net.GetRecentAverageLoss();
| |
Comment(StringFormat("Epoch %d, error %.5f", epoch, loss));
| |
//--- remember the epoch error for saving to a file
| |
if(loss_history.Resize(epoch + 1))
| |
loss_history[epoch] = (float)loss;
| |
}
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Save the history of network errors |
| |
//+------------------------------------------------------------------+
| |
void SaveLossHistory(string path, const vectorf &loss_history)
| |
{
| |
int handle = FileOpen(OutputFileName, FILE_WRITE | FILE_CSV | FILE_ANSI, ",", CP_UTF8);
| |
if(handle == INVALID_HANDLE)
| |
PrintFormat("Error creating loss file: %d", GetLastError());
| |
else
| |
{
| |
for(ulong i = 0; i < loss_history.Size(); i++)
| |
FileWrite(handle, loss_history[i]);
| |
FileClose(handle);
| |
PrintFormat("The dynamics of the error change is saved to a file %s\\%s", TerminalInfoString(TERMINAL_DATA_PATH), OutputFileName);
| |
}
| |
}
| |
//+------------------------------------------------------------------+
|