-
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2502f48
commit d6fb069
Showing
1 changed file
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,326 @@ | ||
//+------------------------------------------------------------------+ | ||
//| lighGBM.mqh | | ||
//| Copyright 2024, MetaQuotes Ltd. | | ||
//| https://www.mql5.com | | ||
//+------------------------------------------------------------------+ | ||
#property copyright "Copyright 2024, MetaQuotes Ltd." | ||
#property link "https://www.mql5.com" | ||
//+------------------------------------------------------------------+ | ||
//| defines | | ||
//+------------------------------------------------------------------+ | ||
|
||
#include <MALE5\MatrixExtend.mqh> | ||
|
||
#define UNDEFINED_REPLACE 1 | ||
|
||
class CXGBoost | ||
{ | ||
|
||
bool initialized; | ||
long onnx_handle; | ||
void PrintTypeInfo(const long num,const string layer,const OnnxTypeInfo& type_info); | ||
long inputs[], outputs[]; | ||
|
||
void replace(long &arr[]) { for (uint i=0; i<arr.Size(); i++) if (arr[i] < 0) arr[i] = UNDEFINED_REPLACE; } | ||
|
||
bool OnnxLoad(long &handle); | ||
|
||
public: | ||
CXGBoost(void); | ||
~CXGBoost(void); | ||
|
||
virtual bool Init(const uchar &onnx_buff[], ulong flags=ONNX_DEFAULT); //Initilaized ONNX model from a resource uchar array with default flag | ||
virtual bool Init(string onnx_filename, uint flags=ONNX_DEFAULT); //Initializes the ONNX model from a .onnx filename given | ||
|
||
virtual long predict_bin(const vector &x); //REturns the predictions for the current given matrix | useful in real-time prediction | ||
virtual vector predict_proba(const vector &x); //Returns the predictions in probability terms | useful in real-time prediction | ||
virtual matrix predict_proba(const matrix &x); //Returns the predicted probability for the whole matrix | useful for testing | ||
virtual vector predict_bin(const matrix &x); //gives out the vector for all the predictions | useful for testing | ||
}; | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
CXGBoost::CXGBoost(void) | ||
{ | ||
|
||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
CXGBoost::~CXGBoost(void) | ||
{ | ||
if (!OnnxRelease(onnx_handle)) | ||
printf("%s Failed to release ONNX handle Err=%d",__FUNCTION__,GetLastError()); | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
bool CXGBoost::OnnxLoad(long &handle) | ||
{ | ||
|
||
//--- since not all sizes defined in the input tensor we must set them explicitly | ||
//--- first index - batch size, second index - series size, third index - number of series (only Close) | ||
|
||
OnnxTypeInfo type_info; //Getting onnx information for Reference In case you forgot what the loaded ONNX is all about | ||
|
||
long input_count=OnnxGetInputCount(handle); | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
Print("model has ",input_count," input(s)"); | ||
|
||
for(long i=0; i<input_count; i++) | ||
{ | ||
string input_name=OnnxGetInputName(handle,i); | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
Print(i," input name is ",input_name); | ||
|
||
if(OnnxGetInputTypeInfo(handle,i,type_info)) | ||
{ | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
PrintTypeInfo(i,"input",type_info); | ||
ArrayCopy(inputs, type_info.tensor.dimensions); | ||
} | ||
} | ||
|
||
long output_count=OnnxGetOutputCount(handle); | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
Print("model has ",output_count," output(s)"); | ||
|
||
for(long i=0; i<output_count; i++) | ||
{ | ||
string output_name=OnnxGetOutputName(handle,i); | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
Print(i," output name is ",output_name); | ||
|
||
if(OnnxGetOutputTypeInfo(handle,i,type_info)) | ||
{ | ||
if (MQLInfoInteger(MQL_DEBUG)) | ||
PrintTypeInfo(i,"output",type_info); | ||
ArrayCopy(outputs, type_info.tensor.dimensions); | ||
} | ||
} | ||
|
||
//--- | ||
|
||
replace(inputs); | ||
replace(outputs); | ||
|
||
//--- | ||
|
||
for (long i=0; i<input_count; i++) | ||
if (!OnnxSetInputShape(handle, i, inputs)) //Giving the Onnx handle the input shape | ||
{ | ||
printf("Failed to set the input shape Err=%d",GetLastError()); | ||
DebugBreak(); | ||
return false; | ||
} | ||
|
||
//--- | ||
|
||
for(long i=0; i<output_count; i++) | ||
{ | ||
if(!OnnxSetOutputShape(handle,i,outputs)) | ||
{ | ||
printf("Failed to set the Output[%d] shape Err=%d",i,GetLastError()); | ||
//DebugBreak(); | ||
//return false; | ||
} | ||
} | ||
|
||
initialized = true; | ||
|
||
Print("ONNX model Initialized"); | ||
return true; | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
bool CXGBoost::Init(string onnx_filename, uint flags=ONNX_DEFAULT) | ||
{ | ||
onnx_handle = OnnxCreate(onnx_filename, flags); | ||
|
||
return OnnxLoad(onnx_handle); | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
bool CXGBoost::Init(const uchar &onnx_buff[], ulong flags=ONNX_DEFAULT) | ||
{ | ||
onnx_handle = OnnxCreateFromBuffer(onnx_buff, flags); //creating onnx handle buffer | ||
|
||
return OnnxLoad(onnx_handle); | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
|
||
void CXGBoost::PrintTypeInfo(const long num,const string layer,const OnnxTypeInfo& type_info) | ||
{ | ||
Print(" type ",EnumToString(type_info.type)); | ||
Print(" data type ",EnumToString(type_info.type)); | ||
|
||
if(type_info.tensor.dimensions.Size()>0) | ||
{ | ||
bool dim_defined=(type_info.tensor.dimensions[0]>0); | ||
string dimensions=IntegerToString(type_info.tensor.dimensions[0]); | ||
|
||
|
||
for(long n=1; n<type_info.tensor.dimensions.Size(); n++) | ||
{ | ||
if(type_info.tensor.dimensions[n]<=0) | ||
dim_defined=false; | ||
dimensions+=", "; | ||
dimensions+=IntegerToString(type_info.tensor.dimensions[n]); | ||
} | ||
Print(" shape [",dimensions,"]"); | ||
//--- not all dimensions defined | ||
if(!dim_defined) | ||
PrintFormat(" %I64d %s shape must be defined explicitly before model inference",num,layer); | ||
//--- reduce shape | ||
uint reduced=0; | ||
long dims[]; | ||
for(long n=0; n<type_info.tensor.dimensions.Size(); n++) | ||
{ | ||
long dimension=type_info.tensor.dimensions[n]; | ||
//--- replace undefined dimension | ||
if(dimension<=0) | ||
dimension=UNDEFINED_REPLACE; | ||
//--- 1 can be reduced | ||
if(dimension>1) | ||
{ | ||
ArrayResize(dims,reduced+1); | ||
dims[reduced++]=dimension; | ||
} | ||
} | ||
//--- all dimensions assumed 1 | ||
if(reduced==0) | ||
{ | ||
ArrayResize(dims,1); | ||
dims[reduced++]=1; | ||
} | ||
//--- shape was reduced | ||
if(reduced<type_info.tensor.dimensions.Size()) | ||
{ | ||
dimensions=IntegerToString(dims[0]); | ||
for(long n=1; n<dims.Size(); n++) | ||
{ | ||
dimensions+=", "; | ||
dimensions+=IntegerToString(dims[n]); | ||
} | ||
string sentence=""; | ||
if(!dim_defined) | ||
sentence=" if undefined dimension set to "+(string)UNDEFINED_REPLACE; | ||
PrintFormat(" shape of %s data can be reduced to [%s]%s",layer,dimensions,sentence); | ||
} | ||
} | ||
else | ||
PrintFormat("no dimensions defined for %I64d %s",num,layer); | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
vector CXGBoost::predict_proba(const vector &x) | ||
{ | ||
vector proba = {}; | ||
if (!this.initialized) | ||
{ | ||
printf("%s The model is not initialized yet to make predictions | call Init function first",__FUNCTION__); | ||
return proba; | ||
} | ||
|
||
//--- | ||
|
||
vectorf x_float; | ||
x_float.Assign(x); | ||
|
||
float output_data[]; | ||
struct Map | ||
{ | ||
ulong key[]; | ||
float value[]; | ||
} output_data_map[]; | ||
|
||
//--- | ||
|
||
ArrayResize(output_data, outputs.Size()); | ||
|
||
if (!OnnxRun(onnx_handle, ONNX_DATA_TYPE_FLOAT, x_float, output_data, output_data_map)) | ||
{ | ||
printf("Failed to get predictions from Onnx err %d",GetLastError()); | ||
return proba; | ||
} | ||
else | ||
proba = MatrixExtend::ArrayToVector(output_data_map[0].value); | ||
|
||
|
||
return proba; //Return the class with highest probability | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
matrix CXGBoost::predict_proba(const matrix &x) | ||
{ | ||
matrix preds = {}; | ||
|
||
for (ulong i=0; i<x.Rows(); i++) | ||
{ | ||
vector row = predict_proba(x.Row(i)); | ||
preds.Resize(i+1, row.Size()); | ||
|
||
preds.Row(row, i); | ||
} | ||
return preds; | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
vector CXGBoost::predict_bin(const matrix &x) | ||
{ | ||
vector preds(x.Rows()); | ||
for (ulong i=0; i<x.Rows(); i++) | ||
{ | ||
preds[i] = (double)predict_bin(x.Row(i)); | ||
} | ||
|
||
return preds; | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ | ||
long CXGBoost::predict_bin(const vector &x) | ||
{ | ||
if (!this.initialized) | ||
{ | ||
printf("%s The model is not initialized yet to make predictions | call Init function first",__FUNCTION__); | ||
return 0; | ||
} | ||
|
||
//--- | ||
|
||
vectorf x_float; | ||
x_float.Assign(x); | ||
|
||
float output_data[]; | ||
struct Map | ||
{ | ||
ulong key[]; | ||
float value[]; | ||
} output_data_map[]; | ||
|
||
//--- | ||
|
||
vector proba = {}; | ||
ArrayResize(output_data, outputs.Size()); | ||
|
||
if (!OnnxRun(onnx_handle, ONNX_DATA_TYPE_FLOAT, x_float, output_data, output_data_map)) | ||
{ | ||
printf("Failed to get predictions from Onnx err %d",GetLastError()); | ||
return -INT_MAX; | ||
} | ||
else | ||
proba = MatrixExtend::ArrayToVector(output_data_map[0].value); | ||
|
||
return (long)output_data_map[0].key[proba.ArgMax()]; //Return the class with highest probability | ||
} | ||
//+------------------------------------------------------------------+ | ||
//| | | ||
//+------------------------------------------------------------------+ |