Классификационные модели библиотеки Scikit-learn и их экспорт в ONNX
Развитие технологий привело к появлению принципиально нового подхода к построению алгоритмов обработки данных. Ранее для решения каждой отдельной задачи требовалась четкая формализация и разработка соответствующих алгоритмов.
В машинном обучении компьютер сам учится находить наилучшие способы обработки данных. Модели машинного обучения способны успешно решать задачи классификации (есть фиксированный набор классов, найти вероятности принадлежности заданного набора признаков к каждому классу) и регрессии (по заданному набору признаков оценить численное значение целевой переменной). На базе этих основных звеньев могут быть построены более сложные модели обработки данных.
В библиотеке Scikit-learn доступно множество инструментов как для классификации, так и для регрессии. Выбор конкретных методов и моделей зависит от характеристик данных, так как разные методы могут обладать разной эффективностью и предоставлять разные результаты в зависимости от задачи.
В пресс-релизе "ONNX Runtime is now open source" утверждается, что ONNX Runtime также поддерживает профиль ONNX-ML :
Профиль ONNX-ML — это часть ONNX, созданная специально для моделей машинного обучения (ML). Он предназначен для описания и представления различных видов ML-моделей, таких как модели классификации, регрессии, кластеризации и других, в удобной форме, которую можно использовать на различных платформах и средах, поддерживающих ONNX. Профиль ONNX-ML упрощает передачу, развертывание и выполнение моделей машинного обучения, делая их более доступными и переносимыми.
В данной статье мы рассмотрим применение всех классификационных моделей пакета Scikit-learn для решения задачи классификации ирисов Фишера, попробуем их сконвертировать в ONNX-формат и использовать полученные модели в программах на MQL5.
Также мы сравним точность работы оригинальных моделей и их ONNX-версий на полном наборе Iris dataset.
Содержание
- 1. Ирисы Фишера
- 2. Модели для классификации
Список классификаторов пакета Scikit-learn
Различное представление выходных данных у моделей iris.mqh - 2.1. SVC Classifier
2.1.1. Код создания модели SVC Classifier
2.1.2. Код на MQL5 для работы с моделью SVC Classifier
2.1.3. ONNX-представление модели SVC Classifier - 2.2. LinearSVC Classifier
2.2.1. Код создания модели LinearSVC Classifier
2.2.2. Код на MQL5 для работы с моделью LinearSVC Classifier
2.2.3. ONNX-представление модели LinearSVC Classifier - 2.3. NuSVC Classifier
2.3.1. Код создания модели NuSVC Classifier
2.3.2. Код на MQL5 для работы с моделью NuSVC Classifier
2.3.3. ONNX-представление модели NuSVC Classifier - 2.4. Radius Neighbors Classifier
2.4.1. Код создания модели Radius Neighbors Classifier
2.4.2. Код на MQL5 для работы с моделью Radius Neighbors Classifier
2.3.3. ONNX-представление модели NuSVC Classifier - 2.5. Ridge Classifier
2.5.1. Код создания модели Ridge Classifier
2.5.2. Код на MQL5 для работы с моделью Ridge Classifier
2.5.3. ONNX-представление модели Ridge Classifier - 2.6. RidgeClassifierCV
2.6.1. Код создания модели RidgeClassifierCV
2.6.2. Код на MQL5 для работы с моделью RidgeClassifierCV
2.6.3. ONNX-представление модели RidgeClassifierCV - 2.7. Random Forest Classifier
2.7.1. Код создания модели Random Forest Classifier
2.7.2. Код на MQL5 для работы с моделью Random Forest Classifier
2.7.3. ONNX-представление модели Random Forest Classifier - 2.8. Gradient Boosting Classifier
2.8.1. Код создания модели Gradient Boosting Classifier
2.8.2. Код на MQL5 для работы с моделью Gradient Boosting Classifier
2.8.3. ONNX-представление модели Gradient Boosting Classifier - 2.9. Adaptive Boosting Classifier
2.9.1. Код создания модели Adaptive Boosting Classifier
2.9.2. Код на MQL5 для работы с моделью Adaptive Boosting Classifier
2.9.3. ONNX-представление модели Adaptive Boosting Classifier - 2.10. Bootstrap Aggregating Classifier
2.10.1. Код создания модели Bootstrap Aggregating Classifier
2.10.2. Код на MQL5 для работы с моделью Bootstrap Aggregating Classifier
2.10.3. ONNX-представление модели Bootstrap Aggregating Classifier - 2.11. K-Nearest Neighbors (K-NN) Classifier
2.11.1. Код создания модели K-Nearest Neighbors (K-NN) Classifier
2.11.2. Код на MQL5 для работы с моделью K-Nearest Neighbors (K-NN) Classifier
2.11.3. ONNX-представление модели K-Nearest Neighbors (K-NN) Classifier - 2.12. Decision Tree Classifier<
2.12.1. Код создания модели Decision Tree Classifier
2.12.2. Код на MQL5 для работы с моделью Decision Tree Classifier
2.12.3. ONNX-представление модели Decision Tree Classifier - 2.13. Logistic Regression Classifier
2.13.1. Код создания модели Logistic Regression Classifier
2.13.2. Код на MQL5 для работы с моделью Logistic Regression Classifier
2.13.3. ONNX-представление модели Logistic Regression Classifier - 2.14. LogisticRegressionCV Classifier
2.14.1. Код создания модели LogisticRegressionCV Classifier
2.14.2. Код на MQL5 для работы с моделью LogisticRegressionCV Classifier
2.14.3. ONNX-представление модели LogisticRegressionCV Classifier - 2.15. Passive-Aggressive (PA) Classifier
2.15.1. Код создания модели Passive-Aggressive (PA) Classifier
2.15.2. Код на MQL5 для работы с моделью Passive-Aggressive (PA) Classifier
2.15.3. ONNX-представление модели Passive-Aggressive (PA) Classifier - 2.16. Perceptron Classifier
2.16.1. Код создания модели Perceptron Classifier
2.16.2. Код на MQL5 для работы с моделью Perceptron Classifier
2.16.3. ONNX-представление модели Perceptron Classifier - 2.17. Stochastic Gradient Descent Classifier
2.17.1. Код создания модели Stochastic Gradient Descent Classifier
2.17.2. Код на MQL5 для работы с моделью Stochastic Gradient Descent Classifier
2.17.3. ONNX-представление модели Stochastic Gradient Descent Classifier - 2.18. Gaussian Naive Bayes (GNB) Classifier
2.18.1. Код создания модели Gaussian Naive Bayes (GNB) Classifier
2.18.2. Код на MQL5 для работы с моделью Gaussian Naive Bayes (GNB) Classifier
2.18.3. ONNX-представление модели Gaussian Naive Bayes (GNB) Classifier - 2.19. Multinomial Naive Bayes (MNB) Classifier
2.19.1. Код создания модели Multinomial Naive Bayes (MNB) Classifier
2.19.2. Код на MQL5 для работы с моделью Multinomial Naive Bayes (MNB) Classifier
2.19.3. ONNX-представление модели Multinomial Naive Bayes (MNB) Classifier - 2.20. Complement Naive Bayes (CNB) Classifier
2.20.1. Код создания модели Complement Naive Bayes (CNB) Classifier
2.20.2. Код на MQL5 для работы с моделью Complement Naive Bayes (CNB) Classifier
2.20.3. ONNX-представление модели Complement Naive Bayes (CNB) Classifier - 2.21. Bernoulli Naive Bayes (BNB) Classifier
2.21.1. Код создания модели Bernoulli Naive Bayes (BNB) Classifier
2.21.2. Код на MQL5 для работы с моделью Bernoulli Naive Bayes (BNB) Classifier
2.21.3. ONNX-представление модели Bernoulli Naive Bayes (BNB) Classifier - 2.22. Multilayer Perceptron Classifier
2.22.1. Код создания модели Multilayer Perceptron Classifier
2.22.2. Код на MQL5 для работы с моделью Multilayer Perceptron Classifier
2.22.3. ONNX-представление модели Multilayer Perceptron Classifier - 2.23. Linear Discriminant Analysis (LDA) Classifier
2.23.1. Код создания модели Linear Discriminant Analysis (LDA) Classifier
2.23.2. Код на MQL5 для работы с моделью Linear Discriminant Analysis (LDA) Classifier
2.23.3. ONNX-представление модели Linear Discriminant Analysis (LDA) Classifier - 2.24. Hist Gradient Boosting
2.24.1. Код создания модели Histogram-Based Gradient Boosting Classifier
2.24.2. Код на MQL5 для работы с моделью Histogram-Based Gradient Boosting Classifier
2.24.3. ONNX-представление модели Histogram-Based Gradient Boosting Classifier - 2.25. CategoricalNB Classifier
2.25.1. Код создания модели CategoricalNB Classifier
2.25.2. Код на MQL5 для работы с моделью CategoricalNB Classifier
2.25.3. ONNX-представление модели CategoricalNB Classifier - 2.26. ExtraTreeClassifier
2.26.1. Код создания модели ExtraTreeClassifier
2.26.2. Код на MQL5 для работы с моделью ExtraTreeClassifier
2.26.3. ONNX-представление модели ExtraTreeClassifier - 2.27. ExtraTreesClassifier
2.27.1. Код создания модели ExtraTreesClassifier
2.27.2. Код на MQL5 для работы с моделью ExtraTreesClassifier
2.27.3. ONNX-представление модели ExtraTreesClassifier - 2.28. Сравнение точности работы всех моделей
2.28.1. Код для расчета всех моделей и построения диаграммы сравнения точности
2.28.2. Код на MQL5 для исполнения всех ONNX-моделей - 2.29. Модели классификации Sckikit-Learn, которые не удалось сконвертировать в ONNX
- 2.29.1. DummyClassifier
2.29.1.1. Код создания модели DummyClassifier - 2.29.2. GaussianProcessClassifier
2.29.2.1. Код создания модели GaussianProcessClassifier - 2.29.3. LabelPropagation Classifier
2.29.3.1. Код создания модели LabelPropagationClassifier - 2.29.4. LabelSpreading Classifier
2.29.4.1. Код создания модели LabelSpreadingClassifier - 2.29.5. NearestCentroid Classifier
2.29.5.1. Код создания модели NearestCentroid - 2.29.6. Quadratic Discriminant Analysis Classifier
2.29.6.1.Код создания модели Quadratic Discriminant Analysis - Выводы
1. Ирисы Фишера
Набор данных Iris - это один из самых известных и широко используемых наборов данных в мире машинного обучения. Он был впервые представлен в 1936 году статистиком и биологом Р.А. Фишером и с тех пор стал классическим набором данных для задачи классификации.
Iris dataset представляет собой набор измерений чашелистиков и лепестков трех видов ирисов - Iris setosa, Iris virginica и Iris versicolor.
Рис.1. Iris setosa
Рис.2. Iris virginica
Рис.3. Iris versicolor
Iris dataset состоит из 150 экземпляров ирисов, по 50 экземпляров каждого из трех видов. Каждый экземпляр имеет четыре числовых признака (в сантиметрах):
- Длина чашелистика (sepal length)
- Ширина чашелистика (sepal width)
- Длина лепестка (petal length)
- Ширина лепестка (petal width)
Каждый экземпляр также имеет соответствующий класс, обозначающий вид ириса (Iris setosa, Iris virginica или Iris versicolor). Этот классификационный атрибут делает Iris dataset идеальным набором данных для задач машинного обучения, таких как классификация и кластеризация.
MetaEditor позволяет работать со скриптами на языке Python. Для создания Python-скрипта нужно в меню "File" MetaEditor выбрать "New". Появится диалоговое окно выбора создаваемого объекта (рис. 4).
Рис.4. Создание скрипта на Python в MQL5 Wizard - Шаг 1
Далее нужно указать имя скрипта, например IRIS.py
Рис.5. Создание скрипта на Python в MQL5 Wizard - Шаг 2 - Имя скрипта
После этого можно указать, какие библиотеки будут использоваться, в нашем случае оставим эти поля пустыми.
Рис.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
После этого на экране появятся графики:
Рис.8. Скрипт IRIS.py в MetaEditor c графиком Sepal Length/Sepal Width
Рис.9. Скрипт IRIS.py в MetaEditor c графиком Petal Length/Petal Width
Рассмотрим их подробнее.
Рис. 10. Диаграмма рассеяния для длины и ширины чашелистика (Scatter Plot Sepal Length vs Sepal Width)
На этой диаграмме мы видим, как разные виды ирисов распределены по длине и ширине чашелистика. Мы можем видеть, что Iris setosa обычно имеет более короткие и широкие чашелистики, чем другие два вида.
Рис.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 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, следует быть внимательным.
Для задачи классификации ирисов Фишера входные тензоры имеют одинаковый вид для всех этих моделей:
1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Выходные тензоры ONNX-моделей различаются.
1. Модели, не требующе пост-обработки
- SVC Classifier;
- LinearSVC Classifier;
- NuSVC Classifier;
- Radius Neighbors Classifier;
- Ridge Classifier;
- Ridge Classifier CV.
1. Name: label, Data Type: tensor(int64), Shape: [None]
2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Они возвращают результат (номер класса) в явном виде в первом выходном целочисленном тензоре label tensor(int64), не требуя пост-обработки.
2. Модели, результаты которых требуют пост-обработки:
- Random Forest Classifier;
- Gradient Boosting Classifier;
- AdaBoost Classifier;
- Bagging Classifier;
- K-NN_Classifier;
- Decision Tree Classifier;
- Logistic Regression Classifier;
- Logistic Regression CV Classifier;
- Passive-Aggressive Classifier;
- Perceptron Classifier;
- SGD Classifier;
- Gaussian Naive Bayes Classifier;
- Multinomial Naive Bayes Classifier;
- Complement Naive Bayes Classifier;
- Bernoulli Naive Bayes Classifier;
- Multilayer Perceptron Classifier;
- Linear Discriminant Analysis Classifier;
- Hist Gradient Boosting Classifier;
- Categorical Naive Bayes Classifier;
- ExtraTree Classifier;
- ExtraTrees Classifier.
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).
Принципы работы:
- SVC (Support Vector Classification)
Принцип работы: SVC является методом классификации, основанным на максимизации зазора между классами. Он ищет оптимальную разделяющую гиперплоскость, которая максимально отделяет классы и поддерживает опорные векторы - точки, ближайшие к гиперплоскости.
Функции ядра: SVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра определяет способ преобразования данных для поиска оптимальной гиперплоскости. - LinearSVC (Linear Support Vector Classification)
Принцип работы: LinearSVC - это вариант SVC, который специализируется на линейной классификации. Он ищет оптимальную линейную разделяющую гиперплоскость, не используя функции ядра. Это делает его быстрее и более эффективным в работе с большими объемами данных. - NuSVC (Nu Support Vector Classification)
Принцип работы: NuSVC также основан на методе опорных векторов, но вводит параметр Nu (nu), который контролирует сложность модели и долю опорных векторов. Значение Nu находится в интервале от 0 до 1 и определяет, какую долю данных можно использовать для опорных векторов и ошибок.
Преимущества:
- SVC
Мощный алгоритм: SVC может обрабатывать сложные задачи классификации и работать с нелинейными данными благодаря использованию функций ядра.
Устойчивость к выбросам: SVC устойчив к выбросам в данных, так как использует опорные векторы для построения разделяющей гиперплоскости. - LinearSVC
Высокая эффективность: LinearSVC быстрее и эффективнее в работе с большими объемами данных, особенно когда данных много и линейная разделяющая гиперплоскость подходит для задачи.
Линейная классификация: Если задача хорошо линейно разделима, LinearSVC может дать хорошие результаты без необходимости использования сложных функций ядра. - NuSVC
Контроль сложности модели: Параметр Nu в NuSVC позволяет контролировать сложность модели и баланс между подгонкой данных и обобщающей способностью.
Устойчивость к выбросам: Как и в случае с SVC, NuSVC устойчив к выбросам, что делает его полезным для задач с неточными данными.
Ограничения:
- SVC
Сложность вычислений: SVC может быть медленным на больших объемах данных и/или при использовании сложных функций ядра.
Чувствительность к выбору ядра: Выбор правильной функции ядра может быть сложной задачей и может сильно влиять на производительность модели. - LinearSVC
Ограничение на линейность: LinearSVC ограничен линейным разделением данных и может давать плохие результаты в случае нелинейных зависимостей между признаками и целевой переменной. - NuSVC
Настройка параметра Nu: Настройка параметра Nu может потребовать времени и экспериментов для достижения оптимальных результатов.
В зависимости от характеристик задачи и объема данных каждый из этих методов может оказаться наилучшим выбором. Важно провести эксперименты и выбрать метод, который наилучшим образом соответствует конкретным требованиям задачи классификации.
2.1. SVC Classifier
Метод классификации Support Vector Classification (SVC) - это мощный алгоритм машинного обучения, который широко применяется для решения задач классификации.
Принципы работы:
- Поиск оптимальной разделяющей гиперплоскости
Принцип работы: Основной идеей SVC является поиск оптимальной разделяющей гиперплоскости в признаковом пространстве. Эта гиперплоскость должна максимально отделять объекты разных классов и поддерживать опорные векторы - точки данных, ближайшие к гиперплоскости.
Максимизация зазора: SVC стремится максимизировать зазор между классами, то есть расстояние от опорных векторов до гиперплоскости. Это позволяет методу быть устойчивым к выбросам и обобщать хорошо на новые данные. - Использование функций ядра
Функции ядра: SVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра позволяет проецировать данные в более высокоразмерное пространство, где задача становится линейной, даже если в исходном пространстве данных нет линейной разделимости.
Выбор ядра: Выбор правильной функции ядра может сильно влиять на производительность модели SVC. Не всегда линейная гиперплоскость является оптимальным решением.
Преимущества:
- Мощный алгоритм. Обработка сложных задач: SVC способен решать сложные задачи классификации, включая задачи с нелинейными зависимостями между признаками и целевой переменной.
- Устойчивость к выбросам: Использование опорных векторов делает метод устойчивым к выбросам в данных. Он не зависит от всей выборки, а только от опорных векторов.
- Гибкость в выборе ядра. Адаптивность к данным: Возможность использования различных функций ядра позволяет адаптировать метод SVC к конкретным данным и искать нелинейные зависимости.
- Хорошая обобщающая способность. Обобщение на новые данные: Модель SVC способна обобщать на новые данные, что делает ее полезной для задач прогнозирования.
Ограничения:
- Сложность вычислений. Время обучения: SVC может быть медленным в обучении, особенно при использовании больших объемов данных или сложных функций ядра.
- Выбор ядра. Выбор оптимальной функции ядра: Выбор правильной функции ядра может потребовать экспериментов и зависит от характеристик данных.
- Чувствительность к масштабу признаков. Нормализация данных: SVC чувствителен к масштабу признаков, поэтому рекомендуется проводить нормализацию или стандартизацию данных перед обучением.
- Интерпретация модели. Сложность интерпретации: Модели 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
Результаты работы скрипта 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
Более подробную информацию об архитектуре модели можно посмотреть при помощи Netron, для этого в описании модели в MetaEditor нужно нажать кнопку "Open in Netron".
Рис.14. ONNX-модель svc_iris.onnx в Netron
Рис.15. ONNX-модель svc_iris.onnx в Netron (параметры ONNX-оператора SVMClassifier)
2.2. LinearSVC Classifier
LinearSVC (Linear Support Vector Classification, линейный метод опорных векторов) - это мощный алгоритм машинного обучения, используемый для задач бинарной и многоклассовой классификации. Он основан на идее поиска гиперплоскости, которая наилучшим образом разделяет данные.
Принципы работы LinearSVC:
- Поиск оптимальной гиперплоскости: Основная идея LinearSVC заключается в поиске оптимальной гиперплоскости, которая максимально разделяет два класса данных. Гиперплоскость - это многомерная плоскость, которая определяется как линейное уравнение.
- Минимизация отступов: LinearSVC стремится минимизировать отступы (расстояния от точек данных до гиперплоскости). Чем больше отступы, тем более надежно гиперплоскость разделяет классы.
- Работа с линейно неразделимыми данными: LinearSVC может работать с данными, которые не могут быть линейно разделены в исходном пространстве, благодаря применению функций ядра (kernel trick), которые позволяют проецировать данные в более высокоразмерное пространство, где они могут быть линейно разделены.
Преимущества LinearSVC:
- Хорошая обобщающая способность: LinearSVC обладает хорошей способностью к обобщению и может давать хорошие результаты на новых, ранее не виденных данных.
- Эффективность: LinearSVC работает быстро на больших наборах данных и требует относительно мало вычислительных ресурсов.
- Работа с линейно неразделимыми данными: Благодаря использованию функций ядра, LinearSVC может решать задачи классификации с линейно неразделимыми данными.
- Масштабируемость: LinearSVC может быть эффективно использован в задачах с большим количеством признаков и большими объемами данных.
Ограничения LinearSVC:
- Только линейные разделяющие гиперплоскости: LinearSVC строит только линейные гиперплоскости разделения, что может быть недостаточно для сложных задач классификации с нелинейными зависимостями.
- Выбор параметров: Выбор правильных параметров (например, параметра регуляризации) может потребовать экспертных знаний или кросс-валидации.
- Чувствительность к выбросам: 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
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
2.3. NuSVC Classifier
Метод классификации Nu-Support Vector Classification (NuSVC) представляет собой мощный алгоритм машинного обучения, основанный на методе опорных векторов (Support Vector Machine, SVM).
Принципы работы NuSVC:
- Метод опорных векторов (SVM): NuSVC является разновидностью SVM, который используется для решения задач бинарной и многоклассовой классификации. Основным принципом SVM является поиск оптимальной разделяющей гиперплоскости, которая максимально отделяет классы и имеет максимальный зазор между ними.
- Параметр Nu: Один из ключевых параметров NuSVC - это параметр Nu (nu), который контролирует сложность модели и определяет долю выборки, которая может быть использована в качестве опорных векторов и ошибок. Значение Nu находится в интервале от 0 до 1, где 0.5 означает, что примерно половина выборки будет использована как опорные векторы и ошибки.
- Подбор параметров: Определение оптимальных значений параметра Nu и других гиперпараметров может потребовать кросс-валидации и поиска наилучших значений на обучающих данных.
- Функции ядра: NuSVC может использовать различные функции ядра, такие как линейная, радиальная базисная функция (RBF), полиномиальная и другие. Функция ядра определяет способ преобразования признакового пространства для поиска разделяющей гиперплоскости.
Преимущества NuSVC:
- Эффективность в многомерных пространствах: NuSVC может работать эффективно в пространствах высокой размерности, что делает его подходящим для задач с большим количеством признаков.
- Устойчивость к выбросам: SVM и NuSVC в частности, устойчивы к выбросам в данных благодаря использованию опорных векторов.
- Контроль сложности модели: Параметр Nu позволяет контролировать сложность модели и управлять балансом между подгонкой данных и обобщающей способностью.
- Хорошая обобщающая способность: SVM и NuSVC, в частности, имеют хорошую обобщающую способность, что позволяет получать хорошие результаты на новых, ранее не виденных данных.
Ограничения NuSVC:
- Неэффективность с большими объемами данных: NuSVC может быть неэффективен при обучении на больших объемах данных из-за сложности вычислений.
- Необходимость подбора параметров: Настройка параметров Nu и функции ядра может потребовать времени и вычислительных ресурсов.
- Линейность функции ядра: Эффективность NuSVC может сильно зависеть от выбора функции ядра, и для некоторых задач может потребоваться экспериментировать с разными функциями.
- Сложность интерпретации: SVM и 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
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
2.4. Radius Neighbors Classifier
Radius Neighbors Classifier - это метод машинного обучения, который также используется для задач классификации и основан на принципе близости объектов. В отличие от классического K-Nearest Neighbors (K-NN) Classifier, где выбирается фиксированное количество ближайших соседей (K), в Radius Neighbors Classifier объекты классифицируются на основе расстояния до ближайших соседей, находящихся внутри определенного радиуса.Принципы работы Radius Neighbors Classifier:
- Определение радиуса: Основной параметр Radius Neighbors Classifier - это радиус, который определяет максимальное расстояние между объектом и его соседями, при котором объект считается близким к классу соседей.
- Поиск ближайших соседей: Для каждого объекта вычисляется расстояние до всех других объектов обучающего набора. Те объекты, которые находятся внутри заданного радиуса, считаются соседями данного объекта.
- Голосование: Radius Neighbors Classifier использует голосование большинства среди соседей для определения класса объекта. Например, если большинство соседей принадлежат к классу A, то объект также будет классифицирован как класс A.
- Адаптивность к плотности данных: Radius Neighbors Classifier подходит для задач, в которых плотность данных в разных областях пространства признаков может быть разной.
- Способность работать с разными формами классов: Этот метод хорошо справляется с задачами, где классы имеют сложные и нелинейные формы.
- Подходит для данных с выбросами: Radius Neighbors Classifier более устойчив к выбросам, чем K-NN, так как он игнорирует соседей, находящихся за пределами радиуса.
- Чувствительность к выбору радиуса: Выбор оптимального значения радиуса может быть нетривиальной задачей и требует настройки.
- Неэффективность на больших наборах данных: При больших объемах данных вычисление расстояний до всех объектов может быть вычислительно затратным.
- Зависимость от плотности данных: Этот метод может быть менее эффективным, если данные имеют неравномерную плотность в пространстве признаков.
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
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
Примечание к методам 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 моделирует вероятность принадлежности объекта к определенному классу с использованием логистической (сигмоидальной) функции.
- Регуляризация L2: В Ridge Classifier добавляется регуляризационный член L2, который штрафует большие значения коэффициентов модели. Это делается для контроля сложности модели и уменьшения переобучения.
- Обучение параметров: Модель Ridge Classifier обучается на обучающем наборе данных с целью настройки весов (коэффициентов) при признаках и параметра регуляризации.
- Снижение переобучения: Регуляризация L2 помогает снизить склонность модели к переобучению, что особенно полезно, когда данных недостаточно.
- Устойчивость к мультиколлинеарности: Ridge Classifier хорошо справляется с проблемой мультиколлинеарности в данных, когда признаки сильно коррелируют между собой.
- Чувствительность к выбору параметра регуляризации: Как и в случае других методов с регуляризацией, выбор правильного значения параметра регуляризации (alpha) требует настройки и оценки.
- Ограничение на многоклассовую классификацию: Ridge Classifier изначально предназначен для бинарной классификации, но может быть адаптирован для многоклассовой классификации с использованием подходов, таких как One-vs-All.
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
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
2.6. RidgeClassifierCV
Метод классификации RidgeClassifierCV представляет собой мощный алгоритм бинарной и многоклассовой классификации, основанный на регрессии Риджа (Ridge Regression).
Принципы работы RidgeClassifierCV:
- Линейная регрессия Риджа: RidgeClassifierCV базируется на линейной регрессии Риджа. Этот метод представляет собой модификацию линейной регрессии, где добавляется L2-регуляризация. Регуляризация помогает контролировать переобучение, уменьшая амплитуду весовых коэффициентов признаков.
- Бинарная и многоклассовая классификация: RidgeClassifierCV может использоваться как для бинарной классификации (когда есть только два класса), так и для многоклассовой классификации (когда есть более двух классов). Для многоклассовой классификации метод преобразует задачу в несколько бинарных задач (один против всех) и строит модель для каждой из них.
- Автоматический выбор параметра регуляризации: Одним из ключевых преимуществ RidgeClassifierCV является встроенная поддержка для кросс-валидации и автоматического выбора оптимального значения параметра регуляризации alpha. Вместо того, чтобы ручным образом подбирать alpha, метод перебирает различные значения alpha и выбирает наилучшее на основе кросс-валидации.
- Устойчивость к мультиколлинеарности: Ridge регрессия хорошо справляется с проблемой мультиколлинеарности, когда признаки сильно коррелированы друг с другом. Регуляризация позволяет контролировать вклад каждого признака, делая модель устойчивой к коррелированным данным.
Преимущества RidgeClassifierCV:
- Автоматический выбор гиперпараметров: Одним из важных преимуществ RidgeClassifierCV является возможность автоматического выбора оптимального значения alpha с использованием кросс-валидации. Это освобождает от необходимости экспериментировать с различными значениями alpha и повышает вероятность получения хороших результатов.
- Устойчивость к переобучению: Регуляризация L2, предоставляемая RidgeClassifierCV, помогает контролировать сложность модели и уменьшает риск переобучения. Это особенно важно в задачах с ограниченным объемом данных.
- Прозрачность и интерпретируемость: RidgeClassifierCV предоставляет интерпретируемые весовые коэффициенты для каждого признака, что позволяет анализировать вклад каждого признака в прогноз и делать выводы о важности признаков.
- Эффективность: Метод имеет высокую эффективность и может быть применен к большим объемам данных.
Ограничения RidgeClassifierCV:
- Линейность: RidgeClassifierCV предполагает линейные зависимости между признаками и целевой переменной. Если данные имеют сильные нелинейные зависимости, метод может быть недостаточно точным.
- Чувствительность к масштабу признаков: Метод чувствителен к масштабу признаков. Рекомендуется провести стандартизацию или нормализацию признаков перед применением 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
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
2.7. Random Forest Classifier
Random Forest Classifier - это ансамблевый метод машинного обучения, который основан на построении нескольких деревьев решений и объединении их результатов для улучшения качества классификации. Этот метод чрезвычайно популярен благодаря своей эффективности и способности работать с разнообразными данными.Принципы работы Random Forest Classifier:
- Баггинг (Bootstrap Aggregating): Random Forest использует метод баггинга, который состоит в создании нескольких подвыборок (bootstrap samples) из обучающих данных с повторением. Для каждой подвыборки строится отдельное дерево решений.
- Случайный выбор признаков: При построении каждого дерева случайным образом выбирается подмножество признаков из всего набора признаков. Это способствует разнообразию деревьев и уменьшает корреляцию между ними.
- Голосование: При классификации объекта каждое дерево дает свой собственный прогноз, и класс, который получает большинство голосов среди всех деревьев, выбирается как итоговый прогноз модели.
- Высокая точность: Random Forest обычно обеспечивает высокую точность классификации за счет усреднения результатов множества деревьев.
- Способность работать с разнообразными данными: Он хорошо работает как с числовыми, так и с категориальными признаками, а также с данными различной структуры.
- Устойчивость к переобучению: Random Forest обладает встроенной регуляризацией, что делает его устойчивым к переобучению.
- Важность признаков: Random Forest может оценить важность признаков, что помогает исследователям и инженерам признаков лучше понимать данные.
- Вычислительная сложность: Обучение Random Forest может быть времязатратным, особенно при большом количестве деревьев и признаков.
- Сложность интерпретации: Из-за большого количества деревьев и случайного выбора признаков интерпретация модели может быть сложной.
- Не гарантирует устойчивость к выбросам: Random Forest не всегда обладает хорошей устойчивостью к выбросам в данных.
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
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
2.8. Gradient Boosting Classifier
Градиентный бустинг является одним из наиболее мощных методов машинного обучения и находит применение в различных областях, включая анализ данных, компьютерное зрение, естественный язык и финансовый анализ, благодаря своей способности к высокой точности и способности работать с разнообразными данными.
Gradient Boosting Classifier - это ансамблевый метод машинного обучения, который строит композицию деревьев решений для решения задач классификации. Этот метод популярен благодаря своей способности к достижению высокой точности и устойчивости к переобучению.
Принципы работы Gradient Boosting Classifier:
- Ансамбль деревьев решений: Gradient Boosting Classifier строит композицию (ансамбль) деревьев решений, где каждое дерево старается улучшить предсказания предыдущего дерева.
- Градиентный спуск: Градиентный бустинг использует градиентный спуск для оптимизации функции потерь. Он минимизирует ошибку классификации, вычисляя градиент функции потерь и обновляя предсказания с учетом этого градиента.
- Взвешивание деревьев: Каждое дерево в композиции имеет вес, и в конечном итоге прогнозы всех деревьев объединяются с учетом их весов.
Преимущества Gradient Boosting Classifier:
- Высокая точность: Gradient Boosting Classifier обычно обеспечивает высокую точность классификации и является одним из наиболее мощных методов машинного обучения.
- Устойчивость к переобучению: Благодаря использованию регуляризации и градиентного спуска, этот метод хорошо устойчив к переобучению, особенно при настройке гиперпараметров.
- Способность к работе с разными типами данных: Gradient Boosting Classifier может работать с различными типами данных, включая числовые и категориальные признаки.
Ограничения Gradient Boosting Classifier:
- Вычислительная сложность: Обучение Gradient Boosting Classifier может быть вычислительно затратным, особенно если используется большое количество деревьев или глубокие деревья.
- Сложность интерпретации: Из-за сложности композиции из множества деревьев интерпретация результатов может быть сложной.
- Не всегда подходит для малых наборов данных: Gradient Boosting обычно требует большого объема данных для эффективной работы, и на малых наборах данных он может быть склонен к переобучению.
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
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
2.9. Adaptive Boosting Classifier
AdaBoost (Adaptive Boosting) Classifier - это ансамблевый метод машинного обучения, который используется для улучшения классификации, путем комбинирования результатов нескольких слабых (например, деревьев решений) классификаторов для создания более сильного алгоритма.Принципы работы AdaBoost Classifier:
- Ансамбль слабых классификаторов: AdaBoost начинается с инициализации каждого образца в обучающем наборе весами, присваивая им одинаковые начальные значения.
- Обучение слабых классификаторов: Затем AdaBoost обучает слабый классификатор (например, дерево решений) на обучающем наборе с учетом весов образцов. Этот классификатор пытается правильно классифицировать образцы.
- Перераспределение весов: AdaBoost изменяет веса образцов, повышая веса неправильно классифицированных образцов и уменьшая веса правильно классифицированных образцов.
- Создание композиции: AdaBoost повторяет процесс обучения слабых классификаторов и перераспределения весов многократно. Затем результаты этих слабых классификаторов объединяются в композицию, где каждый классификатор вносит вклад с учетом его точности.
- Высокая точность: AdaBoost обычно обеспечивает высокую точность классификации за счет комбинации нескольких слабых классификаторов.
- Устойчивость к переобучению: AdaBoost обладает встроенной регуляризацией, что делает его устойчивым к переобучению.
- Способность работать с разными классификаторами: AdaBoost может использовать разные базовые классификаторы, что позволяет адаптировать его под конкретную задачу.
- Чувствительность к выбросам: AdaBoost может быть чувствителен к выбросам в данных, так как они могут иметь большой вес.
- Не всегда подходит для сложных задач: В некоторых сложных задачах AdaBoost может потребовать большого количества базовых классификаторов для достижения хороших результатов.
- Зависимость от качества базовых классификаторов: AdaBoost работает лучше, когда базовые классификаторы лучше чем случайные угадывания.
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
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
2.10. Bootstrap Aggregating Classifier
Bagging (Bootstrap Aggregating) Classifier - это ансамблевый метод машинного обучения, который основан на создании нескольких подвыборок (bootstrap samples) из обучающих данных и построении отдельных моделей на каждой из них, а затем комбинировании результатов для улучшения обобщающей способности модели.Принципы работы Bagging Classifier:
- Создание подвыборок: Bagging начинается с создания нескольких случайных подвыборок (bootstrap samples) из обучающих данных с повторением. Это означает, что одни и те же образцы могут появиться в нескольких подвыборках, а некоторые образцы могут быть пропущены.
- Обучение базовых моделей: На каждой подвыборке обучается отдельная базовая модель (например, дерево решений). Каждая модель обучается независимо от других моделей.
- Агрегация результатов: После обучения всех базовых моделей результаты их предсказаний комбинируются для получения итогового прогноза. В случае бинарной классификации, это может быть сделано путем голосования большинства.
- Уменьшение дисперсии: Bagging уменьшает дисперсию модели путем усреднения результатов нескольких базовых моделей, что может привести к более устойчивым и надежным прогнозам.
- Снижение переобучения: Поскольку каждая базовая модель обучается на разных подвыборках, Bagging может уменьшить склонность модели к переобучению.
- Универсальность: Bagging может использовать различные базовые модели, что позволяет адаптировать его к разным типам данных и задачам.
- Не улучшает смещение: Bagging склонен уменьшать дисперсию, но не смещение модели. Если базовые модели склонны к смещению (например, недообучены), Bagging не исправит эту проблему.
- Не всегда подходит для сложных задач: В некоторых сложных задачах Bagging может потребовать большого количества базовых моделей для достижения хороших результатов.
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
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
2.11. K-Nearest Neighbors (K-NN) Classifier
K-Nearest Neighbors (K-NN) Classifier - это метод машинного обучения, который используется для решения задач классификации и регрессии на основе близости между объектами данных. Он основан на принципе, что объекты, находящиеся близко друг к другу в многомерном признаковом пространстве, имеют схожие характеристики и, следовательно, могут иметь схожие метки классов.
Принципы работы K-NN Classifier:
- Определение близости: K-NN классификатор определяет близость между объектом, который нужно классифицировать, и остальными объектами обучающего набора. Это часто делается с использованием метрики расстояния, такой как евклидово расстояние или манхэттенское расстояние.
- Выбор числа соседей: Параметр K определяет количество ближайших соседей, которые будут использоваться для классификации объекта. Обычно K выбирается исходя из задачи и данных.
- Голосование: K-NN использует голосование большинства среди K ближайших соседей для определения класса объекта. Например, если большинство из K соседей принадлежат к классу A, то объект также будет классифицирован как класс A.
Преимущества K-NN Classifier:
- Простота и интуитивность: K-NN - это простой и интуитивно понятный метод, который легко понять и применить.
- Способность работать с различными типами данных: K-NN может использоваться для разных типов данных, включая числовые, категориальные и текстовые.
- Адаптивность к изменению данных: K-NN может быстро адаптироваться к изменению данных, что делает его подходящим для задач с динамическими данными.
Ограничения K-NN Classifier:
- Чувствительность к выбору K: Выбор оптимального значения K может быть нетривиальной задачей. Слишком маленькое K может привести к переобучению, а слишком большое - к недообучению.
- Чувствительность к масштабированию: K-NN чувствителен к масштабированию признаков, поэтому предварительная нормализация данных может быть важной.
- Вычислительная сложность: При большом объеме данных и большом числе признаков вычисление расстояний между всеми парами объектов может быть вычислительно затратным.
- Недостаток интерпретируемости: Результаты K-NN могут быть сложными для интерпретации, особенно если K велико и данных много.
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
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
2.12. Decision Tree Classifier
Decision Tree Classifier - это метод машинного обучения, который используется для задач классификации, основанный на построении дерева решений. Этот метод разделяет набор данных на более мелкие подгруппы путем выполнения серии условных тестов на признаках и определяет класс объекта на основе пути, который он проходит в дереве.
Принципы работы Decision Tree Classifier:
- Построение дерева решений: Начально все данные представлены в корне дерева. Для каждого узла дерева выполняется разделение данных на две или более подгруппы, основываясь на значениях одного из признаков. Это делается так, чтобы максимально уменьшить неопределенность (например, энтропию или Gini-индекс) в каждой подгруппе.
- Рекурсивное построение: Процесс разделения данных выполняется рекурсивно до достижения листьев дерева. Листья представляют собой конечные классы объектов.
- Принятие решения: Когда объект попадает в дерево, он следует пути от корня к одному из листьев, где определяется его класс на основе большинства объектов в данном листе.
- Простота интерпретации: Деревья решений легко интерпретируются и визуализируются. Решающие правила, используемые для классификации, понятны.
- Работа с различными типами данных: 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
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
Примечание к моделям 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:
- Предсказание вероятности: Логистическая регрессия моделирует вероятность принадлежности объекта к определенному классу с использованием логистической (сигмоидальной) функции.
- Определение границы решения: На основе предсказанных вероятностей логистическая регрессия определяет границу решения, которая разделяет классы. Если вероятность превышает определенный порог (обычно 0.5), объект классифицируется как принадлежащий к одному классу, в противном случае - к другому.
- Обучение параметров: Модель логистической регрессии обучается на обучающем наборе данных путем настройки весов (коэффициентов) при признаках так, чтобы минимизировать функцию потерь.
- Простота и интерпретируемость: Логистическая регрессия - это простая модель, легко интерпретируемая с точки зрения влияния признаков на предсказание классов.
- Эффективность на больших объемах данных: Логистическая регрессия может эффективно обрабатывать большие объемы данных и обучаться на них быстро.
- Использование в ансамблевых методах: Логистическая регрессия может быть использована как базовый классификатор в ансамблевых методах, таких как стекинг.
- Линейность: Логистическая регрессия предполагает линейную зависимость между признаками и логарифмом шансов, что может быть недостаточно для сложных задач.
- Ограничение на многоклассовую классификацию: В исходной форме логистическая регрессия предназначена для бинарной классификации, но существуют методы расширения ее до многоклассовой классификации, такие как One-vs-All (One-vs-Rest).
- Чувствительность к выбросам: Логистическая регрессия может быть чувствительной к выбросам в данных.
Логистическая регрессия - это классический метод машинного обучения, который широко используется в практике для задач классификации, особенно в случаях, когда важна интерпретируемость модели и данные имеют линейную или почти линейную структуру. Она также используется в статистике и медицинском анализе данных для оценки влияния факторов на вероятность событий.
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
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
2.14. LogisticRegressionCV Classifier
LogisticRegressionCV (Logistic Regression с кросс-валидацией) представляет собой мощный и гибкий метод бинарной классификации. Этот метод не только позволяет вам создавать модели классификации на основе логистической регрессии, но и автоматически настраивать параметры для достижения наилучшей производительности.
Принципы работы LogisticRegressionCV:
- Логистическая регрессия: В основе метода LogisticRegressionCV лежит логистическая регрессия. Логистическая регрессия - это статистический метод, используемый для моделирования вероятности принадлежности объекта к одному из двух классов. Эта модель применяется, когда зависимая переменная является бинарной (два класса) или когда она может быть преобразована в бинарную.
- Кросс-валидация: Основное преимущество LogisticRegressionCV заключается в интегрированной кросс-валидации. Это означает, что вместо того, чтобы вручную подбирать оптимальное значение параметра регуляризации C, метод автоматически перебирает различные значения C и выбирает тот, который дает наилучшую производительность на кросс-валидации.
- Выбор оптимального C: LogisticRegressionCV использует стратегию кросс-валидации, чтобы оценить производительность модели при разных значениях C. C - это параметр регуляризации, который контролирует степень регуляризации модели. Маленькое значение C означает сильную регуляризацию, а большое значение C - слабую. Кросс-валидация позволяет выбрать оптимальное значение C для баланса между недообучением и переобучением.
- Регуляризация: LogisticRegressionCV также поддерживает различные типы регуляризации, включая L1 (лассо) и L2 (гребневую) регуляризацию. Эти виды регуляризации помогают улучшить обобщающую способность модели и предотвратить переобучение.
Преимущества LogisticRegressionCV:
- Автоматическая настройка параметров: Одним из главных преимуществ LogisticRegressionCV является его способность автоматически выбирать оптимальное значение параметра C с использованием кросс-валидации. Это снимает с вас бремя ручной настройки модели и позволяет сосредоточиться на данных и задаче.
- Устойчивость к переобучению: Регуляризация, поддерживаемая LogisticRegressionCV, помогает управлять сложностью модели и уменьшить риск переобучения, особенно при наличии малого объема данных.
- Прозрачность: Логистическая регрессия - это метод с хорошей интерпретируемостью. Вы можете анализировать вклад каждого признака в прогноз, что полезно для понимания важности признаков.
- Высокая производительность: Логистическая регрессия может работать быстро и эффективно, особенно когда данных много.
Ограничения 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
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
2.15. Passive-Aggressive (PA) Classifier
Passive-Aggressive (PA) Classifier - это метод машинного обучения, который используется для задач классификации. Основная идея этого метода заключается в том, чтобы адаптировать веса (коэффициенты) модели в процессе обучения, чтобы минимизировать ошибку классификации. Passive-Aggressive Classifier может быть полезен в задачах онлайн-обучения и в ситуациях, когда данные меняются со временем.Принципы работы Passive-Aggressive Classifier:
- Адаптация весов: Вместо того чтобы обновлять веса модели в направлении минимизации функции потерь, как это делается в методе стохастического градиентного спуска, Passive-Aggressive Classifier адаптирует веса в направлении, которое минимизирует ошибку классификации для текущего примера.
- Сохранение агрессивности: Метод имеет параметр, который называется агрессивностью (C), который определяет, насколько сильно нужно адаптировать веса модели. Большие значения C делают метод более агрессивным в адаптации, а маленькие - менее агрессивным.
- Подходит для онлайн-обучения: Passive-Aggressive Classifier может обновляться по мере поступления новых данных, что делает его подходящим для задач онлайн-обучения, где данные поступают потоком.
- Адаптивность к изменению данных: Метод хорошо справляется с изменяющимися данными, так как он адаптирует модель к новым обстоятельствам.
- Чувствительность к выбору параметра агрессивности: Выбор оптимального значения параметра C может потребовать настройки и зависит от характеристик данных.
- Не всегда подходит для сложных задач: 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
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
2.16. Perceptron Classifier
Perceptron Classifier - это линейный бинарный классификатор, который используется для разделения двух классов на основе линейной разделяющей гиперплоскости. Он является одним из самых простых и старых методов машинного обучения, и его основной принцип - это обучение весов (коэффициентов) модели так, чтобы максимизировать правильность классификации на обучающем наборе данных.Принципы работы Perceptron Classifier:
- Линейная гиперплоскость: Perceptron строит линейную гиперплоскость в многомерном пространстве признаков, которая разделяет два класса. Эта гиперплоскость определяется весами (коэффициентами) модели.
- Обучение весов: Начально веса инициализируются случайным образом или нулями. Затем для каждого объекта обучающего набора модель предсказывает класс на основе текущих весов и корректирует их в случае ошибки. Обучение продолжается до тех пор, пока все объекты не будут классифицированы правильно или пока не будет достигнуто максимальное количество итераций.
- Простота: Perceptron - это очень простой алгоритм, легко понимаемый и реализуемый.
- Высокая скорость обучения: Perceptron может обучаться быстро, особенно на больших объемах данных, и он может использоваться в задачах онлайн-обучения.
- Ограничение на линейную разделимость: Perceptron работает хорошо только в случаях, когда данные линейно разделимы. Если данные не могут быть разделены линейно, то Perceptron не сможет достичь высокой точности.
- Чувствительность к выбору начальных весов: Начальное приближение весов может влиять на сходимость алгоритма. Плохой выбор начальных весов может привести к медленной сходимости или даже к нейрону, который не сможет корректно разделить классы.
- Невозможность определения вероятностей: Perceptron не предоставляет оценок вероятности принадлежности к классам, что может быть важным для некоторых задач.
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
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
2.17. Stochastic Gradient Descent Classifier
SGD Classifier (Stochastic Gradient Descent Classifier) - это метод машинного обучения, который используется для задач классификации. Он является частным случаем линейных моделей и представляет собой линейный классификатор, который обучается с использованием стохастического градиентного спуска.Принципы работы SGD Classifier:
- Линейная гиперплоскость: SGD Classifier строит линейную гиперплоскость в многомерном пространстве признаков, которая разделяет два класса. Гиперплоскость определяется весами (коэффициентами) модели.
- Стохастический градиентный спуск: Метод обучается с использованием стохастического градиентного спуска, что означает, что обновление весов модели выполняется на каждом объекте обучающего набора (или на случайно выбранном подмножестве), а не на всем наборе данных. Это делает SGD Classifier подходящим для больших объемов данных и онлайн-обучения.
- Функция потерь: SGD Classifier оптимизирует функцию потерь, такую как логистическая функция потерь для задач бинарной классификации или функция потерь softmax для многоклассовой классификации.
- Скорость обучения: SGD Classifier обучается быстро, особенно на больших объемах данных, благодаря стохастическому градиентному спуску.
- Подходит для онлайн-обучения: Метод хорошо подходит для задач онлайн-обучения, где данные поступают потоком и модель нужно обновлять по мере их поступления.
- Чувствительность к параметрам: SGD Classifier имеет много гиперпараметров, таких как скорость обучения (learning rate) и параметр регуляризации, которые требуют тщательной настройки.
- Инициализация весов: Начальное приближение весов может влиять на сходимость и качество модели.
- Сходимость к локальным минимумам: Из-за стохастической природы метода 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
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
2.18. Gaussian Naive Bayes (GNB) Classifier
Gaussian Naive Bayes (GNB) Classifier - это метод машинного обучения, основанный на байесовской вероятностной модели и используемый для задач классификации. Он является частью семейства наивных байесовских классификаторов и предполагает, что все признаки независимы и имеют нормальное распределение.Принципы работы Gaussian Naive Bayes Classifier:
- Байесовский подход: GNB основан на байесовском подходе к классификации, который использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
- Наивное предположение: Главное предположение, сделанное в GNB, состоит в том, что все признаки являются независимыми и имеют нормальное (гауссовское) распределение. Это наивное предположение, потому что в реальных данных признаки часто коррелируют между собой.
- Обучение параметров: Модель GNB обучается на обучающем наборе данных, вычисляя параметры распределения (среднее и стандартное отклонение) для каждого признака в каждом классе.
- Простота и скорость обучения: GNB - это очень простой алгоритм и обучается быстро даже на больших объемах данных.
- Эффективность для небольших и средних размеров данных: GNB может быть эффективным для задач классификации с небольшим или средним количеством признаков, особенно когда предположение о нормальном распределении признаков хорошо выполняется.
- Наивное предположение: Предположение о независимости признаков и нормальном распределении может быть слишком упрощенным и неверным для реальных данных. Это может привести к снижению точности классификации.
- Чувствительность к выбросам: GNB может быть чувствительным к выбросам в данных, так как они могут значительно исказить параметры нормального распределения.
- Неспособность учесть зависимости между признаками: Из-за предположения о независимости GNB не учитывает зависимости между признаками.
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
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
2.19. Multinomial Naive Bayes (MNB) Classifier
Multinomial Naive Bayes (MNB) Classifier - это метод машинного обучения, основанный на байесовской вероятностной модели, и используется для задач классификации, особенно в текстовой обработке. Он является одним из вариантов наивных байесовских классификаторов и предполагает, что признаки представляют собой счетчики, такие как количество слов в тексте.Принципы работы Multinomial Naive Bayes Classifier:
- Байесовский подход: MNB также основан на байесовском подходе к классификации и использует теорему Байеса для вычисления вероятности принадлежности объекта к каждому классу.
- Предположение о мультиномиальном распределении: Основное предположение MNB заключается в том, что признаки представляют собой счетчики, такие как количество вхождений слов в тексте, и имеют мультиномиальное распределение. Это предположение часто справедливо для текстовых данных.
- Обучение параметров: Модель MNB обучается на обучающем наборе данных, вычисляя параметры распределения для каждого признака в каждом классе.
- Эффективность в текстовой обработке: MNB хорошо работает в задачах анализа текстовых данных, таких как классификация текстов или фильтрация спама, благодаря предположению о счетчиках признаков.
- Простота и скорость обучения: Как и другие наивные байесовские классификаторы, MNB - это простой алгоритм, который обучается быстро, даже на больших объемах текстовых данных.
- Наивное предположение: Предположение о мультиномиальном распределении признаков может быть слишком упрощенным и неверным для реальных данных, особенно в случаях, когда признаки имеют сложную структуру.
- Неспособность учесть порядок слов: MNB не учитывает порядок слов в тексте, что может быть важным в некоторых задачах анализа текста.
- Чувствительность к редким словам: MNB может быть чувствительным к редким словам, и их недостаточное количество может снизить точность классификации.
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
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]