Классификационные модели библиотеки Scikit-learn и их экспорт в ONNX

MetaQuotes | 13 октября, 2023

Развитие технологий привело к появлению принципиально нового подхода к построению алгоритмов обработки данных. Ранее для решения каждой отдельной задачи требовалась четкая формализация и разработка соответствующих алгоритмов.

В машинном обучении компьютер сам учится находить наилучшие способы обработки данных. Модели машинного обучения способны успешно решать задачи классификации (есть фиксированный набор классов, найти вероятности принадлежности заданного набора признаков к каждому классу) и регрессии (по заданному набору признаков оценить численное значение целевой переменной). На базе этих основных звеньев могут быть построены более сложные модели обработки данных.

В библиотеке Scikit-learn доступно множество инструментов как для классификации, так и для регрессии. Выбор конкретных методов и моделей зависит от характеристик данных, так как разные методы могут обладать разной эффективностью и предоставлять разные результаты в зависимости от задачи.

В пресс-релизе "ONNX Runtime is now open source" утверждается, что ONNX Runtime также поддерживает профиль ONNX-ML :

ONNX Runtime is the first publicly available inference engine with full support for ONNX 1.2 and higher including the ONNX-ML profile.

Профиль ONNX-ML — это часть ONNX, созданная специально для моделей машинного обучения (ML). Он предназначен для описания и представления различных видов ML-моделей, таких как модели классификации, регрессии, кластеризации и других, в удобной форме, которую можно использовать на различных платформах и средах, поддерживающих ONNX. Профиль ONNX-ML упрощает передачу, развертывание и выполнение моделей машинного обучения, делая их более доступными и переносимыми.

В данной статье мы рассмотрим применение всех классификационных моделей пакета Scikit-learn для решения задачи классификации ирисов Фишера, попробуем их сконвертировать в ONNX-формат и использовать полученные модели в программах на MQL5.

Также мы сравним точность работы оригинальных моделей и их ONNX-версий на полном наборе Iris dataset.


Содержание



1. Ирисы Фишера

Набор данных Iris - это один из самых известных и широко используемых наборов данных в мире машинного обучения. Он был впервые представлен в 1936 году статистиком и биологом Р.А. Фишером и с тех пор стал классическим набором данных для задачи классификации.

Iris dataset представляет собой набор измерений чашелистиков и лепестков трех видов ирисов - Iris setosa, Iris virginica и Iris versicolor.

Рис.1. Iris setosa

Рис.1. Iris setosa


Рис.2. Iris virginica

Рис.2. Iris virginica


Рис.3. Iris versicolor

Рис.3. Iris versicolor


Iris dataset состоит из 150 экземпляров ирисов, по 50 экземпляров каждого из трех видов. Каждый экземпляр имеет четыре числовых признака (в сантиметрах):

  1. Длина чашелистика (sepal length)
  2. Ширина чашелистика (sepal width)
  3. Длина лепестка (petal length)
  4. Ширина лепестка (petal width)

Каждый экземпляр также имеет соответствующий класс, обозначающий вид ириса (Iris setosa, Iris virginica или Iris versicolor). Этот классификационный атрибут делает Iris dataset идеальным набором данных для задач машинного обучения, таких как классификация и кластеризация.

MetaEditor позволяет работать со скриптами на языке Python. Для создания Python-скрипта нужно в меню "File" MetaEditor выбрать "New". Появится диалоговое окно выбора создаваемого объекта (рис. 4).

Рис.4. Создание скрипта на Python в MQL5 Wizard - Шаг 1

Рис.4. Создание скрипта на Python в MQL5 Wizard - Шаг 1

Далее нужно указать имя скрипта, например IRIS.py

Рис.5. Создание скрипта на Python в MQL5 Wizard - Шаг 2 - Имя скрипта

Рис.5. Создание скрипта на Python в MQL5 Wizard - Шаг 2 - Имя скрипта

После этого можно указать, какие библиотеки будут использоваться, в нашем случае оставим эти поля пустыми.

Рис.6. Создание скрипта на Python в MQL5 Wizard - Шаг 3

Рис.6. Создание скрипта на Python в MQL5 Wizard - Шаг 3


Один из способов начать анализ Iris dataset - это визуализировать данные. Графическое представление позволяет нам лучше понять структуру данных и отношения между признаками.

Например, можно построить диаграмму рассеяния (scatter plot), чтобы увидеть, как различные виды ирисов распределены в пространстве признаков.

Код Python скрипта:

# The script shows the scatter plot of the Iris dataset features
# Copyright 2023, MetaQuotes Ltd.
# https://mql5.com

import matplotlib.pyplot as plt
from sklearn import datasets

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# extract sepal length and sepal width (the first two features)
sepal_length = X[:, 0]
sepal_width = X[:, 1]

# create a scatter plot
plt.figure(figsize=(8, 6))
plt.scatter(sepal_length, sepal_width, c=y, cmap=plt.cm.Set1, edgecolor='k')
plt.xlabel('Sepal Length (cm)')
plt.ylabel('Sepal Width (cm)')
plt.title('Scatter Plot for Sepal Length and Sepal Width')
plt.colorbar(label='Iris Species', ticks=[0, 1, 2])
plt.show()

# save the scatter plot to a file (optional)
# plt.savefig('scatter_plot_sepal_length_width.png')

# Extract petal length and petal width (the third and fourth features)
petal_length = X[:, 2]
petal_width = X[:, 3]

# create a scatter plot
plt.figure(figsize=(8, 6))
plt.scatter(petal_length, petal_width, c=y, cmap=plt.cm.Set1, edgecolor='k')
plt.xlabel('Petal Length (cm)')
plt.ylabel('Petal Width (cm)')
plt.title('Scatter Plot for Petal Length and Petal Width')
plt.colorbar(label='Iris Species', ticks=[0, 1, 2])
plt.show()

# save the scatter plot to a file (optional)
# plt.savefig('scatter_plot_petal_length_width.png')

Для запуска этого скрипта его нужно скопировать в MetaEditor (рис.7) и нажать "Compile".

Рис.7. Скрипт IRIS.py в MetaEditor

Рис.7. Скрипт IRIS.py в MetaEditor


После этого на экране появятся графики:

Рис.8. Скрипт IRIS.py в MetaEditor c графиком Sepal Length/Sepal Width

Рис.8. Скрипт IRIS.py в MetaEditor c графиком Sepal Length/Sepal Width


Рис.9. Скрипт IRIS.py в MetaEditor c графиком Petal Length/Petal Width

Рис.9. Скрипт IRIS.py в MetaEditor c графиком Petal Length/Petal Width


Рассмотрим их подробнее.

Рис. 10. Диаграмма рассеяния для длины и ширины чашелистика (Scatter Plot Sepal Length vs Sepal Width)

Рис. 10. Диаграмма рассеяния для длины и ширины чашелистика (Scatter Plot Sepal Length vs Sepal Width)



На этой диаграмме мы видим, как разные виды ирисов распределены по длине и ширине чашелистика. Мы можем видеть, что Iris setosa обычно имеет более короткие и широкие чашелистики, чем другие два вида.

Рис.11. Диаграмма рассеяния для длины и ширины лепестка (Scatter Plot Petal Length vs Petal Width)

Рис.11. Диаграмма рассеяния для длины и ширины лепестка (Scatter Plot Petal Length vs Petal Width)



На этой диаграмме мы видим, как разные виды ирисов распределены по длине и ширине лепестка. Мы можем заметить, что Iris setosa имеет самые короткие и узкие лепестки, Iris virginica - самые длинные и широкие, а Iris versicolor находится между ними.

Iris dataset является идеальным набором данных для обучения и тестирования моделей машинного обучения. Мы будем использовать его для анализа эффективности моделей машинного обучения для задачи классификации.



2. Модели для классификации

Задача классификации является одной из основных задач машинного обучения, и её целью является разделение данных на различные категории или классы на основе некоторых признаков.

Рассмотрим основные модели машинного обучения пакета scikit-learn.


Список классификаторов пакета Scikit-learn

Для вывода списка доступных классификаторов Scikit-learn можно использовать скрипт:

# ScikitLearnClassifiers.py
# The script lists all the classification algorithms available in scikit-learn
# Copyright 2023, MetaQuotes Ltd.
# https://mql5.com

# print Python version
from platform import python_version  
print("The Python version is ", python_version()) 

# print scikit-learn version
import sklearn
print('The scikit-learn version is {}.'.format(sklearn.__version__))

# print scikit-learn classifiers
from sklearn.utils import all_estimators
classifiers = all_estimators(type_filter='classifier')
for index, (name, ClassifierClass) in enumerate(classifiers, start=1):
    print(f"Classifier {index}: {name}")

Результат:

Python    The Python version is  3.10.0
Python    The scikit-learn version is 1.2.2.
Python    Classifier 1: AdaBoostClassifier
Python    Classifier 2: BaggingClassifier
Python    Classifier 3: BernoulliNB
Python    Classifier 4: CalibratedClassifierCV
Python    Classifier 5: CategoricalNB
Python    Classifier 6: ClassifierChain
Python    Classifier 7: ComplementNB
Python    Classifier 8: DecisionTreeClassifier
Python    Classifier 9: DummyClassifier
Python    Classifier 10: ExtraTreeClassifier
Python    Classifier 11: ExtraTreesClassifier
Python    Classifier 12: GaussianNB
Python    Classifier 13: GaussianProcessClassifier
Python    Classifier 14: GradientBoostingClassifier
Python    Classifier 15: HistGradientBoostingClassifier
Python    Classifier 16: KNeighborsClassifier
Python    Classifier 17: LabelPropagation
Python    Classifier 18: LabelSpreading
Python    Classifier 19: LinearDiscriminantAnalysis
Python    Classifier 20: LinearSVC
Python    Classifier 21: LogisticRegression
Python    Classifier 22: LogisticRegressionCV
Python    Classifier 23: MLPClassifier
Python    Classifier 24: MultiOutputClassifier
Python    Classifier 25: MultinomialNB
Python    Classifier 26: NearestCentroid
Python    Classifier 27: NuSVC
Python    Classifier 28: OneVsOneClassifier
Python    Classifier 29: OneVsRestClassifier
Python    Classifier 30: OutputCodeClassifier
Python    Classifier 31: PassiveAggressiveClassifier
Python    Classifier 32: Perceptron
Python    Classifier 33: QuadraticDiscriminantAnalysis
Python    Classifier 34: RadiusNeighborsClassifier
Python    Classifier 35: RandomForestClassifier
Python    Classifier 36: RidgeClassifier
Python    Classifier 37: RidgeClassifierCV
Python    Classifier 38: SGDClassifier
Python    Classifier 39: SVC
Python    Classifier 40: StackingClassifier
Python    Classifier 41: VotingClassifier

Для удобства в этом списке классификаторов они выделены разными цветами. Модели, которые требуют базовых классификаторов, выделены желтым цветом, в то время как остальные модели могут использоваться самостоятельно.

Забегая вперед, отметим, что зеленым цветом отмечены модели, которые удалось успешно экспортировать в формат ONNX, красным цветом отмечены модели, при конвертации которых в текущей версии scikit-learn 1.2.2 возникают ошибки.


Различное представление выходных данных у моделей

Следует отметить, что различные модели по-разному представляют выходную информацию, поэтому при работе с моделями, конвертированными в ONNX, следует быть внимательным.

Для задачи классификации ирисов Фишера входные тензоры имеют одинаковый вид для всех этих моделей:

Information about input tensors in ONNX:
1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]

Выходные тензоры ONNX-моделей различаются.

1. Модели, не требующе пост-обработки

  1. SVC Classifier;
  2. LinearSVC Classifier;
  3. NuSVC Classifier;
  4. Radius Neighbors Classifier;
  5. Ridge Classifier;
  6. Ridge Classifier CV.
Information about output tensors in ONNX:
1. Name: label, Data Type: tensor(int64), Shape: [None]
2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]

Они возвращают результат (номер класса) в явном виде в первом выходном целочисленном тензоре label tensor(int64), не требуя пост-обработки.

2. Модели, результаты которых требуют пост-обработки:

  1. Random Forest Classifier;
  2. Gradient Boosting Classifier;
  3. AdaBoost Classifier;
  4. Bagging Classifier;
  5. K-NN_Classifier;
  6. Decision Tree Classifier;
  7. Logistic Regression Classifier;
  8. Logistic Regression CV Classifier;
  9. Passive-Aggressive Classifier;
  10. Perceptron Classifier;
  11. SGD Classifier;
  12. Gaussian Naive Bayes Classifier;
  13. Multinomial Naive Bayes Classifier;
  14. Complement Naive Bayes Classifier;
  15. Bernoulli Naive Bayes Classifier;
  16. Multilayer Perceptron Classifier;
  17. Linear Discriminant Analysis Classifier;
  18. Hist Gradient Boosting Classifier;
  19. Categorical  Naive Bayes Classifier;
  20. ExtraTree Classifier;
  21. ExtraTrees Classifier.
Information about output tensors in ONNX:
1. Name: output_label, Data Type: tensor(int64), Shape: [None]
2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []

На выходе возвращают список классов и вероятности принадлежности к каждому классу.

Для получения результата в этих случаях требуется пост-обработка типа seq(map(int64,tensor(float) (нахождение элемента с максимальной вероятностью).

Поэтому нужно быть внимательным и учитывать эти моменты при работе с ONNX-моделями. Пример различной обработки результатов представлен в скрипте в 2.28.2.


iris.mqh

Для проверки моделей на полном наборе iris dataset в MQL5 потребуется формирование данных набора, для этого будет использоваться функция PrepareIrisDataset().

Удобно вынести эти функции в файл iris.mqh

//+------------------------------------------------------------------+
//|                                                         Iris.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"

//+------------------------------------------------------------------+
//| Structure for the IRIS Dataset sample                            |
//+------------------------------------------------------------------+
struct sIRISsample
  {
   int               sample_id;   // sample id (1-150)
   double            features[4]; // SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm
   string            class_name;  // class ("Iris-setosa","Iris-versicolor","Iris-virginica")
   int               class_id;    // class id (0,1,2), calculated by function IRISClassID
  };

//--- Iris dataset
sIRISsample ExtIRISDataset[];
int Exttotal=0;

//+------------------------------------------------------------------+
//| Returns class id by class name                                   |
//+------------------------------------------------------------------+
int IRISClassID(string class_name)
  {
//---
   if(class_name=="Iris-setosa")
      return(0);
   else
      if(class_name=="Iris-versicolor")
         return(1);
      else
         if(class_name=="Iris-virginica")
            return(2);
//---
   return(-1);
  }

//+------------------------------------------------------------------+
//| AddSample                                                        |
//+------------------------------------------------------------------+
bool AddSample(const int Id,const double SepalLengthCm,const double SepalWidthCm,const double PetalLengthCm,const double PetalWidthCm, const string Species)
  {
//---
   ExtIRISDataset[Exttotal].sample_id=Id;
//---
   ExtIRISDataset[Exttotal].features[0]=SepalLengthCm;
   ExtIRISDataset[Exttotal].features[1]=SepalWidthCm;
   ExtIRISDataset[Exttotal].features[2]=PetalLengthCm;
   ExtIRISDataset[Exttotal].features[3]=PetalWidthCm;
//---
   ExtIRISDataset[Exttotal].class_name=Species;
   ExtIRISDataset[Exttotal].class_id=IRISClassID(Species);
//---
   Exttotal++;
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Prepare Iris Dataset                                             |
//+------------------------------------------------------------------+
bool PrepareIrisDataset(sIRISsample &iris_samples[])
  {
   ArrayResize(ExtIRISDataset,150);
   Exttotal=0;
//---
   AddSample(1,5.1,3.5,1.4,0.2,"Iris-setosa");
   AddSample(2,4.9,3.0,1.4,0.2,"Iris-setosa");
   AddSample(3,4.7,3.2,1.3,0.2,"Iris-setosa");
   AddSample(4,4.6,3.1,1.5,0.2,"Iris-setosa");
   AddSample(5,5.0,3.6,1.4,0.2,"Iris-setosa");
   AddSample(6,5.4,3.9,1.7,0.4,"Iris-setosa");
   AddSample(7,4.6,3.4,1.4,0.3,"Iris-setosa");
   AddSample(8,5.0,3.4,1.5,0.2,"Iris-setosa");
   AddSample(9,4.4,2.9,1.4,0.2,"Iris-setosa");
   AddSample(10,4.9,3.1,1.5,0.1,"Iris-setosa");
   AddSample(11,5.4,3.7,1.5,0.2,"Iris-setosa");
   AddSample(12,4.8,3.4,1.6,0.2,"Iris-setosa");
   AddSample(13,4.8,3.0,1.4,0.1,"Iris-setosa");
   AddSample(14,4.3,3.0,1.1,0.1,"Iris-setosa");
   AddSample(15,5.8,4.0,1.2,0.2,"Iris-setosa");
   AddSample(16,5.7,4.4,1.5,0.4,"Iris-setosa");
   AddSample(17,5.4,3.9,1.3,0.4,"Iris-setosa");
   AddSample(18,5.1,3.5,1.4,0.3,"Iris-setosa");
   AddSample(19,5.7,3.8,1.7,0.3,"Iris-setosa");
   AddSample(20,5.1,3.8,1.5,0.3,"Iris-setosa");
   AddSample(21,5.4,3.4,1.7,0.2,"Iris-setosa");
   AddSample(22,5.1,3.7,1.5,0.4,"Iris-setosa");
   AddSample(23,4.6,3.6,1.0,0.2,"Iris-setosa");
   AddSample(24,5.1,3.3,1.7,0.5,"Iris-setosa");
   AddSample(25,4.8,3.4,1.9,0.2,"Iris-setosa");
   AddSample(26,5.0,3.0,1.6,0.2,"Iris-setosa");
   AddSample(27,5.0,3.4,1.6,0.4,"Iris-setosa");
   AddSample(28,5.2,3.5,1.5,0.2,"Iris-setosa");
   AddSample(29,5.2,3.4,1.4,0.2,"Iris-setosa");
   AddSample(30,4.7,3.2,1.6,0.2,"Iris-setosa");
   AddSample(31,4.8,3.1,1.6,0.2,"Iris-setosa");
   AddSample(32,5.4,3.4,1.5,0.4,"Iris-setosa");
   AddSample(33,5.2,4.1,1.5,0.1,"Iris-setosa");
   AddSample(34,5.5,4.2,1.4,0.2,"Iris-setosa");
   AddSample(35,4.9,3.1,1.5,0.2,"Iris-setosa");
   AddSample(36,5.0,3.2,1.2,0.2,"Iris-setosa");
   AddSample(37,5.5,3.5,1.3,0.2,"Iris-setosa");
   AddSample(38,4.9,3.6,1.4,0.1,"Iris-setosa");
   AddSample(39,4.4,3.0,1.3,0.2,"Iris-setosa");
   AddSample(40,5.1,3.4,1.5,0.2,"Iris-setosa");
   AddSample(41,5.0,3.5,1.3,0.3,"Iris-setosa");
   AddSample(42,4.5,2.3,1.3,0.3,"Iris-setosa");
   AddSample(43,4.4,3.2,1.3,0.2,"Iris-setosa");
   AddSample(44,5.0,3.5,1.6,0.6,"Iris-setosa");
   AddSample(45,5.1,3.8,1.9,0.4,"Iris-setosa");
   AddSample(46,4.8,3.0,1.4,0.3,"Iris-setosa");
   AddSample(47,5.1,3.8,1.6,0.2,"Iris-setosa");
   AddSample(48,4.6,3.2,1.4,0.2,"Iris-setosa");
   AddSample(49,5.3,3.7,1.5,0.2,"Iris-setosa");
   AddSample(50,5.0,3.3,1.4,0.2,"Iris-setosa");
   AddSample(51,7.0,3.2,4.7,1.4,"Iris-versicolor");
   AddSample(52,6.4,3.2,4.5,1.5,"Iris-versicolor");
   AddSample(53,6.9,3.1,4.9,1.5,"Iris-versicolor");
   AddSample(54,5.5,2.3,4.0,1.3,"Iris-versicolor");
   AddSample(55,6.5,2.8,4.6,1.5,"Iris-versicolor");
   AddSample(56,5.7,2.8,4.5,1.3,"Iris-versicolor");
   AddSample(57,6.3,3.3,4.7,1.6,"Iris-versicolor");
   AddSample(58,4.9,2.4,3.3,1.0,"Iris-versicolor");
   AddSample(59,6.6,2.9,4.6,1.3,"Iris-versicolor");
   AddSample(60,5.2,2.7,3.9,1.4,"Iris-versicolor");
   AddSample(61,5.0,2.0,3.5,1.0,"Iris-versicolor");
   AddSample(62,5.9,3.0,4.2,1.5,"Iris-versicolor");
   AddSample(63,6.0,2.2,4.0,1.0,"Iris-versicolor");
   AddSample(64,6.1,2.9,4.7,1.4,"Iris-versicolor");
   AddSample(65,5.6,2.9,3.6,1.3,"Iris-versicolor");
   AddSample(66,6.7,3.1,4.4,1.4,"Iris-versicolor");
   AddSample(67,5.6,3.0,4.5,1.5,"Iris-versicolor");
   AddSample(68,5.8,2.7,4.1,1.0,"Iris-versicolor");
   AddSample(69,6.2,2.2,4.5,1.5,"Iris-versicolor");
   AddSample(70,5.6,2.5,3.9,1.1,"Iris-versicolor");
   AddSample(71,5.9,3.2,4.8,1.8,"Iris-versicolor");
   AddSample(72,6.1,2.8,4.0,1.3,"Iris-versicolor");
   AddSample(73,6.3,2.5,4.9,1.5,"Iris-versicolor");
   AddSample(74,6.1,2.8,4.7,1.2,"Iris-versicolor");
   AddSample(75,6.4,2.9,4.3,1.3,"Iris-versicolor");
   AddSample(76,6.6,3.0,4.4,1.4,"Iris-versicolor");
   AddSample(77,6.8,2.8,4.8,1.4,"Iris-versicolor");
   AddSample(78,6.7,3.0,5.0,1.7,"Iris-versicolor");
   AddSample(79,6.0,2.9,4.5,1.5,"Iris-versicolor");
   AddSample(80,5.7,2.6,3.5,1.0,"Iris-versicolor");
   AddSample(81,5.5,2.4,3.8,1.1,"Iris-versicolor");
   AddSample(82,5.5,2.4,3.7,1.0,"Iris-versicolor");
   AddSample(83,5.8,2.7,3.9,1.2,"Iris-versicolor");
   AddSample(84,6.0,2.7,5.1,1.6,"Iris-versicolor");
   AddSample(85,5.4,3.0,4.5,1.5,"Iris-versicolor");
   AddSample(86,6.0,3.4,4.5,1.6,"Iris-versicolor");
   AddSample(87,6.7,3.1,4.7,1.5,"Iris-versicolor");
   AddSample(88,6.3,2.3,4.4,1.3,"Iris-versicolor");
   AddSample(89,5.6,3.0,4.1,1.3,"Iris-versicolor");
   AddSample(90,5.5,2.5,4.0,1.3,"Iris-versicolor");
   AddSample(91,5.5,2.6,4.4,1.2,"Iris-versicolor");
   AddSample(92,6.1,3.0,4.6,1.4,"Iris-versicolor");
   AddSample(93,5.8,2.6,4.0,1.2,"Iris-versicolor");
   AddSample(94,5.0,2.3,3.3,1.0,"Iris-versicolor");
   AddSample(95,5.6,2.7,4.2,1.3,"Iris-versicolor");
   AddSample(96,5.7,3.0,4.2,1.2,"Iris-versicolor");
   AddSample(97,5.7,2.9,4.2,1.3,"Iris-versicolor");
   AddSample(98,6.2,2.9,4.3,1.3,"Iris-versicolor");
   AddSample(99,5.1,2.5,3.0,1.1,"Iris-versicolor");
   AddSample(100,5.7,2.8,4.1,1.3,"Iris-versicolor");
   AddSample(101,6.3,3.3,6.0,2.5,"Iris-virginica");
   AddSample(102,5.8,2.7,5.1,1.9,"Iris-virginica");
   AddSample(103,7.1,3.0,5.9,2.1,"Iris-virginica");
   AddSample(104,6.3,2.9,5.6,1.8,"Iris-virginica");
   AddSample(105,6.5,3.0,5.8,2.2,"Iris-virginica");
   AddSample(106,7.6,3.0,6.6,2.1,"Iris-virginica");
   AddSample(107,4.9,2.5,4.5,1.7,"Iris-virginica");
   AddSample(108,7.3,2.9,6.3,1.8,"Iris-virginica");
   AddSample(109,6.7,2.5,5.8,1.8,"Iris-virginica");
   AddSample(110,7.2,3.6,6.1,2.5,"Iris-virginica");
   AddSample(111,6.5,3.2,5.1,2.0,"Iris-virginica");
   AddSample(112,6.4,2.7,5.3,1.9,"Iris-virginica");
   AddSample(113,6.8,3.0,5.5,2.1,"Iris-virginica");
   AddSample(114,5.7,2.5,5.0,2.0,"Iris-virginica");
   AddSample(115,5.8,2.8,5.1,2.4,"Iris-virginica");
   AddSample(116,6.4,3.2,5.3,2.3,"Iris-virginica");
   AddSample(117,6.5,3.0,5.5,1.8,"Iris-virginica");
   AddSample(118,7.7,3.8,6.7,2.2,"Iris-virginica");
   AddSample(119,7.7,2.6,6.9,2.3,"Iris-virginica");
   AddSample(120,6.0,2.2,5.0,1.5,"Iris-virginica");
   AddSample(121,6.9,3.2,5.7,2.3,"Iris-virginica");
   AddSample(122,5.6,2.8,4.9,2.0,"Iris-virginica");
   AddSample(123,7.7,2.8,6.7,2.0,"Iris-virginica");
   AddSample(124,6.3,2.7,4.9,1.8,"Iris-virginica");
   AddSample(125,6.7,3.3,5.7,2.1,"Iris-virginica");
   AddSample(126,7.2,3.2,6.0,1.8,"Iris-virginica");
   AddSample(127,6.2,2.8,4.8,1.8,"Iris-virginica");
   AddSample(128,6.1,3.0,4.9,1.8,"Iris-virginica");
   AddSample(129,6.4,2.8,5.6,2.1,"Iris-virginica");
   AddSample(130,7.2,3.0,5.8,1.6,"Iris-virginica");
   AddSample(131,7.4,2.8,6.1,1.9,"Iris-virginica");
   AddSample(132,7.9,3.8,6.4,2.0,"Iris-virginica");
   AddSample(133,6.4,2.8,5.6,2.2,"Iris-virginica");
   AddSample(134,6.3,2.8,5.1,1.5,"Iris-virginica");
   AddSample(135,6.1,2.6,5.6,1.4,"Iris-virginica");
   AddSample(136,7.7,3.0,6.1,2.3,"Iris-virginica");
   AddSample(137,6.3,3.4,5.6,2.4,"Iris-virginica");
   AddSample(138,6.4,3.1,5.5,1.8,"Iris-virginica");
   AddSample(139,6.0,3.0,4.8,1.8,"Iris-virginica");
   AddSample(140,6.9,3.1,5.4,2.1,"Iris-virginica");
   AddSample(141,6.7,3.1,5.6,2.4,"Iris-virginica");
   AddSample(142,6.9,3.1,5.1,2.3,"Iris-virginica");
   AddSample(143,5.8,2.7,5.1,1.9,"Iris-virginica");
   AddSample(144,6.8,3.2,5.9,2.3,"Iris-virginica");
   AddSample(145,6.7,3.3,5.7,2.5,"Iris-virginica");
   AddSample(146,6.7,3.0,5.2,2.3,"Iris-virginica");
   AddSample(147,6.3,2.5,5.0,1.9,"Iris-virginica");
   AddSample(148,6.5,3.0,5.2,2.0,"Iris-virginica");
   AddSample(149,6.2,3.4,5.4,2.3,"Iris-virginica");
   AddSample(150,5.9,3.0,5.1,1.8,"Iris-virginica");
//---
   ArrayResize(iris_samples,150);
   for(int i=0; i<Exttotal; i++)
     {
      iris_samples[i]=ExtIRISDataset[i];
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+


Примечание к методам классификации: SVC, LinearSVC и NuSVC

Сравним три популярных метода классификации: SVC (Support Vector Classification), LinearSVC (Linear Support Vector Classification) и NuSVC (Nu Support Vector Classification).

Принципы работы:

  1. SVC (Support Vector Classification)
    Принцип работы: SVC является методом классификации, основанным на максимизации зазора между классами. Он ищет оптимальную разделяющую гиперплоскость, которая максимально отделяет классы и поддерживает опорные векторы - точки, ближайшие к гиперплоскости.
    Функции ядра:  SVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра определяет способ преобразования данных для поиска оптимальной гиперплоскости.

  2. LinearSVC (Linear Support Vector Classification)
    Принцип работы: LinearSVC - это вариант SVC, который специализируется на линейной классификации. Он ищет оптимальную линейную разделяющую гиперплоскость, не используя функции ядра. Это делает его быстрее и более эффективным в работе с большими объемами данных.

  3. NuSVC (Nu Support Vector Classification)
    Принцип работы: NuSVC также основан на методе опорных векторов, но вводит параметр Nu (nu), который контролирует сложность модели и долю опорных векторов. Значение Nu находится в интервале от 0 до 1 и определяет, какую долю данных можно использовать для опорных векторов и ошибок.

Преимущества:

  1. SVC
    Мощный алгоритм: SVC может обрабатывать сложные задачи классификации и работать с нелинейными данными благодаря использованию функций ядра.
    Устойчивость к выбросам: SVC устойчив к выбросам в данных, так как использует опорные векторы для построения разделяющей гиперплоскости.

  2. LinearSVC
    Высокая эффективность: LinearSVC быстрее и эффективнее в работе с большими объемами данных, особенно когда данных много и линейная разделяющая гиперплоскость подходит для задачи.
    Линейная классификация: Если задача хорошо линейно разделима, LinearSVC может дать хорошие результаты без необходимости использования сложных функций ядра.

  3. NuSVC
    Контроль сложности модели: Параметр Nu в NuSVC позволяет контролировать сложность модели и баланс между подгонкой данных и обобщающей способностью.
    Устойчивость к выбросам: Как и в случае с SVC, NuSVC устойчив к выбросам, что делает его полезным для задач с неточными данными.

Ограничения:

  1. SVC
    Сложность вычислений: SVC может быть медленным на больших объемах данных и/или при использовании сложных функций ядра.
    Чувствительность к выбору ядра: Выбор правильной функции ядра может быть сложной задачей и может сильно влиять на производительность модели.

  2. LinearSVC
    Ограничение на линейность: LinearSVC ограничен линейным разделением данных и может давать плохие результаты в случае нелинейных зависимостей между признаками и целевой переменной.

  3. NuSVC
    Настройка параметра Nu: Настройка параметра Nu может потребовать времени и экспериментов для достижения оптимальных результатов.

В зависимости от характеристик задачи и объема данных каждый из этих методов может оказаться наилучшим выбором. Важно провести эксперименты и выбрать метод, который наилучшим образом соответствует конкретным требованиям задачи классификации.


2.1. SVC Classifier

Метод классификации Support Vector Classification (SVC) - это мощный алгоритм машинного обучения, который широко применяется для решения задач классификации.

Принципы работы:

  1. Поиск оптимальной разделяющей гиперплоскости
    Принцип работы: Основной идеей SVC является поиск оптимальной разделяющей гиперплоскости в признаковом пространстве. Эта гиперплоскость должна максимально отделять объекты разных классов и поддерживать опорные векторы - точки данных, ближайшие к гиперплоскости.
    Максимизация зазора: SVC стремится максимизировать зазор между классами, то есть расстояние от опорных векторов до гиперплоскости. Это позволяет методу быть устойчивым к выбросам и обобщать хорошо на новые данные.

  2. Использование функций ядра
    Функции ядра: SVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра позволяет проецировать данные в более высокоразмерное пространство, где задача становится линейной, даже если в исходном пространстве данных нет линейной разделимости.
    Выбор ядра: Выбор правильной функции ядра может сильно влиять на производительность модели SVC. Не всегда линейная гиперплоскость является оптимальным решением.

Преимущества:

Ограничения:

В зависимости от конкретной задачи и объема данных метод SVC может быть мощным инструментом для решения задач классификации. Однако важно учитывать его ограничения и производить настройку параметров, чтобы достичь оптимальных результатов.

2.1.1. Код создания модели SVC Classifier

Этот код демонстрирует процесс обучения модели SVC Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_SVCClassifier.py
# The code demonstrates the process of training SVC model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create an SVC Classifier model with a linear kernel
svc_model = SVC(kernel='linear', C=1.0)

# train the model on the entire dataset
svc_model.fit(X, y)  

# predict classes for the entire dataset
y_pred = svc_model.predict(X) 

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of SVC Classifier model:", accuracy)  

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(svc_model, initial_types=initial_type, target_opset=12) 

# save the model to a file
onnx_filename = data_path +"svc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of SVC Classifier model in ONNX format:", accuracy_onnx)

После запуска скрипта в MetaEditor при помощи кнопки "Compile" во вкладке Journal можно посмотреть результаты его работы.

Рис.12. Результаты работы скрипта Iris_SVMClassifier.py в MetaEditor

Рис.12. Результаты работы скрипта Iris_SVMClassifier.py в MetaEditor

Результаты работы скрипта Iris_SVCClassifier.py:

Python    Accuracy of SVC Classifier model: 0.9933333333333333
Python   
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python   
Python               0       1.00      1.00      1.00        50
Python               1       1.00      0.98      0.99        50
Python               2       0.98      1.00      0.99        50
Python   
Python        accuracy                           0.99       150
Python       macro avg       0.99      0.99      0.99       150
Python    weighted avg       0.99      0.99      0.99       150
Python   
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\svc_iris.onnx
Python   
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python   
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python   
Python    Accuracy of SVC Classifier model in ONNX format: 0.9933333333333333

Здесь можно найти информацию о пути, по которому была сохранена ONNX-модель, типы входных и выходных параметров ONNX-модели, а также точность описания данных Iris dataset.

Точность описания набора данных при помощи SVM Classifier составляет 99%, аналогичную точность показывает модель, экспортированная в ONNX-формат.

Теперь проверим эти результаты из MQL5, запуская построенную модель для каждого из 150 образцов данных. Кроме того, в скрипте есть пример batch-обработки данных.


2.1.2. Код на MQL5 для работы с моделью SVC Classifier

//+------------------------------------------------------------------+
//|                                           Iris_SVCClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "svc_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="SVCClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результаты работы скрипта отображаются во вкладке "Experts" терминала MetaTrader 5.

Iris_SVCClassifier (EURUSD,H1)  model:SVCClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_SVCClassifier (EURUSD,H1)  model:SVCClassifier   correct results: 99.33%
Iris_SVCClassifier (EURUSD,H1)  model=SVCClassifier all samples accuracy=0.993333
Iris_SVCClassifier (EURUSD,H1)  model=SVCClassifier batch test accuracy=1.000000

SVC-модель смогла правильно различить 149 образцов из 150 что является очень хорошим результатом. Модель допустила только одну ошибку классификации Iris dataset, предсказав класс 2 (versicolor) вместо класса 1 (virginica) для образца №84.

Отметим, что точность экспортированной ONNX-модели на полном датасете iris dataset составляет 99.33%, что соответствует точности оригинала.


2.1.3. ONNX-представление модели SVC Classifier

Построенную ONNX-модель можно посмотреть в MetaEditor:


Рис.13. ONNX-модель svc_iris.onnx в MetaEditor

Рис.13. ONNX-модель svc_iris.onnx в MetaEditor


Более подробную информацию об архитектуре модели можно посмотреть при помощи Netron, для этого в описании модели в MetaEditor нужно нажать кнопку "Open in Netron".


Рис.14. ONNX-модель svc_iris.onnx в Netron

Рис.14. ONNX-модель svc_iris.onnx в Netron


Кроме того, указав мышкой на ONNX-операторы, присутствующие в модели, можно получить информацию о параметрах этих операторов (SVMClassifier на рис.15).


Рис.15. ONNX-модель svc_iris.onnx в Netron (параметры ONNX-оператора SVMClassifier)

Рис.15. ONNX-модель svc_iris.onnx в Netron (параметры ONNX-оператора SVMClassifier)



2.2. LinearSVC Classifier

LinearSVC (Linear Support Vector Classification, линейный метод опорных векторов) - это мощный алгоритм машинного обучения, используемый для задач бинарной и многоклассовой классификации. Он основан на идее поиска гиперплоскости, которая наилучшим образом разделяет данные.

Принципы работы LinearSVC:

  1. Поиск оптимальной гиперплоскости: Основная идея LinearSVC заключается в поиске оптимальной гиперплоскости, которая максимально разделяет два класса данных. Гиперплоскость - это многомерная плоскость, которая определяется как линейное уравнение.
  2. Минимизация отступов: LinearSVC стремится минимизировать отступы (расстояния от точек данных до гиперплоскости). Чем больше отступы, тем более надежно гиперплоскость разделяет классы.
  3. Работа с линейно неразделимыми данными: LinearSVC может работать с данными, которые не могут быть линейно разделены в исходном пространстве, благодаря применению функций ядра (kernel trick), которые позволяют проецировать данные в более высокоразмерное пространство, где они могут быть линейно разделены.

Преимущества LinearSVC:

Ограничения LinearSVC:

LinearSVC представляет собой мощный алгоритм классификации, который обладает хорошей способностью к обобщению, высокой эффективностью и поддержкой работы с линейно неразделимыми данными. Он находит применение в различных задачах классификации, особенно в случаях, когда данные можно разделить линейной гиперплоскостью. Однако стоит учитывать, что для сложных задач, требующих моделирования нелинейных зависимостей, LinearSVC может быть менее подходящим выбором, и в таких случаях следует рассмотреть использование методов с более сложными разделяющими поверхностями.


2.2.1. Код создания модели LinearSVC Classifier

Этот код демонстрирует процесс обучения модели LinearSVC Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_LinearSVC.py
# The code demonstrates the process of training LinearSVC model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a LinearSVC model
linear_svc_model = LinearSVC(C=1.0, max_iter=10000)

# train the model on the entire dataset
linear_svc_model.fit(X, y)

# predict classes for the entire dataset
y_pred = linear_svc_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LinearSVC model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(linear_svc_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "linear_svc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LinearSVC model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of LinearSVC model: 0.9666666666666667
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.96      0.94      0.95        50
Python               2       0.94      0.96      0.95        50
Python    
Python        accuracy                           0.97       150
Python       macro avg       0.97      0.97      0.97       150
Python    weighted avg       0.97      0.97      0.97       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\linear_svc_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python    
Python    Accuracy of LinearSVC model in ONNX format: 0.9666666666666667


2.2.2. Код на MQL5 для работы с моделью LinearSVC Classifier

//+------------------------------------------------------------------+
//|                                               Iris_LinearSVC.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "linear_svc_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="LinearSVC";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_LinearSVC (EURUSD,H1)      model:LinearSVC  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_LinearSVC (EURUSD,H1)      model:LinearSVC  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_LinearSVC (EURUSD,H1)      model:LinearSVC  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_LinearSVC (EURUSD,H1)      model:LinearSVC  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_LinearSVC (EURUSD,H1)      model:LinearSVC  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_LinearSVC (EURUSD,H1)      model:LinearSVC   correct results: 96.67%
Iris_LinearSVC (EURUSD,H1)      model=LinearSVC all samples accuracy=0.966667
Iris_LinearSVC (EURUSD,H1)      model=LinearSVC batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 96.67%, что соответствует точности оригинала.


2.2.3. ONNX-представление модели LinearSVC Classifier

Рис.16. ONNX-представление модели LinearSVC Classifier в Netron

Рис.16. ONNX-представление модели LinearSVC Classifier в Netron


2.3. NuSVC Classifier

Метод классификации Nu-Support Vector Classification (NuSVC) представляет собой мощный алгоритм машинного обучения, основанный на методе опорных векторов (Support Vector Machine, SVM).

Принципы работы NuSVC:

  1. Метод опорных векторов (SVM): NuSVC является разновидностью SVM, который используется для решения задач бинарной и многоклассовой классификации. Основным принципом SVM является поиск оптимальной разделяющей гиперплоскости, которая максимально отделяет классы и имеет максимальный зазор между ними.
  2. Параметр Nu: Один из ключевых параметров NuSVC - это параметр Nu (nu), который контролирует сложность модели и определяет долю выборки, которая может быть использована в качестве опорных векторов и ошибок. Значение Nu находится в интервале от 0 до 1, где 0.5 означает, что примерно половина выборки будет использована как опорные векторы и ошибки.
  3. Подбор параметров: Определение оптимальных значений параметра Nu и других гиперпараметров может потребовать кросс-валидации и поиска наилучших значений на обучающих данных.
  4. Функции ядра: NuSVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра определяет способ преобразования признакового пространства для поиска разделяющей гиперплоскости.

Преимущества NuSVC:

Ограничения NuSVC:

Nu-Support Vector Classification (NuSVC) - это мощный метод классификации, основанный на SVM, который имеет ряд преимуществ, включая устойчивость к выбросам и хорошую обобщающую способность. Однако его эффективность может зависеть от выбора параметров и функции ядра, а также он может быть неэффективен при обучении на больших объемах данных. Важно подбирать параметры метода тщательно и адаптировать его к конкретным задачам классификации.


2.3.1. Код создания модели NuSVC Classifier

Этот код демонстрирует процесс обучения модели NuSVC Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_NuSVC.py
# The code demonstrates the process of training NuSVC model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.svm import NuSVC
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a NuSVC model
nusvc_model = NuSVC(nu=0.5, kernel='linear')

# train the model on the entire dataset
nusvc_model.fit(X, y)

# predict classes for the entire dataset
y_pred = nusvc_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of NuSVC model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(nusvc_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "nusvc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of NuSVC model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of NuSVC model: 0.9733333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.96      0.96      0.96        50
Python               2       0.96      0.96      0.96        50
Python    
Python        accuracy                           0.97       150
Python       macro avg       0.97      0.97      0.97       150
Python    weighted avg       0.97      0.97      0.97       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\nusvc_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python    
Python    Accuracy of NuSVC model in ONNX format: 0.9733333333333334


2.3.2. Код на MQL5 для работы с моделью NuSVC Classifier

//+------------------------------------------------------------------+
//|                                                   Iris_NuSVC.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "nusvc_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="NuSVC";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_NuSVC (EURUSD,H1)  model:NuSVC  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_NuSVC (EURUSD,H1)  model:NuSVC  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_NuSVC (EURUSD,H1)  model:NuSVC  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_NuSVC (EURUSD,H1)  model:NuSVC  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_NuSVC (EURUSD,H1)  model:NuSVC   correct results: 97.33%
Iris_NuSVC (EURUSD,H1)  model=NuSVC all samples accuracy=0.973333
Iris_NuSVC (EURUSD,H1)  model=NuSVC batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 97.33%, что соответствует точности оригинала.


2.3.3. ONNX-представление модели NuSVC Classifier

Рис.17. ONNX-представление модели NuSVC Classifier в Netron

Рис.17. ONNX-представление модели NuSVC Classifier в Netron


2.4. Radius Neighbors Classifier

Radius Neighbors Classifier - это метод машинного обучения, который также используется для задач классификации и основан на принципе близости объектов. В отличие от классического K-Nearest Neighbors (K-NN) Classifier, где выбирается фиксированное количество ближайших соседей (K), в Radius Neighbors Classifier объекты классифицируются на основе расстояния до ближайших соседей, находящихся внутри определенного радиуса.

Принципы работы Radius Neighbors Classifier:
  1. Определение радиуса: Основной параметр Radius Neighbors Classifier - это радиус, который определяет максимальное расстояние между объектом и его соседями, при котором объект считается близким к классу соседей.
  2. Поиск ближайших соседей: Для каждого объекта вычисляется расстояние до всех других объектов обучающего набора. Те объекты, которые находятся внутри заданного радиуса, считаются соседями данного объекта.
  3. Голосование: Radius Neighbors Classifier использует голосование большинства среди соседей для определения класса объекта. Например, если большинство соседей принадлежат к классу A, то объект также будет классифицирован как класс A.
Преимущества Radius Neighbors Classifier:
Ограничения Radius Neighbors Classifier:

Radius Neighbors Classifier - это полезный метод машинного обучения в ситуациях, где важна близость объектов и форма классов может быть сложной. Он может использоваться в различных областях, включая анализ изображений, обработку естественного языка и другие.


2.4.1. Код создания модели Radius Neighbors Classifier

Этот код демонстрирует процесс обучения модели Radius Neighbors Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_RadiusNeighborsClassifier.py
# The code demonstrates the process of training an Radius Neughbors model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023 MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.neighbors import RadiusNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Radius Neighbors Classifier model
radius_model = RadiusNeighborsClassifier(radius=1.0)

# train the model on the entire dataset
radius_model.fit(X, y)  

# predict classes for the entire dataset
y_pred = radius_model.predict(X) 

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Radius Neighbors Classifier model:", accuracy)  

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(radius_model, initial_types=initial_type, target_opset=12) 

# save the model to a file
onnx_filename = data_path + "radius_neighbors_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Radius Neighbors Classifier model in ONNX format:", accuracy_onnx)

Результаты работы скрипта Iris_RadiusNeighbors.py:

Python    Accuracy of Radius Neighbors Classifier model: 0.9733333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.94      0.98      0.96        50
Python               2       0.98      0.94      0.96        50
Python    
Python        accuracy                           0.97       150
Python       macro avg       0.97      0.97      0.97       150
Python    weighted avg       0.97      0.97      0.97       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\radius_neighbors_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python    
Python    Accuracy of Radius Neighbors Classifier model in ONNX format: 0.9733333333333334

Точность исходной модели и точность модели, экспортированной в ONNX-формат, совпадают.


2.4.2. Код на MQL5 для работы с моделью Radius Neighbors Classifier

//+------------------------------------------------------------------+
//|                               Iris_RadiusNeighborsClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "radius_neighbors_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="RadiusNeighborsClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_RadiusNeighborsClassifier (EURUSD,H1)      model:RadiusNeighborsClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model:RadiusNeighborsClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model:RadiusNeighborsClassifier  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model:RadiusNeighborsClassifier  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model:RadiusNeighborsClassifier   correct results: 97.33%
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model=RadiusNeighborsClassifier all samples accuracy=0.973333
Iris_RadiusNeighborsClassifier (EURUSD,H1)      model=RadiusNeighborsClassifier batch test accuracy=1.000000

Модель Radius Neighbor Classifier показала точность 97% при 4 ошибках классификации (образцы 78, 107, 127 и 139).

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 97.33%, что соответствует точности оригинала.


2.4.3. ONNX-представление модели Radius Neighbors Classifier

Рис.18. ONNX-представление модели Radius Neighbors Classifier в Netron

Рис.18. ONNX-представление модели Radius Neighbors Classifier в Netron


Примечание к методам RidgeClassifier и RidgeClassifierCV

RidgeClassifier и RidgeClassifierCV - это два метода классификации, основанные на регрессии Риджа (Ridge Regression), но они различаются в способе настройки параметров и автоматическом выборе гиперпараметров:

    RidgeClassifier:

  • RidgeClassifier - это метод классификации, основанный на линейной регрессии Риджа (Ridge Regression), который используется для задач бинарной и многоклассовой классификации.
  • В случае многоклассовой классификации RidgeClassifier преобразует задачу в несколько бинарных задач (один против всех) и строит модель для каждой из них.
  • Параметр регуляризации alpha должен быть вручную настроен пользователем. Это означает, что вам нужно выбрать оптимальное значение alpha путем экспериментов или анализа результатов на валидационных данных.

    RidgeClassifierCV:

  • RidgeClassifierCV - это расширение RidgeClassifier, которое предоставляет встроенную поддержку для кросс-валидации и автоматического выбора оптимального значения параметра регуляризации alpha.
  • Вместо ручной настройки alpha, вы можете передать RidgeClassifierCV список значений alpha для исследования и указать метод кросс-валидации (например, через параметр cv).
  • RidgeClassifierCV автоматически выберет оптимальное значение alpha, которое дает наилучшую производительность на кросс-валидации.

Итак, основное различие между ними заключается в уровне автоматизации выбора оптимального значения параметра регуляризации alpha. RidgeClassifier требует ручной настройки alpha, в то время как RidgeClassifierCV позволяет автоматически выбирать оптимальное значение alpha с использованием кросс-валидации. Выбор между ними зависит от ваших потребностей и желания в автоматизации процесса настройки модели.


2.5. Ridge Classifier

Ridge Classifier - это вариант логистической регрессии, который включает регуляризацию L2 (регуляризация Тихонова) в модель. Регуляризация L2 добавляет штраф к большим значениям коэффициентов модели, что помогает уменьшить переобучение и улучшить обобщающую способность модели.

Принципы работы Ridge Classifier:
Преимущества Ridge Classifier:
Ограничения Ridge Classifier:

Ridge Classifier - это мощный метод машинного обучения, который объединяет преимущества логистической регрессии с регуляризацией для борьбы с переобучением и улучшения обобщающей способности модели. Он находит применение в различных областях, где важна классификация с учетом вероятностей и контроль сложности модели.


2.5.1. Код создания модели Ridge Classifier

Этот код демонстрирует процесс обучения модели Ridge Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_RidgeClassifier.py
# The code demonstrates the process of training Ridge Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Ridge Classifier model
ridge_model = RidgeClassifier()

# train the model on the entire dataset
ridge_model.fit(X, y)  

# predict classes for the entire dataset
y_pred = ridge_model.predict(X) 

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Ridge Classifier model:", accuracy)  

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(ridge_model, initial_types=initial_type, target_opset=12) 

# save the model to a file
onnx_filename = data_path + "ridge_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Ridge Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Ridge Classifier model: 0.8533333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.87      0.66      0.75        50
Python               2       0.73      0.90      0.80        50
Python    
Python        accuracy                           0.85       150
Python       macro avg       0.86      0.85      0.85       150
Python    weighted avg       0.86      0.85      0.85       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\ridge_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python    
Python    Accuracy of Ridge Classifier model in ONNX format: 0.8533333333333334


2.5.2. Код на MQL5 для работы с моделью Ridge Classifier

//+------------------------------------------------------------------+
//|                                         Iris_RidgeClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "ridge_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="RidgeClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier   correct results: 85.33%
Iris_RidgeClassifier (EURUSD,H1)        model=RidgeClassifier all samples accuracy=0.853333
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_RidgeClassifier (EURUSD,H1)        model:RidgeClassifier  FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_RidgeClassifier (EURUSD,H1)        model=RidgeClassifier batch test accuracy=0.000000

На полном наборе iris dataset модель показала эффективность 85.33%, что соответствует точности оригинала.


2.5.3. ONNX-представление модели Ridge Classifier

Рис.19. ONNX-представление модели Ridge Classifier в Netron

Рис.19. ONNX-представление модели Ridge Classifier в Netron


2.6. RidgeClassifierCV

Метод классификации RidgeClassifierCV представляет собой мощный алгоритм бинарной и многоклассовой классификации, основанный на регрессии Риджа (Ridge Regression).

Принципы работы RidgeClassifierCV:

  1. Линейная регрессия Риджа: RidgeClassifierCV базируется на линейной регрессии Риджа. Этот метод представляет собой модификацию линейной регрессии, где добавляется L2-регуляризация. Регуляризация помогает контролировать переобучение, уменьшая амплитуду весовых коэффициентов признаков.
  2. Бинарная и многоклассовая классификация: RidgeClassifierCV может использоваться как для бинарной классификации (когда есть только два класса), так и для многоклассовой классификации (когда есть более двух классов). Для многоклассовой классификации метод преобразует задачу в несколько бинарных задач (один против всех) и строит модель для каждой из них.
  3. Автоматический выбор параметра регуляризации: Одним из ключевых преимуществ RidgeClassifierCV является встроенная поддержка для кросс-валидации и автоматического выбора оптимального значения параметра регуляризации alpha. Вместо того, чтобы ручным образом подбирать alpha, метод перебирает различные значения alpha и выбирает наилучшее на основе кросс-валидации.
  4. Устойчивость к мультиколлинеарности: Ridge регрессия хорошо справляется с проблемой мультиколлинеарности, когда признаки сильно коррелированы друг с другом. Регуляризация позволяет контролировать вклад каждого признака, делая модель устойчивой к коррелированным данным.

Преимущества RidgeClassifierCV:

Ограничения RidgeClassifierCV:

Метод классификации RidgeClassifierCV представляет собой мощный инструмент для бинарной и многоклассовой классификации с автоматическим выбором оптимального параметра регуляризации. Его устойчивость к переобучению, интерпретируемость и эффективность делают его популярным выбором для ряда задач классификации. Однако необходимо помнить о его ограничениях, особенно о предположении о линейных зависимостях между признаками и целевой переменной.


2.6.1. Код создания модели RidgeClassifierCV

Этот код демонстрирует процесс обучения модели RidgeClassifierCV на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_RidgeClassifierCV.py
# The code demonstrates the process of training RidgeClassifierCV model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import RidgeClassifierCV
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a RidgeClassifierCV model
ridge_classifier_cv_model = RidgeClassifierCV()

# train the model on the entire dataset
ridge_classifier_cv_model.fit(X, y)

# predict classes for the entire dataset
y_pred = ridge_classifier_cv_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of RidgeClassifierCV model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(ridge_classifier_cv_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "ridge_classifier_cv_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of RidgeClassifierCV model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of RidgeClassifierCV model: 0.8533333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.87      0.66      0.75        50
Python               2       0.73      0.90      0.80        50
Python    
Python        accuracy                           0.85       150
Python       macro avg       0.86      0.85      0.85       150
Python    weighted avg       0.86      0.85      0.85       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\ridge_classifier_cv_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python    
Python    Accuracy of RidgeClassifierCV model in ONNX format: 0.8533333333333334


2.6.2. Код на MQL5 для работы с моделью RidgeClassifierCV

//+------------------------------------------------------------------+
//|                                       Iris_RidgeClassifierCV.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "ridge_classifier_cv_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   ulong input_shape[]= { batch_size, input_data.Range(1)};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[];
   float output2[][3];
//---
   ArrayResize(output1,(int)batch_size);
   ArrayResize(output2,(int)batch_size);
//---
   ulong output_shape[]= {batch_size};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {batch_size,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
//--- classes are ready in output1[k];
   if(res)
     {
      for(int k=0; k<(int)batch_size; k++)
         model_classes_id[k]=output1[k];
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="RidgeClassifierCV";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV   correct results: 85.33%
Iris_RidgeClassifierCV (EURUSD,H1)      model=RidgeClassifierCV all samples accuracy=0.853333
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_RidgeClassifierCV (EURUSD,H1)      model:RidgeClassifierCV  FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_RidgeClassifierCV (EURUSD,H1)      model=RidgeClassifierCV batch test accuracy=0.000000

Результативность ONNX-модели также полностью соответствует результативности оригинальной модели пакета Scikit-learn (85.33%).


2.6.3. ONNX-представление модели RidgeClassifierCV

Рис.20. ONNX-представление модели RidgeClassifierCV в Netron

Рис.20. ONNX-представление модели RidgeClassifierCV в Netron



2.7. Random Forest Classifier

Random Forest Classifier - это ансамблевый метод машинного обучения, который основан на построении нескольких деревьев решений и объединении их результатов для улучшения качества классификации. Этот метод чрезвычайно популярен благодаря своей эффективности и способности работать с разнообразными данными.

Принципы работы Random Forest Classifier:
  1. Баггинг (Bootstrap Aggregating): Random Forest использует метод баггинга, который состоит в создании нескольких подвыборок (bootstrap samples) из обучающих данных с повторением. Для каждой подвыборки строится отдельное дерево решений.
  2. Случайный выбор признаков: При построении каждого дерева случайным образом выбирается подмножество признаков из всего набора признаков. Это способствует разнообразию деревьев и уменьшает корреляцию между ними.
  3. Голосование: При классификации объекта каждое дерево дает свой собственный прогноз, и класс, который получает большинство голосов среди всех деревьев, выбирается как итоговый прогноз модели.
Преимущества Random Forest Classifier:
Ограничения Random Forest Classifier:

Random Forest Classifier - это сильный алгоритм машинного обучения, который широко применяется в различных областях, включая биомедицинские исследования, финансовый анализ и анализ текстовых данных. Он отлично подходит для решения задач классификации и регрессии и обладает высокой способностью к обобщению.


2.7.1. Код создания модели Random Forest Classifier

Этот код демонстрирует процесс обучения модели Random Forest Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_RandomForestClassifier.py
# The code demonstrates the process of training Random Forest Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023,2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Random Forest Classifier model
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)

# train the model on the entire dataset
rf_model.fit(X, y)

# predict classes for the entire dataset
y_pred = rf_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Random Forest Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(rf_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "rf_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Random Forest Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Random Forest Classifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\rf_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Random Forest Classifier model in ONNX format: 1.0

Модель Random Forest Classifier (и ее ONNX-версия) решают задачу классификации ирисов Фишера со 100% точностью.


2.7.2. Код на MQL5 для работы с моделью Random Forest Classifier

//+------------------------------------------------------------------+
//|                                  Iris_RandomForestClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "rf_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="RandomForestClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_RandomForestClassifier (EURUSD,H1) model:RandomForestClassifier   correct results: 100.00%
Iris_RandomForestClassifier (EURUSD,H1) model=RandomForestClassifier all samples accuracy=1.000000
Iris_RandomForestClassifier (EURUSD,H1) model=RandomForestClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.7.3. ONNX-представление модели Random Forest Classifier

Рис.21. ONNX-представление модели Random Forest Classifier в Netron

Рис.21. ONNX-представление модели Random Forest Classifier в Netron


2.8. Gradient Boosting Classifier

Градиентный бустинг является одним из наиболее мощных методов машинного обучения и находит применение в различных областях, включая анализ данных, компьютерное зрение, естественный язык и финансовый анализ, благодаря своей способности к высокой точности и способности работать с разнообразными данными.   

Gradient Boosting Classifier - это ансамблевый метод машинного обучения, который строит композицию деревьев решений для решения задач классификации. Этот метод популярен благодаря своей способности к достижению высокой точности и устойчивости к переобучению.

Принципы работы Gradient Boosting Classifier:

  1. Ансамбль деревьев решений: Gradient Boosting Classifier строит композицию (ансамбль) деревьев решений, где каждое дерево старается улучшить предсказания предыдущего дерева.
  2. Градиентный спуск: Градиентный бустинг использует градиентный спуск для оптимизации функции потерь. Он минимизирует ошибку классификации, вычисляя градиент функции потерь и обновляя предсказания с учетом этого градиента.
  3. Взвешивание деревьев: Каждое дерево в композиции имеет вес, и в конечном итоге прогнозы всех деревьев объединяются с учетом их весов.

Преимущества Gradient Boosting Classifier:

Ограничения Gradient Boosting Classifier:

Gradient Boosting Classifier - это мощный метод машинного обучения, который часто используется в соревнованиях по анализу данных и решает множество задач классификации успешно. Он способен находить сложные нелинейные зависимости в данных и обладает хорошей обобщающей способностью при правильной настройке гиперпараметров.


2.8.1. Код создания модели Gradient Boosting Classifier

Этот код демонстрирует процесс обучения модели Gradient Boosting Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_GradientBoostingClassifier.py
# The code demonstrates the process of training Gradient Boostring Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Gradient Boosting Classifier model
gb_model = GradientBoostingClassifier(n_estimators=100, random_state=42)

# train the model on the entire dataset
gb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = gb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Gradient Boosting Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(gb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "gb_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Gradient Boosting Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Gradient Boosting Classifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\gb_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Gradient Boosting Classifier model in ONNX format: 1.0

Модель Gradient Boosting Classifier (и ее ONNX-версия) решают задачу классификации ирисов Фишера с точностью 100%.


2.8.2. Код на MQL5 для работы с моделью Gradient Boosting Classifier

//+------------------------------------------------------------------+
//|                              Iris_GradientBoostingClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "gb_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="GradientBoostingClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_GradientBoostingClassifier (EURUSD,H1)     model:GradientBoostingClassifier   correct results: 100.00%
Iris_GradientBoostingClassifier (EURUSD,H1)     model=GradientBoostingClassifier all samples accuracy=1.000000
Iris_GradientBoostingClassifier (EURUSD,H1)     model=GradientBoostingClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.8.3. ONNX-представление модели Gradient Boosting Classifier

Рис.22. ONNX-представление модели Gradient Boosting Classifier в Netron

Рис.22. ONNX-представление модели Gradient Boosting Classifier в Netron


2.9. Adaptive Boosting Classifier

AdaBoost (Adaptive Boosting) Classifier - это ансамблевый метод машинного обучения, который используется для улучшения классификации, путем комбинирования результатов нескольких слабых (например, деревьев решений) классификаторов для создания более сильного алгоритма.

Принципы работы AdaBoost Classifier:
  1. Ансамбль слабых классификаторов: AdaBoost начинается с инициализации каждого образца в обучающем наборе весами, присваивая им одинаковые начальные значения.
  2. Обучение слабых классификаторов: Затем AdaBoost обучает слабый классификатор (например, дерево решений) на обучающем наборе с учетом весов образцов. Этот классификатор пытается правильно классифицировать образцы.
  3. Перераспределение весов: AdaBoost изменяет веса образцов, повышая веса неправильно классифицированных образцов и уменьшая веса правильно классифицированных образцов.
  4. Создание композиции: AdaBoost повторяет процесс обучения слабых классификаторов и перераспределения весов многократно. Затем результаты этих слабых классификаторов объединяются в композицию, где каждый классификатор вносит вклад с учетом его точности.
Преимущества AdaBoost Classifier:
Ограничения AdaBoost Classifier:

AdaBoost Classifier - это мощный алгоритм машинного обучения, который часто используется в практике для решения задач классификации. Он хорошо подходит для задач с двоичными и многоклассовыми классами и может быть адаптирован к различным базовым классификаторам.


2.9.1. Код создания модели Adaptive Boosting Classifier

Этот код демонстрирует процесс обучения модели Adaptive Boosting Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_AdaBoostClassifier.py
# The code demonstrates the process of training AdaBoost Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create an AdaBoost Classifier model
adaboost_model = AdaBoostClassifier(n_estimators=50, random_state=42)

# train the model on the entire dataset
adaboost_model.fit(X, y)

# predict classes for the entire dataset
y_pred = adaboost_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of AdaBoost Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(adaboost_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "adaboost_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of AdaBoost Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of AdaBoost Classifier model: 0.96
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.92      0.96      0.94        50
Python               2       0.96      0.92      0.94        50
Python    
Python        accuracy                           0.96       150
Python       macro avg       0.96      0.96      0.96       150
Python    weighted avg       0.96      0.96      0.96       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\adaboost_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of AdaBoost Classifier model in ONNX format: 0.96


2.9.2. Код на MQL5 для работы с моделью Adaptive Boosting Classifier

//+------------------------------------------------------------------+
//|                                      Iris_AdaBoostClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "adaboost_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="AdaBoostClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AdaBoostClassifier (EURUSD,H1)     model:AdaBoostClassifier   correct results: 96.00%
Iris_AdaBoostClassifier (EURUSD,H1)     model=AdaBoostClassifier all samples accuracy=0.960000
Iris_AdaBoostClassifier (EURUSD,H1)     model=AdaBoostClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 96%, что соответствует точности оригинала.


2.9.3. ONNX-представление модели Adaptive Boosting Classifier

Рис.23. ONNX-представление модели Adaptive Boosting Classifier в Netron

Рис.23. ONNX-представление модели Adaptive Boosting Classifier в Netron


2.10. Bootstrap Aggregating Classifier

Bagging (Bootstrap Aggregating) Classifier - это ансамблевый метод машинного обучения, который основан на создании нескольких подвыборок (bootstrap samples) из обучающих данных и построении отдельных моделей на каждой из них, а затем комбинировании результатов для улучшения обобщающей способности модели.

Принципы работы Bagging Classifier:
  1. Создание подвыборок: Bagging начинается с создания нескольких случайных подвыборок (bootstrap samples) из обучающих данных с повторением. Это означает, что одни и те же образцы могут появиться в нескольких подвыборках, а некоторые образцы могут быть пропущены.
  2. Обучение базовых моделей: На каждой подвыборке обучается отдельная базовая модель (например, дерево решений). Каждая модель обучается независимо от других моделей.
  3. Агрегация результатов: После обучения всех базовых моделей результаты их предсказаний комбинируются для получения итогового прогноза. В случае бинарной классификации, это может быть сделано путем голосования большинства.
Преимущества Bagging Classifier:
Ограничения Bagging Classifier:

Bagging Classifier - это эффективный метод машинного обучения, который может повысить обобщающую способность модели и снизить переобучение. Он часто используется в комбинации с различными базовыми моделями для решения разнообразных задач классификации и регрессии.


2.10.1. Код создания модели Bootstrap Aggregating Classifier

Этот код демонстрирует процесс обучения модели Bootstrap Aggregating Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_BootstrapAggregatingClassifier.py
# The code demonstrates the process of training Bagging Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.ensemble import BaggingClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Bagging Classifier model with a Decision Tree base estimator
bagging_model = BaggingClassifier(n_estimators=100, random_state=42)

# train the model on the entire dataset
bagging_model.fit(X, y)

# predict classes for the entire dataset
y_pred = bagging_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Bagging Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(bagging_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "bagging_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Bagging Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Bagging Classifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\bagging_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Bagging Classifier model in ONNX format: 1.0

Модель Bootstrap Aggregating Classifier (и ее ONNX-версия) показали 100% точность классификации Iris dataset.


2.10.2. Код на MQL5 для работы с моделью Bootstrap Aggregating Classifier

//+------------------------------------------------------------------+
//|                          Iris_BootstrapAggregatingClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "bagging_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="BootstrapAggregatingClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_BootstrapAggregatingClassifier (EURUSD,H1) model:BootstrapAggregatingClassifier   correct results: 100.00%
Iris_BootstrapAggregatingClassifier (EURUSD,H1) model=BootstrapAggregatingClassifier all samples accuracy=1.000000
Iris_BootstrapAggregatingClassifier (EURUSD,H1) model=BootstrapAggregatingClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.10.3. ONNX-представление модели Bootstrap Aggregating Classifier

Рис.24. ONNX-представление модели Bootstrap Aggregating Classifier в Netron

Рис.24. ONNX-представление модели Bootstrap Aggregating Classifier в Netron


2.11. K-Nearest Neighbors (K-NN) Classifier

K-Nearest Neighbors (K-NN) Classifier - это метод машинного обучения, который используется для решения задач классификации и регрессии на основе близости между объектами данных. Он основан на принципе, что объекты, находящиеся близко друг к другу в многомерном признаковом пространстве, имеют схожие характеристики и, следовательно, могут иметь схожие метки классов.

Принципы работы K-NN Classifier:

  1. Определение близости: K-NN классификатор определяет близость между объектом, который нужно классифицировать, и остальными объектами обучающего набора. Это часто делается с использованием метрики расстояния, такой как евклидово расстояние или манхэттенское расстояние.
  2. Выбор числа соседей: Параметр K определяет количество ближайших соседей, которые будут использоваться для классификации объекта. Обычно K выбирается исходя из задачи и данных.
  3. Голосование: K-NN использует голосование большинства среди K ближайших соседей для определения класса объекта. Например, если большинство из K соседей принадлежат к классу A, то объект также будет классифицирован как класс A.

Преимущества K-NN Classifier:

Ограничения K-NN Classifier:

K-NN Classifier - это метод машинного обучения, который может быть полезен в задачах, где близость объектов имеет важное значение, например, в задачах рекомендации, классификации текстовых данных и распознавания образов. Он хорошо подходит для начального анализа данных и быстрого прототипирования моделей.


2.11.1. Код создания модели K-Nearest Neighbors (K-NN) Classifier

Этот код демонстрирует процесс обучения модели K-Nearest Neighbors (K-NN) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_KNearestNeighborsClassifier.py
# The code uses the K-Nearest Neighbors (KNN) Classifier for the Iris dataset, converts the model to ONNX format, saves it, and evaluates its accuracy.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a K-Nearest Neighbors (KNN) Classifier model
knn_model = KNeighborsClassifier(n_neighbors=3)

# train the model on the entire dataset
knn_model.fit(X, y)

# predict classes for the entire dataset
y_pred = knn_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of KNN Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(knn_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "knn_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of KNN Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of KNN Classifier model: 0.96
Python   
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python   
Python               0       1.00      1.00      1.00        50
Python               1       0.94      0.94      0.94        50
Python               2       0.94      0.94      0.94        50
Python   
Python        accuracy                           0.96       150
Python       macro avg       0.96      0.96      0.96       150
Python    weighted avg       0.96      0.96      0.96       150
Python   
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\knn_iris.onnx
Python   
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python   
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python   
Python    Accuracy of KNN Classifier model in ONNX format: 0.96


2.11.2. Код на MQL5 для работы с моделью K-Nearest Neighbors (K-NN) Classifier

//+------------------------------------------------------------------+
//|                             Iris_KNearestNeighborsClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "knn_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="KNearestNeighborsClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier   correct results: 96.00%
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model=KNearestNeighborsClassifier all samples accuracy=0.960000
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model:KNearestNeighborsClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_KNearestNeighborsClassifier (EURUSD,H1)    model=KNearestNeighborsClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 96%, что соответствует точности оригинала.


2.11.3. ONNX-представление модели K-Nearest Neighbors (K-NN) Classifier

Рис.25. ONNX-представление модели K-Nearest Neighbors в Netron

Рис.25. ONNX-представление модели K-Nearest Neighbors в Netron


2.12. Decision Tree Classifier

Decision Tree Classifier - это метод машинного обучения, который используется для задач классификации, основанный на построении дерева решений. Этот метод разделяет набор данных на более мелкие подгруппы путем выполнения серии условных тестов на признаках и определяет класс объекта на основе пути, который он проходит в дереве.

Принципы работы Decision Tree Classifier:

  1. Построение дерева решений: Начально все данные представлены в корне дерева. Для каждого узла дерева выполняется разделение данных на две или более подгруппы, основываясь на значениях одного из признаков. Это делается так, чтобы максимально уменьшить неопределенность (например, энтропию или Gini-индекс) в каждой подгруппе.
  2. Рекурсивное построение: Процесс разделения данных выполняется рекурсивно до достижения листьев дерева. Листья представляют собой конечные классы объектов.
  3. Принятие решения: Когда объект попадает в дерево, он следует пути от корня к одному из листьев, где определяется его класс на основе большинства объектов в данном листе.
Преимущества Decision Tree Classifier:
Ограничения Decision Tree Classifier:

Decision Tree Classifier - это полезный метод машинного обучения для задач классификации, особенно в ситуациях, когда важна интерпретируемость модели и нужно понимать, какие признаки влияют на решение. Этот метод также может быть использован в ансамблевых методах, таких как Random Forest и Gradient Boosting.


2.12.1. Код создания модели Decision Tree Classifier

Этот код демонстрирует процесс обучения модели Decision Tree Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_DecisionTreeClassifier.py
# The code uses the Decision Tree Classifier for the Iris dataset, converts the model to ONNX format, saves it, and evaluates its accuracy.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Decision Tree Classifier model
decision_tree_model = DecisionTreeClassifier(random_state=42)

# train the model on the entire dataset
decision_tree_model.fit(X, y)

# predict classes for the entire dataset
y_pred = decision_tree_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Decision Tree Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(decision_tree_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "decision_tree_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Decision Tree Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Decision Tree Classifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\decision_tree_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Decision Tree Classifier model in ONNX format: 1.0

Модель Decision Tree Classifier (и ее ONNX-версия) показали 100% точность классификации всего набора ирисов Фишера.


2.12.2. Код на MQL5 для работы с моделью Decision Tree Classifier

//+------------------------------------------------------------------+
//|                                  Iris_DecisionTreeClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "decision_tree_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="DecisionTreeClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_DecisionTreeClassifier (EURUSD,H1) model:DecisionTreeClassifier   correct results: 100.00%
Iris_DecisionTreeClassifier (EURUSD,H1) model=DecisionTreeClassifier all samples accuracy=1.000000
Iris_DecisionTreeClassifier (EURUSD,H1) model=DecisionTreeClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.12.3. ONNX-представление модели Decision Tree Classifier

Рис.26. ONNX-представление модели Decision Tree Classifier в Netron

Рис.26. ONNX-представление модели Decision Tree Classifier в Netron


Примечание к моделям LogisticRegression и LogisticRegressionCV

LogisticRegression и LogisticRegressionCV - это два классификатора, используемых для бинарной классификации с помощью логистической регрессии, но они различаются по способу настройки параметров модели:

    LogisticRegression:

  • LogisticRegression представляет собой классификатор, который использует логистическую функцию для моделирования вероятности принадлежности к одному из двух классов (бинарная классификация).
  • Он предоставляет основные параметры для настройки, такие как C (обратная сила регуляризации), penalty (тип регуляризации, например, L1 или L2), solver (алгоритм оптимизации) и другие.
  • При использовании LogisticRegression вы обычно сами выбираете значения параметров и их комбинации, и затем обучаете модель на данных.

    LogisticRegressionCV:

  • LogisticRegressionCV - это расширение LogisticRegression, которое предоставляет встроенную поддержку для кросс-валидации и выбора оптимального значения параметра регуляризации C.
  • Вместо того, чтобы вручную выбирать C, вы можете передать LogisticRegressionCV список значений C для исследования и указать метод кросс-валидации (например, через параметр cv).
  • LogisticRegressionCV автоматически выберет оптимальное значение C, которое дает наилучшую производительность на кросс-валидации.
  • Это удобно, когда вам нужно автоматически настроить регуляризацию, особенно если у вас есть много данных или вы не знаете, какое значение C следует выбрать.

Итак, основное различие между ними заключается в уровне автоматизации настройки параметров C. LogisticRegression требует ручной настройки C, в то время как LogisticRegressionCV позволяет автоматически выбирать оптимальное значение C с помощью кросс-валидации. Выбор между ними зависит от ваших потребностей и желания в автоматизации процесса настройки модели.



2.13. Logistic Regression Classifier

Logistic Regression Classifier - это метод машинного обучения, который используется для задач бинарной и многоклассовой классификации. Название "регрессия" может ввести в заблуждение, но логистическая регрессия фактически предсказывает вероятность принадлежности объекта к одному из классов. Затем на основе этих вероятностей делается окончательное решение о классификации объекта.

Принципы работы Logistic Regression Classifier:
  1. Предсказание вероятности: Логистическая регрессия моделирует вероятность принадлежности объекта к определенному классу с использованием логистической (сигмоидальной) функции.
  2. Определение границы решения: На основе предсказанных вероятностей логистическая регрессия определяет границу решения, которая разделяет классы. Если вероятность превышает определенный порог (обычно 0.5), объект классифицируется как принадлежащий к одному классу, в противном случае - к другому.
  3. Обучение параметров: Модель логистической регрессии обучается на обучающем наборе данных путем настройки весов (коэффициентов) при признаках так, чтобы минимизировать функцию потерь.
Преимущества Logistic Regression Classifier:
Ограничения Logistic Regression Classifier:

Логистическая регрессия - это классический метод машинного обучения, который широко используется в практике для задач классификации, особенно в случаях, когда важна интерпретируемость модели и данные имеют линейную или почти линейную структуру. Она также используется в статистике и медицинском анализе данных для оценки влияния факторов на вероятность событий.


2.13.1. Код создания модели Logistic Regression Classifier

Этот код демонстрирует процесс обучения модели Logistic Regression Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_LogisticRegressionClassifier.py
# The code uses the Logistic Regression Classifier for the Iris dataset, converts the model to ONNX format, saves it, and evaluates its accuracy.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Logistic Regression Classifier model
logistic_regression_model = LogisticRegression(max_iter=1000, random_state=42)

# train the model on the entire dataset
logistic_regression_model.fit(X, y)

# predict classes for the entire dataset
y_pred = logistic_regression_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Logistic Regression Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(logistic_regression_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "logistic_regression_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Logistic Regression Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Logistic Regression Classifier model: 0.9733333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.98      0.94      0.96        50
Python               2       0.94      0.98      0.96        50
Python    
Python        accuracy                           0.97       150
Python       macro avg       0.97      0.97      0.97       150
Python    weighted avg       0.97      0.97      0.97       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\logistic_regression_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Logistic Regression Classifier model in ONNX format: 0.9733333333333334


2.13.2. Код на MQL5 для работы с моделью Regression Classifier

//+------------------------------------------------------------------+
//|                            Iris_LogisticRegressionClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "logistic_regression_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="LogisticRegressionClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+
Результат:
Iris_LogisticRegressionClassifier (EURUSD,H1)   model:LogisticRegressionClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_LogisticRegressionClassifier (EURUSD,H1)   model:LogisticRegressionClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_LogisticRegressionClassifier (EURUSD,H1)   model:LogisticRegressionClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_LogisticRegressionClassifier (EURUSD,H1)   model:LogisticRegressionClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_LogisticRegressionClassifier (EURUSD,H1)   model:LogisticRegressionClassifier   correct results: 97.33%
Iris_LogisticRegressionClassifier (EURUSD,H1)   model=LogisticRegressionClassifier all samples accuracy=0.973333
Iris_LogisticRegressionClassifier (EURUSD,H1)   model=LogisticRegressionClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 97.33%, что соответствует точности оригинала.


2.13.3. ONNX-представление модели Logistic Regression Classifier

Рис.27. ONNX-представление модели Logistic Regression Classifier в Netron

Рис.27. ONNX-представление модели Logistic Regression Classifier в Netron


2.14. LogisticRegressionCV Classifier

LogisticRegressionCV (Logistic Regression с кросс-валидацией) представляет собой мощный и гибкий метод бинарной классификации. Этот метод не только позволяет вам создавать модели классификации на основе логистической регрессии, но и автоматически настраивать параметры для достижения наилучшей производительности.

Принципы работы LogisticRegressionCV:

  1. Логистическая регрессия: В основе метода LogisticRegressionCV лежит логистическая регрессия. Логистическая регрессия - это статистический метод, используемый для моделирования вероятности принадлежности объекта к одному из двух классов. Эта модель применяется, когда зависимая переменная является бинарной (два класса) или когда она может быть преобразована в бинарную.
  2. Кросс-валидация: Основное преимущество LogisticRegressionCV заключается в интегрированной кросс-валидации. Это означает, что вместо того, чтобы вручную подбирать оптимальное значение параметра регуляризации C, метод автоматически перебирает различные значения C и выбирает тот, который дает наилучшую производительность на кросс-валидации.
  3. Выбор оптимального C: LogisticRegressionCV использует стратегию кросс-валидации, чтобы оценить производительность модели при разных значениях C. C - это параметр регуляризации, который контролирует степень регуляризации модели. Маленькое значение C означает сильную регуляризацию, а большое значение C - слабую. Кросс-валидация позволяет выбрать оптимальное значение C для баланса между недообучением и переобучением.
  4. Регуляризация: LogisticRegressionCV также поддерживает различные типы регуляризации, включая L1 (лассо) и L2 (гребневую) регуляризацию. Эти виды регуляризации помогают улучшить обобщающую способность модели и предотвратить переобучение.

Преимущества LogisticRegressionCV:

Ограничения LogisticRegressionCV:

LogisticRegressionCV представляет собой мощный инструмент для бинарной классификации с автоматической настройкой параметров и устойчивостью к переобучению. Он особенно полезен, когда вам нужно быстро создать модель классификации с хорошей интерпретируемостью. Однако важно помнить, что он лучше всего работает в случаях, когда данные имеют линейные или близкие к линейным зависимости.


2.14.1. Код создания модели LogisticRegressionCV Classifier

Этот код демонстрирует процесс обучения модели LogisticRegressionCV Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_LogisticRegressionCVClassifier.py
# The code demonstrates the process of training LogisticRegressionCV model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a LogisticRegressionCV model
logistic_regression_model = LogisticRegressionCV(cv=5, max_iter=1000)

# train the model on the entire dataset
logistic_regression_model.fit(X, y)

# predict classes for the entire dataset
y_pred = logistic_regression_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LogisticRegressionCV model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(logistic_regression_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "logistic_regressioncv_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LogisticRegressionCV model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of LogisticRegressionCV model: 0.98
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.98      0.96      0.97        50
Python               2       0.96      0.98      0.97        50
Python    
Python        accuracy                           0.98       150
Python       macro avg       0.98      0.98      0.98       150
Python    weighted avg       0.98      0.98      0.98       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\logistic_regression_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of LogisticRegressionCV model in ONNX format: 0.98


2.14.2. Код на MQL5 для работы с моделью LogisticRegressionCV Classifier

//+------------------------------------------------------------------+
//|                          Iris_LogisticRegressionCVClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "logistic_regressioncv_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="LogisticRegressionCVClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_LogisticRegressionCVClassifier (EURUSD,H1) model:LogisticRegressionCVClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_LogisticRegressionCVClassifier (EURUSD,H1) model:LogisticRegressionCVClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_LogisticRegressionCVClassifier (EURUSD,H1) model:LogisticRegressionCVClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_LogisticRegressionCVClassifier (EURUSD,H1) model:LogisticRegressionCVClassifier   correct results: 98.00%
Iris_LogisticRegressionCVClassifier (EURUSD,H1) model=LogisticRegressionCVClassifier all samples accuracy=0.980000
Iris_LogisticRegressionCVClassifier (EURUSD,H1) model=LogisticRegressionCVClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 98%, что соответствует точности оригинала.


2.14.3. ONNX-представление модели LogisticRegressionCV Classifier

Рис.28. ONNX-представление модели LogisticRegressionCV Classifier в Netron

Рис.28. ONNX-представление модели LogisticRegressionCV Classifier в Netron



2.15. Passive-Aggressive (PA) Classifier

Passive-Aggressive (PA) Classifier - это метод машинного обучения, который используется для задач классификации. Основная идея этого метода заключается в том, чтобы адаптировать веса (коэффициенты) модели в процессе обучения, чтобы минимизировать ошибку классификации. Passive-Aggressive Classifier может быть полезен в задачах онлайн-обучения и в ситуациях, когда данные меняются со временем.

Принципы работы Passive-Aggressive Classifier:
  1. Адаптация весов: Вместо того чтобы обновлять веса модели в направлении минимизации функции потерь, как это делается в методе стохастического градиентного спуска, Passive-Aggressive Classifier адаптирует веса в направлении, которое минимизирует ошибку классификации для текущего примера.
  2. Сохранение агрессивности: Метод имеет параметр, который называется агрессивностью (C), который определяет, насколько сильно нужно адаптировать веса модели. Большие значения C делают метод более агрессивным в адаптации, а маленькие - менее агрессивным.
Преимущества Passive-Aggressive Classifier:
Ограничения Passive-Aggressive Classifier:

Passive-Aggressive Classifier - это метод машинного обучения, который подходит для задач классификации с изменяющимися данными и для ситуаций, где важно быстро адаптировать модель к новым обстоятельствам. Он находит применение в различных областях, включая анализ текстовых данных, классификацию изображений и другие задачи.


2.15.1. Код создания модели Passive-Aggressive (PA) Classifier

Этот код демонстрирует процесс обучения модели Passive-Aggressive (PA) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_PassiveAgressiveClassifier.py
# The code uses the Passive-Aggressive (PA) Classifier for the Iris dataset, converts the model to ONNX format, saves it, and evaluates its accuracy.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import PassiveAggressiveClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Passive-Aggressive (PA) Classifier model
pa_classifier_model = PassiveAggressiveClassifier(max_iter=1000, random_state=42)

# train the model on the entire dataset
pa_classifier_model.fit(X, y)

# predict classes for the entire dataset
y_pred = pa_classifier_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Passive-Aggressive (PA) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(pa_classifier_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "pa_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Passive-Aggressive (PA) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Passive-Aggressive (PA) Classifier model: 0.96
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.96      0.92      0.94        50
Python               2       0.92      0.96      0.94        50
Python    
Python        accuracy                           0.96       150
Python       macro avg       0.96      0.96      0.96       150
Python    weighted avg       0.96      0.96      0.96       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\pa_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Passive-Aggressive (PA) Classifier model in ONNX format: 0.96


2.15.2. Код на MQL5 для работы с моделью Passive-Aggressive (PA) Classifier

//+------------------------------------------------------------------+
//|                              Iris_PassiveAgressiveClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "pa_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="PassiveAgressiveClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model:PassiveAgressiveClassifier   correct results: 96.00%
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model=PassiveAgressiveClassifier all samples accuracy=0.960000
Iris_PassiveAgressiveClassifier (EURUSD,H1)     model=PassiveAgressiveClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 96%, что соответствует точности оригинала.


2.15.3. ONNX-представление модели Passive-Aggressive (PA) Classifier

Рис.29. ONNX-представление модели Passive-Aggressive (PA) Classifier в Netron

Рис.29. ONNX-представление модели Passive-Aggressive (PA) Classifier в Netron

2.16. Perceptron Classifier

Perceptron Classifier - это линейный бинарный классификатор, который используется для разделения двух классов на основе линейной разделяющей гиперплоскости. Он является одним из самых простых и старых методов машинного обучения, и его основной принцип - это обучение весов (коэффициентов) модели так, чтобы максимизировать правильность классификации на обучающем наборе данных.

Принципы работы Perceptron Classifier:
  1. Линейная гиперплоскость: Perceptron строит линейную гиперплоскость в многомерном пространстве признаков, которая разделяет два класса. Эта гиперплоскость определяется весами (коэффициентами) модели.
  2. Обучение весов: Начально веса инициализируются случайным образом или нулями. Затем для каждого объекта обучающего набора модель предсказывает класс на основе текущих весов и корректирует их в случае ошибки. Обучение продолжается до тех пор, пока все объекты не будут классифицированы правильно или пока не будет достигнуто максимальное количество итераций.
Преимущества Perceptron Classifier:
Ограничения Perceptron Classifier:

Perceptron Classifier - это базовый алгоритм для бинарной классификации, который может быть полезным в простых задачах, когда данные линейно разделимы. Он также может служить в качестве основы для более сложных методов, таких как многослойные нейронные сети. Важно помнить, что в более сложных задачах, где данные имеют сложную структуру, другие методы, такие как логистическая регрессия или метод опорных векторов (SVM), могут предоставить более высокую точность классификации.


2.16.1. Код создания модели Perceptron Classifier

Этот код демонстрирует процесс обучения модели Perceptron Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.# Iris_PerceptronClassifier.py

# Iris_PerceptronClassifier.py
# The code demonstrates the process of training Perceptron Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import Perceptron
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Perceptron Classifier model
perceptron_model = Perceptron(max_iter=1000, random_state=42)

# train the model on the entire dataset
perceptron_model.fit(X, y)

# predict classes for the entire dataset
y_pred = perceptron_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Perceptron Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(perceptron_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "perceptron_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Perceptron Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Perceptron Classifier model: 0.6133333333333333
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      0.80      0.89        50
Python               1       0.46      1.00      0.63        50
Python               2       1.00      0.04      0.08        50
Python    
Python        accuracy                           0.61       150
Python       macro avg       0.82      0.61      0.53       150
Python    weighted avg       0.82      0.61      0.53       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\perceptron_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Perceptron Classifier model in ONNX format: 0.6133333333333333


2.16.2. Код на MQL5 для работы с моделью Perceptron Classifier

//+------------------------------------------------------------------+
//|                                    Iris_PerceptronClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include "iris.mqh"
#resource "perceptron_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="PerceptronClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=2 FAILED [class=1, true class=0] features=(4.90,3.00,1.40,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=9 FAILED [class=1, true class=0] features=(4.40,2.90,1.40,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=10 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=13 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=21 FAILED [class=1, true class=0] features=(5.40,3.40,1.70,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=26 FAILED [class=1, true class=0] features=(5.00,3.00,1.60,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=31 FAILED [class=1, true class=0] features=(4.80,3.10,1.60,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=35 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=42 FAILED [class=1, true class=0] features=(4.50,2.30,1.30,0.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=46 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=103 FAILED [class=1, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=104 FAILED [class=1, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=105 FAILED [class=1, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=106 FAILED [class=1, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=108 FAILED [class=1, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=110 FAILED [class=1, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=111 FAILED [class=1, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=112 FAILED [class=1, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=113 FAILED [class=1, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=114 FAILED [class=1, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=116 FAILED [class=1, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=117 FAILED [class=1, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=118 FAILED [class=1, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=119 FAILED [class=1, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=121 FAILED [class=1, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=123 FAILED [class=1, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=125 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=126 FAILED [class=1, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=129 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=131 FAILED [class=1, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=133 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=136 FAILED [class=1, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=137 FAILED [class=1, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=138 FAILED [class=1, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=140 FAILED [class=1, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=141 FAILED [class=1, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=142 FAILED [class=1, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=144 FAILED [class=1, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=145 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=146 FAILED [class=1, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=147 FAILED [class=1, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=148 FAILED [class=1, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=149 FAILED [class=1, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  sample=150 FAILED [class=1, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier   correct results: 61.33%
Iris_PerceptronClassifier (EURUSD,H1)   model=PerceptronClassifier all samples accuracy=0.613333
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.10)
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90)
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  FAILED [class=1, true class=2] features=(7.10,3.00,5.90,2.10)
Iris_PerceptronClassifier (EURUSD,H1)   model:PerceptronClassifier  FAILED [class=1, true class=2] features=(6.30,2.90,5.60,1.80)
Iris_PerceptronClassifier (EURUSD,H1)   model=PerceptronClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 61.33%, что соответствует точности оригинала.

2.16.3. ONNX-представление модели Perceptron Classifier

Рис.30. ONNX-представление модели Perceptron Classifier в Netron

Рис.30. ONNX-представление модели Perceptron Classifier в Netron


2.17. Stochastic Gradient Descent Classifier

SGD Classifier (Stochastic Gradient Descent Classifier) - это метод машинного обучения, который используется для задач классификации. Он является частным случаем линейных моделей и представляет собой линейный классификатор, который обучается с использованием стохастического градиентного спуска.

Принципы работы SGD Classifier:
  1. Линейная гиперплоскость: SGD Classifier строит линейную гиперплоскость в многомерном пространстве признаков, которая разделяет два класса. Гиперплоскость определяется весами (коэффициентами) модели.
  2. Стохастический градиентный спуск: Метод обучается с использованием стохастического градиентного спуска, что означает, что обновление весов модели выполняется на каждом объекте обучающего набора (или на случайно выбранном подмножестве), а не на всем наборе данных. Это делает SGD Classifier подходящим для больших объемов данных и онлайн-обучения.
  3. Функция потерь: SGD Classifier оптимизирует функцию потерь, такую как логистическая функция потерь для задач бинарной классификации или функция потерь softmax для многоклассовой классификации.
Преимущества SGD Classifier:
Ограничения SGD Classifier:

SGD Classifier - это гибкий метод машинного обучения, который может использоваться для задач бинарной и многоклассовой классификации, особенно в случаях, когда данные имеют большой объем и требуют быстрой обработки. Важно правильно настроить его гиперпараметры и следить за сходимостью для достижения высокой точности классификации.


2.17.1. Код создания модели Stochastic Gradient Descent Classifier

Этот код демонстрирует процесс обучения модели Stochastic Gradient Descent Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_SGDClassifier.py
# The code demonstrates the process of training Stochastic Gradient Descent Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create an SGD Classifier model
sgd_model = SGDClassifier(max_iter=1000, random_state=42)

# train the model on the entire dataset
sgd_model.fit(X, y)

# predict classes for the entire dataset
y_pred = sgd_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of SGD Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(sgd_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "sgd_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of SGD Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of SGD Classifier model: 0.9333333333333333
Python   
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python   
Python               0       0.96      1.00      0.98        50
Python               1       0.88      0.92      0.90        50
Python               2       0.96      0.88      0.92        50
Python   
Python        accuracy                           0.93       150
Python       macro avg       0.93      0.93      0.93       150
Python    weighted avg       0.93      0.93      0.93       150
Python   
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\perceptron_classifier_iris.onnx
Python   
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python   
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python   
Python    Accuracy of SGD Classifier model in ONNX format: 0.9333333333333333


2.17.2. Код на MQL5 для работы с моделью Stochastic Gradient Descent Classifier

//+------------------------------------------------------------------+
//|                                           Iris_SGDClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "sgd_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="SGDClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier   correct results: 93.33%
Iris_SGDClassifier (EURUSD,H1)  model=SGDClassifier all samples accuracy=0.933333
Iris_SGDClassifier (EURUSD,H1)  model:SGDClassifier  FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_SGDClassifier (EURUSD,H1)  model=SGDClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 93.33%, что соответствует точности оригинала.


2.17.3. ONNX-представление модели Stochastic Gradient Descent Classifier

Рис.31. ONNX-представление модели Stochastic Gradient Descent Classifier в Netron

Рис.31. ONNX-представление модели Stochastic Gradient Descent Classifier в Netron


2.18. Gaussian Naive Bayes (GNB) Classifier

Gaussian Naive Bayes (GNB) Classifier - это метод машинного обучения, основанный на байесовской вероятностной модели и используемый для задач классификации. Он является частью семейства наивных байесовских классификаторов и предполагает, что все признаки независимы и имеют нормальное распределение.

Принципы работы Gaussian Naive Bayes Classifier:
  1. Байесовский подход: GNB основан на байесовском подходе к классификации, который использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
  2. Наивное предположение: Главное предположение, сделанное в GNB, состоит в том, что все признаки являются независимыми и имеют нормальное (гауссовское) распределение. Это наивное предположение, потому что в реальных данных признаки часто коррелируют между собой.
  3. Обучение параметров: Модель GNB обучается на обучающем наборе данных, вычисляя параметры распределения (среднее и стандартное отклонение) для каждого признака в каждом классе.
Преимущества Gaussian Naive Bayes Classifier:
Ограничения Gaussian Naive Bayes Classifier:

Gaussian Naive Bayes Classifier - это хороший выбор для простых задач классификации, особенно когда предположение о нормальном распределении признаков более или менее справедливо. Однако в более сложных задачах, где признаки коррелируют между собой или распределение не является нормальным, другие методы, такие как метод опорных векторов (SVM) или градиентный бустинг, могут предоставить более точные результаты.


2.18.1. Код создания модели Gaussian Naive Bayes (GNB) Classifier

Этот код демонстрирует процесс обучения модели Gaussian Naive Bayes (GNB) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_GaussianNaiveBayesClassifier.py
# The code demonstrates the process of training Gaussian Naive Bayes Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Gaussian Naive Bayes (GNB) Classifier model
gnb_model = GaussianNB()

# train the model on the entire dataset
gnb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = gnb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Gaussian Naive Bayes (GNB) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(gnb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "gnb_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Gaussian Naive Bayes (GNB) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Gaussian Naive Bayes (GNB) Classifier model: 0.96
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.94      0.94      0.94        50
Python               2       0.94      0.94      0.94        50
Python    
Python        accuracy                           0.96       150
Python       macro avg       0.96      0.96      0.96       150
Python    weighted avg       0.96      0.96      0.96       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\gnb_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Gaussian Naive Bayes (GNB) Classifier model in ONNX format: 0.96


2.18.2. Код на MQL5 для работы с моделью Gaussian Naive Bayes (GNB) Classifier

//+------------------------------------------------------------------+
//|                            Iris_GaussianNaiveBayesClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "gnb_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="GaussianNaiveBayesClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model:GaussianNaiveBayesClassifier   correct results: 96.00%
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model=GaussianNaiveBayesClassifier all samples accuracy=0.960000
Iris_GaussianNaiveBayesClassifier (EURUSD,H1)   model=GaussianNaiveBayesClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 96%, что соответствует точности оригинала.


2.18.3. ONNX-представление модели Gaussian Naive Bayes (GNB) Classifier

Рис.32. ONNX-представление модели Gaussian Naive Bayes (GNB) Classifier в Netron

Рис.32. ONNX-представление модели Gaussian Naive Bayes (GNB) Classifier в Netron


2.19. Multinomial Naive Bayes (MNB) Classifier

Multinomial Naive Bayes (MNB) Classifier - это метод машинного обучения, основанный на байесовской вероятностной модели, и используется для задач классификации, особенно в текстовой обработке. Он является одним из вариантов наивных байесовских классификаторов и предполагает, что признаки представляют собой счетчики, такие как количество слов в тексте.

Принципы работы Multinomial Naive Bayes Classifier:
  1. Байесовский подход: MNB также основан на байесовском подходе к классификации и использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
  2. Предположение о мультиномиальном распределении: Основное предположение MNB заключается в том, что признаки представляют собой счетчики, такие как количество вхождений слов в тексте, и имеют мультиномиальное распределение. Это предположение часто справедливо для текстовых данных.
  3. Обучение параметров: Модель MNB обучается на обучающем наборе данных, вычисляя параметры распределения для каждого признака в каждом классе.
Преимущества Multinomial Naive Bayes Classifier:
Ограничения Multinomial Naive Bayes Classifier:

Multinomial Naive Bayes Classifier - это полезный метод для задач анализа текста, особенно когда признаки связаны с подсчетами, такими как количество слов в тексте. Он широко используется в натуральной обработке языка (NLP) для задач классификации текста, категоризации документов и других текстовых анализов.


2.19.1. Код создания модели Multinomial Naive Bayes (MNB) Classifier

Этот код демонстрирует процесс обучения модели Multinomial Naive Bayes (MNB) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_MultinomialNaiveBayesClassifier.py
# The code demonstrates the process of training Multinomial Naive Bayes (MNB) Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Multinomial Naive Bayes (MNB) Classifier model
mnb_model = MultinomialNB()

# train the model on the entire dataset
mnb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = mnb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Multinomial Naive Bayes (MNB) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(mnb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "mnb_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Multinomial Naive Bayes (MNB) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Multinomial Naive Bayes (MNB) Classifier model: 0.9533333333333334
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.94      0.92      0.93        50
Python               2       0.92      0.94      0.93        50
Python    
Python        accuracy                           0.95       150
Python       macro avg       0.95      0.95      0.95       150
Python    weighted avg       0.95      0.95      0.95       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\mnb_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Multinomial Naive Bayes (MNB) Classifier model in ONNX format: 0.9533333333333334


2.19.2. Код на MQL5 для работы с моделью Multinomial Naive Bayes (MNB) Classifier

//+------------------------------------------------------------------+
//|                         Iris_MultinomialNaiveBayesClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "mnb_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="MultinomialNaiveBayesClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier   correct results: 95.33%
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model=MultinomialNaiveBayesClassifier all samples accuracy=0.953333
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model=MultinomialNaiveBayesClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 95.33%, что соответствует точности оригинала.


2.19.3. ONNX-представление модели Multinomial Naive Bayes (MNB) Classifier

Рис.33. ONNX-представление модели Multinomial Naive Bayes (MNB) Classifier в Netron

Рис.33. ONNX-представление модели Multinomial Naive Bayes (MNB) Classifier в Netron


2.20. Complement Naive Bayes (CNB) Classifier

Complement Naive Bayes (CNB) Classifier - это вариант наивного байесовского классификатора, который был специально разработан для работы с несбалансированными данными, где один класс может быть значительно более часто встречающимся, чем другой. Этот классификатор представляет собой адаптацию классического наивного байесовского метода, который пытается учесть дисбаланс классов.

Принципы работы Complement Naive Bayes Classifier:
  1. Байесовский подход: Как и другие байесовские классификаторы, CNB основан на байесовском подходе к классификации и использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
  2. Исправление дисбаланса классов: Главное предназначение CNB - это коррекция дисбаланса классов. Вместо того, чтобы учитывать вероятность признаков в классе, как это делает стандартный наивный байесовский метод, CNB пытается учесть вероятность признаков вне класса. Это особенно полезно, когда один класс значительно менее представлен, чем другой.
  3. Обучение параметров: Модель CNB обучается на обучающем наборе данных, вычисляя параметры распределения для каждого признака вне класса.
Преимущества Complement Naive Bayes Classifier:
Ограничения Complement Naive Bayes Classifier:

Complement Naive Bayes Classifier - это хороший выбор для задач классификации на несбалансированных данных, особенно когда один класс значительно менее представлен, чем другой. Он может быть особенно полезным в задачах текстовой классификации, где слова могут быть сильно дисбалансированы по классам, такие как анализ тональности текста или фильтрация спама.


2.20.1. Код создания модели Complement Naive Bayes (CNB) Classifier

Этот код демонстрирует процесс обучения модели Complement Naive Bayes (CNB) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_CNBClassifier.py
# The code demonstrates the process of training Complement Naive Bayes (CNB) Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.naive_bayes import ComplementNB
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Complement Naive Bayes (CNB) Classifier model
cnb_model = ComplementNB()

# train the model on the entire dataset
cnb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = cnb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Complement Naive Bayes (CNB) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(cnb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "cnb_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Complement Naive Bayes (CNB) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Complement Naive Bayes (CNB) Classifier model: 0.6666666666666666
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       0.96      1.00      0.98        50
Python               1       0.00      0.00      0.00        50
Python               2       0.51      1.00      0.68        50
Python    
Python        accuracy                           0.67       150
Python       macro avg       0.49      0.67      0.55       150
Python    weighted avg       0.49      0.67      0.55       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\cnb_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Complement Naive Bayes (CNB) Classifier model in ONNX format: 0.6666666666666666


2.20.2. Код на MQL5 для работы с моделью< Complement Naive Bayes (CNB) Classifier

//+------------------------------------------------------------------+
//|                                           Iris_CNBClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "cnb_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="CNBClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=54 FAILED [class=2, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=55 FAILED [class=2, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=56 FAILED [class=2, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=58 FAILED [class=2, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=59 FAILED [class=2, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=60 FAILED [class=2, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=61 FAILED [class=2, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=63 FAILED [class=2, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=64 FAILED [class=2, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=68 FAILED [class=2, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=70 FAILED [class=2, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=72 FAILED [class=2, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=74 FAILED [class=2, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=75 FAILED [class=2, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=77 FAILED [class=2, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=81 FAILED [class=2, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=82 FAILED [class=2, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=83 FAILED [class=2, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=88 FAILED [class=2, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=90 FAILED [class=2, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=91 FAILED [class=2, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=93 FAILED [class=2, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=94 FAILED [class=2, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=95 FAILED [class=2, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=96 FAILED [class=2, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=97 FAILED [class=2, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=98 FAILED [class=2, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=100 FAILED [class=2, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier   correct results: 66.67%
Iris_CNBClassifier (EURUSD,H1)  model=CNBClassifier all samples accuracy=0.666667
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_CNBClassifier (EURUSD,H1)  model=CNBClassifier batch test accuracy=0.000000
Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 66.67%, что соответствует точности оригинала.


2.20.3. ONNX-представление модели Complement Naive Bayes (CNB) Classifier

Рис.34. ONNX-представление модели Complement Naive Bayes (CNB) Classifier в Netron

Рис.34. ONNX-представление модели Complement Naive Bayes (CNB) Classifier в Netron


2.21. Bernoulli Naive Bayes (BNB) Classifier

Bernoulli Naive Bayes (BNB) Classifier - это еще один вариант наивного байесовского классификатора, который используется для задач бинарной классификации. Этот классификатор особенно полезен в ситуациях, где признаки представлены бинарными данными, например, в задачах анализа текста, где признаками могут быть наличие или отсутствие слов в тексте.

Принципы работы Bernoulli Naive Bayes Classifier:

  1. Байесовский подход: Как и другие байесовские классификаторы, BNB основан на байесовском подходе к классификации и использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
  2. Предположение о бинарных признаках: Основное предположение BNB заключается в том, что признаки представляют собой бинарные данные, то есть они могут иметь только два значения, например, 1 и 0, где 1 обозначает наличие признака, а 0 - его отсутствие.
  3. Обучение параметров: Модель BNB обучается на обучающем наборе данных, вычисляя параметры распределения для каждого признака в каждом классе.
Преимущества Bernoulli Naive Bayes Classifier:
Ограничения Bernoulli Naive Bayes Classifier:

Bernoulli Naive Bayes Classifier - это хороший выбор для задач бинарной классификации с бинарными признаками, такими как анализ тональности текста или классификация спама. Он прост в использовании и хорошо справляется с данными такого типа.


2.21.1. Код создания модели Bernoulli Naive Bayes (BNB) Classifier

Этот код демонстрирует процесс обучения модели Bernoulli Naive Bayes (BNB) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_BNBClassifier.py
# The code demonstrates the process of training Bernoulli Naive Bayes (BNB) Classifier on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Bernoulli Naive Bayes (BNB) Classifier model
bnb_model = BernoulliNB()

# train the model on the entire dataset
bnb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = bnb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Bernoulli Naive Bayes (BNB) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(bnb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "bnb_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Bernoulli Naive Bayes (BNB) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Bernoulli Naive Bayes (BNB) Classifier model: 0.3333333333333333
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       0.33      1.00      0.50        50
Python               1       0.00      0.00      0.00        50
Python               2       0.00      0.00      0.00        50
Python    
Python        accuracy                           0.33       150
Python       macro avg       0.11      0.33      0.17       150
Python    weighted avg       0.11      0.33      0.17       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\bnb_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Bernoulli Naive Bayes (BNB) Classifier model in ONNX format: 0.3333333333333333


2.21.2. Код на MQL5 для работы с моделью Bernoulli Naive Bayes (BNB) Classifier

//+------------------------------------------------------------------+
//|                                           Iris_BNBClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "bnb_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="BNBClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=51 FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=52 FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=53 FAILED [class=0, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=54 FAILED [class=0, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=55 FAILED [class=0, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=56 FAILED [class=0, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=57 FAILED [class=0, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=58 FAILED [class=0, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=59 FAILED [class=0, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=60 FAILED [class=0, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=61 FAILED [class=0, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=62 FAILED [class=0, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=63 FAILED [class=0, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=64 FAILED [class=0, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=66 FAILED [class=0, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=67 FAILED [class=0, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=68 FAILED [class=0, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=69 FAILED [class=0, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=70 FAILED [class=0, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=71 FAILED [class=0, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=72 FAILED [class=0, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=73 FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=74 FAILED [class=0, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=75 FAILED [class=0, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=76 FAILED [class=0, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=77 FAILED [class=0, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=78 FAILED [class=0, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=79 FAILED [class=0, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=81 FAILED [class=0, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=82 FAILED [class=0, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=83 FAILED [class=0, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=84 FAILED [class=0, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=85 FAILED [class=0, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=87 FAILED [class=0, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=88 FAILED [class=0, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=89 FAILED [class=0, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=90 FAILED [class=0, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=91 FAILED [class=0, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=92 FAILED [class=0, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=93 FAILED [class=0, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=94 FAILED [class=0, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=95 FAILED [class=0, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=96 FAILED [class=0, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=97 FAILED [class=0, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=98 FAILED [class=0, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=100 FAILED [class=0, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=101 FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=102 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=103 FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=104 FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=105 FAILED [class=0, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=106 FAILED [class=0, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=107 FAILED [class=0, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=108 FAILED [class=0, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=109 FAILED [class=0, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=110 FAILED [class=0, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=111 FAILED [class=0, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=112 FAILED [class=0, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=113 FAILED [class=0, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=114 FAILED [class=0, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=115 FAILED [class=0, true class=2] features=(5.80,2.80,5.10,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=116 FAILED [class=0, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=117 FAILED [class=0, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=118 FAILED [class=0, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=119 FAILED [class=0, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=120 FAILED [class=0, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=121 FAILED [class=0, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=122 FAILED [class=0, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=123 FAILED [class=0, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=124 FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=125 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=126 FAILED [class=0, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=127 FAILED [class=0, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=128 FAILED [class=0, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=129 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=130 FAILED [class=0, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=131 FAILED [class=0, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=132 FAILED [class=0, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=133 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=134 FAILED [class=0, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=135 FAILED [class=0, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=136 FAILED [class=0, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=137 FAILED [class=0, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=138 FAILED [class=0, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=139 FAILED [class=0, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=140 FAILED [class=0, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=141 FAILED [class=0, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=142 FAILED [class=0, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=143 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=144 FAILED [class=0, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=145 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=146 FAILED [class=0, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=147 FAILED [class=0, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=148 FAILED [class=0, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=149 FAILED [class=0, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=150 FAILED [class=0, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier   correct results: 33.33%
Iris_BNBClassifier (EURUSD,H1)  model=BNBClassifier all samples accuracy=0.333333
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80)
Iris_BNBClassifier (EURUSD,H1)  model=BNBClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 33.33%, что соответствует точности оригинала.


2.21.3. ONNX-представление модели Bernoulli Naive Bayes (BNB) Classifier

Рис.35. ONNX-представление модели Bernoulli Naive Bayes (BNB) Classifier в Netron

Рис.35. ONNX-представление модели Bernoulli Naive Bayes (BNB) Classifier в Netron


2.22. Multilayer Perceptron Classifier

MLP Classifier (Multilayer Perceptron Classifier) - это многозначная нейронная сеть, используемая для задач классификации. Он представляет собой многослойный персептрон, который состоит из нескольких слоев нейронов, включая входной слой, скрытые слои и выходной слой. MLP Classifier обладает способностью обучаться сложным нелинейным зависимостям в данных.

Принципы работы MLP Classifier:
  1. Многослойная архитектура: MLP Classifier имеет многослойную архитектуру, которая включает в себя входной слой, один или несколько скрытых слоев и выходной слой. Каждый нейрон в слоях связан с нейронами в соседних слоях с весами, которые подлежат обучению.
  2. Функции активации: Внутри каждого нейрона применяется функция активации, которая вводит нелинейность в модель и позволяет MLP Classifier моделировать сложные зависимости в данных.
  3. Обучение с помощью обратного распространения ошибки: MLP Classifier обучается с использованием метода обратного распространения ошибки (backpropagation), который минимизирует ошибку между прогнозами модели и истинными метками классов.
Преимущества MLP Classifier:
Ограничения MLP Classifier:

MLP Classifier - это мощный инструмент для задач классификации, особенно в случаях, когда данные имеют сложные зависимости. Он часто используется в областях машинного обучения и глубокого обучения для решения различных задач классификации. Однако для успешного применения этой модели важно правильно настроить ее гиперпараметры и обеспечить достаточный объем обучающих данных.


2.22.1. Код создания модели Multilayer Perceptron Classifier

Этот код демонстрирует процесс обучения модели Multilayer Perceptron Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_MLPClassifier.py
# The code demonstrates the process of training Multilayer Perceptron Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Multilayer Perceptron (MLP) Classifier model
mlp_model = MLPClassifier(max_iter=1000, random_state=42)

# train the model on the entire dataset
mlp_model.fit(X, y)

# predict classes for the entire dataset
y_pred = mlp_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Multilayer Perceptron (MLP) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(mlp_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path +"mlp_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Multilayer Perceptron (MLP) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Multilayer Perceptron (MLP) Classifier model: 0.98
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      0.94      0.97        50
Python               2       0.94      1.00      0.97        50
Python    
Python        accuracy                           0.98       150
Python       macro avg       0.98      0.98      0.98       150
Python    weighted avg       0.98      0.98      0.98       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\mlp_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Multilayer Perceptron (MLP) Classifier model in ONNX format: 0.98


2.22.2. Код на MQL5 для работы с моделью Multilayer Perceptron Classifier

//+------------------------------------------------------------------+
//|                                           Iris_MLPClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "mlp_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="MLPClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier   correct results: 98.00%
Iris_MLPClassifier (EURUSD,H1)  model=MLPClassifier all samples accuracy=0.980000
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_MLPClassifier (EURUSD,H1)  model=MLPClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 98%, что соответствует точности оригинала.


2.22.3. ONNX-представление модели Multilayer Perceptron Classifier

Рис.36. ONNX-представление модели Multilayer Perceptron Classifier в Netron

Рис.36. ONNX-представление модели Multilayer Perceptron Classifier в Netron


2.23. Linear Discriminant Analysis (LDA) Classifier

Linear Discriminant Analysis (LDA) Classifier - это метод машинного обучения, который используется для решения задач классификации. Он относится к семейству методов понижения размерности данных и классификации в пространстве меньшей размерности. LDA строит гиперплоскости таким образом, чтобы максимизировать разделение между классами.

Принципы работы LDA Classifier:
  1. Понижение размерности: Основной идеей LDA является понижение размерности данных. Он стремится найти новое пространство признаков, где классы данных максимально отделены друг от друга.
  2. Максимизация разделения: LDA строит гиперплоскости (линейные комбинации признаков), которые максимизируют разницу между средними значениями признаков в разных классах и минимизируют дисперсию внутри каждого класса.
  3. Обучение параметров: Модель LDA обучается на обучающем наборе данных, вычисляя параметры гиперплоскостей и проекций данных на новое пространство.
Преимущества LDA Classifier:
Ограничения LDA Classifier:

LDA Classifier - это полезный метод для задач классификации и понижения размерности данных, особенно когда есть необходимость улучшить разделение классов. Он часто используется в статистике, биологии, медицинском анализе и других областях для анализа данных и классификации.


2.23.1. Код создания модели Linear Discriminant Analysis (LDA) Classifier

Этот код демонстрирует процесс обучения модели Linear Discriminant Analysis (LDA) Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_LDAClassifier.py
# The code demonstrates the process of training Linear Discriminant Analysis (LDA) Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Linear Discriminant Analysis (LDA) Classifier model
lda_model = LinearDiscriminantAnalysis()

# train the model on the entire dataset
lda_model.fit(X, y)

# predict classes for the entire dataset
y_pred = lda_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Linear Discriminant Analysis (LDA) Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(lda_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path +"lda_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Linear Discriminant Analysis (LDA) Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Linear Discriminant Analysis (LDA) Classifier model: 0.98
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.98      0.96      0.97        50
Python               2       0.96      0.98      0.97        50
Python    
Python        accuracy                           0.98       150
Python       macro avg       0.98      0.98      0.98       150
Python    weighted avg       0.98      0.98      0.98       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\lda_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Linear Discriminant Analysis (LDA) Classifier model in ONNX format: 0.98


2.23.2. Код на MQL5 для работы с моделью Linear Discriminant Analysis (LDA) Classifier

//+------------------------------------------------------------------+
//|                                           Iris_LDAClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "lda_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="LDAClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_LDAClassifier (EURUSD,H1)  model:LDAClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_LDAClassifier (EURUSD,H1)  model:LDAClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_LDAClassifier (EURUSD,H1)  model:LDAClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_LDAClassifier (EURUSD,H1)  model:LDAClassifier   correct results: 98.00%
Iris_LDAClassifier (EURUSD,H1)  model=LDAClassifier all samples accuracy=0.980000
Iris_LDAClassifier (EURUSD,H1)  model=LDAClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 98%, что соответствует точности оригинала.


2.23.3. ONNX-представление модели Linear Discriminant Analysis (LDA) Classifier

Рис.37. ONNX-представление модели Linear Discriminant Analysis (LDA) Classifier в Netron

Рис.37. ONNX-представление модели Linear Discriminant Analysis (LDA) Classifier в Netron


2.24. Hist Gradient Boosting

Hist Gradient Boosting Classifier - это алгоритм машинного обучения, который относится к семейству градиентного бустинга и предназначен для задач классификации. Он является эффективным и мощным методом, который широко применяется в анализе данных и машинном обучении.

Принципы работы Hist Gradient Boosting Classifier:

  1. Градиентный бустинг: Hist Gradient Boosting Classifier базируется на методе градиентного бустинга, который строит ансамбль деревьев решений, чтобы улучшить классификацию. Он делает это путем последовательного обучения слабых моделей и корректировки ошибок предыдущих моделей.
  2. Использование гистограмм: "Hist" в названии означает, что данный алгоритм использует гистограммы для эффективной работы с данными. Вместо полного перебора признаков, Hist Gradient Boosting строит гистограммы признаков, что позволяет быстро вычислять разделение в деревьях решений.
  3. Обучение на остатках: Как и другие методы градиентного бустинга, Hist Gradient Boosting обучает каждое новое дерево на остатках предыдущей модели, чтобы уточнить прогноз.
Преимущества Hist Gradient Boosting Classifier:
Ограничения Hist Gradient Boosting Classifier:

Hist Gradient Boosting Classifier - это мощный алгоритм для задач классификации и регрессии, который обеспечивает высокую точность и эффективность в обработке данных. Он находит применение во многих областях, таких как анализ данных, биоинформатика, финансы и другие.


2.24.1. Код создания модели Histogram-Based Gradient Boosting Classifier

Этот код демонстрирует процесс обучения модели Histogram-Based Gradient Boosting Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_HistGradientBoostingClassifier.py
# The code demonstrates the process of training Histogram-Based Gradient Boosting Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a Histogram-Based Gradient Boosting Classifier model
hist_gradient_boosting_model = HistGradientBoostingClassifier(random_state=42)

# train the model on the entire dataset
hist_gradient_boosting_model.fit(X, y)

# predict classes for the entire dataset
y_pred = hist_gradient_boosting_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Hist Gradient Boosting Classifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(hist_gradient_boosting_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path +"hist_gradient_boosting_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Hist Gradient Boosting Classifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Hist Gradient Boosting Classifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\hist_gradient_boosting_classifier_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of Hist Gradient Boosting Classifier model in ONNX format: 1.0


2.24.2. Код на MQL5 для работы с моделью Histogram-Based Gradient Boosting Classifier

//+------------------------------------------------------------------+
//|                          Iris_HistGradientBoostingClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "hist_gradient_boosting_classifier_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="HistGradientBoostingClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_HistGradientBoostingClassifier (EURUSD,H1) model:HistGradientBoostingClassifier   correct results: 100.00%
Iris_HistGradientBoostingClassifier (EURUSD,H1) model=HistGradientBoostingClassifier all samples accuracy=1.000000
Iris_HistGradientBoostingClassifier (EURUSD,H1) model=HistGradientBoostingClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.24.3. ONNX-представление модели Histogram-Based Gradient Boosting Classifier

Рис.38. ONNX-представление модели Histogram-Based Gradient Boosting Classifier в Netro

Рис.38. ONNX-представление модели Histogram-Based Gradient Boosting Classifier в Netron


2.25. CategoricalNB Classifier

CategoricalNB - это алгоритм классификации, основанный на теореме Байеса. Он специально разработан для наборов данных с категориальными признаками и широко используется в классификации текста, обнаружении спама и других приложениях, связанных с дискретными данными.

Принципы работы CategoricalNB:

  1. Наивный байесовский классификатор: CategoricalNB является одним из видов наивных байесовских классификаторов, основанных на теореме Байеса. Он вычисляет вероятность принадлежности к определенному классу для набора признаков, используя условные вероятности каждого признака при условии класса.
  2. Категориальные признаки: В отличие от Гауссова наивного байесовского классификатора, который предполагает непрерывные признаки с нормальным распределением, CategoricalNB подходит для наборов данных с категориальными признаками. Он моделирует вероятностное распределение каждого признака для каждого класса.
  3. Предположение о независимости: "Наивность" в наивном байесовском классификаторе происходит из предположения о независимости признаков. CategoricalNB предполагает, что признаки условно независимы при условии класса. Несмотря на то, что это предположение не всегда соблюдается на практике, наивные байесовские методы могут давать хорошие результаты на многих реальных наборах данных.

Преимущества CategoricalNB:

Ограничения CategoricalNB:

CategoricalNB - ценный алгоритм классификации, особенно подходящий для наборов данных с категориальными признаками. Его простота, эффективность и интерпретируемость делают его полезным инструментом для различных задач классификации. Несмотря на ограничения, такие как предположение о независимости, он остается популярным выбором для классификации текста и других задач, где преобладают дискретные данные. При работе с категориальными данными рассмотрение CategoricalNB в качестве базовой модели часто является разумным выбором. Однако важно оценить его производительность по сравнению с более сложными моделями, особенно если в данных присутствуют зависимости между признаками.


2.25.1. Код создания модели CategoricalNB Classifier

Этот код демонстрирует процесс обучения модели CategoricalNB Classifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_CategoricalNBClassifier.py
# The code demonstrates the process of training CategoricalNB Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.naive_bayes import CategoricalNB
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a CategoricalNB model
categorical_nb_model = CategoricalNB()

# train the model on the entire dataset
categorical_nb_model.fit(X, y)

# predict classes for the entire dataset
y_pred = categorical_nb_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of CategoricalNB model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(categorical_nb_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "categorical_nb_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of CategoricalNB model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of CategoricalNB model: 0.9333333333333333
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.86      0.96      0.91        50
Python               2       0.95      0.84      0.89        50
Python    
Python        accuracy                           0.93       150
Python       macro avg       0.94      0.93      0.93       150
Python    weighted avg       0.94      0.93      0.93       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\categorical_nb_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of CategoricalNB model in ONNX format: 0.9333333333333333


2.25.2. Код на MQL5 для работы с моделью CategoricalNB Classifier

//+------------------------------------------------------------------+
//|                                 Iris_CategoricalNBClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "categorical_nb_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="CategoricalNBClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+
Результат:
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier   correct results: 93.33%
Iris_CategoricalNBClassifier (EURUSD,H1)        model=CategoricalNBClassifier all samples accuracy=0.933333
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90)
Iris_CategoricalNBClassifier (EURUSD,H1)        model=CategoricalNBClassifier batch test accuracy=0.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 93.33%, что соответствует точности оригинала.


2.25.3. ONNX-представление модели CategoricalNB Classifier

Рис.39. ONNX-представление модели CategoricalNB Classifier в Netron

Рис.39. ONNX-представление модели CategoricalNB Classifier в Netron



Примечание к моделям ExtraTreeClassifier и ExtraTreesClassifier

ExtraTreeClassifier и ExtraTreesClassifier - это два разных классификатора, и их основное отличие заключается в том, как они работают:

ExtraTreeClassifier (Extremely Randomized Trees Classifier):

  • Этот классификатор также известен как Extremely Randomized Trees или Extra-Trees.
  • Он основан на идее случайных деревьев решений.
  • В ExtraTreeClassifier, выбор разделения для каждого узла дерева происходит случайным образом, без какого-либо предварительного поиска наилучшего разделения.
  • Это делает классификатор менее вычислительно интенсивным, чем классический случайный лес (Random Forest), так как он не требует вычисления оптимальных разделений для каждого узла.
  • В ExtraTreeClassifier часто используются случайные пороги для признаков и случайное разделение, что приводит к более случайным деревьям.
  • Отсутствие поиска наилучших разделений делает ExtraTreeClassifier более быстрым, но менее точным по сравнению с Random Forest.
ExtraTreesClassifier (Extremely Randomized Trees Classifier):
  • ExtraTreesClassifier - это также классификатор, основанный на методе Extremely Randomized Trees.
  • Основное отличие между ExtraTreesClassifier и ExtraTreeClassifier заключается в том, что ExtraTreesClassifier проводит случайное разбиение для выбора наилучших разделений в каждом узле дерева.
  • Это означает, что ExtraTreesClassifier применяет случайный лес (Random Forest) с дополнительным уровнем случайности при выборе оптимальных разделений.
  • ExtraTreesClassifier обычно более точен, чем ExtraTreeClassifier, потому что он проводит случайное разбиение для нахождения наилучших признаков для разделения.
  • Однако ExtraTreesClassifier может быть более вычислительно интенсивным из-за необходимости проводить более широкий поиск оптимальных разделений.
Таким образом, основное отличие между этими двумя классификаторами заключается в уровне случайности при выборе разделений. ExtraTreeClassifier делает случайный выбор для каждого узла без предварительного поиска наилучших разделений, в то время как ExtraTreesClassifier проводит случайное разбиение с поиском наилучших разделений для каждого узла.


2.26. ExtraTreeClassifier

ExtraTreeClassifier, или Extremely Randomized Trees, представляет собой мощный алгоритм машинного обучения, который используется в задачах классификации и регрессии. Этот алгоритм основан на идее решающих деревьев и предлагает улучшения в сравнении с традиционными случайными лесами и решающими деревьями.

Принципы работы ExtraTreeClassifier:

  1. Случайное разбиение узлов: Основной принцип ExtraTreeClassifier заключается в том, что он случайным образом выбирает разбиение для каждого узла дерева. Это отличается от традиционных решающих деревьев, которые выбирают наилучший признак для разбиения. ExtraTreeClassifier делает разбиение без учета наилучшего разделения, что делает его более случайным и устойчивым к переобучению.
  2. Агрегация результатов: В процессе построения ансамбля ExtraTreeClassifier создает множество случайных деревьев и агрегирует их результаты. Это делается для повышения обобщающей способности модели и снижения дисперсии. Ансамбль деревьев позволяет бороться с проблемой переобучения и повышает стабильность предсказаний.
  3. Случайные пороги: При разбиении узла ExtraTreeClassifier выбирает случайные пороги для каждого признака, вместо определенных оптимальных значений. Это приводит к большей случайности и устойчивости модели.

Преимущества ExtraTreeClassifier:

Ограничения ExtraTreeClassifier:

ExtraTreeClassifier - это мощный алгоритм машинного обучения, который обладает устойчивостью к переобучению и высокой скоростью обучения. Он может быть полезным в различных задачах классификации и регрессии, особенно когда есть ограниченные вычислительные ресурсы. Однако важно учитывать случайный характер этого алгоритма и его ограничения, такие как неустойчивость к выбросам и менее высокая интерпретируемость. При использовании ExtraTreeClassifier важно тщательно настраивать его параметры и учитывать особенности данных.


2.26.1. Код создания модели ExtraTreeClassifier

Этот код демонстрирует процесс обучения модели ExtraTreeClassifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_ExtraTreeClassifier.py
# The code demonstrates the process of training ExtraTree Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.tree import ExtraTreeClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create an ExtraTreeClassifier model
extra_tree_model = ExtraTreeClassifier()

# train the model on the entire dataset
extra_tree_model.fit(X, y)

# predict classes for the entire dataset
y_pred = extra_tree_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of ExtraTreeClassifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(extra_tree_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "extra_tree_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of ExtraTreeClassifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of ExtraTreeClassifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\extra_tree_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of ExtraTreeClassifier model in ONNX format: 1.0


2.26.2. Код на MQL5 для работы с моделью ExtraTreeClassifier

//+------------------------------------------------------------------+
//|                                     Iris_ExtraTreeClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "extra_tree_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="ExtraTreeClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_ExtraTreeClassifier (EURUSD,H1)    model:ExtraTreeClassifier   correct results: 100.00%
Iris_ExtraTreeClassifier (EURUSD,H1)    model=ExtraTreeClassifier all samples accuracy=1.000000
Iris_ExtraTreeClassifier (EURUSD,H1)    model=ExtraTreeClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.26.3. ONNX-представление модели ExtraTreeClassifier

Рис.40. ONNX-представление модели ExtraTreeClassifier в Netron

Рис.40. ONNX-представление модели ExtraTreeClassifier в Netron


2.27. ExtraTreesClassifier

ExtraTreesClassifier - это мощный алгоритм машинного обучения, который используется для задач классификации. Этот алгоритм является расширением и улучшением Random Forest (случайного леса) и предлагает ряд преимуществ и недостатков.

Принципы работы ExtraTreesClassifier:

  1. Bootstrap выборки: Подобно случайному лесу, ExtraTreesClassifier использует метод бутстрэпа для создания множества подвыборок из обучающего набора данных. Это означает, что для каждого дерева создается случайная подвыборка с повторениями из исходных данных.
  2. Случайные разбиения: В отличие от случайного леса, где для каждого узла дерева выбирается наилучший признак для разбиения, в ExtraTreesClassifier используются случайные признаки и случайные пороги для разделения узлов. Это делает деревья более случайными и уменьшает переобучение.
  3. Голосование: После построения множества деревьев каждое дерево голосует за класс объекта. В итоге, класс, набравший наибольшее количество голосов, становится предсказанным классом.

Преимущества ExtraTreesClassifier:

  1. Снижение переобучения: Использование случайных разбиений и случайных признаков делает ExtraTreesClassifier менее подверженным переобучению по сравнению с традиционными решающими деревьями.
  2. Высокая скорость обучения: ExtraTreesClassifier требует меньше вычислительных ресурсов для обучения по сравнению с некоторыми другими алгоритмами, такими как градиентный бустинг.
  3. Устойчивость к выбросам: Благодаря использованию ансамбля деревьев и случайных разбиений, ExtraTreesClassifier обычно более устойчив к выбросам в данных.

Ограничения ExtraTreesClassifier:

ExtraTreesClassifier представляет собой мощный алгоритм классификации, который обладает устойчивостью к переобучению, высокой скоростью обучения и устойчивостью к выбросам. Он может быть полезным инструментом в анализе данных и решении задач классификации, особенно в случаях, когда имеются большие объемы данных и требуется эффективное решение. Однако важно учитывать, что алгоритм не всегда является наилучшим выбором, и его эффективность может зависеть от конкретной задачи и данных.


2.27.1. Код создания модели ExtraTreesClassifier

Этот код демонстрирует процесс обучения модели ExtraTreesClassifier на наборе данных Iris, экспорта ее в формат ONNX и выполнения классификации с использованием модели ONNX. Также он оценивает точность как исходной модели, так и модели ONNX.

# Iris_ExtraTreesClassifier.py
# The code demonstrates the process of training ExtraTrees Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create an ExtraTreesClassifier model
extra_trees_model = ExtraTreesClassifier()

# train the model on the entire dataset
extra_trees_model.fit(X, y)

# predict classes for the entire dataset
y_pred = extra_trees_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of ExtraTreesClassifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(extra_trees_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "extra_trees_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of ExtraTreesClassifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of ExtraTreesClassifier model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\extra_trees_iris.onnx
Python    
Python    Information about input tensors in ONNX:
Python    1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python    
Python    Information about output tensors in ONNX:
Python    1. Name: output_label, Data Type: tensor(int64), Shape: [None]
Python    2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
Python    
Python    Accuracy of ExtraTreesClassifier model in ONNX format: 1.


2.27.2. Код на MQL5 для работы с моделью ExtraTreesClassifier

//+------------------------------------------------------------------+
//|                                    Iris_ExtraTreesClassifier.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"
#resource "extra_trees_iris.onnx" as const uchar ExtModel[];

//+------------------------------------------------------------------+
//| Test IRIS dataset samples                                        |
//+------------------------------------------------------------------+
bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
  {
//--- check number of input samples
   ulong batch_size=input_data.Range(0);
   if(batch_size==0)
      return(false);
//--- prepare output array
   ArrayResize(model_classes_id,(int)batch_size);
//---
   float output_data[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } output_data_map[];
//--- check consistency
   bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
//---
   if(res)
     {
      //--- set input shape
      ulong input_shape[]= {batch_size,input_data.Range(1)};
      OnnxSetInputShape(model,0,input_shape);
      //--- set output shapeы
      ulong output_shape1[]= {batch_size};
      ulong output_shape2[]= {batch_size};
      OnnxSetOutputShape(model,0,output_shape1);
      OnnxSetOutputShape(model,1,output_shape2);
      //--- run the model
      res=OnnxRun(model,0,input_data,output_data,output_data_map);
      //--- postprocessing
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         for(uint n=0; n<output_data_map.Size(); n++)
           {
            int model_class_id=-1;
            int max_idx=-1;
            float max_value=-1;
            //--- copy to arrays
            ArrayCopy(output_keys,output_data_map[n].key);
            ArrayCopy(output_values,output_data_map[n].value);
            //ArrayPrint(output_keys);
            //ArrayPrint(output_values);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
            //--- store the result to the output array
            model_classes_id[n]=model_class_id;
            //Print("model_class_id=",model_class_id);
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Test all samples from IRIS dataset (150)                         |
//| Here we test all samples with batch=1, sample by sample          |
//+------------------------------------------------------------------+
bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("iris dataset not prepared");
      return(false);
     }
//--- show dataset
   for(int k=0; k<total_samples; k++)
     {
      //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }
//--- array for output classes
   int model_output_classes_id[];
//--- check all Iris dataset samples
   int correct_results=0;
   for(int k=0; k<total_samples; k++)
     {
      //--- input array
      float iris_sample_input_data[1][4];
      //--- prepare input data from kth iris sample dataset
      iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
      iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
      iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
      iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
      //--- run model
      bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
      //--- check result
      if(res)
        {
         if(model_output_classes_id[0]==iris_samples[k].class_id)
           {
            correct_results++;
           }
         else
           {
            PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
           }
        }
     }
   model_accuracy=1.0*correct_results/total_samples;
//---
   PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
//---
   return(true);
  }

//+------------------------------------------------------------------+
//| Here we test batch execution of the model                        |
//+------------------------------------------------------------------+
bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
  {
   model_accuracy=0;
//--- array for output classes
   int model_output_classes_id[];
   int correct_results=0;
   int total_results=0;
   bool res=false;

//--- run batch with 3 samples
   float input_data_batch3[3][4]=
     {
        {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
        {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
        {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
     };
   int correct_classes_batch3[3]= {0,1,2};
//--- run model
   res=TestSamples(model,input_data_batch3,model_output_classes_id);
   if(res)
     {
      //--- check result
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         //--- check result
         if(model_output_classes_id[j]==correct_classes_batch3[j])
            correct_results++;
         else
           {
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- run batch with 10 samples
   float input_data_batch10[10][4]=
     {
        {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
        {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
        {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
        {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
        {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
        {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
        {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
        {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
        {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
        {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
     };
//--- correct classes for all 10 samples in the batch
   int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};

//--- run model
   res=TestSamples(model,input_data_batch10,model_output_classes_id);
//--- check result
   if(res)
     {
      for(int j=0; j<ArraySize(model_output_classes_id); j++)
        {
         if(model_output_classes_id[j]==correct_classes_batch10[j])
            correct_results++;
         else
           {
            double f1=input_data_batch10[j][0];
            double f2=input_data_batch10[j][1];
            double f3=input_data_batch10[j][2];
            double f4=input_data_batch10[j][3];
            PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
           }
         total_results++;
        }
     }
   else
      return(false);

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="ExtraTreesClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_ExtraTreesClassifier (EURUSD,H1)   model:ExtraTreesClassifier   correct results: 100.00%
Iris_ExtraTreesClassifier (EURUSD,H1)   model=ExtraTreesClassifier all samples accuracy=1.000000
Iris_ExtraTreesClassifier (EURUSD,H1)   model=ExtraTreesClassifier batch test accuracy=1.000000

Точность экспортированной ONNX-модели на полном датасете iris dataset составляет 100%, что соответствует точности оригинала.


2.27.3. ONNX-представление модели ExtraTreesClassifier

Рис.41. ONNX-представление модели ExtraTrees Classifier в Netron

Рис.41. ONNX-представление модели ExtraTrees Classifier в Netron


2.28. Сравнение точности работы всех моделей

Теперь рассмотрим все модели вместе и сравним качество их работы. Сначала проведем сравнение средствами Python, затем загрузим и исполним сохраненные ONNX-модели в MetaTrader 5.

2.28.1. Код для расчета всех моделей и построения диаграммы сравнения точности

Скрипт рассчитывает 27 моделей классификации пакета Scikit-learn на полном датасете ирисов Фишера, экспортирует модели в ONNX-формат, исполняет их и производит сравнение точности оригинальных и ONNX-моделей.

# Iris_AllClassifiers.py
# The code demonstrates the process of training 27 Classifier models on the Iris dataset, exports them to ONNX format, and making predictions using the ONNX model. 
# It also evaluates the accuracy of both the original and the ONNX models.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.metrics import accuracy_score
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
import matplotlib.pyplot as plt
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create and train each classifier model
from sklearn.svm import SVC
svc_model = SVC()
svc_model.fit(X, y)

from sklearn.ensemble import RandomForestClassifier
random_forest_model = RandomForestClassifier(random_state=42)
random_forest_model.fit(X, y)

from sklearn.ensemble import GradientBoostingClassifier
gradient_boosting_model = GradientBoostingClassifier(random_state=42)
gradient_boosting_model.fit(X, y)

from sklearn.ensemble import AdaBoostClassifier
adaboost_model = AdaBoostClassifier(random_state=42)
adaboost_model.fit(X, y)

from sklearn.ensemble import BaggingClassifier
bagging_model = BaggingClassifier(random_state=42)
bagging_model.fit(X, y)

from sklearn.neighbors import KNeighborsClassifier
knn_model = KNeighborsClassifier()
knn_model.fit(X, y)

from sklearn.neighbors import RadiusNeighborsClassifier
radius_neighbors_model = RadiusNeighborsClassifier(radius=1.0)
radius_neighbors_model.fit(X, y)

from sklearn.tree import DecisionTreeClassifier
decision_tree_model = DecisionTreeClassifier(random_state=42)
decision_tree_model.fit(X, y)

from sklearn.linear_model import LogisticRegression
logistic_regression_model = LogisticRegression(max_iter=1000, random_state=42)
logistic_regression_model.fit(X, y)

from sklearn.linear_model import RidgeClassifier
ridge_classifier_model = RidgeClassifier(random_state=42)
ridge_classifier_model.fit(X, y)

from sklearn.linear_model import PassiveAggressiveClassifier
passive_aggressive_model = PassiveAggressiveClassifier(max_iter=1000, random_state=42)
passive_aggressive_model.fit(X, y)

from sklearn.linear_model import Perceptron
perceptron_model = Perceptron(max_iter=1000, random_state=42)
perceptron_model.fit(X, y)

from sklearn.linear_model import SGDClassifier
sgd_model = SGDClassifier(max_iter=1000, random_state=42)
sgd_model.fit(X, y)

from sklearn.naive_bayes import GaussianNB
gaussian_nb_model = GaussianNB()
gaussian_nb_model.fit(X, y)

from sklearn.naive_bayes import MultinomialNB
multinomial_nb_model = MultinomialNB()
multinomial_nb_model.fit(X, y)

from sklearn.naive_bayes import ComplementNB
complement_nb_model = ComplementNB()
complement_nb_model.fit(X, y)

from sklearn.naive_bayes import BernoulliNB
bernoulli_nb_model = BernoulliNB()
bernoulli_nb_model.fit(X, y)

from sklearn.naive_bayes import CategoricalNB
categorical_nb_model = CategoricalNB()
categorical_nb_model.fit(X, y)

from sklearn.tree import ExtraTreeClassifier
extra_tree_model = ExtraTreeClassifier(random_state=42)
extra_tree_model.fit(X, y)

from sklearn.ensemble import ExtraTreesClassifier
extra_trees_model = ExtraTreesClassifier(random_state=42)
extra_trees_model.fit(X, y)

from sklearn.svm import LinearSVC  # Import LinearSVC
linear_svc_model = LinearSVC(random_state=42)
linear_svc_model.fit(X, y)

from sklearn.svm import NuSVC
nu_svc_model = NuSVC()
nu_svc_model.fit(X, y)

from sklearn.linear_model import LogisticRegressionCV
logistic_regression_cv_model = LogisticRegressionCV(cv=5, max_iter=1000, random_state=42)
logistic_regression_cv_model.fit(X, y)

from sklearn.neural_network import MLPClassifier
mlp_model = MLPClassifier(max_iter=1000, random_state=42)
mlp_model.fit(X, y)

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda_model = LinearDiscriminantAnalysis()
lda_model.fit(X, y)

from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier
hist_gradient_boosting_model = HistGradientBoostingClassifier(random_state=42)
hist_gradient_boosting_model.fit(X, y)

from sklearn.linear_model import RidgeClassifierCV
ridge_classifier_cv_model = RidgeClassifierCV()
ridge_classifier_cv_model.fit(X, y)

# define a dictionary to store results
results = {}

# loop through the models
for model_name, classifier_model in [
    ('SVC Classifier', svc_model),
    ('Random Forest Classifier', random_forest_model),
    ('Gradient Boosting Classifier', gradient_boosting_model),
    ('AdaBoost Classifier', adaboost_model),
    ('Bagging Classifier', bagging_model),
    ('K-NN Classifier', knn_model),
    ('Radius Neighbors Classifier', radius_neighbors_model),
    ('Decision Tree Classifier', decision_tree_model),
    ('Logistic Regression Classifier', logistic_regression_model),
    ('Ridge Classifier', ridge_classifier_model),
    ('Ridge ClassifierCV', ridge_classifier_cv_model),
    ('Passive-Aggressive Classifier', passive_aggressive_model),
    ('Perceptron Classifier', perceptron_model),
    ('SGD Classifier', sgd_model),
    ('Gaussian Naive Bayes Classifier', gaussian_nb_model),
    ('Multinomial Naive Bayes Classifier', multinomial_nb_model),
    ('Complement Naive Bayes Classifier', complement_nb_model),
    ('Bernoulli Naive Bayes Classifier', bernoulli_nb_model),
    ('Categorical Naive Bayes Classifier', categorical_nb_model),
    ('Extra Tree Classifier', extra_tree_model),
    ('Extra Trees Classifier', extra_trees_model),
    ('LinearSVC Classifier', linear_svc_model),
    ('NuSVC Classifier', nu_svc_model),
    ('Logistic RegressionCV Classifier', logistic_regression_cv_model),
    ('MLP Classifier', mlp_model),
    ('Linear Discriminant Analysis Classifier', lda_model),
    ('Hist Gradient Boosting Classifier', hist_gradient_boosting_model)
]:
    # predict classes for the entire dataset
    y_pred = classifier_model.predict(X)

    # evaluate the model's accuracy
    accuracy = accuracy_score(y, y_pred)

    # define the input data type
    initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

    # export the model to ONNX format with float data type
    onnx_model = convert_sklearn(classifier_model, initial_types=initial_type, target_opset=12)

    # save the model to a file
    onnx_filename = data_path + f"{model_name.lower().replace(' ', '_')}_iris.onnx"
    with open(onnx_filename, "wb") as f:
        f.write(onnx_model.SerializeToString())

    # load the ONNX model and make predictions
    onnx_session = ort.InferenceSession(onnx_filename)
    input_name = onnx_session.get_inputs()[0].name
    output_name = onnx_session.get_outputs()[0].name

    # convert data to floating-point format (float32)
    X_float32 = X.astype(np.float32)

    # predict classes for the entire dataset using ONNX
    y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

    # evaluate the accuracy of the ONNX model
    accuracy_onnx = accuracy_score(y, y_pred_onnx)

    # store results
    results[model_name] = {
        'accuracy': accuracy,
        'accuracy_onnx': accuracy_onnx
    }

    # print the accuracy of the original model and the ONNX model
    #print(f"{model_name} - Original Accuracy: {accuracy}, ONNX Accuracy: {accuracy_onnx}")

# sort the models based on accuracy
sorted_results = dict(sorted(results.items(), key=lambda item: item[1]['accuracy'], reverse=True))

# print the sorted results
print("Sorted Results:")
for model_name, metrics in sorted_results.items():
    print(f"{model_name} - Original Accuracy: {metrics['accuracy']:.4f}, ONNX Accuracy: {metrics['accuracy_onnx']:.4f}")

# create comparison plots for sorted results
fig, ax = plt.subplots(figsize=(12, 8))

model_names = list(sorted_results.keys())
accuracies = [sorted_results[model_name]['accuracy'] for model_name in model_names]
accuracies_onnx = [sorted_results[model_name]['accuracy_onnx'] for model_name in model_names]

bar_width = 0.35
index = range(len(model_names))

bar1 = plt.bar(index, accuracies, bar_width, label='Model Accuracy')
bar2 = plt.bar([i + bar_width for i in index], accuracies_onnx, bar_width, label='ONNX Accuracy')

plt.xlabel('Models')
plt.ylabel('Accuracy')
plt.title('Comparison of Model and ONNX Accuracy (Sorted)')
plt.xticks([i + bar_width / 2 for i in index], model_names, rotation=90, ha='center')
plt.legend()

plt.tight_layout()
plt.show()

Результат:

Python  Sorted Results:
Python  Random Forest Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Bagging Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Decision Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Extra Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Extra Trees Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Hist Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Logistic RegressionCV Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  MLP Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  Linear Discriminant Analysis Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  SVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  Radius Neighbors Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  Logistic Regression Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  NuSVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  K-NN Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
Python  LinearSVC Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
Python  AdaBoost Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Passive-Aggressive Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Gaussian Naive Bayes Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Multinomial Naive Bayes Classifier - Original Accuracy: 0.9533, ONNX Accuracy: 0.9533
Python  SGD Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
Python  Categorical Naive Bayes Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
Python  Ridge Classifier - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
Python  Ridge ClassifierCV - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
Python  Complement Naive Bayes Classifier - Original Accuracy: 0.6667, ONNX Accuracy: 0.6667
Python  Perceptron Classifier - Original Accuracy: 0.6133, ONNX Accuracy: 0.6133
Python  Bernoulli Naive Bayes Classifier - Original Accuracy: 0.3333, ONNX Accuracy: 0.3333
Скрипт также выведет картинку со сводными результатами для всех 27 моделей.

Рис. 42. Сравнение точности работы 27 моделей классификации и их ONNX-версий для Iris dataset

Рис. 42. Сравнение точности работы 27 моделей классификации и их ONNX-версий для Iris dataset



Исходя из результатов оценки точности (accuracy) оригинальных моделей и их ONNX-версий, можно сделать следующие выводы:

Семь моделей показали идеальную точность (1.0000) как в оригинальной, так и в ONNX-версии модели. Эти модели включают:

  1. Random Forest Classifier
  2. Gradient Boosting Classifier
  3. Bagging Classifier
  4. Decision Tree Classifier
  5. Extra Tree Classifier
  6. Extra Trees Classifier
  7. Hist Gradient Boosting Classifier

ONNX-представления этих моделей также сохраняют высокую точность.

Три модели - Logistic RegressionCV Classifier, MLP Classifier и Linear Discriminant Analysis Classifier - достигли высокой точности в исходной и ONNX-версии с точностью 0.9800. Это модели, которые хорошо работают в обоих представлениях.

Множество моделей, включая SVC Classifier, Radius Neighbors Classifier, NuSVC Classifier, K-NN Classifier, LinearSVC Classifier, AdaBoost Classifier, Passive-Aggressive Classifier, Gaussian Naive Bayes Classifier, и Multinomial Naive Bayes Classifier, показали хорошую точность в исходной и ONNX-версии с точностью 0.9733, 0.9667 или 0.9600. Эти модели также сохраняют свою точность в ONNX-представлении.

Модели, такие как SGD Classifier, Categorical Naive Bayes Classifier, Ridge Classifier, Complement Naive Bayes Classifier, Perceptron Classifier и Bernoulli Naive Bayes Classifier, имеют более низкую точность. Они также хорошо справляются с сохранением точности в ONNX.

Все рассмотренные модели сохраняют свою точность при экспорте в ONNX-формат, что говорит о том, что ONNX предоставляет эффективный способ сохранения и восстановления моделей машинного обучения. Однако важно помнить, что качество экспортируемой модели может зависеть от конкретного алгоритма и параметров модели.


2.28.2. Код на MQL5 для исполнения всех ONNX-моделей

Скрипт исполняет все сохраненные скриптом из 2.28.1 ONNX-модели на полном наборе данных ирисов Фишера.

//+------------------------------------------------------------------+
//|                                          Iris_AllClassifiers.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "iris.mqh"

//+------------------------------------------------------------------+
//| TestSampleSequenceMapOutput                                      |
//+------------------------------------------------------------------+
bool TestSampleSequenceMapOutput(long model,sIRISsample &iris_sample, int &model_class_id)
  {
//---
   model_class_id=-1;
   float input_data[1][4];
   for(int k=0; k<4; k++)
     {
      input_data[0][k]=(float)iris_sample.features[k];
     }
//---
   float out1[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } out2[];
//---
   bool res=ArrayResize(out1,input_data.Range(0))==input_data.Range(0);
//---
   if(res)
     {
      ulong input_shape[]= { input_data.Range(0), input_data.Range(1) };
      ulong output_shape[]= { input_data.Range(0) };
      //---
      OnnxSetInputShape(model,0,input_shape);
      OnnxSetOutputShape(model,0,output_shape);
      //---
      res=OnnxRun(model,0,input_data,out1,out2);
      //---
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         model_class_id=-1;
         int max_idx=-1;
         float max_value=-1;
         //---
         for(uint n=0; n<out2.Size(); n++)
           {
            //--- copy to arrays
            ArrayCopy(output_keys,out2[n].key);
            ArrayCopy(output_values,out2[n].value);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| TestSampleTensorOutput                                           |
//+------------------------------------------------------------------+
bool TestSampleTensorOutput(long model,sIRISsample &iris_sample, int &model_class_id)
  {
//---
   model_class_id=-1;
   float input_data[1][4];
   for(int k=0; k<4; k++)
     {
      input_data[0][k]=(float)iris_sample.features[k];
     }
//---
   ulong input_shape[]= { 1, 4};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[1];
   float output2[1,3];
//---
   ulong output_shape[]= {1};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {1,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,0,input_data,output1,output2);
//--- result for these models in output1[0];
   if(res)
      model_class_id=output1[0];
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("error in loading iris dataset from iris.csv");
      return(false);
     }
   /*for(int k=0; k<total_samples; k++)
     {
      PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }*/
//----

   string iris_models[]=
     {
      "random_forest_classifier_iris.onnx",
      "gradient_boosting_classifier_iris.onnx",
      "bagging_classifier_iris.onnx",
      "decision_tree_classifier_iris.onnx",
      "extra_tree_classifier_iris.onnx",
      "extra_trees_classifier_iris.onnx",
      "hist_gradient_boosting_classifier_iris.onnx",
      "logistic_regressioncv_classifier_iris.onnx",
      "mlp_classifier_iris.onnx",
      "linear_discriminant_analysis_classifier_iris.onnx",
      "svc_classifier_iris.onnx",
      "radius_neighbors_classifier_iris.onnx",
      "logistic_regression_classifier_iris.onnx",
      "nusvc_classifier_iris.onnx",
      "k-nn_classifier_iris.onnx",
      "linearsvc_classifier_iris.onnx",
      "adaboost_classifier_iris.onnx",
      "passive-aggressive_classifier_iris.onnx",
      "gaussian_naive_bayes_classifier_iris.onnx",
      "multinomial_naive_bayes_classifier_iris.onnx",
      "sgd_classifier_iris.onnx",
      "categorical_naive_bayes_classifier_iris.onnx",
      "ridge_classifier_iris.onnx",
      "ridge_classifiercv_iris.onnx",
      "complement_naive_bayes_classifier_iris.onnx",
      "perceptron_classifier_iris.onnx",
      "bernoulli_naive_bayes_classifier_iris.onnx"
     };

//--- test all iris dataset sample by sample
   for(int i=0; i<ArraySize(iris_models); i++)
     {
      //--- load ONNX-model
      string model_name="IRIS_models\\"+iris_models[i];
      //---
      long model=OnnxCreate(model_name,0);
      if(model==INVALID_HANDLE)
        {
         PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
        }
      else
        {
         //--- check all samples
         int correct_results=0;
         for(int k=0; k<total_samples; k++)
           {
            int model_class_id=-1;
            //--- select data output processor
            string current_model=iris_models[i];
            if(current_model=="svc_classifier_iris.onnx" || current_model=="linearsvc_classifier_iris.onnx" || current_model=="nusvc_classifier_iris.onnx" || current_model=="ridge_classifier_iris.onnx" || current_model=="ridge_classifiercv_iris.onnx" || current_model=="radius_neighbors_classifier_iris.onnx")
              {
               TestSampleTensorOutput(model,iris_samples[k],model_class_id);
              }
            else
              {
               TestSampleSequenceMapOutput(model,iris_samples[k],model_class_id);
              }
            //---
            if(model_class_id==iris_samples[k].class_id)
              {
               correct_results++;
               //PrintFormat("sample=%d OK [class=%d]",iris_samples[k].sample_id,model_class_id);
              }
            else
              {
               //PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_class_id,iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
              }
           }
         PrintFormat("%d model:%s   accuracy: %.4f",i+1,model_name,1.0*correct_results/total_samples);
         //--- release model
         OnnxRelease(model);
        }
      //---
     }
   return(0);
  }
//+------------------------------------------------------------------+

Результат:

Iris_AllClassifiers (EURUSD,H1) 1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) 16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) 17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533
Iris_AllClassifiers (EURUSD,H1) 21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) 22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) 23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) 24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) ONNX: Removing initializer 'class_log_prior'. It is not used by any node and should be removed from the model.
Iris_AllClassifiers (EURUSD,H1) 25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667
Iris_AllClassifiers (EURUSD,H1) 26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133
Iris_AllClassifiers (EURUSD,H1) 27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333

Сравнивая с результатами скрипта 2.28.1.1:

Python  Random Forest Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000

Python  Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000

Python  Bagging Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000

Python  Decision Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000

Python  Extra Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000

Python  Extra Trees Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000

Python  Hist Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000

Python  Logistic RegressionCV Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800

Python  MLP Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800

Python  Linear Discriminant Analysis Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800

Python  SVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733

Python  Radius Neighbors Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733

Python  Logistic Regression Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733

Python  NuSVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733

Python  K-NN Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667

Python  LinearSVC Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667

Python  AdaBoost Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600

Python  Passive-Aggressive Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600

Python  Gaussian Naive Bayes Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600

Python  Multinomial Naive Bayes Classifier - Original Accuracy: 0.9533, ONNX Accuracy: 0.9533
20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533

Python  SGD Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333

Python  Categorical Naive Bayes Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333

Python  Ridge Classifier - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533

Python  Ridge ClassifierCV - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533

Python  Complement Naive Bayes Classifier - Original Accuracy: 0.6667, ONNX Accuracy: 0.6667
25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667

Python  Perceptron Classifier - Original Accuracy: 0.6133, ONNX Accuracy: 0.6133
26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133

Python  Bernoulli Naive Bayes Classifier - Original Accuracy: 0.3333, ONNX Accuracy: 0.3333
27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333

отметим, что исполнение всех сохраненных ONNX-моделей в MQL5 полностью соответствует результатам 2.28.1.

Таким образом, рассмотренные нами модели, конвертированные в ONNX-формат, сохранили точность работы.

Следует отметить идеальную точность классификации (accuracy=1.0) для iris dataset у 7 моделей:

  1. Random Forest Classifier;
  2. Gradient Boosting Classifier;
  3. Bagging Classifier;
  4. Decision Tree Classifier;
  5. Extra Tree Classifier;
  6. Extra Trees Classifier;
  7. Histogram Gradient Boosting Classifier.

Остальные 20 моделей допускали ошибки классификации.

Если раскомментировать строку 208, то скрипт также выведет образцы iris dataset, которые были неверно классифицированы каждой из моделей:

Iris_AllClassifiers (EURUSD,H1) 1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) 9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) 13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) 15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) 22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) ONNX: Removing initializer 'class_log_prior'. It is not used by any node and should be removed from the model.
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=54 FAILED [class=2, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=55 FAILED [class=2, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=56 FAILED [class=2, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=58 FAILED [class=2, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=59 FAILED [class=2, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=60 FAILED [class=2, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=61 FAILED [class=2, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=63 FAILED [class=2, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=64 FAILED [class=2, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=68 FAILED [class=2, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=70 FAILED [class=2, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=72 FAILED [class=2, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=74 FAILED [class=2, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=75 FAILED [class=2, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=77 FAILED [class=2, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=81 FAILED [class=2, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=82 FAILED [class=2, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=83 FAILED [class=2, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=88 FAILED [class=2, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=90 FAILED [class=2, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=91 FAILED [class=2, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=93 FAILED [class=2, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=94 FAILED [class=2, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=95 FAILED [class=2, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=96 FAILED [class=2, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=97 FAILED [class=2, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=98 FAILED [class=2, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=100 FAILED [class=2, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) 25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=2 FAILED [class=1, true class=0] features=(4.90,3.00,1.40,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=9 FAILED [class=1, true class=0] features=(4.40,2.90,1.40,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=10 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=13 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=21 FAILED [class=1, true class=0] features=(5.40,3.40,1.70,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=26 FAILED [class=1, true class=0] features=(5.00,3.00,1.60,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=31 FAILED [class=1, true class=0] features=(4.80,3.10,1.60,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=35 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=42 FAILED [class=1, true class=0] features=(4.50,2.30,1.30,0.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=46 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=103 FAILED [class=1, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=104 FAILED [class=1, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=105 FAILED [class=1, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=106 FAILED [class=1, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=108 FAILED [class=1, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=110 FAILED [class=1, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=111 FAILED [class=1, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=112 FAILED [class=1, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=113 FAILED [class=1, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=114 FAILED [class=1, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=116 FAILED [class=1, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=117 FAILED [class=1, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=118 FAILED [class=1, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=119 FAILED [class=1, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=121 FAILED [class=1, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=123 FAILED [class=1, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=125 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=126 FAILED [class=1, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=129 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=131 FAILED [class=1, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=133 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=136 FAILED [class=1, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=137 FAILED [class=1, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=138 FAILED [class=1, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=140 FAILED [class=1, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=141 FAILED [class=1, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=142 FAILED [class=1, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=144 FAILED [class=1, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=145 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=146 FAILED [class=1, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=147 FAILED [class=1, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=148 FAILED [class=1, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=149 FAILED [class=1, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=150 FAILED [class=1, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_AllClassifiers (EURUSD,H1) 26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=51 FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=52 FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=0, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=54 FAILED [class=0, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=55 FAILED [class=0, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=56 FAILED [class=0, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=57 FAILED [class=0, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=58 FAILED [class=0, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=59 FAILED [class=0, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=60 FAILED [class=0, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=61 FAILED [class=0, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=62 FAILED [class=0, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=63 FAILED [class=0, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=64 FAILED [class=0, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=66 FAILED [class=0, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=67 FAILED [class=0, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=68 FAILED [class=0, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=0, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=70 FAILED [class=0, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=0, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=72 FAILED [class=0, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=74 FAILED [class=0, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=75 FAILED [class=0, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=76 FAILED [class=0, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=77 FAILED [class=0, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=0, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=79 FAILED [class=0, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=81 FAILED [class=0, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=82 FAILED [class=0, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=83 FAILED [class=0, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=0, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=85 FAILED [class=0, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=87 FAILED [class=0, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=88 FAILED [class=0, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=89 FAILED [class=0, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=90 FAILED [class=0, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=91 FAILED [class=0, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=92 FAILED [class=0, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=93 FAILED [class=0, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=94 FAILED [class=0, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=95 FAILED [class=0, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=96 FAILED [class=0, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=97 FAILED [class=0, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=98 FAILED [class=0, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=100 FAILED [class=0, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=101 FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=102 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=103 FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=104 FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=105 FAILED [class=0, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=106 FAILED [class=0, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=0, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=108 FAILED [class=0, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=109 FAILED [class=0, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=110 FAILED [class=0, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=111 FAILED [class=0, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=112 FAILED [class=0, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=113 FAILED [class=0, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=114 FAILED [class=0, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=115 FAILED [class=0, true class=2] features=(5.80,2.80,5.10,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=116 FAILED [class=0, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=117 FAILED [class=0, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=118 FAILED [class=0, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=119 FAILED [class=0, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=120 FAILED [class=0, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=121 FAILED [class=0, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=122 FAILED [class=0, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=123 FAILED [class=0, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=124 FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=125 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=126 FAILED [class=0, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=127 FAILED [class=0, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=128 FAILED [class=0, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=129 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=130 FAILED [class=0, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=131 FAILED [class=0, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=132 FAILED [class=0, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=133 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=0, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=135 FAILED [class=0, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=136 FAILED [class=0, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=137 FAILED [class=0, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=138 FAILED [class=0, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=139 FAILED [class=0, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=140 FAILED [class=0, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=141 FAILED [class=0, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=142 FAILED [class=0, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=143 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=144 FAILED [class=0, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=145 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=146 FAILED [class=0, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=147 FAILED [class=0, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=148 FAILED [class=0, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=149 FAILED [class=0, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=150 FAILED [class=0, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_AllClassifiers (EURUSD,H1) 27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333


2.29. Модели классификации Sckikit-Learn, которые не удалось сконвертировать в ONNX

Некоторые модели классификации не получилось сконвертировать в формат ONNX из-за ошибок в работе convert_sklearn.


2.29.1. DummyClassifier

DummyClassifier - это классификатор в библиотеке Scikit-learn, который используется в качестве простой базовой модели для задачи классификации. Он предназначен для тестирования и оценки производительности других более сложных моделей классификации.

Принцип работы:

DummyClassifier работает очень просто - он делает случайные или наивные предсказания, не учитывая входные данные. Возможные стратегии (стратегия выбирается с помощью параметра strategy):

  1. "most_frequent" (Самый часто встречающийся класс): Эта стратегия всегда предсказывает класс, который встречается наиболее часто в обучающем наборе данных. Это может быть полезно в ситуациях, когда классы несбалансированы, и доминирующий класс нужно предсказать.
  2. "stratified" (Стратифицированный выбор): Эта стратегия пытается сделать предсказания, которые соответствуют распределению классов в обучающем наборе данных. Она использует случайное угадывание, но учитывает пропорции классов.
  3. "uniform" (Равномерное распределение): Эта стратегия делает случайные предсказания с равной вероятностью для каждого класса. Она полезна, если классы сбалансированы, и вы хотите проверить, как ваша модель справится в среднем.

Возможности:

Ограничения:

В целом, DummyClassifier - это полезный инструмент для начального тестирования и оценки моделей классификации, но его использование ограничено в сложных задачах и не может заменить более продвинутые алгоритмы классификации.

2.29.1.1. Код создания модели DummyClassifier

# Iris_DummyClassifier.py
# The code demonstrates the process of training DummyClassifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a DummyClassifier model with the strategy "most_frequent"
dummy_classifier = DummyClassifier(strategy="most_frequent")

# train the model on the entire dataset
dummy_classifier.fit(X, y)

# predict classes for the entire dataset
y_pred = dummy_classifier.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of DummyClassifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(dummy_classifier, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "dummy_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print model path
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of DummyClassifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of DummyClassifier model: 0.3333333333333333
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       0.33      1.00      0.50        50
Python               1       0.00      0.00      0.00        50
Python               2       0.00      0.00      0.00        50
Python    
Python        accuracy                           0.33       150
Python       macro avg       0.11      0.33      0.17       150
Python    weighted avg       0.11      0.33      0.17       150
Python   

Модель была построена и отработала в Sckit-learn, однако при конвертации в ONNX произошли ошибки.

Во вкладке Errors выводятся сообщения об ошибках конвертации модели в формат ONNX:

    onnx_model = convert_sklearn(dummy_classifier, initial_types=initial_type, target_opset=12)    Iris_DummyClassifier.py    44    1
    onnx_model = convert_topology(    convert.py    208    1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types()    _topology.py    1163    1
    raise MissingShapeCalculator(    _topology.py    629    1
skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.dummy.DummyClassifier'>'.    _topology.py    629    1
It usually means the pipeline being converted contains a    _topology.py    629    1
transformer or a predictor with no corresponding converter    _topology.py    629    1
implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
in another library, you need to register    _topology.py    629    1
the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
update_registered_converter). If the model is not yet covered    _topology.py    629    1
by sklearn-onnx, you may raise an issue to    _topology.py    629    1
https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
to get the converter implemented or even contribute to the    _topology.py    629    1
project. If the model is a custom model, a new converter must    _topology.py    629    1
be implemented. Examples can be found in the gallery.    _topology.py    629    1
Iris_DummyClassifier.py finished in 2071 ms        19    1

Таким образом, модель DummyClassifier не удалось сконвертировать в ONNX.


2.29.2. GaussianProcessClassifier

GaussianProcessClassifier - это классификатор, который использует гауссовский процесс для задачи классификации. Он относится к семейству моделей, использующих гауссовские процессы, и может быть полезным в задачах, где необходима вероятностная оценка классов.

Принцип работы:

  1. GaussianProcessClassifier использует гауссовский процесс для моделирования отображения из пространства признаков в пространство вероятностных оценок классов.
  2. Он строит вероятностную модель для каждого класса, оценивая вероятность принадлежности точки к каждому классу.
  3. При классификации он выбирает класс с наивысшей вероятностью для данной точки.

Возможности:

Ограничения:

GaussianProcessClassifier полезен в задачах, где важна вероятностная оценка классов и когда можно справиться с вычислительными затратами. В противном случае, для задач классификации на больших данных или с простыми структурами данных могут быть более подходящие алгоритмы классификации.

2.29.2.1. Код создания модели GaussianProcessClassifier

# Iris_GaussianProcessClassifier.py
# The code demonstrates the process of training Iris_GaussianProcess Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.gaussian_process import GaussianProcessClassifier
from sklearn.gaussian_process.kernels import RBF
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a GaussianProcessClassifier model with an RBF kernel
kernel = 1.0 * RBF(1.0)
gpc_model = GaussianProcessClassifier(kernel=kernel)

# train the model on the entire dataset
gpc_model.fit(X, y)

# predict classes for the entire dataset
y_pred = gpc_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of GaussianProcessClassifier model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(gpc_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "gpc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print the path to the model
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of GaussianProcessClassifier model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of GaussianProcessClassifier model: 0.9866666666666667
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.98      0.98      0.98        50
Python               2       0.98      0.98      0.98        50
Python    
Python        accuracy                           0.99       150
Python       macro avg       0.99      0.99      0.99       150
Python    weighted avg       0.99      0.99      0.99       150
Python   

Ошибки во вкладке Errors:

    onnx_model = convert_sklearn(gpc_model, initial_types=initial_type, target_opset=12)    Iris_GaussianProcessClassifier.py    46    1
    onnx_model = convert_topology(    convert.py    208    1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_converter(operator, container, verbose=verbose)    _topology.py    1349    1
    conv(self.scopes[0], operator, container)    _topology.py    1132    1
    return self._fct(*args)    _registration.py    27    1
    raise NotImplementedError("Only binary classification is iplemented.")    gaussian_process.py    247    1
NotImplementedError: Only binary classification is iplemented.    gaussian_process.py    247    1
Iris_GaussianProcessClassifier.py finished in 4004 ms        9    1

Таким образом, модель GaussianProcessClassifier также не удалось сконвертировать в ONNX.


2.29.3. LabelPropagation Classifier

LabelPropagation - это метод полу-надзорного (semi-supervised) обучения, используемый для задач классификации. Основная идея этого метода заключается в распространении меток (классов) от размеченных объектов на неразмеченные объекты в графовой структуре данных.

Процесс работы LabelPropagation:

  1. Начинается с построения графа, где узлы представляют объекты данных, а рёбра между узлами отражают сходство или близость между объектами.
  2. Начальное размещение меток: Размеченные объекты получают свои метки, а неразмеченные объекты начинают с некоторой неопределенной метки.
  3. Метки распространяются по графу: Метки с размеченных объектов распространяются на неразмеченные объекты с учетом сходства между объектами. Это сходство может определяться различными способами, например, на основе ближайших соседей в графе.
  4. Итеративный процесс: Метки могут изменяться на протяжении нескольких итераций, где каждая итерация обновляет метки на неразмеченных объектах на основе текущих меток и сходства между объектами.
  5. Стабилизация: Процесс продолжается до тех пор, пока метки не стабилизируются или не выполняется определенный критерий останова.

Достоинства LabelPropagation:

Ограничения LabelPropagation:

LabelPropagation - это мощный метод, но он требует осторожного настройки параметров и анализа графовой структуры данных для достижения хороших результатов.

2.29.3.1. Код создания модели LabelPropagationClassifier

# Iris_LabelPropagationClassifier.py

# The code demonstrates the process of training LabelPropagation Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.semi_supervised import LabelPropagation
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
from sys import argv

# define the path for saving the model
data_path = argv[0]
last_index = data_path.rfind("\\") + 1
data_path = data_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a LabelPropagation model
lp_model = LabelPropagation()

# train the model on the entire dataset
lp_model.fit(X, y)

# predict classes for the entire dataset
y_pred = lp_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LabelPropagation model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(lp_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "lp_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print the path to the model
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LabelPropagation model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of LabelPropagation model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python   

Модель построилась, однако при конвертации в ONNX-формат возникли ошибки.

Ошибки во вкладке Errors:

    onnx_model = convert_sklearn(lp_model, initial_types=initial_type, target_opset=12)    Iris_LabelPropagation.py    44    1
    onnx_model = convert_topology(    convert.py    208    1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types()    _topology.py    1163    1
    raise MissingShapeCalculator(    _topology.py    629    1
skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.semi_supervised._label_propagation.LabelPropagation'>'.    _topology.py    629    1
It usually means the pipeline being converted contains a    _topology.py    629    1
transformer or a predictor with no corresponding converter    _topology.py    629    1
implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
in another library, you need to register    _topology.py    629    1
the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
update_registered_converter). If the model is not yet covered    _topology.py    629    1
by sklearn-onnx, you may raise an issue to    _topology.py    629    1
https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
to get the converter implemented or even contribute to the    _topology.py    629    1
project. If the model is a custom model, a new converter must    _topology.py    629    1
be implemented. Examples can be found in the gallery.    _topology.py    629    1
Iris_LabelPropagation.py finished in 2064 ms        19    1

Таким образом, модель LabelPropagation Classifier также не удалось сконвертировать в ONNX.


2.29.4. LabelSpreading Classifier

LabelSpreading - это метод полу-надзорного (semi-supervised) обучения, используемый для задач классификации. Он основан на идее распространения меток (классов) от размеченных объектов на неразмеченные объекты в графовой структуре данных, похожей на LabelPropagation. Однако LabelSpreading включает в себя дополнительную стабилизацию и регуляризацию процесса распространения меток.

Процесс работы LabelSpreading:

  1. Начинается с построения графа, где узлы представляют объекты данных, а рёбра между узлами отражают сходство или близость между объектами.
  2. Начальное размещение меток: Размеченные объекты получают свои метки, а неразмеченные объекты начинают с некоторой неопределенной метки.
  3. Метки распространяются по графу: Метки с размеченных объектов распространяются на неразмеченные объекты с учетом сходства между объектами.
  4. Регуляризация и стабилизация: LabelSpreading включает в себя регуляризацию, которая помогает стабилизировать процесс распространения меток и уменьшить переобучение. Это достигается путем учета не только сходства между объектами, но и различия между метками соседних объектов.
  5. Итеративный процесс: Метки могут изменяться на протяжении нескольких итераций, где каждая итерация обновляет метки на неразмеченных объектах на основе текущих меток и регуляризации.
  6. Стабилизация: Процесс продолжается до тех пор, пока метки не стабилизируются или не выполняется определенный критерий останова.

Достоинства LabelSpreading:

Ограничения LabelSpreading:

LabelSpreading - это метод, который также требует осторожной настройки и может быть мощным средством для использования неразмеченных данных в задачах классификации.

2.29.4.1. Код создания модели LabelSpreadingClassifier

# Iris_LabelSpreadingClassifier.py
# The code demonstrates the process of training LabelSpreading Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.


# import necessary libraries
from sklearn import datasets
from sklearn.semi_supervised import LabelSpreading
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
import sys

# get the script path
script_path = sys.argv[0]
last_index = script_path.rfind("\\") + 1
data_path = script_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a LabelSpreading model
ls_model = LabelSpreading()

# train the model on the entire dataset
ls_model.fit(X, y)

# predict classes for the entire dataset
y_pred = ls_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LabelSpreading model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(ls_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "ls_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print the path to the model
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LabelSpreading model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of LabelSpreading model: 1.0
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       1.00      1.00      1.00        50
Python               2       1.00      1.00      1.00        50
Python    
Python        accuracy                           1.00       150
Python       macro avg       1.00      1.00      1.00       150
Python    weighted avg       1.00      1.00      1.00       150
Python   

Во вкладке Errors выводится информация об ошибках конвертации в ONNX.

    onnx_model = convert_sklearn(ls_model, initial_types=initial_type, target_opset=12)    Iris_LabelSpreading.py    45    1
    onnx_model = convert_topology(    convert.py    208    1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types()    _topology.py    1163    1
    raise MissingShapeCalculator(    _topology.py    629    1
skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.semi_supervised._label_propagation.LabelSpreading'>'.    _topology.py    629    1
It usually means the pipeline being converted contains a    _topology.py    629    1
transformer or a predictor with no corresponding converter    _topology.py    629    1
implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
in another library, you need to register    _topology.py    629    1
the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
update_registered_converter). If the model is not yet covered    _topology.py    629    1
by sklearn-onnx, you may raise an issue to    _topology.py    629    1
https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
to get the converter implemented or even contribute to the    _topology.py    629    1
project. If the model is a custom model, a new converter must    _topology.py    629    1
be implemented. Examples can be found in the gallery.    _topology.py    629    1
Iris_LabelSpreading.py finished in 2032 ms        19    1

Модель LabelPropagation Classifier также не удалось сконвертировать в ONNX.


2.29.5. NearestCentroid Classifier

NearestCentroid - это метод классификации, который основан на идее определения центроида для каждого класса и классификации объектов на основе ближайшего центроида. Этот метод подходит для задач с несколькими классами и хорошо работает на наборах данных с линейно разделимыми классами.

Процесс работы NearestCentroid:

  1. Для каждого класса вычисляется центроид, который представляет собой среднее значение признаков всех объектов, принадлежащих этому классу. Это можно сделать путем вычисления среднего значения по каждому признаку для объектов данного класса.
  2. При классификации нового объекта вычисляется его ближайший центроид среди центроидов всех классов.
  3. Новый объект относится к классу, центроид которого оказался ближе всего к нему в метрическом пространстве.

Достоинства NearestCentroid:

Ограничения NearestCentroid:

NearestCentroid - это простой и интерпретируемый метод классификации, который может быть полезным в определенных сценариях, особенно при линейной разделимости классов и отсутствии выбросов в данных.

2.29.5.1.Код создания модели NearestCentroid

# Iris_NearestCentroidClassifier.py
# The code demonstrates the process of training NearestCentroid Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.neighbors import NearestCentroid
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
import sys

# get the script path
script_path = sys.argv[0]
last_index = script_path.rfind("\\") + 1
data_path = script_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a NearestCentroid model
nc_model = NearestCentroid()

# train the model on the entire dataset
nc_model.fit(X, y)

# predict classes for the entire dataset
y_pred = nc_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of NearestCentroid model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(nc_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "nc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print the path to the model
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of NearestCentroid model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of NearestCentroid model: 0.9266666666666666
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.87      0.92      0.89        50
Python               2       0.91      0.86      0.89        50
Python    
Python        accuracy                           0.93       150
Python       macro avg       0.93      0.93      0.93       150
Python    weighted avg       0.93      0.93      0.93       150
Python   

Ошибки во вкладке Errors:

    onnx_model = convert_sklearn(nc_model, initial_types=initial_type, target_opset=12)    Iris_NearestCentroid.py    45    1
    onnx_model = convert_topology(    convert.py    208    1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types()    _topology.py    1163    1
    raise MissingShapeCalculator(    _topology.py    629    1
skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.neighbors._nearest_centroid.NearestCentroid'>'.    _topology.py    629    1
It usually means the pipeline being converted contains a    _topology.py    629    1
transformer or a predictor with no corresponding converter    _topology.py    629    1
implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
in another library, you need to register    _topology.py    629    1
the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
update_registered_converter). If the model is not yet covered    _topology.py    629    1
by sklearn-onnx, you may raise an issue to    _topology.py    629    1
https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
to get the converter implemented or even contribute to the    _topology.py    629    1
project. If the model is a custom model, a new converter must    _topology.py    629    1
be implemented. Examples can be found in the gallery.    _topology.py    629    1
Iris_NearestCentroid.py finished in 2131 ms        19    1
Модель NearestCentroid Classifier также не удалось сконвертировать в ONNX.


2.29.6. Quadratic Discriminant Analysis Classifier

Quadratic Discriminant Analysis (QDA) - это метод классификации, который использует вероятностную модель для разделения данных на классы. Он является обобщением линейного дискриминантного анализа (LDA) и позволяет учитывать ковариацию признаков внутри каждого класса. Основная идея QDA заключается в том, чтобы моделировать распределение признаков для каждого класса и затем использовать это распределение для классификации новых объектов.

Процесс работы QDA:

  1. Для каждого класса вычисляются параметры распределения, такие как среднее значение и ковариационная матрица признаков. Эти параметры оцениваются на основе обучающих данных для каждого класса.
  2. По полученным параметрам можно вычислить плотности вероятности для каждого класса с использованием многомерного нормального распределения (или квадратичной функции распределения).
  3. При классификации нового объекта вычисляются значения плотностей вероятности для каждого класса, и объект относится к классу с наибольшей вероятностью.

Достоинства Quadratic Discriminant Analysis (QDA):

Ограничения Quadratic Discriminant Analysis (QDA):

Quadratic Discriminant Analysis (QDA) - это мощный метод классификации, который подходит для различных типов данных и может учесть ковариацию признаков внутри классов. Однако он также имеет ограничения, которые следует учитывать при его использовании.

2.29.6.1.Код создания модели Quadratic Discriminant Analysis

# Iris_QuadraticDiscriminantAnalysisClassifier.py
# The code demonstrates the process of training Quadratic Discriminant Analysis Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
# It also evaluates the accuracy of both the original model and the ONNX model.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# import necessary libraries
from sklearn import datasets
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
from sklearn.metrics import accuracy_score, classification_report
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
import sys

# get the script path
script_path = sys.argv[0]
last_index = script_path.rfind("\\") + 1
data_path = script_path[0:last_index]

# load the Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target

# create a QuadraticDiscriminantAnalysis model
qda_model = QuadraticDiscriminantAnalysis()

# train the model on the entire dataset
qda_model.fit(X, y)

# predict classes for the entire dataset
y_pred = qda_model.predict(X)

# evaluate the model's accuracy
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Quadratic Discriminant Analysis model:", accuracy)

# display the classification report
print("\nClassification Report:\n", classification_report(y, y_pred))

# define the input data type
initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]

# export the model to ONNX format with float data type
onnx_model = convert_sklearn(qda_model, initial_types=initial_type, target_opset=12)

# save the model to a file
onnx_filename = data_path + "qda_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# print the path to the model
print(f"Model saved to {onnx_filename}")

# load the ONNX model and make predictions
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# display information about input tensors in ONNX
print("\nInformation about input tensors in ONNX:")
for i, input_tensor in enumerate(onnx_session.get_inputs()):
    print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

# display information about output tensors in ONNX
print("\nInformation about output tensors in ONNX:")
for i, output_tensor in enumerate(onnx_session.get_outputs()):
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# convert data to floating-point format (float32)
X_float32 = X.astype(np.float32)

# predict classes for the entire dataset using ONNX
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# evaluate the accuracy of the ONNX model
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Quadratic Discriminant Analysis model in ONNX format:", accuracy_onnx)

Результат:

Python    Accuracy of Quadratic Discriminant Analysis model: 0.98
Python    
Python    Classification Report:
Python                   precision    recall  f1-score   support
Python    
Python               0       1.00      1.00      1.00        50
Python               1       0.98      0.96      0.97        50
Python               2       0.96      0.98      0.97        50
Python    
Python        accuracy                           0.98       150
Python       macro avg       0.98      0.98      0.98       150
Python    weighted avg       0.98      0.98      0.98       150
Python    
Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx

На этот раз модель сохранилась в ONNX, однако при ее выполнении во вкладке Errors выводятся ошибки:

    onnx_session = ort.InferenceSession(onnx_filename)    Iris_QuadraticDiscriminantAnalysisClassifier.py    55    1
    self._create_inference_session(providers, provider_options, disabled_optimizers)    onnxruntime_inference_collection.py    383    1
    sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model)    onnxruntime_inference_collection.py    424    1
onnxruntime.capi.onnxruntime_pybind11_state.InvalidGraph: [ONNXRuntimeError] : 10 : INVALID_GRAPH : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx failed:This is an invalid mode    onnxruntime_inference_collection.py    424    1
Iris_QuadraticDiscriminantAnalysisClassifier.py finished in 2063 ms        5    1

Конвертация модели Quadratic Discriminant Analysis Classifier в ONNX произошла с ошибкой.


Выводы

На примере набора данных Iris было проведено исследование 33 моделей классификации, доступных в библиотеке Scikit-learn версии 1.2.2.

1. Из этого набора 6 моделей столкнулись с трудностями при конвертации в ONNX-формат:

  1. DummyClassifier: Dummy Classifier (Фиктивный классификатор);
  2. GaussianProcessClassifier:  Gaussian Process Classifier (Классификатор гауссовых процессов);
  3. LabelPropagation : Label Propagation Classifier (Классификатор распространения меток);
  4. LabelSpreading : Label Spreading Classifier (Классификатор распространения меток);
  5. NearestCentroid: Nearest Centroid Classifier (Классификатор ближайшего центроида);
  6. QuadraticDiscriminantAnalysis: Quadratic Discriminant Analysis Classifier (Классификатор квадратичного дискриминантного анализа).

По всей видимости, эти модели более сложны с точки зрения их структуры и/или логики, и их адаптация для выполнения в формате ONNX может потребовать дополнительных усилий. Также возможно, что они используют специфические структуры данных или алгоритмы, которые не полностью поддерживаются или не подходят для формата ONNX.

2. Остальные 27 моделей были успешно конвертированы в формат ONNX и продемонстрировали сохранение своей точности, что подчеркивает эффективность ONNX как средства для сохранения и восстановления моделей машинного обучения. Это дает возможность легко переносить модели между различными средами и использовать их в различных приложениях, сохраняя при этом качество их работы.

Полный список моделей, успешно сконвертированных в ONNX-формат:

  1. SVC: Support Vector Classifier (Классификатор опорных векторов);
  2. LinearSVC: Linear Support Vector Classifier (Линейный классификатор опорных векторов);
  3. NuSVC: Nu Support Vector Classifier (Классификатор опорных векторов с параметром Nu);
  4. AdaBoostClassifier: Adaptive Boosting Classifier (Классификатор на основе адаптивного усиления);
  5. BaggingClassifier: Bootstrap Aggregating Classifier (Классификатор на основе бутстреп-агрегации);
  6. BernoulliNB: Bernoulli Naive Bayes (Наивный байесовский классификатор с предположением о распределении Бернулли);
  7. CategoricalNB: Categorical Naive Bayes (Наивный байесовский классификатор для категориальных данных);
  8. ComplementNB: Complement Naive Bayes (Наивный байесовский классификатор Complement);
  9. DecisionTreeClassifier: Decision Tree Classifier (Классификатор на основе дерева решений);
  10. ExtraTreeClassifier: Extra Tree Classifier (Классификатор на основе экстра-дерева);
  11. ExtraTreesClassifier: Extra Trees Classifier (Классификатор на основе экстра-деревьев);
  12. GaussianNB: Gaussian Naive Bayes (Наивный байесовский классификатор с предположением о гауссовском распределении);
  13. GradientBoostingClassifier: Gradient Boosting Classifier (Классификатор на основе градиентного бустинга);
  14. HistGradientBoostingClassifier: Histogram-Based Gradient Boosting Classifier (Классификатор на основе гистограммного градиентного бустинга);
  15. KNeighborsClassifier: k-Nearest Neighbors Classifier (Классификатор k-ближайших соседей);
  16. LinearDiscriminantAnalysis: Linear Discriminant Analysis (Линейный дискриминантный анализ);
  17. LogisticRegression: Logistic Regression Classifier (Логистический регрессионный классификатор);
  18. LogisticRegressionCV: Logistic Regression Classifier with Cross-Validation (Логистический регрессионный классификатор с кросс-валидацией);
  19. MLPClassifier: Multi-Layer Perceptron Classifier (Классификатор многослойного персептрона);
  20. MultinomialNB: Multinomial Naive Bayes (Наивный байесовский классификатор с предположением о мультиномиальном распределении);
  21. PassiveAggressiveClassifier: Passive-Aggressive Classifier (Пассивно-агрессивный классификатор);
  22. Perceptron: Perceptron (Персептрон);
  23. RadiusNeighborsClassifier: Radius Neighbors Classifier (Классификатор на основе радиуса соседей);
  24. RandomForestClassifier: Random Forest Classifier (Случайный лес - классификатор);
  25. RidgeClassifier: Ridge Classifier (Классификатор на основе ридж-регрессии);
  26. RidgeClassifierCV: Ridge Classifier with Cross-Validation (Классификатор на основе ридж-регрессии с кросс-валидацией);
  27. SGDClassifier: Stochastic Gradient Descent Classifier (Классификатор стохастического градиентного спуска)

3. Дополнительно, в ходе исследования были выделены модели, которые продемонстрировали выдающуюся эффективность в задаче классификации данных Iris. Классификационные модели, такие как Random Forest Classifier, Gradient Boosting Classifier, Bagging Classifier, Decision Tree Classifier, Extra Tree Classifier, Extra Trees Classifier и Hist Gradient Boosting Classifier, достигли идеальной точности в предсказаниях. Это означает, что они способны с высокой точностью определить класс, к которому принадлежит каждый образец ириса в наборе данных.

Эти результаты могут быть особенно полезными при выборе наилучшей модели для конкретной задачи классификации. Модели, достигшие идеальной точности на данных Iris, могут оказаться идеальным выбором, если ваша задача связана с анализом или классификацией подобных данных.

Таким образом, проведенное исследование подчеркивает важность правильного выбора модели для конкретной задачи и позволяет выделить наиболее перспективные варианты для дальнейших исследований и применения в практических задачах.


Заключение

В статье были проанализированы 33 модели классификации на примере набора данных Iris, используя библиотеку Scikit-learn версии 1.2.2.

Из всех рассмотренных моделей, 6 оказались сложными для конвертации в формат ONNX. Эти модели включают в себя Dummy Classifier, Gaussian Process Classifier, Label Propagation Classifier, Label Spreading Classifier, Nearest Centroid Classifier и Quadratic Discriminant Analysis Classifier. Вероятно, их сложная структура или логика требуют дополнительной адаптации для успешной конвертации в формат ONNX.

Остальные 27 моделей были успешно преобразованы в формат ONNX и продемонстрировали сохранение своей точности. Это подтверждает, что ONNX является эффективным средством для сохранения и восстановления моделей машинного обучения, обеспечивая их переносимость и сохранение качества работы.

Важно отметить, что некоторые модели, такие как Random Forest Classifier, Gradient Boosting Classifier, Bagging Classifier, Decision Tree Classifier, Extra Tree Classifier, Extra Trees Classifier и Hist Gradient Boosting Classifier, достигли идеальной точности при классификации данных Iris. Эти модели могут быть особенно привлекательными для задач классификации, где высокая точность играет ключевую роль.

Данное исследование подчеркивает важность правильного выбора модели для конкретных задач и демонстрирует преимущества использования ONNX в сохранении и применении моделей машинного обучения для задач классификации.

Все скрипты из статьи также доступны в публичном проекте  MQL5\Shared Projects\Scikit.Classification.ONNX.