Repository: MegaJoctan/MALE5
Branch: MQL5-ML
Commit: d6541568aaba
Files: 47
Total size: 473.0 KB
Directory structure:
gitextract_ly_vy9wi/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── Examples/
│ ├── Classifier Model Example.mq5
│ └── Regressor Model Example.mq5
├── LICENSE
├── MqPlotLib/
│ └── plots.mqh
├── Neural Networks/
│ ├── Pattern Nets.mqh
│ ├── README.md
│ ├── Regressor Nets.mqh
│ ├── initializers.mqh
│ ├── kohonen maps.mqh
│ └── optimizers.mqh
├── Numpy/
│ └── Numpy.mqh
├── Pandas/
│ ├── Incremental LE.mqh
│ └── pandas.mqh
├── README.md
├── Sklearn/
│ ├── Cluster/
│ │ ├── DBSCAN.mqh
│ │ ├── Hierachical Clustering.mqh
│ │ ├── KMeans.mqh
│ │ └── base.mqh
│ ├── Decomposition/
│ │ ├── LDA.mqh
│ │ ├── NMF.mqh
│ │ ├── PCA.mqh
│ │ ├── README.md
│ │ ├── TruncatedSVD.mqh
│ │ └── base.mqh
│ ├── Ensemble/
│ │ ├── AdaBoost.mqh
│ │ ├── README.md
│ │ └── Random Forest.mqh
│ ├── Linear Models/
│ │ ├── Linear Regression.mqh
│ │ ├── Logistic Regression.mqh
│ │ ├── README.md
│ │ └── Ridge.mqh
│ ├── Naive Bayes/
│ │ ├── Naive Bayes.mqh
│ │ ├── README.md
│ │ └── naive bayes visuals.py
│ ├── Neighbors/
│ │ └── KNN_nearest_neighbors.mqh
│ ├── Tree/
│ │ ├── README.md
│ │ └── tree.mqh
│ ├── metrics.mqh
│ └── preprocessing.mqh
├── Stats Models/
│ ├── ADF.mqh
│ ├── ARIMA.mqh
│ └── OLS.mqh
├── Tensors.mqh
├── Utils.mqh
└── requirements.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
ko_fi: omegajoctan
custom: ['https://www.mql5.com/en/users/omegajoctan/seller']
custom: ['https://www.buymeacoffee.com/omegajoctan']
================================================
FILE: .gitignore
================================================
*.ex5
*.psd
*.zip
*.rar
*.xlsx
Todo's.txt
logisticwiki.txt
/venv
/Neural Nets Pro
================================================
FILE: Examples/Classifier Model Example.mq5
================================================
//+------------------------------------------------------------------+
//| Classifier Model Sample.mq5 |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
#property version "1.00"
#include
#include
#include //helper functions for for data manipulations
#include //fo measuring the performance
StandardizationScaler scaler; //standardization scaler from preprocessing.mqh
CDecisionTreeClassifier *decision_tree; //a decision tree classifier model
MqlRates rates[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Model selection
decision_tree = new CDecisionTreeClassifier(2, 5); //a decision tree classifier from DecisionTree class
//---
vector open, high, low, close;
int data_size = 1000;
//--- Getting the open, high, low and close values for the past 1000 bars, starting from the recent closed bar of 1
open.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_OPEN, 1, data_size);
high.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_HIGH, 1, data_size);
low.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_LOW, 1, data_size);
close.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_CLOSE, 1, data_size);
matrix X(data_size, 3); //creating the x matrix
//--- Assigning the open, high, and low price values to the x matrix
X.Col(open, 0);
X.Col(high, 1);
X.Col(low, 2);
//--- Since we are using the x variables to predict y, we choose the close price to be the target variable
vector y(data_size);
for (int i=0; iopen[i]) //a bullish candle appeared
y[i] = 1; //buy signal
else
{
y[i] = 0; //sell signal
}
}
//--- We split the data into training and testing samples for training and evaluation
matrix X_train, X_test;
vector y_train, y_test;
double train_size = 0.7; //70% of the data should be used for training the rest for testing
int random_state = 42; //we put a random state to shuffle the data so that a machine learning model understands the patterns and not the order of the dataset, this makes the model durable
MatrixExtend::TrainTestSplitMatrices(X, y, X_train, y_train, X_test, y_test, train_size, random_state); // we split the x and y data into training and testing samples
//--- Normalizing the independent variables
X_train = scaler.fit_transform(X_train); // we fit the scaler on the training data and transform the data alltogether
X_test = scaler.transform(X_test); // we transform the new data this way
//--- Training the model
decision_tree.fit(X_train, y_train); //The training function
//--- Measuring predictive accuracy
vector train_predictions = decision_tree.predict_bin(X_train);
Print("Training results classification report");
Metrics::classification_report(y_train, train_predictions);
//--- Evaluating the model on out-of-sample predictions
vector test_predictions = decision_tree.predict_bin(X_test);
Print("Testing results classification report");
Metrics::classification_report(y_test, test_predictions);
//---
ArraySetAsSeries(rates, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
delete (decision_tree); //We have to delete the AI model object from the memory
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Making predictions live from the market
CopyRates(Symbol(), PERIOD_D1, 1, 3, rates); //Get the very recent information from the market
vector x = {rates[0].open, rates[0].high, rates[0].low}; //Assigning data from the recent candle in a similar way to the training data
x = scaler.transform(x);
int signal = (int)decision_tree.predict_bin(x);
Comment("Signal = ",signal==1?"BUY":"SELL"); //Ternary operator for checking if the signal is either buy or sell
}
//+------------------------------------------------------------------+
================================================
FILE: Examples/Regressor Model Example.mq5
================================================
//+------------------------------------------------------------------+
//| Regressor Model sample.mq5 |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
#property version "1.00"
#include
#include
#include //helper functions for for data manipulations
#include //fo measuring the performance
StandardizationScaler scaler; //standardization scaler from preprocessing.mqh
CDecisionTreeRegressor *decision_tree; //a decision tree classifier model
MqlRates rates[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Model selection
decision_tree = new CDecisionTreeRegressor(2, 5); //a decision tree classifier from DecisionTree class
vector open, high, low, close;
int data_size = 1000; //bars
//--- Getting the open, high, low and close values for the past 1000 bars, starting from the recent closed bar of 1
open.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_OPEN, 1, data_size);
high.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_HIGH, 1, data_size);
low.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_LOW, 1, data_size);
close.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_CLOSE, 1, data_size);
matrix X(data_size, 3); //creating the x matrix
//--- Assigning the open, high, and low price values to the x matrix
X.Col(open, 0);
X.Col(high, 1);
X.Col(low, 2);
vector y = close; // The target variable is the close price, using open, high and low values were want to predict the next closing price
//--- We split the data into training and testing samples for training and evaluation
matrix X_train, X_test;
vector y_train, y_test;
double train_size = 0.7; //70% of the data to be used for training the rest 30% for testing
int random_state = 42; //we put a random state to shuffle the data so that a machine learning model understands the patterns and not the order of the dataset, this makes the model durable
MatrixExtend::TrainTestSplitMatrices(X, y, X_train, y_train, X_test, y_test, train_size, random_state); // we split the x and y data into training and testing samples
//--- Normalizing the independent variables
X_train = scaler.fit_transform(X_train); // we fit the scaler on the training data and transform the data alltogether
X_test = scaler.transform(X_test); // we transform the new data this way
//--- Training the model
decision_tree.fit(X_train, y_train); //The training function
//--- Measuring predictive accuracy
vector train_predictions = decision_tree.predict(X_train);
printf("Decision decision_tree training r2_score = %.3f ",Metrics::RegressionMetric(y_train, train_predictions, METRIC_R_SQUARED));
//--- Evaluating the model on out-of-sample predictions
vector test_predictions = decision_tree.predict(X_test);
printf("Decision decision_tree out-of-sample r2_score = %.3f ",Metrics::r_squared(y_test, test_predictions));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
delete (decision_tree); //We have to delete the AI model object from the memory
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Making predictions live from the market
CopyRates(Symbol(), PERIOD_D1, 1, 3, rates); //Get the very recent information from the market
vector x = {rates[0].open, rates[0].high, rates[0].low}; //Assigning data from the recent candle in a similar way to the training data
x = scaler.transform(x);
double predicted_close_price = decision_tree.predict(x);
Comment("Next closing price predicted is = ",predicted_close_price);
}
//+------------------------------------------------------------------+
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Omega Joctan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MqPlotLib/plots.mqh
================================================
//+------------------------------------------------------------------+
//| plots.mqh |
//| Copyright 2022, Fxalgebra.com |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Fxalgebra.com"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#include
#include
class CPlots
{
protected:
CGraphic *graph;
long m_chart_id;
int m_subwin;
int m_x1, m_x2;
int m_y1, m_y2;
string m_font_family;
bool m_chart_show;
string m_plot_names[];
ENUM_CURVE_TYPE m_curve_type;
bool GraphCreate(string plot_name);
vector m_x, m_y;
string x_label, y_label;
public:
CPlots(long chart_id=0, int sub_win=0 ,int x1=30, int y1=40, int x2=550, int y2=310, string font_family="Consolas", bool chart_show=true);
~CPlots(void);
bool Plot(string plot_name, vector& x, vector& y, string x_axis_label, string y_axis_label, string label, ENUM_CURVE_TYPE curve_type=CURVE_POINTS_AND_LINES,color clr = clrDodgerBlue, bool points_fill = true);
bool AddPlot(vector &v,string label="plt",color clr=clrOrange);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPlots::CPlots(long chart_id=0, int sub_win=0 ,int x1=30, int y1=40, int x2=550, int y2=310, string font_family="Consolas", bool chart_show=true):
m_chart_id(chart_id),
m_subwin(sub_win),
m_x1(x1),
m_y1(y1),
m_x2(x2),
m_y2(y2),
m_font_family(font_family),
m_chart_show(chart_show)
{
graph = new CGraphic();
ChartRedraw(m_chart_id);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPlots::~CPlots(void)
{
for (int i=0; i
#include
#ifndef RANDOM_STATE
#define RANDOM_STATE 42
#endif
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum activation
{
AF_HARD_SIGMOID_ = AF_HARD_SIGMOID,
AF_SIGMOID_ = AF_SIGMOID,
AF_SWISH_ = AF_SWISH,
AF_SOFTSIGN_ = AF_SOFTSIGN,
AF_TANH_ = AF_TANH
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CPatternNets
{
private:
vector W_CONFIG;
vector W; //Weights vector
vector B; //Bias vector
activation A_FX;
protected:
ulong inputs;
ulong outputs;
ulong rows;
vector HL_CONFIG;
bool SoftMaxLayer;
vector classes;
void SoftMaxLayerFX(matrix &mat);
public:
CPatternNets(matrix &xmatrix, vector &yvector,vector &HL_NODES, activation ActivationFx, bool SoftMaxLyr=false);
~CPatternNets(void);
int PatternNetFF(vector &in_vector);
vector PatternNetFF(matrix &xmatrix);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPatternNets::CPatternNets(matrix &xmatrix, vector &yvector,vector &HL_NODES, activation ActivationFx, bool SoftMaxLyr=false)
{
A_FX = ActivationFx;
inputs = xmatrix.Cols();
rows = xmatrix.Rows();
SoftMaxLayer = SoftMaxLyr;
//--- Normalize data
if (rows != yvector.Size())
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return;
}
classes = MatrixExtend::Unique(yvector);
outputs = classes.Size();
HL_CONFIG.Copy(HL_NODES);
HL_CONFIG.Resize(HL_CONFIG.Size()+1); //Add the output layer
HL_CONFIG[HL_CONFIG.Size()-1] = (int)outputs; //Append one node to the output layer
//---
W_CONFIG.Resize(HL_CONFIG.Size());
B.Resize((ulong)HL_CONFIG.Sum());
//--- GENERATE WEIGHTS
ulong layer_input = inputs;
for (ulong i=0; i\n",
"HIDDEN LAYERS + OUTPUT ",HL_CONFIG,"\n",
"INPUTS ",inputs," | OUTPUTS ",outputs," W CONFIG ",W_CONFIG,"\n",
"activation ",EnumToString(A_FX)," SoftMaxLayer = ",bool(SoftMaxLayer)
);
Print("WEIGHTS ",W,"\nBIAS ",B);
#endif
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPatternNets::~CPatternNets(void)
{
ZeroMemory(W);
ZeroMemory(B);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CPatternNets::PatternNetFF(vector &in_vector)
{
matrix L_INPUT = {}, L_OUTPUT={}, L_WEIGHTS = {};
vector v_weights ={};
ulong w_start = 0;
L_INPUT = MatrixExtend::VectorToMatrix(in_vector);
vector L_BIAS_VECTOR = {};
matrix L_BIAS_MATRIX = {};
ulong b_start = 0;
for (ulong i=0; i ",i);
Print("L_WEIGHTS\n",L_WEIGHTS,"\nL_INPUT\n",L_INPUT,"\nL_BIAS\n",L_BIAS_MATRIX);
#endif
L_OUTPUT = L_WEIGHTS.MatMul(L_INPUT);
L_OUTPUT = L_OUTPUT+L_BIAS_MATRIX; //Add bias
//---
if (i==W_CONFIG.Size()-1) //Last layer
{
if (SoftMaxLayer)
{
Print("Before softmax\n",L_OUTPUT);
SoftMaxLayerFX(L_OUTPUT);
Print("After\n",L_OUTPUT);
}
else
L_OUTPUT.Activation(L_OUTPUT, ENUM_ACTIVATION_FUNCTION(A_FX));
}
else
L_OUTPUT.Activation(L_OUTPUT, ENUM_ACTIVATION_FUNCTION(A_FX));
//---
L_INPUT.Copy(L_OUTPUT); //Assign outputs to the inputs
w_start += (ulong)W_CONFIG[i]; //New weights copy
b_start += (ulong)HL_CONFIG[i];
}
#ifdef DEBUG_MODE
Print("--> outputs\n ",L_OUTPUT);
#endif
vector v_out = MatrixExtend::MatrixToVector(L_OUTPUT);
return((int)classes[v_out.ArgMax()]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CPatternNets::SoftMaxLayerFX(matrix &mat)
{
vector ret = MatrixExtend::MatrixToVector(mat);
ret.Activation(ret, AF_SOFTMAX);
mat = MatrixExtend::VectorToMatrix(ret, mat.Cols());
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CPatternNets::PatternNetFF(matrix &xmatrix)
{
vector v(xmatrix.Rows());
for (ulong i=0; i &mat)`: Applies the SoftMax function to a matrix (used for the output layer if `SoftMaxLyr` is True).
**III. Class Functionality:**
1. **Initialization:**
* The constructor validates data dimensions and parses user-defined hyperparameters.
* The network architecture (number of layers and neurons) is determined based on the provided configuration.
* Weights (connections between neurons) and biases (individual offsets for each neuron) are randomly initialized.
2. **Forward Pass:**
* The provided input vector is fed into the first layer.
* Each layer performs the following steps:
* Calculates the weighted sum of the previous layer's outputs.
* Adds the bias term to the weighted sum.
* Applies the chosen activation function to the result.
* This process continues through all layers until the final output layer is reached.
3. **SoftMax Layer (Optional)**
* If the `SoftMaxLyr` flag is True, the output layer uses the SoftMax function to ensure the output values sum to 1 and represent class probabilities.
4. **Prediction:**
* For single-sample prediction (`PatternNetFF(vector &in_vector)`), the class label with the **highest output value** is returned.
* For batch prediction (`PatternNetFF(matrix &xmatrix)`), a vector containing the predicted class label for each sample in the input matrix is returned.
**IV. Additional Notes:**
* The class provides several debug statements (disabled by default) to print intermediate calculations for debugging purposes.
* The code uses helper functions from the `MatrixExtend` class (not documented here) for matrix and vector operations.
* Choosing the appropriate network architecture, activation function, and learning approach (not implemented in this class) is crucial for optimal performance on specific tasks.
By understanding the theoretical foundation and functionalities of the `CPatternNets` class, MQL5 users can leverage neural networks for various pattern recognition tasks, including:
* **Classification:** Classifying data points into predefined categories based on their features.
* **Anomaly detection:** Identifying data points that deviate significantly from the expected patterns.
* **Feature learning:** Extracting hidden patterns or representations from the data.
## Regression Neural Network
This documentation explains the `CRegressorNets` class in MQL5, which implements a **Multi-Layer Perceptron (MLP)** for regression tasks.
**I. Regression vs. Classification:**
* **Regression:** Predicts continuous output values.
* **Classification:** Assigns data points to predefined categories.
**II. MLP Neural Network:**
An MLP is a type of **feed-forward neural network** used for supervised learning tasks like regression. It consists of:
* **Input layer:** Receives the input data.
* **Hidden layers:** Process and transform the information.
* **Output layer:** Produces the final prediction (continuous value in regression).
**III. CRegressorNets Class:**
The `CRegressorNets` class provides functionalities for training and using an MLP for regression in MQL5:
**Public Functions:**
* `CRegressorNets(vector &HL_NODES, activation ActivationFX=AF_RELU_)` Constructor:
* `HL_NODES`: Vector specifying the number of neurons in each hidden layer.
* `ActivationFX`: Activation function (enum specifying the type of activation function).
* `~CRegressorNets(void)` Destructor.
* `void fit(matrix &x, vector &y)` Trains the model on the provided data (`x` - features, `y` - target values).
* `double predict(vector &x)` Predicts the output value for a single input vector.
* `vector predict(matrix &x)` Predicts output values for all rows in the input matrix.
**Internal Functions (not directly accessible)**
* `CalcTimeElapsed(double seconds)`: Calculates and returns a string representing the elapsed time in a human-readable format (not relevant for core functionality).
* `RegressorNetsBackProp(matrix& x, vector &y, uint epochs, double alpha, loss LossFx=LOSS_MSE_, optimizer OPTIMIZER=OPTIMIZER_ADAM)`: Performs backpropagation for training (details not provided but likely involve calculating gradients and updating weights and biases).
* Optimizer functions (e.g., `AdamOptimizerW`, `AdamOptimizerB`) Implement specific optimization algorithms like Adam for updating weights and biases during training.
**Other Class Members:**
* `mlp_struct mlp`: Stores information about the network architecture (inputs, hidden layers, and outputs).
* `CTensors*` pointers: Represent tensors holding weights, biases, and other internal calculations (specific implementation likely relies on a custom tensor library).
* `matrix` variables: Used for calculations during training and may hold temporary data (e.g., `W_MATRIX`, `B_MATRIX`).
* `vector` variables: Store network configuration details (e.g., `W_CONFIG`, `HL_CONFIG`).
* `bool isBackProp`: Flag indicating if backpropagation is being performed (private).
* `matrix` variables: Used for storing intermediate results during backpropagation (e.g., `ACTIVATIONS`, `Partial_Derivatives`).
**IV. Additional Notes:**
* The class provides various activation function options and supports different loss functions (e.g., Mean Squared Error, Mean Absolute Error) for selecting the appropriate evaluation metric during training.
* The class implements the Adam optimizer, one of several optimization algorithms used for efficient training of neural networks.
* Detailed implementation of the backpropagation algorithm is not provided but is likely the core functionality for training the network.
**Reference**
* [Data Science and Machine Learning (Part 12): Can Self-Training Neural Networks Help You Outsmart the Stock Market?](https://www.mql5.com/en/articles/12209)
* [Data Science and Machine Learning — Neural Network (Part 01): Feed Forward Neural Network demystified](https://www.mql5.com/en/articles/11275)
* [Data Science and Machine Learning — Neural Network (Part 02): Feed forward NN Architectures Design](https://www.mql5.com/en/articles/11334)
================================================
FILE: Neural Networks/Regressor Nets.mqh
================================================
//+------------------------------------------------------------------+
//| neural_nn_lib.mqh |
//| Copyright 2022, Omega Joctan. |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Omega Joctan."
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| Regressor Neural Networks | Neural Networks for solving |
//| regression problems in contrast to classification problems, |
//| here we deal with continuous variables |
//+------------------------------------------------------------------+
#include ;
#include ;
#include
#include
#include
#include "optimizers.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum activation
{
AF_ELU_ = AF_ELU,
AF_EXP_ = AF_EXP,
AF_GELU_ = AF_GELU,
AF_LINEAR_ = AF_LINEAR,
AF_LRELU_ = AF_LRELU,
AF_RELU_ = AF_RELU,
AF_SELU_ = AF_SELU,
AF_TRELU_ = AF_TRELU,
AF_SOFTPLUS_ = AF_SOFTPLUS
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum loss
{
LOSS_MSE_ = LOSS_MSE, // Mean Squared Error
LOSS_MAE_ = LOSS_MAE, // Mean Absolute Error
LOSS_MSLE_ = LOSS_MSLE, // Mean Squared Logarithmic Error
LOSS_POISSON_ = LOSS_POISSON // Poisson Loss
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct backprop //This structure returns the loss information obtained from the backpropagation function
{
vector training_loss,
validation_loss;
void Init(ulong epochs)
{
training_loss.Resize(epochs);
validation_loss.Resize(epochs);
}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct mlp_struct //multi layer perceptron information structure
{
ulong inputs;
ulong hidden_layers;
ulong outputs;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CRegressorNets
{
mlp_struct mlp;
CTensors *Weights_tensor; //Weight Tensor
CTensors *Bias_tensor;
CTensors *Input_tensor;
CTensors *Output_tensor;
protected:
activation A_FX;
loss m_loss_function;
bool trained;
string ConvertTime(double seconds);
//-- for backpropn
vector W_CONFIG;
vector HL_CONFIG;
bool isBackProp;
matrix ACTIVATIONS;
matrix Partial_Derivatives;
int m_random_state;
private:
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerSGD *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerAdaDelta *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerAdaGrad *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerAdam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerNadam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual backprop backpropagation(const matrix& x, const vector &y, OptimizerRMSprop *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
public:
CRegressorNets(vector &HL_NODES, activation AF_=AF_RELU_, loss m_loss_function=LOSS_MSE_, int random_state=42);
~CRegressorNets(void);
virtual void fit(const matrix &x, const vector &y, OptimizerSGD *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual void fit(const matrix &x, const vector &y, OptimizerAdaDelta *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual void fit(const matrix &x, const vector &y, OptimizerAdaGrad *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual void fit(const matrix &x, const vector &y, OptimizerAdam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual void fit(const matrix &x, const vector &y, OptimizerNadam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual void fit(const matrix &x, const vector &y, OptimizerRMSprop *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false);
virtual double predict(const vector &x);
virtual vector predict(const matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRegressorNets::CRegressorNets(vector &HL_NODES, activation AF_=AF_RELU_, loss LOSS_=LOSS_MSE_, int random_state=42)
:A_FX(AF_),
m_loss_function(LOSS_),
isBackProp(false),
m_random_state(random_state)
{
HL_CONFIG.Copy(HL_NODES);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRegressorNets::~CRegressorNets(void)
{
if (CheckPointer(this.Weights_tensor) != POINTER_INVALID) delete(this.Weights_tensor);
if (CheckPointer(this.Bias_tensor) != POINTER_INVALID) delete(this.Bias_tensor);
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
isBackProp = false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CRegressorNets::predict(const vector &x)
{
if (!trained)
{
printf("%s Train the model first before using it to make predictions | call the fit function first",__FUNCTION__);
return 0;
}
matrix L_INPUT = MatrixExtend::VectorToMatrix(x);
matrix L_OUTPUT ={};
for(ulong i=0; i= 60)
{
minutes = (uint)(seconds / 60.0) ;
seconds = fmod(seconds, 1.0) * 60;
time_str = StringFormat("%d Minutes and %.3f Seconds", minutes, seconds);
}
if (minutes >= 60)
{
hours = (uint)(minutes / 60.0);
minutes = minutes % 60;
time_str = StringFormat("%d Hours and %d Minutes", hours, minutes);
}
if (time_str == "")
{
time_str = StringFormat("%.3f Seconds", seconds);
}
return time_str;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerSGD *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerSGD optimizer_weights = optimizer;
OptimizerSGD optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerAdaDelta *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerAdaDelta optimizer_weights = optimizer;
OptimizerAdaDelta optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerAdaGrad *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerAdaGrad optimizer_weights = optimizer;
OptimizerAdaGrad optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerAdam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerAdam optimizer_weights = optimizer;
OptimizerAdam optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerNadam *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerNadam optimizer_weights = optimizer;
OptimizerNadam optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
backprop CRegressorNets::backpropagation(const matrix& x, const vector &y, OptimizerRMSprop *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
isBackProp = true;
//---
backprop backprop_struct;
backprop_struct.Init(epochs);
ulong rows = x.Rows();
mlp.inputs = x.Cols();
mlp.outputs = 1;
//---
vector v2 = {(double)mlp.outputs}; //Adding the output layer to the mix of hidden layers
HL_CONFIG = MatrixExtend::concatenate(HL_CONFIG, v2);
mlp.hidden_layers = HL_CONFIG.Size();
W_CONFIG.Resize(HL_CONFIG.Size());
//---
if (y.Size() != rows)
{
Print(__FUNCTION__," FATAL | Number of rows in the x matrix is not the same the y vector size ");
return backprop_struct;
}
matrix W, B;
//--- GENERATE WEIGHTS
this.Weights_tensor = new CTensors((uint)mlp.hidden_layers);
this.Bias_tensor = new CTensors((uint)mlp.hidden_layers);
this.Input_tensor = new CTensors((uint)mlp.hidden_layers);
this.Output_tensor = new CTensors((uint)mlp.hidden_layers);
ulong layer_input = mlp.inputs;
for (ulong i=0; i\n",
"HL_CONFIG ",HL_CONFIG," TOTAL HL(S) ",mlp.hidden_layers,"\n",
"W_CONFIG ",W_CONFIG," ACTIVATION ",EnumToString(A_FX),"\n",
"NN INPUTS ",mlp.inputs," OUTPUT ",mlp.outputs
);
//--- Optimizer
OptimizerRMSprop optimizer_weights = optimizer;
OptimizerRMSprop optimizer_bias = optimizer;
if (batch_size>0)
{
OptimizerMinBGD optimizer_weights;
OptimizerMinBGD optimizer_bias;
}
//--- Cross validation
CCrossValidation cross_validation;
CTensors *cv_tensor;
matrix validation_data = MatrixExtend::concatenate(x, y);
matrix validation_x;
vector validation_y;
cv_tensor = cross_validation.KFoldCV(validation_data, 10); //k-fold cross validation | 10 folds selected
//---
matrix DELTA = {};
double actual=0, pred=0;
matrix temp_inputs ={};
matrix dB = {}; //Bias Derivatives
matrix dW = {}; //Weight Derivatives
for (ulong epoch=0; epoch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
}
else //Batch Gradient Descent
{
for (uint batch=0, batch_start=0, batch_end=batch_size; batch=0 && !IsStopped(); layer--) //Loop through the network backward from last to first layer
{
Partial_Derivatives = this.Output_tensor.Get(int(layer));
temp_inputs = this.Input_tensor.Get(int(layer));
Partial_Derivatives.Derivative(Partial_Derivatives, ENUM_ACTIVATION_FUNCTION(A_FX));
if (mlp.hidden_layers-1 == layer) //Last layer
{
LossGradient = pred_v.LossGradient(actual_v, ENUM_LOSS_FUNCTION(m_loss_function));
DELTA.Col(LossGradient, 0);
}
else
{
W = this.Weights_tensor.Get(layer+1);
DELTA = (W.Transpose().MatMul(DELTA)) * Partial_Derivatives;
}
//-- Observation | DeLTA matrix is same size as the bias matrix
W = this.Weights_tensor.Get(layer);
B = this.Bias_tensor.Get(layer);
//--- Derivatives wrt weights and bias
dB = DELTA;
dW = DELTA.MatMul(temp_inputs.Transpose());
//--- Weights updates
optimizer_weights.update(W, dW);
optimizer_bias.update(B, dB);
this.Weights_tensor.Add(W, layer);
this.Bias_tensor.Add(B, layer);
}
}
pred_v = predict(batch_x);
batch_loss[batch] = pred_v.Loss(batch_y, ENUM_LOSS_FUNCTION(m_loss_function));
batch_loss[batch] = MathIsValidNumber(batch_loss[batch]) ? (batch_loss[batch]>1e6 ? 1e6 : batch_loss[batch]) : 1e6; //Check for nan and return some large value if it is nan
batch_accuracy[batch] = Metrics::r_squared(batch_y, pred_v);
if (show_batch_progress)
printf("----> batch[%d/%d] batch-loss %.5f accuracy %.3f",batch+1,num_batches,batch_loss[batch], batch_accuracy[batch]);
}
}
//--- End of an epoch
vector validation_loss(cv_tensor.SIZE);
vector validation_acc(cv_tensor.SIZE);
for (ulong i=0; i1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
else
{
backprop_struct.training_loss[epoch] = batch_loss.Mean();
backprop_struct.training_loss[epoch] = MathIsValidNumber(backprop_struct.training_loss[epoch]) ? (backprop_struct.training_loss[epoch]>1e6 ? 1e6 : backprop_struct.training_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
backprop_struct.validation_loss[epoch] = validation_loss.Mean();
backprop_struct.validation_loss[epoch] = MathIsValidNumber(backprop_struct.validation_loss[epoch]) ? (backprop_struct.validation_loss[epoch]>1e6 ? 1e6 : backprop_struct.validation_loss[epoch]) : 1e6; //Check for nan and return some large value if it is nan
}
double epoch_stop = GetTickCount();
printf("--> Epoch [%d/%d] training -> loss %.8f accuracy %.3f validation -> loss %.5f accuracy %.3f | Elapsed %s ",epoch+1,epochs,backprop_struct.training_loss[epoch],Metrics::r_squared(y, pred_v),backprop_struct.validation_loss[epoch],validation_acc.Mean(),this.ConvertTime((epoch_stop-epoch_start)/1000.0));
}
isBackProp = false;
if (CheckPointer(this.Input_tensor) != POINTER_INVALID) delete(this.Input_tensor);
if (CheckPointer(this.Output_tensor) != POINTER_INVALID) delete(this.Output_tensor);
if (CheckPointer(optimizer)!=POINTER_INVALID)
delete optimizer;
return backprop_struct;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRegressorNets::fit(const matrix &x, const vector &y, OptimizerSGD *optimizer, const uint epochs, uint batch_size=0, bool show_batch_progress=false)
{
trained = true; //The fit method has been called
vector epochs_vector(epochs); for (uint i=0; i
enum enum_weight_initializers
{
WEIGHT_INTIALIZER_XAVIER, //Xavier weights
WEIGHT_INTIALIZER_HE, //He weights
WEIGHT_INTIALIZER_RANDOM //Random weights
};
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
class CWeightsInitializers
{
protected:
static double random() //Generates a random float between 0 (inclusive) and 1 (exclusive)
{
return 0 + double((MathRand() / 32767.0) * (0.9 - 0));
}
static double uniform(double low, double high)
{
return low + (high - low) * random();
}
static matrix uniform(double low, double high, ulong rows, ulong cols)
{
matrix return_matrix(rows, cols);
for (ulong i=0; i
#include
#include
#include
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CKohonenMaps
{
protected:
C2DTensor *cluster_tensor;
CPlots plt;
double Euclidean_distance(const vector &v1, const vector &v2);
string CalcTimeElapsed(double seconds);
matrix c_matrix; //Clusters
matrix w_matrix; //Weights matrix
vector w_vector; //weights vector
matrix o_matrix; //Output layer matrix
uint m_clusters;
double m_alpha;
uint m_epochs;
int m_random_state;
ulong n, m;
public:
CKohonenMaps(uint clusters=2, double alpha=0.01, uint epochs=100, int random_state=42);
~CKohonenMaps(void);
void fit(const matrix &x);
int predict(const vector &x);
vector predict(const matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CKohonenMaps::CKohonenMaps(uint clusters=2, double alpha=0.01, uint epochs=100, int random_state=42)
:m(clusters),
m_alpha(alpha),
m_epochs(epochs),
m_random_state(random_state),
m_clusters(clusters)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CKohonenMaps::fit(const matrix &x)
{
n = (uint)x.Cols(); //number of features
ulong rows = x.Rows();
cluster_tensor = new C2DTensor();
cluster_tensor.Init((int)m);
w_matrix =MatrixExtend::Random(0.0, 1.0, n, m, m_random_state);
#ifdef DEBUG_MODE
Print("w x\n",w_matrix,"\nMatrix\n",x);
#endif
vector D(m); //Euclidean distance btn clusters
for (uint epoch=0; epoch= 60)
time_str = StringFormat("%d Minutes and %.3f Seconds ",minutes=(int)round(seconds/60.0), ((int)seconds % 60));
if (minutes >= 60)
time_str = StringFormat("%d Hours %d Minutes and %.3f Seconds ",hours=(int)round(minutes/60.0), minutes, ((int)seconds % 60));
else
time_str = StringFormat("%.3f Seconds ",seconds);
return time_str;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
================================================
FILE: Neural Networks/optimizers.mqh
================================================
//+------------------------------------------------------------------+
//| optimizers.mqh |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//|Class containing optimizers for updating neural network parameters|
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//| Adam (Adaptive Moment Estimation): Adam is an adaptive learning |
//| rate optimizer that maintains learning rates for each parameter |
//| and adapts them based on the first and second moments of the |
//| gradients. It's known for its fast convergence and robustness to |
//| noisy gradients |
//| |
//+------------------------------------------------------------------+
/*enum Optimizer {
OPTIMIZER_SGD, //Stochastic Gradient Descent
OPTIMIZER_MiniBatchGD, //Mini-Batch Gradiend Descent
OPTIMIZER_Adam, //Adaptive Moment Estimation
OPTIMIZER_RMSprop, //Root Mean Square Propagation
OPTIMIZER_Adagrad, //Adaptive Gradient Descent
OPTIMIZER_Adadelta, //Adadelta
OPTIMIZER_Nadam //Nesterov-accelerated Adaptive Moment Estimation
};
*/
class OptimizerAdam
{
protected:
int time_step;
double m_learning_rate;
double m_beta1;
double m_beta2;
double m_epsilon;
matrix moment; //first moment estimate
matrix cache; //second moment estimate
public:
OptimizerAdam(double learning_rate=0.01, double beta1=0.9, double beta2=0.999, double epsilon=1e-8);
~OptimizerAdam(void);
virtual void update(matrix ¶meters, matrix &gradients);
};
//+------------------------------------------------------------------+
//| Initializes the Adam optimizer with hyperparameters. |
//| |
//| learning_rate: Step size for parameter updates |
//| beta1: Decay rate for the first moment estimate |
//| (moving average of gradients). |
//| beta2: Decay rate for the second moment estimate |
//| (moving average of squared gradients). |
//| epsilon: Small value for numerical stability. |
//+------------------------------------------------------------------+
OptimizerAdam::OptimizerAdam(double learning_rate=0.010000, double beta1=0.9, double beta2=0.999, double epsilon=1e-8):
time_step(0),
m_learning_rate(learning_rate),
m_beta1(beta1),
m_beta2(beta2),
m_epsilon(epsilon)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
OptimizerAdam::~OptimizerAdam(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OptimizerAdam::update(matrix ¶meters,matrix &gradients)
{
// Initialize moment and cache matrices if not already initialized
if (moment.Rows() != parameters.Rows() || moment.Cols() != parameters.Cols())
{
moment.Resize(parameters.Rows(), parameters.Cols());
moment.Fill(0.0);
}
if (cache.Rows() != parameters.Rows() || cache.Cols() != parameters.Cols())
{
cache.Resize(parameters.Rows(), parameters.Cols());
cache.Fill(0.0);
}
this.time_step++;
this.moment = this.m_beta1 * this.moment + (1 - this.m_beta1) * gradients;
this.cache = this.m_beta2 * this.cache + (1 - this.m_beta2) * MathPow(gradients, 2);
//--- Bias correction
matrix moment_hat = this.moment / (1 - MathPow(this.m_beta1, this.time_step));
matrix cache_hat = this.cache / (1 - MathPow(this.m_beta2, this.time_step));
parameters -= (this.m_learning_rate * moment_hat) / (MathPow(cache_hat, 0.5) + this.m_epsilon);
}
//+------------------------------------------------------------------+
//| |
//| |
//| Stochastic Gradient Descent (SGD): |
//| |
//| This is the simplest optimizer |
//| It updates the parameters using the gradients of the loss |
//| function computed on individual training samples or mini-batches.|
//| |
//| |
//+------------------------------------------------------------------+
class OptimizerSGD
{
protected:
double m_learning_rate;
public:
OptimizerSGD(double learning_rate=0.01);
~OptimizerSGD(void);
virtual void update(matrix ¶meters, matrix &gradients);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
OptimizerSGD::OptimizerSGD(double learning_rate=0.01):
m_learning_rate(learning_rate)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
OptimizerSGD::~OptimizerSGD(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OptimizerSGD::update(matrix ¶meters, matrix &gradients)
{
parameters -= this.m_learning_rate * gradients;
}
//+------------------------------------------------------------------+
//| Batch Gradient Descent (BGD): This optimizer computes the |
//| gradients of the loss function on the entire training dataset |
//| and updates the parameters accordingly. It can be slow and |
//| memory-intensive for large datasets but tends to provide a |
//| stable convergence. |
//+------------------------------------------------------------------+
class OptimizerMinBGD: public OptimizerSGD
{
public:
OptimizerMinBGD(double learning_rate=0.01);
~OptimizerMinBGD(void);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
OptimizerMinBGD::OptimizerMinBGD(double learning_rate=0.010000): OptimizerSGD(learning_rate)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
OptimizerMinBGD::~OptimizerMinBGD(void)
{
}
//+------------------------------------------------------------------+
//| |
//| |
//| |
//| RMSprop (Root Mean Square Propagation): RMSprop is similar to |
//| Adam but only uses the first moment of the gradients to adapt the|
//| moment learning rates. It's effective in handling non-stationary |
//| objectives and can converge quickly for many problems. |
//| |
//| |
//+------------------------------------------------------------------+
class OptimizerRMSprop
{
protected:
double m_learning_rate;
double m_decay_rate;
double m_epsilon;
matrix cache;
//Dividing double/matrix causes compilation error | this is the fix to the issue
matrix divide(const double numerator, const matrix &denominator)
{
matrix res = denominator;
for (ulong i=0; i
M A L E 5
A python-like Machine Learning Library for MQL5
English | Russian
## About the Project
MALE5 is a machine-learning repository for creating trading systems in the c++ like, MQL5 programming language.
It was developed to help build machine learning-based trading robots, effortlessly in the [MetaTrader5](https://www.metatrader5.com/en/automated-trading/metaeditor) platform
**My goal is to make the library**
- **Python-Like, Simple to use**
- **Flexible:** To be usable within a Trading Robot(EA), Custom Indicator, and Scripts.
- **Resource-efficient:** To make it not consume a lot of resources and memory.
**Disclaimer**
*This project is not for MQL5 programming language beginners. Those with prior experience of machine learning using python might find this project easy to understand*
## Installing
Download the zip file from the releases section extract the library. Go under MQL5 folder in your MetaEditor, from there paste the MALE5 directory you extracted under the Include folder.

## Getting Started with the Library
In this project, machine learning models can be placed into three categories. *See the tables below*.
## 01: Predictive Models
Unlike others which are mostly used for analysis and data processing. These AI models when given an inputs (predictors) in matrices or vectors they provide predictions. Currently Available models include.
| **Model Type** | **Models** |
|------------------------------|----------------------------------|
| **Linear Models** | - Linear regression |
| | - Logistic regression |
| | - Ridge regression |
| | - Polynomial regression |
| **Decision Trees** | - Decision tree |
| **Ensemble Models** | - AdaBoost tree |
| | - Random forest |
| **Support Vector Machine** | - SVM |
| **Neural Networks** | - Regressor Neural Networks |
| | - Pattern Recognition Neural Networks |
| | - Kohonen Maps |
| **Naïve Bayes** | - Naïve Bayes models |
### Traininng the predictive models
All the predictive models functions for training and deploying them for predictions follow similar patterns.
[](https://www.youtube.com/watch?v=wKk85PZ2ra8&t=1s)
#### For Regression Problems
A regression problem is a type of predictive modeling problem where the goal is to predict a continuous output variable based on one or more input features. In other words, given input data, the model aims to determine a continuous value. For example, predicting the next closing price of a financial instrument.
**How Regression Models Work**
Firstly, we import the necessary libraries we need. In this example, we will be using the Decision Tree Regressor.
```MQL5
#include
#include
#include //helper functions for data manipulations
#include //for measuring the performance
StandardizationScaler scaler; //standardization scaler from preprocessing.mqh
CDecisionTreeRegressor *decision_tree; //a decision tree regressor model
```
1. **Data Collection**
Gather a dataset with input features (also called predictors or attributes) and the corresponding target variable (the output).
```MQL5
vector open, high, low, close;
int data_size = 1000;
//--- Getting the open, high, low, and close values for the past 1000 bars, starting from the recent closed bar of 1
open.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_OPEN, 1, data_size);
high.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_HIGH, 1, data_size);
low.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_LOW, 1, data_size);
close.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_CLOSE, 1, data_size);
matrix X(data_size, 3); //creating the X matrix
//--- Assigning the open, high, and low price values to the X matrix
X.Col(open, 0);
X.Col(high, 1);
X.Col(low, 2);
vector y = close; // The target variable is the close price
```
2. **Preprocessing**
This involves cleaning and preprocessing the data, which might involve handling missing values, normalizing the data, and encoding categorical variables if possible in the data.
```MQL5
//--- We split the data into training and testing samples for training and evaluation
matrix X_train, X_test;
vector y_train, y_test;
double train_size = 0.7; //70% of the data should be used for training, the rest for testing
int random_state = 42; //we put a random state to shuffle the data
MatrixExtend::TrainTestSplitMatrices(X, y, X_train, y_train, X_test, y_test, train_size, random_state); // we split the X and y data into training and testing samples
//--- Normalizing the independent variables
X_train = scaler.fit_transform(X_train); // fit the scaler on the training data and transform the data
X_test = scaler.transform(X_test); // transform the test data
// Print the processed data for verification
//Print("X_train\n",X_train,"\nX_test\n",X_test,"\ny_train\n",y_train,"\ny_test\n",y_test);
```
3. **Model Selection**
Choose a regression algorithm. Common algorithms for this type of problem include linear regression, decision trees, support vector regression (SVR), k-nearest neighbors (K-NN), and neural networks.
```MQL5
//--- Model selection
decision_tree = new CDecisionTreeRegressor(2, 5); //a decision tree regressor from DecisionTree class
```
4. **Training**
Use the training data to teach the model how to map input features to the correct continuous output values. During training, the model learns patterns in the data.
```MQL5
//--- Training the model
decision_tree.fit(X_train, y_train); // The training function
```
5. **Evaluation**
Assess the model's performance on a separate test dataset to ensure it can generalize well to new, unseen data. Metrics like R-squared (R²) score, mean absolute error (MAE), and root mean squared error (RMSE) are often used to evaluate the model.
```MQL5
//--- Measuring predictive accuracy
vector train_predictions = decision_tree.predict(X_train);
printf("Decision Tree training R² score = %.3f ", Metrics::r_squared(y_train, train_predictions));
//--- Evaluating the model on out-of-sample predictions
vector test_predictions = decision_tree.predict(X_test);
printf("Decision Tree out-of-sample R² score = %.3f ", Metrics::r_squared(y_test, test_predictions));
```
6. **Prediction**
Once trained and evaluated, the model can be used to predict the continuous values of new, unseen data points.
```MQL5
void OnTick()
{
//--- Making predictions live from the market
CopyRates(Symbol(), PERIOD_D1, 1, 1, rates); // Get the very recent information from the market
vector x = {rates[0].open, rates[0].high, rates[0].low}; // Assigning data from the recent candle in a similar way to the training data
double predicted_close_price = decision_tree.predict(x);
Comment("Next closing price predicted is = ", predicted_close_price);
}
```
**Types of Regression**
- **Simple Linear Regression:** Predicting a continuous target variable based on a single input feature.
- **Multiple Linear Regression:** Predicting a continuous target variable based on multiple input features.
- **Polynomial Regression:** Predicting a continuous target variable using polynomial terms of the input features.
- **Non-Linear Regression:** Predicting a continuous target variable using non-linear relationships between input features and the target variable.
#### For Classification Problems
A classification problem is a type of predictive modeling problem where the goal is to predict the category or class that a given data point belongs to. In other words, given input data, the model aims to determine which of the predefined classes the input belongs to. Forexample when trying to predict whether the next candle will be either bullish or bearish.
**How Classification Models Work**
Firstly, we import the necessary libraries we need. In this example we will be using the Decision Tree Classifier.
```MQL5
#include
#include
#include //helper functions for data manipulations
#include //fo measuring the performance
StandardizationScaler scaler; //standardization scaler from preprocessing.mqh
CDecisionTreeClassifier *decision_tree; //a decision tree classifier model
```
1. **Data Collection**
Gather a dataset with input features (also called predictors or attributes) and the corresponding class labels (the output).
>
```MQL5
vector open, high, low, close;
int data_size = 1000;
//--- Getting the open, high, low and close values for the past 1000 bars, starting from the recent closed bar of 1
open.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_OPEN, 1, data_size);
high.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_HIGH, 1, data_size);
low.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_LOW, 1, data_size);
close.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_CLOSE, 1, data_size);
decision_tree = new CDecisionTreeClassifier(2, 5);
matrix X(data_size, 3); //creating the x matrix
//--- Assigning the open, high, and low price values to the x matrix
X.Col(open, 0);
X.Col(high, 1);
X.Col(low, 2);
```
2. **Preprocessing**
This involves clearning and preprocess the data, which might involve analytical techniques such as handling missing values, **normalizing the data**, and encoding categorical variables.
```MQL5
//--- We split the data into training and testing samples for training and evaluation
matrix X_train, X_test;
vector y_train, y_test;
double train_size = 0.7; //70% of the data should be used for training the rest for testing
int random_state = 42; //we put a random state to shuffle the data so that a machine learning model understands the patterns and not the order of the dataset, this makes the model durable
MatrixExtend::TrainTestSplitMatrices(X, y, X_train, y_train, X_test, y_test, train_size, random_state); // we split the x and y data into training and testing samples
//--- Normalizing the independent variables
X_train = scaler.fit_transform(X_train); // we fit the scaler on the training data and transform the data alltogether
X_test = scaler.transform(X_test); // we transform the new data this way
//Print("X_train\n",X_train,"\nX_test\n",X_test,"\ny_train\n",y_train,"\ny_test\n",y_test);
```
3. **Model Selection**
Choose a classification algorithm. Common algorithms for this type of problem include; logistic regression, decision trees, support vector machines (SVM), k-nearest neighbors (K-NN), and neural networks.
```MQL5
//--- Model selection
decision_tree = new CDecisionTreeClassifier(2, 5); //a decision tree classifier from DecisionTree class
```
4. **Training**
Use the training data to teach the model how to map input features to the correct class labels. During training, the model learns patterns in the data.
```MQL5
//--- Training the model
decision_tree.fit(X_train, y_train); //The training function
```
5. **Evaluation**
Assess the model's performance on a separate test dataset to ensure it can generalize well to new, unseen data. Metrics like accuracy score, precision, recall, and F1 score are often used to evaluate the model.
```MQL5
//--- Measuring predictive accuracy
vector train_predictions = decision_tree.predict_bin(X_train);
printf("Decision decision_tree training accuracy = %.3f ",Metrics::accuracy_score(y_train, train_predictions));
//--- Evaluating the model on out-of-sample predictions
vector test_predictions = decision_tree.predict_bin(X_test);
printf("Decision decision_tree out-of-sample accuracy = %.3f ",Metrics::accuracy_score(y_test, test_predictions));
```
6. **Prediction**
Once trained and evaluated, the model can be used to predict the class labels of new, unseen data points.
Unlike the `predict` function which is used to obtain predictions in regressors. The predictive functions for the classifiers have different slightly for different name starting with the word predict.
- **predict_bin** - This function can be used to predict the classes in as integer values for classification models. For example: The next candle will be ***bullish*** or ***bearish***
- **predict_proba** - This function predicts the probabilities of a certain classes as double values from 0 for a 0% probability chance to 1 for a 100% probability chance. For example: [0.64, 0.36] probability the next candle will be bullish is 64%, while the probability the next candle will be bearish is 36%
```
void OnTick()
{
//--- Making predictions live from the market
CopyRates(Symbol(), PERIOD_D1, 1, 1, rates); //Get the very recent information from the market
vector x = {rates[0].open, rates[0].high, rates[0].low}; //Assigning data from the recent candle in a similar way to the training data
double predicted_close_price = decision_tree.predict_bin(x);
Comment("Next closing price predicted is = ",predicted_close_price);
}
```
**Types of Classification**
- **Binary Classification:** There are only two classes. For example, classifying emails as spam or not spam.
- **Multiclass Classification:** There are more than two classes. For example, classifying types of animals in an image (e.g., cats, dogs, birds).
- **Multilabel Classification:** Each instance can be assigned multiple classes. For example, tagging a photo with multiple labels like "beach", "sunset", and "vacation".
## 02: Clustering Algorithms
These algorithms are for the special purppose of classifying and grouping data with similar patterns and contents together, they excel in data mining situations
| **Model Type** | **Models** |
|------------------------------|----------------------------------|
| **Nearest Neighbors** | - KNN nearest neighbors |
| **K-Means Clustering** | - k-Means clustering algorithm |
| **Neural Networks** | - Kohonen Maps |
## 03: Dimensionality Reduction Algorithms
These algorithms are widely used in situations where the dataset is huge and needs adjustmets. they excel at reducing the size of datasets without losing much information. Consider them as the algorithms to zip and shread information in the proper way.
| **Model Type** | **Models** |
|----------------------------------------|-------------------------------------|
| **Dimensionality Reduction Algorithms**| - Linear Discriminant Analysis (LDA)|
| | - Non Negative Matrix Factorization (NMF) |
| | - Principal Component Analysis (PCA) |
| | - Truncated SVD |
## Basic Library functionality & helpers
* [MatrixExtend (MatrixExtend.mqh)](https://github.com/MegaJoctan/MALE5/wiki#matrixextendmatrixextendmqh)
* [Cross Validation Library (cross_validation.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Cross-Validation-Library)
* [Linear Algebra Library (linalg.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Linear-Algebra-Library)
* [Kernels library (kernels.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Kernels-Library)
* [Metrics library (metrics.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Metrics-library)
* [Pre-processing library (preprocessing.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Pre-processing-library)
* [Tensor library (Tensor.mqh)](https://github.com/MegaJoctan/MALE5/wiki/Tensor-Library)
## Opening an issue
You can also post bug reports and feature requests (only) in [GitHub issues](https://github.com/MegaJoctan/MALE5/issues).
## Support the Project
If you find this project helpful, Support us by taking one or more of the actions
[BuyMeCoffee](https://www.buymeacoffee.com/omegajoctan)
[OurProducts](https://www.mql5.com/en/users/omegajoctan/seller)
Register to our recommended broker:
[ICMarkets](https://icmarkets.com/?camp=74639)
## Let's work together
[HIRE ME on MQL5.com by clicking on this link](https://www.mql5.com/en/job/new?prefered=omegajoctan)
================================================
FILE: Sklearn/Cluster/DBSCAN.mqh
================================================
//+------------------------------------------------------------------+
//| DBSCAN.mqh |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
#include
#include
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
class CDBSCAN
{
protected:
double m_epsilon;
uint m_minsamples;
vector labels;
vector get_neighbors(matrix &x, ulong point_index);
bool expand_cluster(matrix &x, ulong point_index, ulong cluster_id, const vector &neighbors, const vector &cluster_labels);
public:
CDBSCAN(double epsilon, uint min_samples);
~CDBSCAN(void);
vector fit_predict(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CDBSCAN::CDBSCAN(double epsilon, uint min_samples):
m_epsilon(epsilon),
m_minsamples(min_samples)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CDBSCAN::~CDBSCAN(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CDBSCAN::get_neighbors(matrix &x, ulong point_index)
{
vector neighbors = {};
for (ulong i=0, count=0; i0)
{
vector current_point = {points_to_explore[0]};
points_to_explore.Resize(0);
Print("current point: ", current_point);
vector current_neighbors = this.get_neighbors(x, (int)current_point[0]);
if (current_neighbors.Size() >= this.m_minsamples)
for (int neighbor_index=0; neighbor_index<(int)current_neighbors.Size(); neighbor_index++)
{
if (this.labels[neighbor_index] == 0 || this.labels[neighbor_index] == -1)
{
if (this.labels[neighbor_index] == 0)
{
points_to_explore.Resize(1);
points_to_explore[0] = neighbor_index;
Print("points to explore: ",points_to_explore);
}
this.labels[neighbor_index] = (int)cluster_id;
}
}
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CDBSCAN::fit_predict(matrix &x)
{
this.labels.Resize(x.Rows()); labels.Fill(0);
ulong cluster_id = 0;
for (ulong i=0; i
CGraphic graph;
#include
//+------------------------------------------------------------------+
enum errors
{
KM_ERR001, //clusters not matching in size Error
KM_ERR002
};
//+------------------------------------------------------------------+
class CKMeans
{
private:
ulong n; //number of samples
uint m_clusters;
ulong m_cols;
matrix InitialCentroids;
vector cluster_assign;
protected:
matrix Matrix;
bool ErrMsg(errors err);
bool ScatterCurvePlots(
string obj_name,
double &x[],
double &y[],
double &curveArr[],
string legend,
string x_axis_label = "x-axis",
string y_axis_label = "y-axis",
color clr = clrDodgerBlue,
bool points_fill = true
);
public:
CKMeans(const matrix &_Matrix, int clusters=3);
~CKMeans(void);
void KMeansClustering(matrix &clustered_matrix, matrix ¢roids, int iterations = 1, bool rand_cluster =false);
void ElbowMethod(const int initial_k=1, int total_k=10, bool showPlot = true);
void FilterZero(vector &vec);
void matrixtoArray(matrix &mat, double &Array[]);
};
//+------------------------------------------------------------------+
CKMeans::CKMeans(const matrix &_Matrix, int clusters=3)
{
m_clusters = clusters;
Matrix.Copy(_Matrix);
m_cols = Matrix.Cols();
n = Matrix.Rows(); //number of elements | Matrix Rows
}
//+------------------------------------------------------------------+
CKMeans::~CKMeans(void)
{
ZeroMemory(m_clusters);
ZeroMemory(InitialCentroids);
ZeroMemory(cluster_assign);
}
//+------------------------------------------------------------------+
void CKMeans::KMeansClustering(matrix &clustered_matrix, matrix ¢roids, int iterations = 1, bool rand_cluster =false)
{
InitialCentroids.Resize(m_clusters, m_cols);
cluster_assign.Resize(n);
clustered_matrix.Resize(m_clusters, m_cols*n);
clustered_matrix.Fill(NULL);
vector cluster_comb_v = {};
matrix cluster_comb_m = {};
vector rand_v = {};
ulong rand_ = 0;
for(ulong i=0; i= m_clusters)
break;
}
} //end of iterations
ZeroMemory(centroids);
centroids.Copy(InitialCentroids);
}
//+------------------------------------------------------------------+
bool CKMeans::ErrMsg(errors err)
{
switch(err)
{
case KM_ERR001:
printf("%s Clusters not matching in Size ", EnumToString(KM_ERR001));
break;
default:
break;
}
return(true);
}
//+------------------------------------------------------------------+
void CKMeans::ElbowMethod(const int initial_k=1, int total_k=10, bool showPlot = true)
{
matrix clustered_mat, _centroids = {};
if(total_k > (int)n)
total_k = (int)n; //k should always be less than n
vector centroid_v= {}, x_y_z= {};
vector short_v = {}; //vector for each point
vector minus_v = {}; //vector to store the minus operation output
double wcss = 0;
double WCSS[];
ArrayResize(WCSS, total_k);
double kArray[];
ArrayResize(kArray, total_k);
for(int k=initial_k, count_k=0; k
#include
#include
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
class BaseClustering
{
public:
BaseClustering(void);
~BaseClustering(void);
static matrix Get(matrix &X, vector &index, int axis=0);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix BaseClustering::Get(matrix &X, vector &index, int axis=0)
{
matrix ret_matrix = {};
ulong row_col=0;
bool isRows = true;
switch(axis)
{
case 0:
row_col = X.Rows();
isRows = true;
break;
case 1:
row_col = X.Cols();
isRows = false;
break;
default:
printf("%s Invalid axis %d ",__FUNCTION__,axis);
return ret_matrix;
break;
}
//---
ret_matrix.Resize(isRows?index.Size():X.Rows(), isRows?X.Cols(): index.Size());
for (ulong i=0, count=0; i
#include
enum lda_criterion //selecting best components criteria selection
{
CRITERION_VARIANCE,
CRITERION_KAISER,
CRITERION_SCREE_PLOT
};
class CLDA
{
CPlots plt;
CNumpy np;
protected:
uint m_components;
lda_criterion m_criterion;
matrix projection_matrix;
ulong num_features;
double m_regparam;
vector mean;
uint calculate_variance(vector &eigen_values, double threshold=0.95);
uint calculate_kaiser(vector &eigen_values);
uint CLDA::extract_components(vector &eigen_values, double threshold=0.95);
public:
CLDA(uint k=NULL, lda_criterion CRITERION_=CRITERION_SCREE_PLOT, double reg_param =1e-6);
~CLDA(void);
matrix fit_transform(const matrix &x, const vector &y);
matrix transform(const matrix &x);
vector transform(const vector &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CLDA::CLDA(uint k=NULL, lda_criterion CRITERION_=CRITERION_SCREE_PLOT, double reg_param=1e-6)
:m_components(k),
m_criterion(CRITERION_),
m_regparam(reg_param)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CLDA::~CLDA(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CLDA::fit_transform(const matrix &x, const vector &y)
{
vector classes = np.unique(y).unique;
ulong num_classes = classes.Size();
num_features = x.Cols();
this.mean = x.Mean(0);
matrix x_centered = BaseDimRed::subtract(x, this.mean);
matrix class_means(classes.Size(), x.Cols());
class_means.Fill(0.0);
for (ulong i=0; i= threshold);
k = (uint)v.ArgMax() + 1;
return k;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
uint CLDA::calculate_kaiser(vector &eigen_values)
{
vector v(eigen_values.Size()); v.Fill(0.0);
for (ulong i=0; i= 1);
return uint(v.Sum());
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
uint CLDA::extract_components(vector &eigen_values, double threshold=0.95)
{
uint k = 0;
switch(m_criterion)
{
case CRITERION_VARIANCE:
k = calculate_variance(eigen_values, threshold);
break;
case CRITERION_KAISER:
k = calculate_kaiser(eigen_values);
break;
case CRITERION_SCREE_PLOT:
{
vector v_cols(eigen_values.Size());
for (ulong i=0; i
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CNMF
{
protected:
CNumpy np;
uint m_components;
uint m_max_iter;
int m_randseed;
ulong n_features;
matrix W; //Basic matrix
matrix H; //coefficient matrix
double m_tol; //loss tolerance
public:
CNMF(uint max_iter=100, double tol=1e-4, int random_state=-1);
~CNMF(void);
matrix fit_transform(matrix &X, uint k=2);
matrix transform(matrix &X);
vector transform(vector &X);
uint select_best_components(matrix &X);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CNMF::CNMF(uint max_iter=100, double tol=1e-4,int random_state=-1)
:m_max_iter(max_iter),
m_randseed(random_state),
m_tol(tol)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CNMF::~CNMF(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CNMF::transform(matrix &X)
{
n_features = X.Cols();
if (m_components>n_features)
{
printf("%s Number of dimensions K[%d] is supposed to be <= number of features %d",__FUNCTION__,m_components,n_features);
this.m_components = (uint)n_features;
}
if (this.W.Rows()==0 || this.H.Rows()==0)
{
Print(__FUNCTION__," Model not fitted. Call fit method first.");
matrix mat={};
return mat;
}
return X.MatMul(this.H.Transpose());
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CNMF::transform(vector &X)
{
matrix INPUT_MAT = np.expand_dims(X, 0);
matrix OUTPUT_MAT = transform(INPUT_MAT);
return np.flatten(OUTPUT_MAT);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CNMF::fit_transform(matrix &X, uint k=2)
{
ulong m = X.Rows(), n = X.Cols();
double best_frobenius_norm = DBL_MIN;
m_components = m_components == 0 ? (uint)n : k;
//--- Initialize Random values
np.random.seed(this.m_randseed);
this.W = np.random.randn((uint)m, this.m_components);
this.H = np.random.randn(this.m_components, (uint)n);
//--- Update factors
vector loss(this.m_max_iter);
for (uint i=0; i
#include
//+------------------------------------------------------------------+
//| Principal Component Analysis Class |
//+------------------------------------------------------------------+
class CPCA
{
enum criterion
{
CRITERION_VARIANCE,
CRITERION_KAISER,
CRITERION_SCREE_PLOT
};
CPlots plt;
CNumpy np;
protected:
uint m_components;
criterion m_criterion;
matrix components_matrix;
vector mean;
uint n_features;
uint extract_components(vector &eigen_values, double threshold=0.95);
public:
CPCA(int k=0, criterion CRITERION_=CRITERION_SCREE_PLOT);
~CPCA(void);
matrix fit_transform(matrix &X);
matrix transform(matrix &X);
vector transform(vector &X);
bool save(string dir);
bool load(string dir);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPCA::CPCA(int k=0, criterion CRITERION_=CRITERION_SCREE_PLOT)
:m_components(k),
m_criterion(CRITERION_)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPCA::~CPCA(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CPCA::fit_transform(matrix &X)
{
n_features = (uint)X.Cols();
if (m_components>n_features)
{
printf("%s Number of dimensions K[%d] is supposed to be <= number of features %d",__FUNCTION__,m_components,n_features);
this.m_components = (int)n_features;
Print(__LINE__);
}
//---
this.mean = X.Mean(0);
matrix X_centered = BaseDimRed::subtract(X, this.mean);
BaseDimRed::ReplaceNaN(X_centered);
matrix cov_matrix = cova(X_centered, false);
matrix eigen_vectors;
vector eigen_values;
BaseDimRed::ReplaceNaN(cov_matrix);
if (!cov_matrix.Eig(eigen_vectors, eigen_values))
printf("Failed to caculate Eigen matrix and vectors Err=%d",GetLastError());
//--- Sort eigenvectors by decreasing eigenvalues
vector args = np.argsort(eigen_values);
args = np.reverse(args);
eigen_values = BaseDimRed::Sort(eigen_values, args);
eigen_vectors = BaseDimRed::Sort(eigen_vectors, args);
//---
if (MQLInfoInteger(MQL_DEBUG))
Print("Eigen values: ",eigen_values);
if (m_components==0)
m_components = this.extract_components(eigen_values);
else
this.extract_components(eigen_values);
if (MQLInfoInteger(MQL_DEBUG))
printf("%s Selected components %d",__FUNCTION__,m_components);
this.components_matrix = BaseDimRed::Slice(eigen_vectors, m_components, 1); //Get the components matrix
//MatrixExtend::NormalizeDouble_(this.components_matrix, 5);
//this.components_matrix = scaler.fit_transform(this.components_matrix.Transpose()); //Normalize components matrix
this.components_matrix = this.components_matrix.Transpose();
if (MQLInfoInteger(MQL_DEBUG))
Print("components_matrix\n",components_matrix);
//---
return X_centered.MatMul(components_matrix.Transpose()); //return the pca scores
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CPCA::transform(matrix &X)
{
if (X.Cols()!=this.n_features)
{
printf("%s Inconsistent input X matrix size, It is supposed to be of size %d same as the matrix used under fit_transform",__FUNCTION__,n_features);
this.m_components = n_features;
}
matrix X_centered = BaseDimRed::subtract(X, this.mean);
return X_centered.MatMul(this.components_matrix.Transpose()); //return the pca scores
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CPCA::transform(vector &X)
{
matrix INPUT_MAT = np.expand_dims(X, 0);
matrix OUTPUT_MAT = transform(INPUT_MAT);
return np.flatten(OUTPUT_MAT);
}
//+------------------------------------------------------------------+
//| Select the number of components based on some criterion |
//+------------------------------------------------------------------+
uint CPCA::extract_components(vector &eigen_values, double threshold=0.95)
{
uint k = 0;
vector eigen_pow = MathPow(eigen_values, 2);
vector cum_sum = eigen_pow.CumSum();
double sum = eigen_pow.Sum();
switch(m_criterion)
{
case CRITERION_VARIANCE:
{
vector cumulative_variance = cum_sum / sum;
if (MQLInfoInteger(MQL_DEBUG))
Print("Cummulative variance: ",cumulative_variance);
vector v(cumulative_variance.Size()); v.Fill(0.0);
for (ulong i=0; i= threshold);
k = (uint)v.ArgMax() + 1;
}
break;
case CRITERION_KAISER:
{
vector v(eigen_values.Size()); v.Fill(0.0);
for (ulong i=0; i= 1);
k = uint(v.Sum());
}
break;
case CRITERION_SCREE_PLOT:
{
vector v_cols(eigen_values.Size());
for (ulong i=0; i= 1);
k = uint(v.Sum());
}
break;
}
return (k);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CPCA::save(string dir)
{
matrix m = CUtils::VectorToMatrix(this.mean, this.mean.Size());
if (!CUtils::WriteCsv(dir+"\\PCA-Mean.csv",m,NULL,false,8))
{
Print("Failed to Save PCA-Mean information to ",dir);
return false;
}
//---
if (!CUtils::WriteCsv(dir+"\\PCA-ComponentsMatrix.csv",this.components_matrix,NULL,false,8))
{
Print("Failed to Save PCA-ComponentsMatrix information to ",dir);
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CPCA::load(string dir)
{
string header;
matrix m = CUtils::ReadCsv(dir+"\\PCA-Mean.csv",header);
if (m.Rows()==0)
return false;
this.mean = CUtils::MatrixToVector(m);
this.n_features = (uint)this.mean.Size();
//---
this.components_matrix = CUtils::ReadCsv(dir+"\\PCA-ComponentsMatrix.csv",header);
//printf("Components Matrix[%dx%d]",components_matrix.Rows(),components_matrix.Cols());
if (components_matrix.Rows()==0)
return false;
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix cova(matrix &data, bool row_var=true)
{
if (row_var)
data = data.Transpose(); // Transpose if each row represents a data point
// Step 1: Center the data
matrix centered_data = BaseDimRed::subtract(data, data.Mean(0));
// Step 2: Calculate the covariance matrix
matrix covariance_matrix = centered_data.Transpose().MatMul(centered_data) / (data.Rows() - 1);
return covariance_matrix;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
================================================
FILE: Sklearn/Decomposition/README.md
================================================
## Linear Discriminant Analysis (LDA)
This documentation explains the `CLDA` class in MQL5, which implements **Linear Discriminant Analysis (LDA)** for dimensionality reduction and classification tasks.
**I. LDA Theory:**
LDA is a supervised learning technique that aims to find **linear projections** of the data that **maximize the separation between different classes** while **minimizing variance within each class**. This makes it particularly useful for **classification** problems where the goal is to distinguish between distinct groups.
**II. CLDA Class:**
The `CLDA` class provides functionalities for performing LDA in MQL5:
**Public Functions:**
* `CLDA(uint k=NULL, lda_criterion CRITERION_=CRITERION_SCREE_PLOT, double reg_param =1e-6)` Constructor, allows setting hyperparameters:
* `k`: Number of components to extract (default: None, determined automatically).
* `CRITERION_`: Criterion for selecting the best components (default: CRITERION_SCREE_PLOT).
* `reg_param`: Regularization parameter to prevent overfitting (default: 1e-6).
* `~CLDA(void)`: Destructor.
* `matrix fit_transform(const matrix &x, const vector &y):` Trains the model on the provided data (`x` - independent variables, `y` - class labels) and returns the transformed data.
* `matrix transform(const matrix &x):` Transforms new data (`x`) using the trained model.
* `vector transform(const vector &x):` Transforms a single new data point (`x`) using the trained model.
**Internal Functions:**
* `calculate_variance(vector &eigen_values, double threshold=0.95)`: Calculates the number of components based on explained variance (optional).
* `calculate_kaiser(vector &eigen_values)`: Calculates the number of components based on Kaiser's criterion (optional).
* `extract_components(vector &eigen_values, double threshold=0.95)`: Extracts the selected number of components based on the chosen criterion.
**III. Additional Notes:**
* The `m_criterion` member variable allows choosing the criterion for selecting the number of components:
* **CRITERION_VARIANCE:** Retains components that explain a specific percentage of variance (set by `threshold`).
* **CRITERION_KAISER:** Retains components with eigenvalues greater than 1.
* **CRITERION_SCREE_PLOT:** Analyzes the scree plot to visually determine the number of components with significant eigenvalues.
* The `m_regparam` member variable allows for regularization to prevent overfitting.
* The class internally uses the `CPlots` class (not documented here) for potential visualization purposes (e.g., scree plot).
## Principal Component Analysis (PCA)
This documentation explains the `CPCA` class in MQL5, which implements **Principal Component Analysis (PCA)** for dimensionality reduction and data visualization tasks.
**I. PCA Theory:**
PCA is an unsupervised learning technique that aims to find a **linear transformation** of the data that captures the most **variance** in a **reduced number of dimensions**. This allows for:
* **Dimensionality reduction:** Reduce the number of features while retaining most of the information in the data.
* **Data visualization:** Project high-dimensional data onto a lower-dimensional space for easier visualization.
**II. CPCA Class:**
The `CPCA` class provides functionalities for performing PCA in MQL5:
**Public Functions:**
* `CPCA(int k=0, criterion CRITERION_=CRITERION_SCREE_PLOT)` Constructor, allows setting hyperparameters:
* `k`: Number of components to extract (default: 0, determined automatically using the chosen criterion).
* `CRITERION_`: Criterion for selecting the best components (default: CRITERION_SCREE_PLOT).
* `~CPCA(void)` Destructor.
* `matrix fit_transform(matrix &X)` Trains the model on the provided data (`X`) and returns the transformed data.
* `matrix transform(matrix &X)` Transforms new data (`X`) using the trained model.
* `vector transform(vector &X)` Transforms a single new data point (`X`) using the trained model.
* `bool save(string dir)` Saves the model parameters to a specified directory (`dir`).
* `bool load(string dir)` Loads the model parameters from a specified directory (`dir`).
**Internal Functions:**
* `extract_components(vector &eigen_values, double threshold=0.95)`: Extracts the selected number of components based on the chosen criterion (similar to the `CLDA` class).
**III. Additional Notes:**
* The `m_criterion` member variable allows choosing the criterion for selecting the number of components (same options as in `CLDA`).
* The class internally uses the `CPlots` class (not documented here) for potential visualization purposes.
* Saving and loading functionalities allow for model persistence and reusability.
## Non-Negative Matrix Factorization (NMF)
This documentation explains the `CNMF` class in MQL5, which implements **Non-Negative Matrix Factorization (NMF)** for data decomposition and feature extraction tasks.
**I. NMF Theory:**
NMF is a dimensionality reduction technique that decomposes a **non-negative matrix** `V` into two **non-negative matrices** `W` and `H`:
* `V` (shape: `m x n`): The input data matrix, where `m` is the number of data points and `n` is the number of features.
* `W` (shape: `m x k`): The **basis matrix**, where `k` is the number of chosen components and each row represents a **basis vector**.
* `H` (shape: `k x n`): The **coefficient matrix**, where each row corresponds to a basis vector and each element represents the **contribution** of that basis vector to a specific feature in the original data.
By finding a suitable factorization, NMF aims to represent the original data as a **linear combination** of basis vectors while preserving the non-negative nature of the input data. This allows for:
* **Dimensionality reduction:** Reduce the number of features while still capturing essential information.
* **Feature extraction:** Identify underlying factors or patterns in the data through the basis vectors.
* **Data interpretation:** Gain insights into the data by analyzing the non-negative contributions of basis vectors to each feature.
**II. CNMF Class:**
The `CNMF` class provides functionalities for performing NMF :
**Public Functions:**
* `CNMF(uint max_iter=100, double tol=1e-4, int random_state=-1)` Constructor, allows setting hyperparameters:
* `max_iter`: Maximum number of iterations for the NMF algorithm (default: 100).
* `tol`: Tolerance for convergence (default: 1e-4).
* `random_state`: Random seed for initialization (default: -1, uses random seed).
* `~CNMF(void)` Destructor.
* `matrix fit_transform(matrix &X, uint k=2)` Trains the model on the provided data (`X`) and returns the decomposed components (`W` and `H`).
* `k`: Number of components to extract (default: 2).
* `matrix transform(matrix &X)` Transforms new data (`X`) using the trained model.
* `vector transform(vector &X)` Transforms a single new data point (`X`) using the trained model.
* `uint select_best_components(matrix &X)` Analyzes the input data and suggests an appropriate number of components (implementation details might vary depending on the specific NMF algorithm used).
**III. Additional Notes:**
* The internal implementation details of the NMF algorithm might vary depending on the specific chosen library or technique.
* Choosing the appropriate number of components is crucial for optimal performance and avoiding overfitting. The `select_best_components` function is helpful as a starting point, but further evaluation and domain knowledge might be needed for optimal selection.
By understanding the theoretical foundation and functionalities of the `CNMF` class, MQL5 users can leverage NMF for various tasks, including:
* Dimensionality reduction for data visualization or machine learning algorithms that require lower-dimensional inputs.
* Feature extraction to identify underlying structure or patterns in non-negative data.
* Topic modeling for analyzing text data or other types of document collections.
## Truncated Singular Value Decomposition (Truncated SVD)
This documentation explains the `CTruncatedSVD` class in MQL5, which implements **Truncated Singular Value Decomposition (Truncated SVD)** for dimensionality reduction and data visualization tasks.
**I. Truncated SVD Theory:**
Truncated SVD is a dimensionality reduction technique based on **Singular Value Decomposition (SVD)**. SVD decomposes a matrix `X` into three matrices:
* `U`: A left singular vectors matrix.
* `Σ`: A diagonal matrix containing the singular values of `X`.
* `V^T`: A right singular vectors matrix (transposed).
Truncated SVD retains only **k** top singular values from `Σ` and their corresponding columns from `U` and `V^T`. This creates a lower-dimensional representation of the original data that captures most of the variance.
**II. CTruncatedSVD Class:**
The `CTruncatedSVD` class provides functionalities for performing Truncated SVD in MQL5:
**Public Functions:**
* `CTruncatedSVD(uint k=0):` Constructor, allows setting the number of components (`k`) to retain (default: 0, determined automatically).
* `~CTruncatedSVD(void):` Destructor.
* `matrix fit_transform(matrix& X):` Trains the model on the provided data (`X`) and returns the transformed data.
* `matrix transform(matrix &X):` Transforms new data (`X`) using the trained model.
* `vector transform(vector &X):` Transforms a single new data point (`X`) using the trained model.
* `ulong _select_n_components(vector &singular_values):` (Internal function, not directly exposed to users) Determines the number of components based on the explained variance ratio (implementation details may vary).
**III. Additional Notes:**
* The class internally uses the `CPlots` class (not documented here) for potential visualization purposes.
* Choosing the appropriate number of components (`k`) is crucial for balancing dimensionality reduction and information preservation. The `_select_n_components` function might use different criteria (e.g., explained variance ratio) for automatic selection, and user discretion might be needed depending on the specific task.
By understanding the theoretical foundation and functionalities of the `CTruncatedSVD` class, MQL5 users can leverage Truncated SVD for:
* **Dimensionality reduction:** Reduce the number of features while retaining most of the information.
* **Data visualization:** Project high-dimensional data onto a lower-dimensional space for easier visualization.
* **Feature extraction:** Identify underlying factors or patterns in the data through the singular vectors (although not explicitly mentioned in the class functionalities).
It's important to note that the specific implementation details of the `CTruncatedSVD` class and the `_select_n_components` function might vary depending on the chosen MQL5 library or framework. Refer to the specific documentation of your chosen library for the most accurate and up-to-date information.
**Reference:**
* [Data Science and Machine Learning (Part 18): The battle of Mastering Market Complexity, Truncated SVD Versus NMF](https://www.mql5.com/en/articles/13968)
* [Data Science and Machine Learning(Part 20) : Algorithmic Trading Insights, A Faceoff Between LDA and PCA in MQL5](https://www.mql5.com/en/articles/14128)
================================================
FILE: Sklearn/Decomposition/TruncatedSVD.mqh
================================================
//+------------------------------------------------------------------+
//| CTruncatedSVD.mqh |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#include "base.mqh"
#include
class CTruncatedSVD
{
CPlots plt;
uint m_components;
ulong n_features;
matrix components_;
vector mean;
vector explained_variance_;
public:
CTruncatedSVD(uint k=0);
~CTruncatedSVD(void);
matrix fit_transform(matrix& X);
matrix transform(matrix &X);
vector transform(vector &X);
ulong _select_n_components(vector &singular_values);
};
//+------------------------------------------------------------------+
//| Once the k value is left to default value of zero, the function |
//| _select_n_components will be used to find the best number of |
//| components to use |
//+------------------------------------------------------------------+
CTruncatedSVD::CTruncatedSVD(uint k=0)
:m_components(k)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CTruncatedSVD::~CTruncatedSVD(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CTruncatedSVD::fit_transform(matrix &X)
{
n_features = X.Cols();
if (m_components>n_features)
{
printf("%s Number of dimensions K[%d] is supposed to be <= number of features %d",__FUNCTION__,m_components,n_features);
this.m_components = (uint)n_features;
}
//--- Center the data (subtract mean)
this.mean = X.Mean(0);
matrix X_centered = BaseDimRed::subtract(X, this.mean);
//--- Compute the covariance matrix
BaseDimRed::ReplaceNaN(X_centered);
matrix cov_matrix = X_centered.Cov(false);
//--- Perform SVD on the covariance matrix
matrix U={}, Vt={};
vector Sigma={};
BaseDimRed::ReplaceNaN(cov_matrix);
if (!cov_matrix.SVD(U,Vt,Sigma))
Print(__FUNCTION__," Line ",__LINE__," Failed to calculate SVD Err=",GetLastError());
if (m_components == 0)
{
m_components = (uint)this._select_n_components(Sigma);
Print(__FUNCTION__," Best value of K = ",m_components);
}
this.components_ = BaseDimRed::Slice(Vt, this.m_components).Transpose();
BaseDimRed::ReplaceNaN(this.components_);
if (MQLInfoInteger(MQL_DEBUG))
Print("components_T[",components_.Rows(),"X",components_.Cols(),"]\n",this.components_);
this.explained_variance_ = MathPow(BaseDimRed::Slice(Sigma, this.m_components), 2) / (X.Rows() - 1);
return X_centered.MatMul(components_);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix CTruncatedSVD::transform(matrix &X)
{
matrix X_centered = BaseDimRed::subtract(X, this.mean);
if (X.Cols()!=this.n_features)
{
printf("%s Inconsistent input X matrix size, It is supposed to be of size %d same as the matrix used under fit_transform",__FUNCTION__,n_features);
this.m_components = (uint)n_features;
}
return X_centered.MatMul(components_);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CTruncatedSVD::transform(vector &X)
{
matrix INPUT_MAT = MatrixExtend::VectorToMatrix(X, X.Size());
matrix OUTPUT_MAT = transform(INPUT_MAT);
return MatrixExtend::MatrixToVector(OUTPUT_MAT);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
ulong CTruncatedSVD::_select_n_components(vector &singular_values)
{
double total_variance = MathPow(singular_values.Sum(), 2);
vector explained_variance_ratio = MathPow(singular_values, 2).CumSum() / total_variance;
if (MQLInfoInteger(MQL_DEBUG))
Print(__FUNCTION__," Explained variance ratio ",explained_variance_ratio);
vector k(explained_variance_ratio.Size());
for (uint i=0; i
class BaseDimRed
{
public:
BaseDimRed(void);
~BaseDimRed(void);
static matrix Slice(const matrix &mat, uint from_0_to, int axis=0);
static vector Slice(const vector &v, uint from_0_to);
static matrix subtract(const matrix&mat, const vector &v);
static void ReplaceNaN(matrix &mat);
static matrix Sort(matrix &mat, vector &args);
static vector Sort(vector &v, vector &args);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
matrix BaseDimRed::Slice(const matrix &mat, uint from_0_to, int axis=0)
{
matrix ret = {};
if (from_0_to==0)
{
printf("%s Cannot slice a vector from index 0 to 0",__FUNCTION__);
return mat;
}
switch(axis)
{
case 0:
ret.Resize(from_0_to, mat.Cols());
for (uint i=0; i
#include
//+------------------------------------------------------------------+
//| Model class |
//+------------------------------------------------------------------+
#include
#include
//+------------------------------------------------------------------+
//| AdaBoost class for Decision Tree |
//+------------------------------------------------------------------+
namespace DecisionTree
{
class AdaBoost
{
protected:
vector m_alphas;
vector classes_in_data;
int m_random_state;
bool m_boostrapping;
uint m_min_split, m_max_depth;
CDecisionTreeClassifier *weak_learners[]; //store weak_learner pointers for memory allocation tracking
CDecisionTreeClassifier *weak_learner;
uint m_estimators;
public:
AdaBoost(uint min_split, uint max_split, uint n_estimators=50, int random_state=42, bool bootstrapping=true);
~AdaBoost(void);
void fit(matrix &x, vector &y);
int predict(vector &x);
vector predict(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
AdaBoost::AdaBoost(uint min_split, uint max_split, uint n_estimators=50, int random_state=42, bool bootstrapping=true)
:m_estimators(n_estimators),
m_random_state(random_state),
m_boostrapping(bootstrapping),
m_min_split(min_split),
m_max_depth(max_split)
{
ArrayResize(weak_learners, m_estimators); //Resizing the array to retain the number of base weak_learners
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
AdaBoost::~AdaBoost(void)
{
for (uint i=0; i
#include
enum errors_classifier
{
ERR_ACCURACY
};
class CRandomForestClassifier
{
protected:
uint m_ntrees;
uint m_maxdepth;
uint m_minsplit;
int m_random_state;
CDecisionTreeClassifier *forest[];
string ConvertTime(double seconds);
double err_metric(errors_classifier err, vector &actual, vector &preds);
public:
CRandomForestClassifier(uint n_trees=100, uint minsplit=NULL, uint max_depth=NULL, int random_state=-1);
~CRandomForestClassifier(void);
void fit(matrix &x, vector &y, bool replace=true, errors_classifier err=ERR_ACCURACY);
double predict(vector &x);
vector predict(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRandomForestClassifier::CRandomForestClassifier(uint n_trees=100, uint minsplit=NULL, uint max_depth=NULL, int random_state=-1):
m_ntrees(n_trees),
m_maxdepth(max_depth),
m_minsplit(minsplit),
m_random_state(random_state)
{
ArrayResize(forest, n_trees);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRandomForestClassifier::~CRandomForestClassifier(void)
{
for (uint i=0; i Tree <%d> Rand Seed <%s> Accuracy Score: %.3f Time taken: %s",i+1,m_random_state==-1?"None":string(m_random_state),this.err_metric(err, y_subset, preds), ConvertTime((current_time - time_start) / 1000.0));
}
m_ntrees = ArraySize(forest); //The successfully build trees
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CRandomForestClassifier::predict(vector &x)
{
vector predictions(m_ntrees); //predictions from all the trees
for (uint i=0; i= 60)
{
minutes = (uint)(seconds / 60.0) ;
seconds = fmod(seconds, 1.0) * 60;
time_str = StringFormat("%d Minutes and %.3f Seconds", minutes, seconds);
}
if (minutes >= 60)
{
hours = (uint)(minutes / 60.0);
minutes = minutes % 60;
time_str = StringFormat("%d Hours and %d Minutes", hours, minutes);
}
if (time_str == "")
{
time_str = StringFormat("%.3f Seconds", seconds);
}
return time_str;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CRandomForestClassifier::err_metric(errors_classifier err, vector &actual, vector &preds)
{
return Metrics::accuracy_score(actual, preds);
}
//+------------------------------------------------------------------+
//| |
//| |
//| |
//| |
//| Random Forest for regression problems |
//| |
//| |
//| |
//| |
//+------------------------------------------------------------------+
enum errors_regressor
{
ERR_R2_SCORE,
ERR_ADJUSTED_R
};
class CRandomForestRegressor
{
private:
uint m_ntrees;
uint m_maxdepth;
uint m_minsplit;
int m_random_state;
CDecisionTreeRegressor *forest[];
string ConvertTime(double seconds);
double err_metric(errors_regressor err,vector &actual,vector &preds);
public:
CRandomForestRegressor(uint n_trees=100, uint minsplit=NULL, uint max_depth=NULL, int random_state=-1);
~CRandomForestRegressor(void);
void fit(matrix &x, vector &y, bool replace=true, errors_regressor err=ERR_R2_SCORE);
double predict(vector &x);
vector predict(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRandomForestRegressor::CRandomForestRegressor(uint n_trees=100, uint minsplit=NULL, uint max_depth=NULL, int random_state=-1):
m_ntrees(n_trees),
m_maxdepth(max_depth),
m_minsplit(minsplit),
m_random_state(random_state)
{
ArrayResize(forest, n_trees);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CRandomForestRegressor::~CRandomForestRegressor(void)
{
for (uint i=0; i= 60)
{
minutes = (uint)(seconds / 60.0) ;
seconds = fmod(seconds, 1.0) * 60;
time_str = StringFormat("%d Minutes and %.3f Seconds", minutes, seconds);
}
if (minutes >= 60)
{
hours = (uint)(minutes / 60.0);
minutes = minutes % 60;
time_str = StringFormat("%d Hours and %d Minutes", hours, minutes);
}
if (time_str == "")
{
time_str = StringFormat("%.3f Seconds", seconds);
}
return time_str;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRandomForestRegressor::fit(matrix &x, vector &y, bool replace=true, errors_regressor err=ERR_R2_SCORE)
{
matrix x_subset;
vector y_subset;
matrix data = MatrixExtend::concatenate(x, y, 1);
matrix temp_data = data;
vector preds;
datetime time_start = GetTickCount(), current_time;
Print("[ Regressor Random Forest Building ]");
for (uint i=0; i Tree <%d> Rand Seed <%s> R_2 Score: %.3f Time taken: %s",i+1,m_random_state==-1?"None":string(m_random_state),this.err_metric(err, y_subset, preds), ConvertTime((current_time - time_start) / 1000.0));
}
m_ntrees = ArraySize(forest); //The successfully build trees
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CRandomForestRegressor::predict(vector &x)
{
vector predictions(m_ntrees); //predictions from all the trees
for (uint i=0; i
#include
#include
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CLogisticRegression
{
private:
CNumpy np;
vector classes_in_data;
bool istrained;
bool checkIsTrained(string func)
{
if(!istrained)
{
Print(func," Tree not trained, Call fit function first to train the model");
return false;
}
return (true);
}
bool CheckSamplesSize(string func, ulong size)
{
if(size != m_features)
{
printf("%s x sample size doesn't align with the training data m_features %d",func, size);
return false;
}
return true;
}
matrix weights;
double bias;
//---
uint m_epochs;
double m_alpha;
double m_tol;
ulong m_features;
int m_random_seed;
public:
CLogisticRegression(uint epochs=10, double alpha=0.01, double tol=1e-8, int random_seed = 0);
~CLogisticRegression(void);
void fit(matrix &x, vector &y);
int predict(vector &x);
vector predict(matrix &x);
double predict_proba(vector &x);
vector predict_proba(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CLogisticRegression::CLogisticRegression(uint epochs=10, double alpha=0.01, double tol=1e-8, int random_seed=0)
:istrained(false),
m_epochs(epochs),
m_alpha(alpha),
m_tol(tol),
m_random_seed(random_seed)
{
}
//+------------------------------------------------------------------+
//| This is where the logistic model gets trained |
//+------------------------------------------------------------------+
void CLogisticRegression::fit(matrix &x, vector &y)
{
ulong m = x.Rows(), n = x.Cols();
m_features = n;
np.random.seed(m_random_seed);
vector rand_v = np.random.uniform(-1, 1, (uint)n);
this.weights = np.expand_dims(rand_v, 1);
//---
matrix dw; //derivative wrt weights &
double db; //bias respectively
vector preds;
istrained = true;
double prev_cost = -DBL_MAX, cost =0;
for(ulong i=0; i Logistic regression build epoch [%d/%d] mse %.5f",i+1,m_epochs, cost);
this.weights -= this.m_alpha * dw;
this.bias -= this.bias * db;
if(MathAbs(prev_cost - cost) < this.m_tol)
{
Print("Converged!!!");
break;
}
prev_cost = cost;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CLogisticRegression::~CLogisticRegression(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CLogisticRegression::predict(vector &x)
{
if(!checkIsTrained(__FUNCTION__))
return 0;
if(!CheckSamplesSize(__FUNCTION__,x.Size()))
return 0;
matrix x_mat = np.expand_dims(x, 1);
matrix preds = (x_mat.MatMul(this.weights) + this.bias);
preds.Activation(preds, AF_HARD_SIGMOID);
if(preds.Rows()>1)
{
printf("%s The outcome from a sigmoid must be a scalar value",__FUNCTION__);
return 0;
}
return (int)(preds[0][0]>=0.5);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CLogisticRegression::predict(matrix &x)
{
vector v(x.Rows());
for(ulong i=0; i1)
{
printf("%s The outcome from a sigmoid must be a scalar value",__FUNCTION__);
return 0;
}
return preds[0][0];
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CLogisticRegression::predict_proba(matrix &x)
{
vector v(x.Rows());
for(ulong i=0; i
#include
#include "Linear Regression.mqh"
//+------------------------------------------------------------------+
class CRidgeregression
{
protected:
matrix XMatrix; //matrix of independent variables
matrix YMatrix;
vector yVector; // Vector of target variables
matrix Id_matrix; //Identity matrix
matrix Betas;
ulong n; //No of samples
ulong k; //No of regressors
public:
CRidgeregression(matrix &_matrix);
~CRidgeregression(void);
double RSS;
double Lr_accuracy;
vector L2Norm(double lambda); //Ridge regression
};
//+------------------------------------------------------------------+
/*
CRidgeregression::CRidgeregression(matrix &_matrix)
{
n = _matrix.Rows();
k = _matrix.Cols();
MatrixExtend::XandYSplitMatrices(_matrix,XMatrix,yVector);
YMatrix = MatrixExtend::VectorToMatrix(yVector);
//---
Id_matrix.Resize(k,k);
Id_matrix.Identity();
}
*/
//+------------------------------------------------------------------+
CRidgeregression::~CRidgeregression(void)
{
ZeroMemory(XMatrix);
ZeroMemory(yVector);
ZeroMemory(yVector);
ZeroMemory(Id_matrix);
}
//+------------------------------------------------------------------+
/*
vector CRidgeregression::L2Norm(double lambda)
{
matrix design = MatrixExtend::DesignMatrix(XMatrix);
matrix XT = design.Transpose();
matrix XTX = XT.MatMul(design);
matrix lamdaxI = lambda * Id_matrix;
matrix sum_matrix = XTX + lamdaxI;
matrix Inverse_sum = sum_matrix.Inv();
matrix XTy = XT.MatMul(YMatrix);
Betas = Inverse_sum.MatMul(XTy);
#ifdef DEBUG_MODE
//Print("Betas\n",Betas);
#endif
return(MatrixExtend::MatrixToVector(Betas));
}
//+------------------------------------------------------------------+
*/
================================================
FILE: Sklearn/Naive Bayes/Naive Bayes.mqh
================================================
//+------------------------------------------------------------------+
//| Naive Bayes.mqh |
//| Copyright 2022, Fxalgebra.com |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Fxalgebra.com"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#include
//+------------------------------------------------------------------+
//| N A I V E B A Y E S |
//| |
//| suitable for classification of discrete values, that have |
//| been load to a matrix using the method ReadCSVEncode from |
//| CUtils::mqh |
//| |
//+------------------------------------------------------------------+
class CNaiveBayes
{
protected:
uint n_features;
vector y_target;
vector class_proba; //prior class probability
vector features_proba; //features probability
vector c_prior_proba; //class prior probability
vector c_evidence; //class evidence
vector calcProba(vector &v_features);
public:
vector classes; //classes available
CNaiveBayes(void);
~CNaiveBayes(void);
void fit(matrix &x, vector &y);
int predict(vector &x);
vector predict(matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CNaiveBayes::CNaiveBayes(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CNaiveBayes::fit(matrix &x, vector &y)
{
ulong samples = x.Rows(),
features = x.Cols();
vector unique = CUtils::Unique_count(y);
this.class_proba = unique / samples;
if (MQLInfoInteger(MQL_DEBUG))
Print("class probabilities: ",class_proba);
/*
y_target = y;
n_features = x.Cols();
classes = CUtils::Unique(y);
c_evidence.Resize((ulong)classes.Size());
n = y.Size();
if (n==0) { Print("--> n == 0 | Naive Bayes class failed"); return; }
//---
vector v = {};
for (ulong i=0; i GROUPS ",classes);
Print("Prior Class Proba ",c_prior_proba,"\nEvidence ",c_evidence);
*/
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CNaiveBayes::~CNaiveBayes(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CNaiveBayes::predict(vector &x)
{
vector v = calcProba(x);
double sum = v.Sum();
for (ulong i=0; i n == 0 | Gaussian Naive Bayes class failed"); return; }
//---
vector v = {};
for (ulong i=0; i GROUPS ",classes);
Print("\n---> Prior_proba ",c_prior_proba," Evidence ",c_evidence);
//---
during_training = false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CGaussianNaiveBayes::~CGaussianNaiveBayes(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CGaussianNaiveBayes::predict_bin(vector &x)
{
if (x.Size() != m_cols)
{
Print("CRITICAL | The given x have different size than the trained x");
return (-1);
}
vector p = calcProba(x);
return((int)classes[p.ArgMax()]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CGaussianNaiveBayes::predict_proba(vector &x)
{
vector x = x;
vector ret_v = {};
if (x.Size() != m_cols)
{
Print("CRITICAL | The given x have different size than the trained x");
return (ret_v);
}
ret_v = calcProba(x);
return (ret_v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector CGaussianNaiveBayes::predict_bin(matrix &x)
{
ulong rows = x.Rows();
vector v(rows), pred(rows);
for (ulong i=0; i> Proba ",proba," prior proba ",c_prior_proba);
#endif
}
//--- Normalize probabilities
proba_v = proba_v / proba_v.Sum();
return proba_v;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
*/
================================================
FILE: Sklearn/Naive Bayes/README.md
================================================
## Naive Bayes Classifier
This documentation explains the `CNaiveBayes` class in MQL5, which implements a **Naive Bayes classifier** for classification tasks.
**I. Naive Bayes Theory:**
Naive Bayes is a probabilistic classifier based on **Bayes' theorem**. It assumes that the features used for classification are **independent** of each other given the class label. This simplifies the calculations involved in making predictions.
**II. CNaiveBayes Class:**
The `CNaiveBayes` class provides functionalities for training and using a Naive Bayes classifier in MQL5:
**Public Functions:**
* **CNaiveBayes(void):** Constructor.
* **~CNaiveBayes(void):** Destructor.
* **void fit(matrix &x, vector &y):** Trains the model on the provided data (`x` - features, `y` - target labels).
* **int predict(vector &x):** Predicts the class label for a single input vector.
* **vector predict(matrix &x):** Predicts class labels for all rows in the input matrix.
**Internal Variables:**
* `n_features`: Number of features in the data.
* `y_target`: Vector of target labels used during training.
* `classes`: Vector containing the available class labels.
* `class_proba`: Vector storing the prior probability of each class.
* `features_proba`: Matrix storing the conditional probability of each feature value given each class.
* `c_prior_proba`: Vector storing the calculated prior probability of each class after training.
* `c_evidence`: Vector storing the calculated class evidence for a new data point.
* `calcProba(vector &v_features)`: Internal function (not directly accessible) that likely calculates the class probabilities for a given feature vector.
**III. Class Functionality:**
1. **Training:**
* The `fit` function takes the input data (features and labels) and performs the following:
* Calculates the prior probability of each class (number of samples belonging to each class divided by the total number of samples).
* Estimates the conditional probability of each feature value given each class (using techniques like Laplace smoothing to handle unseen features).
* These probabilities are stored in the internal variables for later use in prediction.
2. **Prediction:**
* The `predict` functions take a new data point (feature vector) and:
* Calculate the class evidence for each class using Bayes' theorem, considering the prior probabilities and conditional probabilities of the features.
* The class with the **highest class evidence** is predicted as the most likely class for the new data point.
**IV. Additional Notes:**
* The class assumes the data is already preprocessed and ready for use.
**Reference**
* [Data Science and Machine Learning (Part 11): Naïve Bayes, Probability theory in Trading](https://www.mql5.com/en/articles/12184)
================================================
FILE: Sklearn/Naive Bayes/naive bayes visuals.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 14 10:37:36 2023
@author: Omega Joctan
"""
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Change this directory to be the one containing the Files
directory = r"C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\F4F6C6D7A7155578A6DEA66D12B1D40D\MQL5\Files\NAIVE BAYES"
data = pd.read_csv(f"{directory}\\vars.csv")
for var in data:
sns.distplot(data[var])
plt.show()
================================================
FILE: Sklearn/Neighbors/KNN_nearest_neighbors.mqh
================================================
//+------------------------------------------------------------------+
//| KNN_nearest_neighbors.mqh |
//| Copyright 2022, Omega Joctan. |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Omega Joctan."
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
bool isdebug = true;
//+------------------------------------------------------------------+
class CKNNNearestNeighbors
{
private:
uint k;
matrix Matrix;
ulong m_rows, m_cols;
vector m_target;
vector m_classesVector;
matrix m_classesMatrix;
double Euclidean_distance(const vector &v1, const vector &v2);
vector ClassVector(); //global vector of target classes
void MatrixRemoveRow(matrix &mat, ulong row);
void VectorRemoveIndex(vector &v, ulong index);
double Mse(vector &A, vector &P);
public:
CKNNNearestNeighbors(matrix &Matrix_, uint k_);
CKNNNearestNeighbors(matrix &Matrix_);
~CKNNNearestNeighbors(void);
int KNNAlgorithm(vector &vector_);
vector CrossValidation_LOOCV(uint initial_k = 0, uint final_k=1); //Leave One out Cross Validation (LOOCV)
matrix ConfusionMatrix(vector &A,vector &P);
float TrainTest(double train_size = 0.7); //returns accuracy of the tested dataset
};
//+------------------------------------------------------------------+
CKNNNearestNeighbors:: CKNNNearestNeighbors(matrix &Matrix_, uint k_)
{
k = k_;
if(k %2 ==0)
{
k = k+1;
if (isdebug)
printf("K = %d is an even number, It will be added by One so it becomes an odd Number %d", k_, k);
}
Matrix.Copy(Matrix_);
m_rows = Matrix.Rows();
m_cols = Matrix.Cols();
m_target = Matrix.Col(m_cols-1);
m_classesVector = ClassVector();
if (isdebug)
Print("classes vector | Neighbors ", m_classesVector);
}
//+------------------------------------------------------------------+
CKNNNearestNeighbors::CKNNNearestNeighbors(matrix &Matrix_)
{
Matrix.Copy(Matrix_);
k = (int)round(MathSqrt(Matrix.Rows()));
k = k%2 ==0 ? k+1 : k; //make sure the value of k ia an odd number
m_rows = Matrix.Rows();
m_cols = Matrix.Cols();
m_target = Matrix.Col(m_cols-1);
m_classesVector = ClassVector();
Print("classes vector | Neighbors ", m_classesVector);
}
//+------------------------------------------------------------------+
CKNNNearestNeighbors::~CKNNNearestNeighbors(void)
{
ZeroMemory(k);
ZeroMemory(m_classesVector);
ZeroMemory(m_classesMatrix);
}
//+------------------------------------------------------------------+
int CKNNNearestNeighbors::KNNAlgorithm(vector &vector_)
{
vector vector_2 = {};
vector euc_dist;
euc_dist.Resize(m_rows);
//matrix temp_matrix = Matrix;
//temp_matrix.Resize(Matrix.Rows(), Matrix.Cols()-1); //remove the last column of independent variables
for(ulong i=0; i (int)k)
{
max = ArrayMaximum(eucArray);
ArrayRemove(eucArray, max, 1);
ArrayRemove(track, max, 1);
}
}
ArrayCopy(NN, eucArray);
/*
Print("NN ");
ArrayPrint(NN);
Print("Track ");
ArrayPrint(track);
*/
//--- Voting process
vector votes(m_classesVector.Size());
for(ulong i=0; i1) k = z;
double sum_mse = 0;
for (ulong i=0; i ",acc,"%");
return(acc);
}
//+------------------------------------------------------------------+
================================================
FILE: Sklearn/Tree/README.md
================================================
## Decision Trees in MQL5: Classification and Regression
Decision trees are powerful machine learning algorithms that use a tree-like structure to make predictions. They work by splitting the data based on features (independent variables) into increasingly homogeneous subsets, ultimately reaching leaves representing the final prediction. MQL5 offers functionalities for implementing both **classification** and **regression** decision trees through the `tree.mqh` library.
**Decision Tree Theory (Basic Overview):**
1. **Start with the entire dataset at the root node.**
2. **Choose the feature and threshold that best splits the data into two subsets such that each subset is more homogeneous concerning the target variable (dependent variable).**
* For classification, this often involves maximizing information gain or minimizing Gini impurity.
* For regression, it involves maximizing variance reduction between the parent node and child nodes.
3. **Repeat step 2 for each child node recursively until a stopping criterion is met (e.g., reaching a maximum depth, minimum samples per node, or sufficient homogeneity).**
4. **Assign a prediction value to each leaf node.**
* For classification, this is the most frequent class in the leaf node.
* For regression, this is the average value of the target variable in the leaf node.
**CDecisionTreeClassifier Class:**
This class implements a decision tree for classification tasks. It offers the following functionalities:
* `CDecisionTreeClassifier(uint min_samples_split=2, uint max_depth=2, mode mode_=MODE_GINI)` Constructor, allows setting hyperparameters (minimum samples per split, maximum tree depth, and splitting criterion).
* `~CDecisionTreeClassifier(void)` Destructor.
* `void fit(const matrix &x, const vector &y)` Trains the model on the provided data (`x` - independent variables, `y` - class labels).
* `void print_tree(Node *tree, string indent=" ",string padl=")` Prints the tree structure in a readable format.
* `double predict(const vector &x)` Predicts the class label for a new data point (`x`).
* `vector predict(const matrix &x)` Predicts class labels for multiple new data points (`x`).
**CDecisionTreeRegressor Class:**
This class inherits from `CDecisionTreeClassifier` and specializes in regression tasks. It overrides specific functions and implements different splitting criteria:
* `CDecisionTreeRegressor(uint min_samples_split=2, uint max_depth=2):` Constructor, allows setting hyperparameters (minimum samples per split and maximum tree depth).
* `~CDecisionTreeRegressor(void):` Destructor.
* `void fit(matrix &x, vector &y):` Trains the model on the provided data (`x` - independent variables, `y` - continuous values).
* `double predict(const vector &x)` Predicts the continuous value for a new data point (`x`).
**Additional Notes:**
* Both classes use internal helper functions for building the tree, calculating splitting criteria (information gain, Gini impurity, variance reduction), and making predictions.
* The `check_is_fitted` function ensures the model is trained before allowing predictions.
* Choosing appropriate hyperparameters (especially maximum depth) is crucial to avoid overfitting the model.
**Reference**
[Data Science and Machine Learning (Part 16): A Refreshing Look at Decision Trees](https://www.mql5.com/en/articles/13862)
================================================
FILE: Sklearn/Tree/tree.mqh
================================================
//+------------------------------------------------------------------+
//| tree.mqh |
//| Copyright 2023, Omega Joctan |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Omega Joctan"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#include
#define log2(leaf_value) MathLog(leaf_value) / MathLog(2)
class Node
{
public:
// for decision node
uint feature_index;
double threshold;
double info_gain;
// for leaf node
double leaf_value;
Node *left_child; //left child Node
Node *right_child; //right child Node
Node() : left_child(NULL), right_child(NULL) {} // default constructor
Node(int feature_index_, double threshold_=0.0, Node *left_=NULL, Node *right_=NULL, double info_gain_=NULL, double value_=NULL)
: left_child(left_), right_child(right_)
{
this.feature_index = feature_index_;
this.threshold = threshold_;
this.info_gain = info_gain_;
this.leaf_value = value_;
}
void __Print__()
{
printf("feature_index: %d \nthreshold: %f \ninfo_gain: %f \nleaf_value: %f",feature_index,threshold, info_gain, leaf_value);
}
};
struct split_info
{
uint feature_index;
double threshold;
matrix dataset_left,
dataset_right;
double info_gain;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum mode {MODE_ENTROPY, MODE_GINI};
class CDecisionTreeClassifier
{
protected:
Node *build_tree(matrix &data, uint curr_depth=0);
double calculate_leaf_value(vector &Y);
bool is_fitted;
bool check_is_fitted(string func)
{
if (!is_fitted)
{
Print(func," Tree not trained, Call fit function first to train the model");
return false;
}
return (true);
}
//---
uint m_max_depth;
uint m_min_samples_split;
mode m_mode;
double gini_index(vector &y);
double entropy(vector &y);
double information_gain(vector &parent, vector &l_child, vector &r_child);
split_info get_best_split(const matrix &data, uint num_features);
split_info split_data(const matrix &data, uint feature_index, double threshold=0.5);
double make_predictions(const vector &x, const Node &tree);
void delete_tree(Node* node);
Node *nodes[]; //Keeping track of all the nodes in a tree
public:
Node *root;
CDecisionTreeClassifier(uint min_samples_split=2, uint max_depth=2, mode mode_=MODE_GINI);
~CDecisionTreeClassifier(void);
void fit(const matrix &x, const vector &y);
void print_tree(Node *tree, string indent=" ",string padl="");
double predict(const vector &x);
vector predict(const matrix &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CDecisionTreeClassifier::CDecisionTreeClassifier(uint min_samples_split=2, uint max_depth=2, mode mode_=MODE_GINI)
{
m_min_samples_split = min_samples_split;
m_max_depth = max_depth;
m_mode = mode_;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CDecisionTreeClassifier::~CDecisionTreeClassifier(void)
{
#ifdef DEBUG_MODE
Print(__FUNCTION__," Deleting Tree nodes =",nodes.Size());
#endif
this.delete_tree(root);
for (int i=0; i<(int)nodes.Size(); i++)
this.delete_tree(nodes[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CDecisionTreeClassifier::delete_tree(Node* node)
{
if (CheckPointer(node) != POINTER_INVALID)
{
delete_tree(node.left_child);
delete_tree(node.right_child);
delete node;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::gini_index(vector &y)
{
vector unique = CUtils::Unique_count(y);
vector probabilities = unique / (double)y.Size();
return 1.0 - MathPow(probabilities, 2).Sum();
}
//+------------------------------------------------------------------+
//| function to compute entropy |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::entropy(vector &y)
{
vector class_labels = CUtils::Unique_count(y);
vector p_cls = class_labels / double(y.Size());
vector entropy = (-1 * p_cls) * log2(p_cls);
return entropy.Sum();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::information_gain(vector &parent, vector &l_child, vector &r_child)
{
double weight_left = l_child.Size() / (double)parent.Size(),
weight_right = r_child.Size() / (double)parent.Size();
double gain =0;
switch(m_mode)
{
case MODE_GINI:
gain = gini_index(parent) - ( (weight_left*gini_index(l_child)) + (weight_right*gini_index(r_child)) );
break;
case MODE_ENTROPY:
gain = entropy(parent) - ( (weight_left*entropy(l_child)) + (weight_right*entropy(r_child)) );
break;
}
return gain;
}
//+------------------------------------------------------------------+
//| function to print the tree |
//+------------------------------------------------------------------+
void CDecisionTreeClassifier::print_tree(Node *tree, string indent=" ",string padl="")
{
if (tree.leaf_value != NULL)
Print((padl+indent+": "),tree.leaf_value);
else //if we havent' reached the leaf node keep printing child trees
{
padl += " ";
Print((padl+indent)+": X_",tree.feature_index, "<=", tree.threshold, "?", tree.info_gain);
print_tree(tree.left_child, "left","--->"+padl);
print_tree(tree.right_child, "right","--->"+padl);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CDecisionTreeClassifier::fit(const matrix &x, const vector &y)
{
matrix data = CUtils::concatenate(x, y, 1);
this.root = this.build_tree(data);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
split_info CDecisionTreeClassifier::split_data(const matrix &data, uint feature_index, double threshold=0.5)
{
int left_size=0, right_size =0;
vector row = {};
split_info split;
ulong cols = data.Cols(),
rows = data.Rows();
split.dataset_left.Resize(0, cols);
split.dataset_right.Resize(0, cols);
for (ulong i=0; i0 && split.dataset_right.Rows() > 0)
{
y_v = data.Col(data.Cols()-1);
right_v = split.dataset_right.Col(split.dataset_right.Cols()-1);
left_v = split.dataset_left.Col(split.dataset_left.Cols()-1);
double curr_info_gain = this.information_gain(y_v, left_v, right_v);
if (curr_info_gain > max_info_gain)
{
#ifdef DEBUG_MODE
printf(" split left: [%dx%d] split right: [%dx%d] curr_info_gain: %f max_info_gain: %f",split.dataset_left.Rows(),split.dataset_left.Cols(),split.dataset_right.Rows(),split.dataset_right.Cols(),curr_info_gain,max_info_gain);
#endif
best_split.feature_index = i;
best_split.threshold = possible_thresholds[j];
best_split.dataset_left = split.dataset_left;
best_split.dataset_right = split.dataset_right;
best_split.info_gain = curr_info_gain;
max_info_gain = curr_info_gain;
}
}
}
}
return best_split;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Node *CDecisionTreeClassifier::build_tree(matrix &data, uint curr_depth=0)
{
matrix X;
vector Y;
if (!CUtils::XandYSplitMatrices(data,X,Y)) //Split the input matrix into feature matrix X and target vector Y.
{
#ifdef DEBUG_MODE
printf("%s Line %d Failed to build a tree Data Empty",__FUNCTION__,__LINE__);
#endif
return NULL; //return null pointer
}
is_fitted = true;
ulong samples = X.Rows(), features = X.Cols(); //Get the number of samples and features in the dataset.
ArrayResize(nodes, nodes.Size()+1); //Append the nodes to memory
Node *left_child, *right_child;
if (samples >= m_min_samples_split && curr_depth<=m_max_depth)
{
split_info best_split = this.get_best_split(data, (uint)features);
#ifdef DEBUG_MODE
Print(__FUNCTION__," | ",__LINE__,"\nbest_split left: [",best_split.dataset_left.Rows(),"x",best_split.dataset_left.Cols(),"]\nbest_split right: [",best_split.dataset_right.Rows(),"x",best_split.dataset_right.Cols(),"]\nfeature_index: ",best_split.feature_index,"\nInfo gain: ",best_split.info_gain,"\nThreshold: ",best_split.threshold);
#endif
if (best_split.info_gain > 0)
{
left_child = this.build_tree(best_split.dataset_left, curr_depth+1);
right_child = this.build_tree(best_split.dataset_right, curr_depth+1);
nodes[nodes.Size()-1] = new Node(best_split.feature_index,best_split.threshold,left_child,right_child,best_split.info_gain);
return nodes[nodes.Size()-1];
}
}
nodes[nodes.Size()-1] = new Node();
nodes[nodes.Size()-1].leaf_value = this.calculate_leaf_value(Y);
return nodes[nodes.Size()-1];
}
//+------------------------------------------------------------------+
//| returns the element from Y that has the highest count, |
//| effectively finding the most common element in the list. |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::calculate_leaf_value(vector &Y)
{
vector uniques_count = CUtils::Unique_count(Y);
vector unique = CUtils::Unique(Y);
return unique[uniques_count.ArgMax()];
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::make_predictions(const vector &x, const Node &tree)
{
if (!check_is_fitted(__FUNCTION__))
return 0;
//if (CheckPointer(tree)=POINTER_INVALID)
if (tree.leaf_value != NULL) //This is a leaf_value
return tree.leaf_value;
#ifdef DEBUG_MODE
printf("Tree.threshold %f tree.feature_index %d leaf_value %f",tree.threshold,tree.feature_index,tree.leaf_value);
#endif
if (tree.feature_index>=x.Size())
return tree.leaf_value;
double feature_value = x[tree.feature_index];
double pred = 0;
if (feature_value <= tree.threshold)
{
if (CheckPointer(tree.left_child)!=POINTER_INVALID)
pred = this.make_predictions(x, tree.left_child);
}
else
{
if (CheckPointer(tree.right_child)!=POINTER_INVALID)
pred = this.make_predictions(x, tree.right_child);
}
return pred;
}
//+------------------------------------------------------------------+
//| Commonly used for making predictions in REAL-TIME |
//+------------------------------------------------------------------+
double CDecisionTreeClassifier::predict(const vector &x)
{
if (!check_is_fitted(__FUNCTION__))
return 0;
return this.make_predictions(x, this.root);
}
//+------------------------------------------------------------------+
//| Commonly used for making predictions in TRAIN-TEST |
//+------------------------------------------------------------------+
vector CDecisionTreeClassifier::predict(const matrix &x)
{
vector ret(x.Rows());
if (!check_is_fitted(__FUNCTION__))
return ret;
for (ulong i=0; i0 && split.dataset_right.Rows() > 0)
{
y_v = data.Col(data.Cols()-1);
right_v = split.dataset_right.Col(split.dataset_right.Cols()-1);
left_v = split.dataset_left.Col(split.dataset_left.Cols()-1);
double curr_info_gain = this.variance_reduction(y_v, left_v, right_v);
if (curr_info_gain > max_info_gain)
{
#ifdef DEBUG_MODE
printf(__FUNCTION__," | ",__LINE__,"\nsplit left: [%dx%d] split right: [%dx%d] curr_info_gain: %f max_info_gain: %f",split.dataset_left.Rows(),split.dataset_left.Cols(),split.dataset_right.Rows(),split.dataset_right.Cols(),curr_info_gain,max_info_gain);
#endif
best_split.feature_index = i;
best_split.threshold = possible_thresholds[j];
best_split.dataset_left = split.dataset_left;
best_split.dataset_right = split.dataset_right;
best_split.info_gain = curr_info_gain;
max_info_gain = curr_info_gain;
}
}
}
}
return best_split;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Node *CDecisionTreeRegressor::build_tree(matrix &data, uint curr_depth=0)
{
matrix X;
vector Y;
if (!CUtils::XandYSplitMatrices(data,X,Y)) //Split the input matrix into feature matrix X and target vector Y.
{
#ifdef DEBUG_MODE
printf("%s Line %d Failed to build a tree Data Empty",__FUNCTION__,__LINE__);
#endif
return NULL; //Return a NULL pointer
}
ulong samples = X.Rows(), features = X.Cols(); //Get the number of samples and features in the dataset.
ArrayResize(nodes, nodes.Size()+1); //Append the nodes to memory
Node *left_child, *right_child;
if (samples >= m_min_samples_split && curr_depth<=m_max_depth)
{
split_info best_split = this.get_best_split(data, (uint)features);
#ifdef DEBUG_MODE
Print(__FUNCTION__," | ",__LINE__,"\nbest_split left: [",best_split.dataset_left.Rows(),"x",best_split.dataset_left.Cols(),"]\nbest_split right: [",best_split.dataset_right.Rows(),"x",best_split.dataset_right.Cols(),"]\nfeature_index: ",best_split.feature_index,"\nInfo gain: ",best_split.info_gain,"\nThreshold: ",best_split.threshold);
#endif
if (best_split.info_gain > 0)
{
left_child = this.build_tree(best_split.dataset_left, curr_depth+1);
right_child = this.build_tree(best_split.dataset_right, curr_depth+1);
nodes[nodes.Size()-1] = new Node(best_split.feature_index,best_split.threshold,left_child,right_child,best_split.info_gain);
return nodes[nodes.Size()-1];
}
}
nodes[nodes.Size()-1] = new Node();
nodes[nodes.Size()-1].leaf_value = this.calculate_leaf_value(Y);
return nodes[nodes.Size()-1];
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CDecisionTreeRegressor::fit(matrix &x, vector &y)
{
matrix data = CUtils::concatenate(x, y, 1);
this.root = this.build_tree(data);
is_fitted = true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CDecisionTreeRegressor::calculate_leaf_value(vector &Y)
{
return Y.Mean();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
================================================
FILE: Sklearn/metrics.mqh
================================================
//+------------------------------------------------------------------+
//| metrics.mqh |
//| Copyright 2022, Fxalgebra.com |
//| https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Fxalgebra.com"
#property link "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| defines |
//| |
//| |
//+------------------------------------------------------------------+
#include
#include
struct roc_curve_struct
{
vector TPR,
FPR,
Thresholds;
};
struct confusion_matrix_struct
{
matrix MATRIX;
vector CLASSES;
vector TP,
TN,
FP,
FN;
};
enum regression_metrics
{
METRIC_R_SQUARED, // R-squared
METRIC_ADJUSTED_R, // Adjusted R-squared
METRIC_RSS, // Residual Sum of Squares
METRIC_MSE, // Mean Squared Error
METRIC_RMSE, // Root Mean Squared Error
METRIC_MAE // Mean Absolute Error
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class Metrics
{
protected:
static int SearchPatterns(const vector &True, int value_A, const vector &B, int value_B);
static confusion_matrix_struct confusion_matrix(const vector &True, const vector &Preds);
public:
Metrics(void);
~Metrics(void);
//--- Regression metrics
static double r_squared(const vector &True, const vector &Pred);
static double adjusted_r(const vector &True, const vector &Pred, uint indep_vars = 1);
static double rss(const vector &True, const vector &Pred);
static double mse(const vector &True, const vector &Pred);
static double rmse(const vector &True, const vector &Pred);
static double mae(const vector &True, const vector &Pred);
static double RegressionMetric(const vector &True, const vector &Pred, regression_metrics METRIC_);
//--- Classification metrics
static double accuracy_score(const vector &True, const vector &Pred);
static vector accuracy(const vector &True, const vector &Preds);
static vector precision(const vector &True, const vector &Preds);
static vector recall(const vector &True, const vector &Preds);
static vector f1_score(const vector &True, const vector &Preds);
static vector specificity(const vector &True, const vector &Preds);
static roc_curve_struct roc_curve(const vector &True, const vector &Preds, bool show_roc_curve=false);
static void classification_report(const vector &True, const vector &Pred, bool show_roc_curve=false);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Metrics::Metrics(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Metrics::~Metrics(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::r_squared(const vector &True, const vector &Pred)
{
return(Pred.RegressionMetric(True, REGRESSION_R2));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::adjusted_r(const vector &True, const vector &Pred, uint indep_vars = 1)
{
if(True.Size() != Pred.Size())
{
Print(__FUNCTION__, " Vector True and P are not equal in size ");
return(0);
}
double r2 = r_squared(True, Pred);
ulong N = Pred.Size();
return(1 - ((1 - r2) * (N - 1)) / (N - indep_vars - 1));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
confusion_matrix_struct Metrics::confusion_matrix(const vector &True, const vector &Preds)
{
confusion_matrix_struct confusion_matrix;
vector classes = CNumpy::unique(True).unique;
confusion_matrix.CLASSES = classes;
//--- Fill the confusion matrix
matrix MATRIX(classes.Size(), classes.Size());
MATRIX.Fill(0.0);
for(ulong i = 0; i < classes.Size(); i++)
for(ulong j = 0; j < classes.Size(); j++)
MATRIX[i][j] = SearchPatterns(True, (int)classes[i], Preds, (int)classes[j]);
confusion_matrix.MATRIX = MATRIX;
confusion_matrix.TP = MATRIX.Diag();
confusion_matrix.FP = MATRIX.Sum(0) - confusion_matrix.TP;
confusion_matrix.FN = MATRIX.Sum(1) - confusion_matrix.TP;
confusion_matrix.TN = MATRIX.Sum() - (confusion_matrix.TP + confusion_matrix.FP + confusion_matrix.FN);
return confusion_matrix;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector Metrics::accuracy(const vector &True,const vector &Preds)
{
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
return (conf_m.TP + conf_m.TN) / conf_m.MATRIX.Sum();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector Metrics::precision(const vector &True,const vector &Preds)
{
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
return conf_m.TP / (conf_m.TP + conf_m.FP + DBL_EPSILON);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector Metrics::f1_score(const vector &True,const vector &Preds)
{
vector precision = precision(True, Preds);
vector recall = recall(True, Preds);
return 2 * precision * recall / (precision + recall + DBL_EPSILON);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector Metrics::recall(const vector &True,const vector &Preds)
{
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
return conf_m.TP / (conf_m.TP + conf_m.FN + DBL_EPSILON);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
vector Metrics::specificity(const vector &True,const vector &Preds)
{
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
return conf_m.TN / (conf_m.TN + conf_m.FP + DBL_EPSILON);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
roc_curve_struct Metrics::roc_curve(const vector &True,const vector &Preds, bool show_roc_curve=false)
{
roc_curve_struct roc;
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
roc.TPR = recall(True, Preds);
roc.FPR = conf_m.FP / (conf_m.FP + conf_m.TN + DBL_EPSILON);
if (show_roc_curve)
{
CPlots plt;
plt.Plot("Roc Curve",roc.FPR,roc.TPR,"roc_curve","False Positive Rate(FPR)","True Positive Rate(TPR)");
while (MessageBox("Close or Cancel ROC CURVE to proceed","Roc Curve",MB_OK)<0)
Sleep(1);
}
return roc;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::accuracy_score(const vector &True, const vector &Preds)
{
confusion_matrix_struct conf_m = confusion_matrix(True, Preds);
return conf_m.MATRIX.Diag().Sum() / (conf_m.MATRIX.Sum() + DBL_EPSILON);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Metrics::classification_report(const vector &True, const vector &Pred, bool show_roc_curve=false)
{
vector accuracy = accuracy(True, Pred);
vector precision = precision(True, Pred);
vector specificity = specificity(True, Pred);
vector recall = recall(True, Pred);
vector f1_score = f1_score(True, Pred);
double acc = accuracy_score(True, Pred);
confusion_matrix_struct conf_m = confusion_matrix(True, Pred);
//--- support
ulong size = conf_m.MATRIX.Rows();
vector support(size);
for(ulong i = 0; i < size; i++)
support[i] = NormalizeDouble(MathIsValidNumber(conf_m.MATRIX.Row(i).Sum()) ? conf_m.MATRIX.Row(i).Sum() : 0, 8);
int total_size = (int)conf_m.MATRIX.Sum();
//--- Avg and w avg
vector avg, w_avg;
avg.Resize(5);
w_avg.Resize(5);
avg[0] = precision.Mean();
avg[1] = recall.Mean();
avg[2] = specificity.Mean();
avg[3] = f1_score.Mean();
avg[4] = total_size;
//--- w avg
vector support_prop = support / double(total_size + 1e-10);
vector c = precision * support_prop;
w_avg[0] = c.Sum();
c = recall * support_prop;
w_avg[1] = c.Sum();
c = specificity * support_prop;
w_avg[2] = c.Sum();
c = f1_score * support_prop;
w_avg[3] = c.Sum();
w_avg[4] = (int)total_size;
//--- Report
string report = "\n[CLS] \t\t\t\t\t\t\tprecision \trecall \tspecificity \tf1 score \tsupport";
for(ulong i = 0; i < size; i++)
{
report += "\n\t\t[" + string(conf_m.CLASSES[i])+"]\t\t\t";
//for (ulong j=0; j<3; j++)
report += StringFormat("\t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t %d", precision[i], recall[i], specificity[i], f1_score[i], (int)support[i]);
}
report += "\n";
report += StringFormat("\naccuracy\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t %.2f \t\t\t\t %d",acc,(int)conf_m.MATRIX.Sum());
report += StringFormat("\naverage\t\t\t\t\t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t %d", avg[0], avg[1], avg[2], avg[3], (int)avg[4]);
report += StringFormat("\nWeighed avg\t\t\t \t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t\t %.2f \t\t\t\t %d", w_avg[0], w_avg[1], w_avg[2], w_avg[3], (int)w_avg[4]);
Print("Confusion Matrix\n", conf_m.MATRIX);
Print("\nClassification Report\n", report);
roc_curve(True, Pred, show_roc_curve);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::rss(const vector &True, const vector &Pred)
{
vector c = True - Pred;
c = MathPow(c, 2);
return (c.Sum());
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::mse(const vector &True, const vector &Pred)
{
vector c = True - Pred;
c = MathPow(c, 2);
return(c.Sum() / c.Size());
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int Metrics::SearchPatterns(const vector &True, int value_A, const vector &B, int value_B)
{
int count=0;
for(ulong i = 0; i < True.Size(); i++)
if(True[i] == value_A && B[i] == value_B)
count++;
return count;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::rmse(const vector &True, const vector &Pred)
{
return Pred.RegressionMetric(True, REGRESSION_RMSE);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::mae(const vector &True, const vector &Pred)
{
return Pred.RegressionMetric(True, REGRESSION_MAE);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Metrics::RegressionMetric(const vector &True,const vector &Pred,regression_metrics METRIC_)
{
double err = 0;
switch (METRIC_)
{
case METRIC_MSE:
err = mse(True, Pred);
break;
case METRIC_RMSE:
err = rmse(True, Pred);
break;
case METRIC_MAE:
err = mae(True, Pred);
break;
case METRIC_RSS:
err = rss(True, Pred);
break;
case METRIC_R_SQUARED:
err = r_squared(True, Pred);
break;
case METRIC_ADJUSTED_R:
err = adjusted_r(True, Pred);
break;
default:
break;
}
return err;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
================================================
FILE: Stats Models/ADF.mqh
================================================
//+------------------------------------------------------------------+
//| ADF.mqh |
//| Copyright 2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#include